2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
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
45 from samba.auth import system_session, admin_session
47 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
50 check_all_substituted,
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.sambadns import setup_ad_dns, create_dns_update_list
85 from samba.schema import Schema
86 from samba.samdb import SamDB
87 from samba.dbchecker import dbcheck
90 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
91 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
92 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
93 DEFAULTSITE = "Default-First-Site-Name"
94 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
98 """Return an absolute path to the provision tempate file specified by file"""
99 return os.path.join(samba.param.setup_dir(), file)
102 class ProvisionPaths(object):
105 self.shareconf = None
116 self.dns_keytab = None
119 self.private_dir = None
122 class ProvisionNames(object):
129 self.ldapmanagerdn = None
130 self.dnsdomain = None
132 self.netbiosname = None
138 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
139 """Get key provision parameters (realm, domain, ...) from a given provision
141 :param samdb: An LDB object connected to the sam.ldb file
142 :param secretsdb: An LDB object connected to the secrets.ldb file
143 :param idmapdb: An LDB object connected to the idmap.ldb file
144 :param paths: A list of path to provision object
145 :param smbconf: Path to the smb.conf file
146 :param lp: A LoadParm object
147 :return: A list of key provision parameters
149 names = ProvisionNames()
150 names.adminpass = None
152 # NT domain, kerberos realm, root dn, domain dn, domain dns name
153 names.domain = string.upper(lp.get("workgroup"))
154 names.realm = lp.get("realm")
155 names.dnsdomain = names.realm.lower()
156 basedn = samba.dn_from_dns_name(names.dnsdomain)
157 names.realm = string.upper(names.realm)
159 # Get the netbiosname first (could be obtained from smb.conf in theory)
160 res = secretsdb.search(expression="(flatname=%s)" %
161 names.domain,base="CN=Primary Domains",
162 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
163 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
165 names.smbconf = smbconf
167 # That's a bit simplistic but it's ok as long as we have only 3
169 current = samdb.search(expression="(objectClass=*)",
170 base="", scope=ldb.SCOPE_BASE,
171 attrs=["defaultNamingContext", "schemaNamingContext",
172 "configurationNamingContext","rootDomainNamingContext"])
174 names.configdn = current[0]["configurationNamingContext"]
175 configdn = str(names.configdn)
176 names.schemadn = current[0]["schemaNamingContext"]
177 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
178 current[0]["defaultNamingContext"][0]))):
179 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
180 "is not the same ..." % (paths.samdb,
181 str(current[0]["defaultNamingContext"][0]),
182 paths.smbconf, basedn)))
184 names.domaindn=current[0]["defaultNamingContext"]
185 names.rootdn=current[0]["rootDomainNamingContext"]
187 res3 = samdb.search(expression="(objectClass=site)",
188 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
189 names.sitename = str(res3[0]["cn"])
191 # dns hostname and server dn
192 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
193 base="OU=Domain Controllers,%s" % basedn,
194 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
195 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
197 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
198 attrs=[], base=configdn)
199 names.serverdn = server_res[0].dn
201 # invocation id/objectguid
202 res5 = samdb.search(expression="(objectClass=*)",
203 base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
204 attrs=["invocationID", "objectGUID"])
205 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
206 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
209 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
210 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
211 "objectSid","msDS-Behavior-Version" ])
212 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
213 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
214 if res6[0].get("msDS-Behavior-Version") is None or \
215 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
216 names.domainlevel = DS_DOMAIN_FUNCTION_2000
218 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
221 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
222 base="CN=Policies,CN=System," + basedn,
223 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
224 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
226 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
228 base="CN=Policies,CN=System," + basedn,
229 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
231 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
233 names.policyid_dc = None
234 res9 = idmapdb.search(expression="(cn=%s)" %
235 (security.SID_BUILTIN_ADMINISTRATORS),
238 names.wheel_gid = res9[0]["xidNumber"]
240 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
243 def update_provision_usn(samdb, low, high, id, replace=False):
244 """Update the field provisionUSN in sam.ldb
246 This field is used to track range of USN modified by provision and
248 This value is used afterward by next provision to figure out if
249 the field have been modified since last provision.
251 :param samdb: An LDB object connect to sam.ldb
252 :param low: The lowest USN modified by this upgrade
253 :param high: The highest USN modified by this upgrade
254 :param id: The invocation id of the samba's dc
255 :param replace: A boolean indicating if the range should replace any
256 existing one or appended (default)
261 entry = samdb.search(base="@PROVISION",
262 scope=ldb.SCOPE_BASE,
263 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
264 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
265 if not re.search(';', e):
266 e = "%s;%s" % (e, id)
269 tab.append("%s-%s;%s" % (low, high, id))
270 delta = ldb.Message()
271 delta.dn = ldb.Dn(samdb, "@PROVISION")
272 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
273 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
274 entry = samdb.search(expression='provisionnerID=*',
275 base="@PROVISION", scope=ldb.SCOPE_BASE,
276 attrs=["provisionnerID"])
277 if len(entry) == 0 or len(entry[0]) == 0:
278 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
282 def set_provision_usn(samdb, low, high, id):
283 """Set the field provisionUSN in sam.ldb
284 This field is used to track range of USN modified by provision and
286 This value is used afterward by next provision to figure out if
287 the field have been modified since last provision.
289 :param samdb: An LDB object connect to sam.ldb
290 :param low: The lowest USN modified by this upgrade
291 :param high: The highest USN modified by this upgrade
292 :param id: The invocationId of the provision"""
295 tab.append("%s-%s;%s" % (low, high, id))
297 delta = ldb.Message()
298 delta.dn = ldb.Dn(samdb, "@PROVISION")
299 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
300 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
304 def get_max_usn(samdb,basedn):
305 """ This function return the biggest USN present in the provision
307 :param samdb: A LDB object pointing to the sam.ldb
308 :param basedn: A string containing the base DN of the provision
310 :return: The biggest USN in the provision"""
312 res = samdb.search(expression="objectClass=*",base=basedn,
313 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
314 controls=["search_options:1:2",
315 "server_sort:1:1:uSNChanged",
316 "paged_results:1:1"])
317 return res[0]["uSNChanged"]
320 def get_last_provision_usn(sam):
321 """Get USNs ranges modified by a provision or an upgradeprovision
323 :param sam: An LDB object pointing to the sam.ldb
324 :return: a dictionnary which keys are invocation id and values are an array
325 of integer representing the different ranges
328 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
329 base="@PROVISION", scope=ldb.SCOPE_BASE,
330 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
331 except ldb.LdbError, (ecode, emsg):
332 if ecode == ldb.ERR_NO_SUCH_OBJECT:
339 if entry[0].get("provisionnerID"):
340 for e in entry[0]["provisionnerID"]:
342 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
343 tab1 = str(r).split(';')
348 if (len(myids) > 0 and id not in myids):
350 tab2 = p.split(tab1[0])
351 if range.get(id) == None:
353 range[id].append(tab2[0])
354 range[id].append(tab2[1])
360 class ProvisionResult(object):
371 def check_install(lp, session_info, credentials):
372 """Check whether the current install seems ok.
374 :param lp: Loadparm context
375 :param session_info: Session information
376 :param credentials: Credentials
378 if lp.get("realm") == "":
379 raise Exception("Realm empty")
380 samdb = Ldb(lp.samdb_url(), session_info=session_info,
381 credentials=credentials, lp=lp)
382 if len(samdb.search("(cn=Administrator)")) != 1:
383 raise ProvisioningError("No administrator account found")
386 def findnss(nssfn, names):
387 """Find a user or group from a list of possibilities.
389 :param nssfn: NSS Function to try (should raise KeyError if not found)
390 :param names: Names to check.
391 :return: Value return by first names list.
398 raise KeyError("Unable to find user/group in %r" % names)
401 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
402 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
405 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
406 """Setup a ldb in the private dir.
408 :param ldb: LDB file to import data into
409 :param ldif_path: Path of the LDIF file to load
410 :param subst_vars: Optional variables to subsitute in LDIF.
411 :param nocontrols: Optional list of controls, can be None for no controls
413 assert isinstance(ldif_path, str)
414 data = read_and_sub_file(ldif_path, subst_vars)
415 ldb.add_ldif(data, controls)
418 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
419 """Modify a ldb in the private dir.
421 :param ldb: LDB object.
422 :param ldif_path: LDIF file path.
423 :param subst_vars: Optional dictionary with substitution variables.
425 data = read_and_sub_file(ldif_path, subst_vars)
426 ldb.modify_ldif(data, controls)
429 def setup_ldb(ldb, ldif_path, subst_vars):
430 """Import a LDIF a file into a LDB handle, optionally substituting
433 :note: Either all LDIF data will be added or none (using transactions).
435 :param ldb: LDB file to import into.
436 :param ldif_path: Path to the LDIF file.
437 :param subst_vars: Dictionary with substitution variables.
439 assert ldb is not None
440 ldb.transaction_start()
442 setup_add_ldif(ldb, ldif_path, subst_vars)
444 ldb.transaction_cancel()
447 ldb.transaction_commit()
450 def provision_paths_from_lp(lp, dnsdomain):
451 """Set the default paths for provisioning.
453 :param lp: Loadparm context.
454 :param dnsdomain: DNS Domain name
456 paths = ProvisionPaths()
457 paths.private_dir = lp.get("private dir")
459 # This is stored without path prefix for the "privateKeytab" attribute in
460 # "secrets_dns.ldif".
461 paths.dns_keytab = "dns.keytab"
462 paths.keytab = "secrets.keytab"
464 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
465 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
466 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
467 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
468 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
469 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
470 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
471 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
472 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
473 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
474 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
475 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
476 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
477 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
478 paths.phpldapadminconfig = os.path.join(paths.private_dir,
479 "phpldapadmin-config.php")
480 paths.hklm = "hklm.ldb"
481 paths.hkcr = "hkcr.ldb"
482 paths.hkcu = "hkcu.ldb"
483 paths.hku = "hku.ldb"
484 paths.hkpd = "hkpd.ldb"
485 paths.hkpt = "hkpt.ldb"
486 paths.sysvol = lp.get("path", "sysvol")
487 paths.netlogon = lp.get("path", "netlogon")
488 paths.smbconf = lp.configfile
492 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
493 serverrole=None, rootdn=None, domaindn=None, configdn=None,
494 schemadn=None, serverdn=None, sitename=None):
495 """Guess configuration settings to use."""
498 hostname = socket.gethostname().split(".")[0]
500 netbiosname = lp.get("netbios name")
501 if netbiosname is None:
502 netbiosname = hostname
503 # remove forbidden chars
505 for x in netbiosname:
506 if x.isalnum() or x in VALID_NETBIOS_CHARS:
507 newnbname = "%s%c" % (newnbname, x)
508 # force the length to be <16
509 netbiosname = newnbname[0:15]
510 assert netbiosname is not None
511 netbiosname = netbiosname.upper()
512 if not valid_netbios_name(netbiosname):
513 raise InvalidNetbiosName(netbiosname)
515 if dnsdomain is None:
516 dnsdomain = lp.get("realm")
517 if dnsdomain is None or dnsdomain == "":
518 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
520 dnsdomain = dnsdomain.lower()
522 if serverrole is None:
523 serverrole = lp.get("server role")
524 if serverrole is None:
525 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
527 serverrole = serverrole.lower()
529 realm = dnsdomain.upper()
531 if lp.get("realm") == "":
532 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
534 if lp.get("realm").upper() != realm:
535 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))
537 if lp.get("server role").lower() != serverrole:
538 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))
540 if serverrole == "domain controller":
542 # This will, for better or worse, default to 'WORKGROUP'
543 domain = lp.get("workgroup")
544 domain = domain.upper()
546 if lp.get("workgroup").upper() != domain:
547 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))
550 domaindn = samba.dn_from_dns_name(dnsdomain)
552 if domain == netbiosname:
553 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
557 domaindn = "DC=" + netbiosname
559 if not valid_netbios_name(domain):
560 raise InvalidNetbiosName(domain)
562 if hostname.upper() == realm:
563 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
564 if netbiosname.upper() == realm:
565 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
567 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
573 configdn = "CN=Configuration," + rootdn
575 schemadn = "CN=Schema," + configdn
580 names = ProvisionNames()
581 names.rootdn = rootdn
582 names.domaindn = domaindn
583 names.configdn = configdn
584 names.schemadn = schemadn
585 names.ldapmanagerdn = "CN=Manager," + rootdn
586 names.dnsdomain = dnsdomain
587 names.domain = domain
589 names.netbiosname = netbiosname
590 names.hostname = hostname
591 names.sitename = sitename
592 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
593 netbiosname, sitename, configdn)
598 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
599 targetdir, sid_generator="internal", eadb=False, lp=None):
600 """Create a new smb.conf file based on a couple of basic settings.
602 assert smbconf is not None
604 hostname = socket.gethostname().split(".")[0]
605 netbiosname = hostname.upper()
606 # remove forbidden chars
608 for x in netbiosname:
609 if x.isalnum() or x in VALID_NETBIOS_CHARS:
610 newnbname = "%s%c" % (newnbname, x)
611 #force the length to be <16
612 netbiosname = newnbname[0:15]
614 netbiosname = hostname.upper()
616 if serverrole is None:
617 serverrole = "standalone"
619 assert serverrole in ("domain controller", "member server", "standalone")
620 if serverrole == "domain controller":
622 elif serverrole == "member server":
623 smbconfsuffix = "member"
624 elif serverrole == "standalone":
625 smbconfsuffix = "standalone"
627 if sid_generator is None:
628 sid_generator = "internal"
630 assert domain is not None
631 domain = domain.upper()
633 assert realm is not None
634 realm = realm.upper()
637 lp = samba.param.LoadParm()
638 #Load non-existant file
639 if os.path.exists(smbconf):
641 if eadb and not lp.get("posix:eadb"):
642 if targetdir is not None:
643 privdir = os.path.join(targetdir, "private")
645 privdir = lp.get("private dir")
646 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
648 if targetdir is not None:
649 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
650 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
651 statedir_line = "state directory = " + os.path.abspath(targetdir)
652 cachedir_line = "cache directory = " + os.path.abspath(targetdir)
654 lp.set("lock dir", os.path.abspath(targetdir))
655 lp.set("state directory", os.path.abspath(targetdir))
656 lp.set("cache directory", os.path.abspath(targetdir))
663 sysvol = os.path.join(lp.get("state directory"), "sysvol")
664 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
666 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
668 "NETBIOS_NAME": netbiosname,
671 "SERVERROLE": serverrole,
672 "NETLOGONPATH": netlogon,
673 "SYSVOLPATH": sysvol,
674 "PRIVATEDIR_LINE": privatedir_line,
675 "LOCKDIR_LINE": lockdir_line,
676 "STATEDIR_LINE": statedir_line,
677 "CACHEDIR_LINE": cachedir_line
680 # reload the smb.conf
683 # and dump it without any values that are the default
684 # this ensures that any smb.conf parameters that were set
685 # on the provision/join command line are set in the resulting smb.conf
686 f = open(smbconf, mode='w')
692 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
693 users_gid, wheel_gid):
694 """setup reasonable name mappings for sam names to unix names.
696 :param samdb: SamDB object.
697 :param idmap: IDmap db object.
698 :param sid: The domain sid.
699 :param domaindn: The domain DN.
700 :param root_uid: uid of the UNIX root user.
701 :param nobody_uid: uid of the UNIX nobody user.
702 :param users_gid: gid of the UNIX users group.
703 :param wheel_gid: gid of the UNIX wheel group.
705 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
706 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
708 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
709 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
712 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
713 provision_backend, names, schema, serverrole,
715 """Setup the partitions for the SAM database.
717 Alternatively, provision() may call this, and then populate the database.
719 :note: This will wipe the Sam Database!
721 :note: This function always removes the local SAM LDB file. The erase
722 parameter controls whether to erase the existing data, which
723 may not be stored locally but in LDAP.
726 assert session_info is not None
728 # We use options=["modules:"] to stop the modules loading - we
729 # just want to wipe and re-initialise the database, not start it up
732 os.unlink(samdb_path)
736 samdb = Ldb(url=samdb_path, session_info=session_info,
737 lp=lp, options=["modules:"])
739 ldap_backend_line = "# No LDAP backend"
740 if provision_backend.type is not "ldb":
741 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
743 samdb.transaction_start()
745 logger.info("Setting up sam.ldb partitions and settings")
746 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
747 "LDAP_BACKEND_LINE": ldap_backend_line
751 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
752 "BACKEND_TYPE": provision_backend.type,
753 "SERVER_ROLE": serverrole
756 logger.info("Setting up sam.ldb rootDSE")
757 setup_samdb_rootdse(samdb, names)
759 samdb.transaction_cancel()
762 samdb.transaction_commit()
765 def secretsdb_self_join(secretsdb, domain,
766 netbiosname, machinepass, domainsid=None,
767 realm=None, dnsdomain=None,
769 key_version_number=1,
770 secure_channel_type=SEC_CHAN_WKSTA):
771 """Add domain join-specific bits to a secrets database.
773 :param secretsdb: Ldb Handle to the secrets database
774 :param machinepass: Machine password
776 attrs = ["whenChanged",
783 if realm is not None:
784 if dnsdomain is None:
785 dnsdomain = realm.lower()
786 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
789 shortname = netbiosname.lower()
791 # We don't need to set msg["flatname"] here, because rdn_name will handle
792 # it, and it causes problems for modifies anyway
793 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
794 msg["secureChannelType"] = [str(secure_channel_type)]
795 msg["objectClass"] = ["top", "primaryDomain"]
796 if dnsname is not None:
797 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
798 msg["realm"] = [realm]
799 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
800 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
801 msg["privateKeytab"] = ["secrets.keytab"]
803 msg["secret"] = [machinepass]
804 msg["samAccountName"] = ["%s$" % netbiosname]
805 msg["secureChannelType"] = [str(secure_channel_type)]
806 if domainsid is not None:
807 msg["objectSid"] = [ndr_pack(domainsid)]
809 # This complex expression tries to ensure that we don't have more
810 # than one record for this SID, realm or netbios domain at a time,
811 # but we don't delete the old record that we are about to modify,
812 # because that would delete the keytab and previous password.
813 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
814 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
815 scope=ldb.SCOPE_ONELEVEL)
818 secretsdb.delete(del_msg.dn)
820 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
823 msg["priorSecret"] = [res[0]["secret"][0]]
824 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
827 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
832 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
838 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
839 secretsdb.modify(msg)
840 secretsdb.rename(res[0].dn, msg.dn)
842 spn = [ 'HOST/%s' % shortname ]
843 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
844 # we are a domain controller then we add servicePrincipalName
845 # entries for the keytab code to update.
846 spn.extend([ 'HOST/%s' % dnsname ])
847 msg["servicePrincipalName"] = spn
852 def setup_secretsdb(paths, session_info, backend_credentials, lp):
853 """Setup the secrets database.
855 :note: This function does not handle exceptions and transaction on purpose,
856 it's up to the caller to do this job.
858 :param path: Path to the secrets database.
859 :param session_info: Session info.
860 :param credentials: Credentials
861 :param lp: Loadparm context
862 :return: LDB handle for the created secrets database
864 if os.path.exists(paths.secrets):
865 os.unlink(paths.secrets)
867 keytab_path = os.path.join(paths.private_dir, paths.keytab)
868 if os.path.exists(keytab_path):
869 os.unlink(keytab_path)
871 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
872 if os.path.exists(dns_keytab_path):
873 os.unlink(dns_keytab_path)
877 secrets_ldb = Ldb(path, session_info=session_info,
880 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
881 secrets_ldb = Ldb(path, session_info=session_info,
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()
909 def setup_privileges(path, session_info, lp):
910 """Setup the privileges database.
912 :param path: Path to the privileges database.
913 :param session_info: Session info.
914 :param credentials: Credentials
915 :param lp: Loadparm context
916 :return: LDB handle for the created secrets database
918 if os.path.exists(path):
920 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
921 privilege_ldb.erase()
922 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
925 def setup_registry(path, session_info, lp):
926 """Setup the registry.
928 :param path: Path to the registry database
929 :param session_info: Session information
930 :param credentials: Credentials
931 :param lp: Loadparm context
933 reg = samba.registry.Registry()
934 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
935 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
936 provision_reg = setup_path("provision.reg")
937 assert os.path.exists(provision_reg)
938 reg.diff_apply(provision_reg)
941 def setup_idmapdb(path, session_info, lp):
942 """Setup the idmap database.
944 :param path: path to the idmap database
945 :param session_info: Session information
946 :param credentials: Credentials
947 :param lp: Loadparm context
949 if os.path.exists(path):
952 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
954 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
958 def setup_samdb_rootdse(samdb, names):
959 """Setup the SamDB rootdse.
961 :param samdb: Sam Database handle
963 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
964 "SCHEMADN": names.schemadn,
965 "DOMAINDN": names.domaindn,
966 "ROOTDN" : names.rootdn,
967 "CONFIGDN": names.configdn,
968 "SERVERDN": names.serverdn,
972 def setup_self_join(samdb, admin_session_info, names, fill, machinepass, dnspass,
973 domainsid, next_rid, invocationid,
974 policyguid, policyguid_dc, domainControllerFunctionality,
975 ntdsguid, dc_rid=None):
976 """Join a host to its own domain."""
977 assert isinstance(invocationid, str)
978 if ntdsguid is not None:
979 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
986 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
987 "CONFIGDN": names.configdn,
988 "SCHEMADN": names.schemadn,
989 "DOMAINDN": names.domaindn,
990 "SERVERDN": names.serverdn,
991 "INVOCATIONID": invocationid,
992 "NETBIOSNAME": names.netbiosname,
993 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
994 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
995 "DOMAINSID": str(domainsid),
996 "DCRID": str(dc_rid),
997 "SAMBA_VERSION_STRING": version,
998 "NTDSGUID": ntdsguid_line,
999 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1000 domainControllerFunctionality),
1001 "RIDALLOCATIONSTART": str(next_rid + 100),
1002 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1004 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1005 "POLICYGUID": policyguid,
1006 "POLICYGUID_DC": policyguid_dc,
1007 "DNSDOMAIN": names.dnsdomain,
1008 "DOMAINDN": names.domaindn})
1010 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1011 if fill == FILL_FULL:
1012 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1013 "CONFIGDN": names.configdn,
1014 "SCHEMADN": names.schemadn,
1015 "DOMAINDN": names.domaindn,
1016 "SERVERDN": names.serverdn,
1017 "INVOCATIONID": invocationid,
1018 "NETBIOSNAME": names.netbiosname,
1019 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1020 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1021 "DOMAINSID": str(domainsid),
1022 "DCRID": str(dc_rid),
1023 "SAMBA_VERSION_STRING": version,
1024 "NTDSGUID": ntdsguid_line,
1025 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1026 domainControllerFunctionality)})
1028 # Setup fSMORoleOwner entries to point at the newly created DC entry
1029 setup_modify_ldif(samdb, 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
1041 # to modify a serverReference under cn=config when we are a subdomain, we must
1042 # be system due to ACLs
1043 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1044 "DOMAINDN": names.domaindn,
1045 "SERVERDN": names.serverdn,
1046 "NETBIOSNAME": names.netbiosname,
1049 samdb.set_session_info(admin_session_info)
1051 # This is Samba4 specific and should be replaced by the correct
1052 # DNS AD-style setup
1053 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1054 "DNSDOMAIN": names.dnsdomain,
1055 "DOMAINDN": names.domaindn,
1056 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1057 "HOSTNAME" : names.hostname,
1058 "DNSNAME" : '%s.%s' % (
1059 names.netbiosname.lower(), names.dnsdomain.lower())
1063 def getpolicypath(sysvolpath, dnsdomain, guid):
1064 """Return the physical path of policy given its guid.
1066 :param sysvolpath: Path to the sysvol folder
1067 :param dnsdomain: DNS name of the AD domain
1068 :param guid: The GUID of the policy
1069 :return: A string with the complete path to the policy folder
1073 guid = "{%s}" % guid
1074 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1078 def create_gpo_struct(policy_path):
1079 if not os.path.exists(policy_path):
1080 os.makedirs(policy_path, 0775)
1081 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1082 "[General]\r\nVersion=0")
1083 p = os.path.join(policy_path, "MACHINE")
1084 if not os.path.exists(p):
1085 os.makedirs(p, 0775)
1086 p = os.path.join(policy_path, "USER")
1087 if not os.path.exists(p):
1088 os.makedirs(p, 0775)
1091 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1092 """Create the default GPO for a domain
1094 :param sysvolpath: Physical path for the sysvol folder
1095 :param dnsdomain: DNS domain name of the AD domain
1096 :param policyguid: GUID of the default domain policy
1097 :param policyguid_dc: GUID of the default domain controler policy
1099 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1100 create_gpo_struct(policy_path)
1102 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1103 create_gpo_struct(policy_path)
1106 def setup_samdb(path, session_info, provision_backend, lp, names,
1107 logger, fill, serverrole, schema, am_rodc=False):
1108 """Setup a complete SAM Database.
1110 :note: This will wipe the main SAM database file!
1113 # Also wipes the database
1114 setup_samdb_partitions(path, logger=logger, lp=lp,
1115 provision_backend=provision_backend, session_info=session_info,
1116 names=names, serverrole=serverrole, schema=schema)
1118 # Load the database, but don's load the global schema and don't connect
1120 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1121 credentials=provision_backend.credentials, lp=lp,
1122 global_schema=False, am_rodc=am_rodc)
1124 logger.info("Pre-loading the Samba 4 and AD schema")
1126 # Load the schema from the one we computed earlier
1127 samdb.set_schema(schema)
1129 # Set the NTDS settings DN manually - in order to have it already around
1130 # before the provisioned tree exists and we connect
1131 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1133 # And now we can connect to the DB - the schema won't be loaded from the
1139 def fill_samdb(samdb, lp, names,
1140 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1141 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1142 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1143 next_rid=None, dc_rid=None):
1145 if next_rid is None:
1148 # Provision does not make much sense values larger than 1000000000
1149 # as the upper range of the rIDAvailablePool is 1073741823 and
1150 # we don't want to create a domain that cannot allocate rids.
1151 if next_rid < 1000 or next_rid > 1000000000:
1152 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1153 error += "the valid range is %u-%u. The default is %u." % (
1154 1000, 1000000000, 1000)
1155 raise ProvisioningError(error)
1157 # ATTENTION: Do NOT change these default values without discussion with the
1158 # team and/or release manager. They have a big impact on the whole program!
1159 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1161 if dom_for_fun_level is None:
1162 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1164 if dom_for_fun_level > domainControllerFunctionality:
1165 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!")
1167 domainFunctionality = dom_for_fun_level
1168 forestFunctionality = dom_for_fun_level
1170 # Set the NTDS settings DN manually - in order to have it already around
1171 # before the provisioned tree exists and we connect
1172 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1174 samdb.transaction_start()
1176 # Set the domain functionality levels onto the database.
1177 # Various module (the password_hash module in particular) need
1178 # to know what level of AD we are emulating.
1180 # These will be fixed into the database via the database
1181 # modifictions below, but we need them set from the start.
1182 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1183 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1184 samdb.set_opaque_integer("domainControllerFunctionality",
1185 domainControllerFunctionality)
1187 samdb.set_domain_sid(str(domainsid))
1188 samdb.set_invocation_id(invocationid)
1190 logger.info("Adding DomainDN: %s" % names.domaindn)
1192 # impersonate domain admin
1193 admin_session_info = admin_session(lp, str(domainsid))
1194 samdb.set_session_info(admin_session_info)
1195 if domainguid is not None:
1196 domainguid_line = "objectGUID: %s\n-" % domainguid
1198 domainguid_line = ""
1200 descr = b64encode(get_domain_descriptor(domainsid))
1201 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1202 "DOMAINDN": names.domaindn,
1203 "DOMAINSID": str(domainsid),
1204 "DESCRIPTOR": descr,
1205 "DOMAINGUID": domainguid_line
1208 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1209 "DOMAINDN": names.domaindn,
1210 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1211 "NEXTRID": str(next_rid),
1212 "DEFAULTSITE": names.sitename,
1213 "CONFIGDN": names.configdn,
1214 "POLICYGUID": policyguid,
1215 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1216 "SAMBA_VERSION_STRING": version
1219 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1220 if fill == FILL_FULL:
1221 logger.info("Adding configuration container")
1222 descr = b64encode(get_config_descriptor(domainsid))
1223 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1224 "CONFIGDN": names.configdn,
1225 "DESCRIPTOR": descr,
1228 # The LDIF here was created when the Schema object was constructed
1229 logger.info("Setting up sam.ldb schema")
1230 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1231 samdb.modify_ldif(schema.schema_dn_modify)
1232 samdb.write_prefixes_from_schema()
1233 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1234 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1235 {"SCHEMADN": names.schemadn})
1237 # Now register this container in the root of the forest
1238 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1239 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1243 samdb.transaction_cancel()
1246 samdb.transaction_commit()
1248 samdb.transaction_start()
1250 samdb.invocation_id = invocationid
1252 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1253 if fill == FILL_FULL:
1254 logger.info("Setting up sam.ldb configuration data")
1255 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1256 "CONFIGDN": names.configdn,
1257 "NETBIOSNAME": names.netbiosname,
1258 "DEFAULTSITE": names.sitename,
1259 "DNSDOMAIN": names.dnsdomain,
1260 "DOMAIN": names.domain,
1261 "SCHEMADN": names.schemadn,
1262 "DOMAINDN": names.domaindn,
1263 "SERVERDN": names.serverdn,
1264 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1265 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1268 logger.info("Setting up display specifiers")
1269 display_specifiers_ldif = read_ms_ldif(
1270 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1271 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1272 {"CONFIGDN": names.configdn})
1273 check_all_substituted(display_specifiers_ldif)
1274 samdb.add_ldif(display_specifiers_ldif)
1276 logger.info("Adding users container")
1277 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1278 "DOMAINDN": names.domaindn})
1279 logger.info("Modifying users container")
1280 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1281 "DOMAINDN": names.domaindn})
1282 logger.info("Adding computers container")
1283 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1284 "DOMAINDN": names.domaindn})
1285 logger.info("Modifying computers container")
1286 setup_modify_ldif(samdb,
1287 setup_path("provision_computers_modify.ldif"), {
1288 "DOMAINDN": names.domaindn})
1289 logger.info("Setting up sam.ldb data")
1290 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1291 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1292 "DOMAINDN": names.domaindn,
1293 "NETBIOSNAME": names.netbiosname,
1294 "DEFAULTSITE": names.sitename,
1295 "CONFIGDN": names.configdn,
1296 "SERVERDN": names.serverdn,
1297 "RIDAVAILABLESTART": str(next_rid + 600),
1298 "POLICYGUID_DC": policyguid_dc
1301 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1302 if fill == FILL_FULL:
1303 setup_modify_ldif(samdb,
1304 setup_path("provision_configuration_references.ldif"), {
1305 "CONFIGDN": names.configdn,
1306 "SCHEMADN": names.schemadn})
1308 logger.info("Setting up well known security principals")
1309 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1310 "CONFIGDN": names.configdn,
1313 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1314 setup_modify_ldif(samdb,
1315 setup_path("provision_basedn_references.ldif"),
1316 {"DOMAINDN": names.domaindn})
1318 logger.info("Setting up sam.ldb users and groups")
1319 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1320 "DOMAINDN": names.domaindn,
1321 "DOMAINSID": str(domainsid),
1322 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1323 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1326 logger.info("Setting up self join")
1327 setup_self_join(samdb, admin_session_info, names=names, fill=fill, 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 adminpass is None:
1475 adminpass = samba.generate_random_password(12, 32)
1476 if krbtgtpass is None:
1477 krbtgtpass = samba.generate_random_password(128, 255)
1478 if machinepass is None:
1479 machinepass = samba.generate_random_password(128, 255)
1481 dnspass = samba.generate_random_password(128, 255)
1483 samdb = fill_samdb(samdb, lp, names, logger=logger,
1484 domainsid=domainsid, schema=schema, domainguid=domainguid,
1485 policyguid=policyguid, policyguid_dc=policyguid_dc,
1486 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1487 invocationid=invocationid, machinepass=machinepass,
1488 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1489 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1490 next_rid=next_rid, dc_rid=dc_rid)
1492 if serverrole == "domain controller":
1493 # Set up group policies (domain policy and domain controller
1495 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1497 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.wheel_gid,
1498 domainsid, names.dnsdomain, names.domaindn, lp)
1500 secretsdb_self_join(secrets_ldb, domain=names.domain,
1501 realm=names.realm, dnsdomain=names.dnsdomain,
1502 netbiosname=names.netbiosname, domainsid=domainsid,
1503 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1505 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1506 # In future, this might be determined from some configuration
1507 kerberos_enctypes = str(ENC_ALL_TYPES)
1510 msg = ldb.Message(ldb.Dn(samdb,
1511 samdb.searchone("distinguishedName",
1512 expression="samAccountName=%s$" % names.netbiosname,
1513 scope=ldb.SCOPE_SUBTREE)))
1514 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1515 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1516 name="msDS-SupportedEncryptionTypes")
1518 except ldb.LdbError, (enum, estr):
1519 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1520 # It might be that this attribute does not exist in this schema
1523 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1524 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1525 dnspass=dnspass, os_level=dom_for_fun_level,
1526 targetdir=targetdir, site=DEFAULTSITE)
1528 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1529 attribute="objectGUID")
1530 assert isinstance(domainguid, str)
1532 lastProvisionUSNs = get_last_provision_usn(samdb)
1533 maxUSN = get_max_usn(samdb, str(names.rootdn))
1534 if lastProvisionUSNs is not None:
1535 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1537 set_provision_usn(samdb, 0, maxUSN, invocationid)
1539 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1540 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1541 { 'NTDSGUID' : names.ntdsguid })
1543 # fix any dangling GUIDs from the provision
1544 logger.info("Fixing provision GUIDs")
1545 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True, quiet=True)
1546 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'])
1559 samdb.transaction_commit()
1561 def provision(logger, session_info, credentials, smbconf=None,
1562 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1563 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1564 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1565 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1566 domainguid=None, policyguid=None, policyguid_dc=None,
1567 dns_backend=None, dnspass=None,
1568 invocationid=None, machinepass=None, ntdsguid=None,
1569 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1570 serverrole=None, dom_for_fun_level=None,
1571 backend_type=None, sitename=None,
1572 ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1573 useeadb=False, am_rodc=False,
1577 :note: caution, this wipes all existing data!
1581 roles["ROLE_STANDALONE"] = "standalone"
1582 roles["ROLE_DOMAIN_MEMBER"] = "member server"
1583 roles["ROLE_DOMAIN_BDC"] = "domain controller"
1584 roles["ROLE_DOMAIN_PDC"] = "domain controller"
1585 roles["dc"] = "domain controller"
1586 roles["member"] = "member server"
1587 roles["domain controller"] = "domain controller"
1588 roles["member server"] = "member server"
1589 roles["standalone"] = "standalone"
1592 serverrole = roles[serverrole]
1594 raise ProvisioningError('server role (%s) should be one of "domain controller", "member server", "standalone"' % serverrole)
1596 if ldapadminpass is None:
1597 # Make a new, random password between Samba and it's LDAP server
1598 ldapadminpass=samba.generate_random_password(128, 255)
1600 if backend_type is None:
1601 backend_type = "ldb"
1603 if domainsid is None:
1604 domainsid = security.random_sid()
1606 domainsid = security.dom_sid(domainsid)
1608 sid_generator = "internal"
1609 if backend_type == "fedora-ds":
1610 sid_generator = "backend"
1612 root_uid = findnss_uid([root or "root"])
1613 nobody_uid = findnss_uid([nobody or "nobody"])
1614 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1616 wheel_gid = findnss_gid(["wheel", "adm"])
1618 wheel_gid = findnss_gid([wheel])
1620 bind_gid = findnss_gid(["bind", "named"])
1624 if targetdir is not None:
1625 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1626 elif smbconf is None:
1627 smbconf = samba.param.default_path()
1628 if not os.path.exists(os.path.dirname(smbconf)):
1629 os.makedirs(os.path.dirname(smbconf))
1631 # only install a new smb.conf if there isn't one there already
1632 if os.path.exists(smbconf):
1633 # if Samba Team members can't figure out the weird errors
1634 # loading an empty smb.conf gives, then we need to be smarter.
1635 # Pretend it just didn't exist --abartlet
1636 data = open(smbconf, 'r').read()
1637 data = data.lstrip()
1638 if data is None or data == "":
1639 make_smbconf(smbconf, hostname, domain, realm,
1640 serverrole, targetdir, sid_generator, useeadb,
1643 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1644 targetdir, sid_generator, useeadb, lp=lp)
1647 lp = samba.param.LoadParm()
1649 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1650 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1651 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1652 sitename=sitename, rootdn=rootdn)
1653 paths = provision_paths_from_lp(lp, names.dnsdomain)
1655 paths.bind_gid = bind_gid
1656 paths.wheel_gid = wheel_gid
1659 logger.info("Looking up IPv4 addresses")
1660 hostips = interface_ips_v4(lp)
1661 if len(hostips) > 0:
1663 if len(hostips) > 1:
1664 logger.warning("More than one IPv4 address found. Using %s",
1666 if hostip == "127.0.0.1":
1669 logger.warning("No IPv4 address will be assigned")
1672 logger.info("Looking up IPv6 addresses")
1673 hostips = interface_ips_v6(lp, linklocal=False)
1675 hostip6 = hostips[0]
1676 if len(hostips) > 1:
1677 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1679 logger.warning("No IPv6 address will be assigned")
1681 names.hostip = hostip
1682 names.hostip6 = hostip6
1684 if serverrole is None:
1685 serverrole = lp.get("server role")
1687 if not os.path.exists(paths.private_dir):
1688 os.mkdir(paths.private_dir)
1689 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1690 os.mkdir(os.path.join(paths.private_dir, "tls"))
1692 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1694 schema = Schema(domainsid, invocationid=invocationid,
1695 schemadn=names.schemadn)
1697 if backend_type == "ldb":
1698 provision_backend = LDBBackend(backend_type, paths=paths,
1699 lp=lp, credentials=credentials,
1700 names=names, logger=logger)
1701 elif backend_type == "existing":
1702 # If support for this is ever added back, then the URI will need to be specified again
1703 provision_backend = ExistingBackend(backend_type, paths=paths,
1704 lp=lp, credentials=credentials,
1705 names=names, logger=logger,
1706 ldap_backend_forced_uri=None)
1707 elif backend_type == "fedora-ds":
1708 provision_backend = FDSBackend(backend_type, paths=paths,
1709 lp=lp, credentials=credentials,
1710 names=names, logger=logger, domainsid=domainsid,
1711 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1712 slapd_path=slapd_path,
1714 elif backend_type == "openldap":
1715 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1716 lp=lp, credentials=credentials,
1717 names=names, logger=logger, domainsid=domainsid,
1718 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1719 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1721 raise ValueError("Unknown LDAP backend type selected")
1723 provision_backend.init()
1724 provision_backend.start()
1726 # only install a new shares config db if there is none
1727 if not os.path.exists(paths.shareconf):
1728 logger.info("Setting up share.ldb")
1729 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1731 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1733 logger.info("Setting up secrets.ldb")
1734 secrets_ldb = setup_secretsdb(paths,
1735 session_info=session_info,
1736 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1739 logger.info("Setting up the registry")
1740 setup_registry(paths.hklm, session_info,
1743 logger.info("Setting up the privileges database")
1744 setup_privileges(paths.privilege, session_info, lp=lp)
1746 logger.info("Setting up idmap db")
1747 idmap = setup_idmapdb(paths.idmapdb,
1748 session_info=session_info, lp=lp)
1750 setup_name_mappings(idmap, sid=str(domainsid),
1751 root_uid=root_uid, nobody_uid=nobody_uid,
1752 users_gid=users_gid, wheel_gid=wheel_gid)
1754 logger.info("Setting up SAM db")
1755 samdb = setup_samdb(paths.samdb, session_info,
1756 provision_backend, lp, names, logger=logger,
1757 serverrole=serverrole,
1758 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
1760 if serverrole == "domain controller":
1761 if paths.netlogon is None:
1762 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1763 logger.info("Please either remove %s or see the template at %s" %
1764 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1765 assert paths.netlogon is not None
1767 if paths.sysvol is None:
1768 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1769 " are configuring a DC.")
1770 logger.info("Please either remove %s or see the template at %s" %
1771 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1772 assert paths.sysvol is not None
1774 if not os.path.isdir(paths.netlogon):
1775 os.makedirs(paths.netlogon, 0755)
1777 if samdb_fill == FILL_FULL:
1778 provision_fill(samdb, secrets_ldb, logger,
1779 names, paths, schema=schema, targetdir=targetdir,
1780 samdb_fill=samdb_fill, hostip=hostip, hostip6=hostip6, domainsid=domainsid,
1781 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
1782 krbtgtpass=krbtgtpass, domainguid=domainguid,
1783 policyguid=policyguid, policyguid_dc=policyguid_dc,
1784 invocationid=invocationid, machinepass=machinepass,
1785 ntdsguid=ntdsguid, dns_backend=dns_backend, dnspass=dnspass,
1786 serverrole=serverrole, dom_for_fun_level=dom_for_fun_level,
1787 am_rodc=am_rodc, lp=lp)
1789 create_krb5_conf(paths.krb5conf,
1790 dnsdomain=names.dnsdomain, hostname=names.hostname,
1792 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1793 "generated at %s", paths.krb5conf)
1795 if serverrole == "domain controller":
1796 create_dns_update_list(lp, logger, paths)
1798 provision_backend.post_setup()
1799 provision_backend.shutdown()
1801 create_phpldapadmin_config(paths.phpldapadminconfig,
1804 secrets_ldb.transaction_cancel()
1807 # Now commit the secrets.ldb to disk
1808 secrets_ldb.transaction_commit()
1810 # the commit creates the dns.keytab, now chown it
1811 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1812 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1814 os.chmod(dns_keytab_path, 0640)
1815 os.chown(dns_keytab_path, -1, paths.bind_gid)
1817 if not os.environ.has_key('SAMBA_SELFTEST'):
1818 logger.info("Failed to chown %s to bind gid %u",
1819 dns_keytab_path, paths.bind_gid)
1821 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1822 paths.phpldapadminconfig)
1824 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1825 logger.info("Server Role: %s" % serverrole)
1826 logger.info("Hostname: %s" % names.hostname)
1827 logger.info("NetBIOS Domain: %s" % names.domain)
1828 logger.info("DNS Domain: %s" % names.dnsdomain)
1829 logger.info("DOMAIN SID: %s" % str(domainsid))
1830 if samdb_fill == FILL_FULL:
1831 logger.info("Admin password: %s" % adminpass)
1832 if provision_backend.type is not "ldb":
1833 if provision_backend.credentials.get_bind_dn() is not None:
1834 logger.info("LDAP Backend Admin DN: %s" %
1835 provision_backend.credentials.get_bind_dn())
1837 logger.info("LDAP Admin User: %s" %
1838 provision_backend.credentials.get_username())
1840 logger.info("LDAP Admin Password: %s" %
1841 provision_backend.credentials.get_password())
1843 if provision_backend.slapd_command_escaped is not None:
1844 # now display slapd_command_file.txt to show how slapd must be
1846 logger.info("Use later the following commandline to start slapd, then Samba:")
1847 logger.info(provision_backend.slapd_command_escaped)
1848 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1849 provision_backend.ldapdir)
1851 result = ProvisionResult()
1852 result.domaindn = domaindn
1853 result.paths = paths
1854 result.names = names
1856 result.samdb = samdb
1857 result.idmap = idmap
1861 def provision_become_dc(smbconf=None, targetdir=None,
1862 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1863 serverdn=None, domain=None, hostname=None, domainsid=None,
1864 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1865 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1866 dns_backend=None, root=None, nobody=None, users=None, wheel=None, backup=None,
1867 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1868 sitename=None, debuglevel=1):
1870 logger = logging.getLogger("provision")
1871 samba.set_debug_level(debuglevel)
1873 res = provision(logger, system_session(), None,
1874 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1875 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1876 configdn=configdn, serverdn=serverdn, domain=domain,
1877 hostname=hostname, hostip=None, domainsid=domainsid,
1878 machinepass=machinepass, serverrole="domain controller",
1879 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
1880 res.lp.set("debuglevel", str(debuglevel))
1884 def create_phpldapadmin_config(path, ldapi_uri):
1885 """Create a PHP LDAP admin configuration file.
1887 :param path: Path to write the configuration to.
1889 setup_file(setup_path("phpldapadmin-config.php"), path,
1890 {"S4_LDAPI_URI": ldapi_uri})
1893 def create_krb5_conf(path, dnsdomain, hostname, realm):
1894 """Write out a file containing zone statements suitable for inclusion in a
1895 named.conf file (including GSS-TSIG configuration).
1897 :param path: Path of the new named.conf file.
1898 :param dnsdomain: DNS Domain name
1899 :param hostname: Local hostname
1900 :param realm: Realm name
1902 setup_file(setup_path("krb5.conf"), path, {
1903 "DNSDOMAIN": dnsdomain,
1904 "HOSTNAME": hostname,
1909 class ProvisioningError(Exception):
1910 """A generic provision error."""
1912 def __init__(self, value):
1916 return "ProvisioningError: " + self.value
1919 class InvalidNetbiosName(Exception):
1920 """A specified name was not a valid NetBIOS name."""
1921 def __init__(self, name):
1922 super(InvalidNetbiosName, self).__init__(
1923 "The name '%r' is not a valid NetBIOS name" % name)