Add an option to have s3fs as file server for samba4
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / provision / __init__.py
blob6bb09b7fe9e9769f11890d463ff0b07fc7fae541
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
31 import os
32 import re
33 import pwd
34 import grp
35 import logging
36 import time
37 import uuid
38 import socket
39 import urllib
40 import string
42 import ldb
44 from samba.auth import system_session, admin_session
45 import samba
46 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
47 from samba import (
48 Ldb,
49 MAX_NETBIOS_NAME_LEN,
50 check_all_substituted,
51 is_valid_netbios_char,
52 setup_file,
53 substitute_var,
54 valid_netbios_name,
55 version,
57 from samba.dcerpc import security, misc
58 from samba.dcerpc.misc import (
59 SEC_CHAN_BDC,
60 SEC_CHAN_WKSTA,
62 from samba.dsdb import (
63 DS_DOMAIN_FUNCTION_2003,
64 DS_DOMAIN_FUNCTION_2008_R2,
65 ENC_ALL_TYPES,
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 (
72 ExistingBackend,
73 FDSBackend,
74 LDBBackend,
75 OpenLDAPBackend,
77 from samba.provision.descriptor import (
78 get_config_descriptor,
79 get_domain_descriptor
81 from samba.provision.common import (
82 setup_path,
83 setup_add_ldif,
84 setup_modify_ldif,
86 from samba.provision.sambadns import (
87 setup_ad_dns,
88 create_dns_update_list
91 import samba.param
92 import samba.registry
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):
106 def __init__(self):
107 self.shareconf = None
108 self.hklm = None
109 self.hkcu = None
110 self.hkcr = None
111 self.hku = None
112 self.hkpd = None
113 self.hkpt = None
114 self.samdb = None
115 self.idmapdb = None
116 self.secrets = None
117 self.keytab = None
118 self.dns_keytab = None
119 self.dns = None
120 self.winsdb = None
121 self.private_dir = None
122 self.phpldapadminconfig = None
125 class ProvisionNames(object):
127 def __init__(self):
128 self.rootdn = None
129 self.domaindn = None
130 self.configdn = None
131 self.schemadn = None
132 self.ldapmanagerdn = None
133 self.dnsdomain = None
134 self.realm = None
135 self.netbiosname = None
136 self.domain = None
137 self.hostname = None
138 self.sitename = None
139 self.smbconf = 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)
161 # netbiosname
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
171 # partitions
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"]
189 # default site name
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]))
211 # domain guid/sid
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
220 else:
221 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
223 # policy guid
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("}","")
228 # dc policy guid
229 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
230 " Policy)",
231 base="CN=Policies,CN=System," + basedn,
232 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
233 if len(res8) == 1:
234 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
235 else:
236 names.policyid_dc = None
237 res9 = idmapdb.search(expression="(cn=%s)" %
238 (security.SID_BUILTIN_ADMINISTRATORS),
239 attrs=["xidNumber"])
240 if len(res9) == 1:
241 names.wheel_gid = res9[0]["xidNumber"]
242 else:
243 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
244 return names
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
251 upgradeprovision.
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)
263 tab = []
264 if not replace:
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)
271 tab.append(str(e))
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")
283 samdb.modify(delta)
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
289 upgradeprovision.
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"""
298 tab = []
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)
305 samdb.add(delta)
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
313 (ie. DC=foo, DC=bar)
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
331 try:
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:
337 return None
338 raise
339 if len(entry):
340 myids = []
341 range = {}
342 p = re.compile(r'-')
343 if entry[0].get("provisionnerID"):
344 for e in entry[0]["provisionnerID"]:
345 myids.append(str(e))
346 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
347 tab1 = str(r).split(';')
348 if len(tab1) == 2:
349 id = tab1[1]
350 else:
351 id = "default"
352 if (len(myids) > 0 and id not in myids):
353 continue
354 tab2 = p.split(tab1[0])
355 if range.get(id) == None:
356 range[id] = []
357 range[id].append(tab2[0])
358 range[id].append(tab2[1])
359 return range
360 else:
361 return None
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
372 def __init__(self):
373 self.server_role = None
374 self.paths = None
375 self.domaindn = None
376 self.lp = None
377 self.samdb = None
378 self.idmap = None
379 self.names = 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."""
387 logger.info(
388 "Once the above files are installed, your Samba4 server will "
389 "be ready to use")
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:
399 logger.info(
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.
430 for name in names:
431 try:
432 return nssfn(name)
433 except KeyError:
434 pass
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
481 return paths
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."""
496 if hostname is None:
497 hostname = socket.gethostname().split(".")[0]
499 netbiosname = lp.get("netbios name")
500 if netbiosname is None:
501 netbiosname = determine_netbios_name(hostname)
502 netbiosname = netbiosname.upper()
503 if not valid_netbios_name(netbiosname):
504 raise InvalidNetbiosName(netbiosname)
506 if dnsdomain is None:
507 dnsdomain = lp.get("realm")
508 if dnsdomain is None or dnsdomain == "":
509 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
511 dnsdomain = dnsdomain.lower()
513 if serverrole is None:
514 serverrole = lp.get("server role")
515 if serverrole is None:
516 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
518 serverrole = serverrole.lower()
520 realm = dnsdomain.upper()
522 if lp.get("realm") == "":
523 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
525 if lp.get("realm").upper() != realm:
526 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))
528 if lp.get("server role").lower() != serverrole:
529 raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("server role"), lp.configfile, serverrole))
531 if serverrole == "domain controller":
532 if domain is None:
533 # This will, for better or worse, default to 'WORKGROUP'
534 domain = lp.get("workgroup")
535 domain = domain.upper()
537 if lp.get("workgroup").upper() != domain:
538 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))
540 if domaindn is None:
541 domaindn = samba.dn_from_dns_name(dnsdomain)
543 if domain == netbiosname:
544 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
545 else:
546 domain = netbiosname
547 if domaindn is None:
548 domaindn = "DC=" + netbiosname
550 if not valid_netbios_name(domain):
551 raise InvalidNetbiosName(domain)
553 if hostname.upper() == realm:
554 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
555 if netbiosname.upper() == realm:
556 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
557 if domain == realm:
558 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
560 if rootdn is None:
561 rootdn = domaindn
563 if configdn is None:
564 configdn = "CN=Configuration," + rootdn
565 if schemadn is None:
566 schemadn = "CN=Schema," + configdn
568 if sitename is None:
569 sitename = DEFAULTSITE
571 names = ProvisionNames()
572 names.rootdn = rootdn
573 names.domaindn = domaindn
574 names.configdn = configdn
575 names.schemadn = schemadn
576 names.ldapmanagerdn = "CN=Manager," + rootdn
577 names.dnsdomain = dnsdomain
578 names.domain = domain
579 names.realm = realm
580 names.netbiosname = netbiosname
581 names.hostname = hostname
582 names.sitename = sitename
583 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
584 netbiosname, sitename, configdn)
586 return names
589 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
590 serverrole=None, sid_generator=None, eadb=False, lp=None,
591 global_param=None):
592 """Create a new smb.conf file based on a couple of basic settings.
594 assert smbconf is not None
596 if hostname is None:
597 hostname = socket.gethostname().split(".")[0]
599 netbiosname = determine_netbios_name(hostname)
601 if serverrole is None:
602 serverrole = "standalone"
604 if sid_generator is None:
605 sid_generator = "internal"
607 assert domain is not None
608 domain = domain.upper()
610 assert realm is not None
611 realm = realm.upper()
613 global_settings = {
614 "passdb backend": "samba4",
615 "netbios name": netbiosname,
616 "workgroup": domain,
617 "realm": realm,
618 "server role": serverrole,
621 if lp is None:
622 lp = samba.param.LoadParm()
623 #Load non-existant file
624 if os.path.exists(smbconf):
625 lp.load(smbconf)
626 if eadb and not lp.get("posix:eadb"):
627 if targetdir is not None:
628 privdir = os.path.join(targetdir, "private")
629 else:
630 privdir = lp.get("private dir")
631 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
633 if global_param is not None:
634 for ent in global_param:
635 if global_param[ent] is not None:
636 global_settings[ent] = " ".join(global_param[ent])
638 if targetdir is not None:
639 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
640 global_settings["lock dir"] = os.path.abspath(targetdir)
641 global_settings["state directory"] = os.path.abspath(targetdir)
642 global_settings["cache directory"] = os.path.abspath(targetdir)
644 lp.set("lock dir", os.path.abspath(targetdir))
645 lp.set("state directory", os.path.abspath(targetdir))
646 lp.set("cache directory", os.path.abspath(targetdir))
648 shares = {}
649 if serverrole == "domain controller":
650 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
651 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
652 "scripts")
654 f = open(smbconf, 'w')
655 try:
656 f.write("[globals]\n")
657 for key, val in global_settings.iteritems():
658 f.write("\t%s = %s\n" % (key, val))
659 f.write("\n")
661 for name, path in shares.iteritems():
662 f.write("[%s]\n" % name)
663 f.write("\tpath = %s\n" % path)
664 f.write("\tread only = no\n")
665 f.write("\n")
666 finally:
667 f.close()
668 # reload the smb.conf
669 lp.load(smbconf)
671 # and dump it without any values that are the default
672 # this ensures that any smb.conf parameters that were set
673 # on the provision/join command line are set in the resulting smb.conf
674 f = open(smbconf, mode='w')
675 try:
676 lp.dump(f, False)
677 finally:
678 f.close()
681 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
682 users_gid, wheel_gid):
683 """setup reasonable name mappings for sam names to unix names.
685 :param samdb: SamDB object.
686 :param idmap: IDmap db object.
687 :param sid: The domain sid.
688 :param domaindn: The domain DN.
689 :param root_uid: uid of the UNIX root user.
690 :param nobody_uid: uid of the UNIX nobody user.
691 :param users_gid: gid of the UNIX users group.
692 :param wheel_gid: gid of the UNIX wheel group.
694 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
695 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
697 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
698 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
701 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
702 provision_backend, names, schema, serverrole,
703 erase=False):
704 """Setup the partitions for the SAM database.
706 Alternatively, provision() may call this, and then populate the database.
708 :note: This will wipe the Sam Database!
710 :note: This function always removes the local SAM LDB file. The erase
711 parameter controls whether to erase the existing data, which
712 may not be stored locally but in LDAP.
715 assert session_info is not None
717 # We use options=["modules:"] to stop the modules loading - we
718 # just want to wipe and re-initialise the database, not start it up
720 try:
721 os.unlink(samdb_path)
722 except OSError:
723 pass
725 samdb = Ldb(url=samdb_path, session_info=session_info,
726 lp=lp, options=["modules:"])
728 ldap_backend_line = "# No LDAP backend"
729 if provision_backend.type != "ldb":
730 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
732 samdb.transaction_start()
733 try:
734 logger.info("Setting up sam.ldb partitions and settings")
735 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
736 "LDAP_BACKEND_LINE": ldap_backend_line
740 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
741 "BACKEND_TYPE": provision_backend.type,
742 "SERVER_ROLE": serverrole
745 logger.info("Setting up sam.ldb rootDSE")
746 setup_samdb_rootdse(samdb, names)
747 except:
748 samdb.transaction_cancel()
749 raise
750 else:
751 samdb.transaction_commit()
754 def secretsdb_self_join(secretsdb, domain,
755 netbiosname, machinepass, domainsid=None,
756 realm=None, dnsdomain=None,
757 keytab_path=None,
758 key_version_number=1,
759 secure_channel_type=SEC_CHAN_WKSTA):
760 """Add domain join-specific bits to a secrets database.
762 :param secretsdb: Ldb Handle to the secrets database
763 :param machinepass: Machine password
765 attrs = ["whenChanged",
766 "secret",
767 "priorSecret",
768 "priorChanged",
769 "krb5Keytab",
770 "privateKeytab"]
772 if realm is not None:
773 if dnsdomain is None:
774 dnsdomain = realm.lower()
775 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
776 else:
777 dnsname = None
778 shortname = netbiosname.lower()
780 # We don't need to set msg["flatname"] here, because rdn_name will handle
781 # it, and it causes problems for modifies anyway
782 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
783 msg["secureChannelType"] = [str(secure_channel_type)]
784 msg["objectClass"] = ["top", "primaryDomain"]
785 if dnsname is not None:
786 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
787 msg["realm"] = [realm]
788 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
789 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
790 msg["privateKeytab"] = ["secrets.keytab"]
792 msg["secret"] = [machinepass]
793 msg["samAccountName"] = ["%s$" % netbiosname]
794 msg["secureChannelType"] = [str(secure_channel_type)]
795 if domainsid is not None:
796 msg["objectSid"] = [ndr_pack(domainsid)]
798 # This complex expression tries to ensure that we don't have more
799 # than one record for this SID, realm or netbios domain at a time,
800 # but we don't delete the old record that we are about to modify,
801 # because that would delete the keytab and previous password.
802 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
803 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
804 scope=ldb.SCOPE_ONELEVEL)
806 for del_msg in res:
807 secretsdb.delete(del_msg.dn)
809 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
811 if len(res) == 1:
812 msg["priorSecret"] = [res[0]["secret"][0]]
813 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
815 try:
816 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
817 except KeyError:
818 pass
820 try:
821 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
822 except KeyError:
823 pass
825 for el in msg:
826 if el != 'dn':
827 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
828 secretsdb.modify(msg)
829 secretsdb.rename(res[0].dn, msg.dn)
830 else:
831 spn = [ 'HOST/%s' % shortname ]
832 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
833 # we are a domain controller then we add servicePrincipalName
834 # entries for the keytab code to update.
835 spn.extend([ 'HOST/%s' % dnsname ])
836 msg["servicePrincipalName"] = spn
838 secretsdb.add(msg)
841 def setup_secretsdb(paths, session_info, backend_credentials, lp):
842 """Setup the secrets database.
844 :note: This function does not handle exceptions and transaction on purpose,
845 it's up to the caller to do this job.
847 :param path: Path to the secrets database.
848 :param session_info: Session info.
849 :param credentials: Credentials
850 :param lp: Loadparm context
851 :return: LDB handle for the created secrets database
853 if os.path.exists(paths.secrets):
854 os.unlink(paths.secrets)
856 keytab_path = os.path.join(paths.private_dir, paths.keytab)
857 if os.path.exists(keytab_path):
858 os.unlink(keytab_path)
860 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
861 if os.path.exists(dns_keytab_path):
862 os.unlink(dns_keytab_path)
864 path = paths.secrets
866 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
867 secrets_ldb.erase()
868 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
869 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
870 secrets_ldb.transaction_start()
871 try:
872 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
874 if (backend_credentials is not None and
875 backend_credentials.authentication_requested()):
876 if backend_credentials.get_bind_dn() is not None:
877 setup_add_ldif(secrets_ldb,
878 setup_path("secrets_simple_ldap.ldif"), {
879 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
880 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
882 else:
883 setup_add_ldif(secrets_ldb,
884 setup_path("secrets_sasl_ldap.ldif"), {
885 "LDAPADMINUSER": backend_credentials.get_username(),
886 "LDAPADMINREALM": backend_credentials.get_realm(),
887 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
889 except:
890 secrets_ldb.transaction_cancel()
891 raise
892 return secrets_ldb
895 def setup_privileges(path, session_info, lp):
896 """Setup the privileges database.
898 :param path: Path to the privileges database.
899 :param session_info: Session info.
900 :param credentials: Credentials
901 :param lp: Loadparm context
902 :return: LDB handle for the created secrets database
904 if os.path.exists(path):
905 os.unlink(path)
906 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
907 privilege_ldb.erase()
908 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
911 def setup_registry(path, session_info, lp):
912 """Setup the registry.
914 :param path: Path to the registry database
915 :param session_info: Session information
916 :param credentials: Credentials
917 :param lp: Loadparm context
919 reg = samba.registry.Registry()
920 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
921 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
922 provision_reg = setup_path("provision.reg")
923 assert os.path.exists(provision_reg)
924 reg.diff_apply(provision_reg)
927 def setup_idmapdb(path, session_info, lp):
928 """Setup the idmap database.
930 :param path: path to the idmap database
931 :param session_info: Session information
932 :param credentials: Credentials
933 :param lp: Loadparm context
935 if os.path.exists(path):
936 os.unlink(path)
938 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
939 idmap_ldb.erase()
940 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
941 return idmap_ldb
944 def setup_samdb_rootdse(samdb, names):
945 """Setup the SamDB rootdse.
947 :param samdb: Sam Database handle
949 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
950 "SCHEMADN": names.schemadn,
951 "DOMAINDN": names.domaindn,
952 "ROOTDN" : names.rootdn,
953 "CONFIGDN": names.configdn,
954 "SERVERDN": names.serverdn,
958 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
959 dnspass, domainsid, next_rid, invocationid, policyguid, policyguid_dc,
960 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
961 """Join a host to its own domain."""
962 assert isinstance(invocationid, str)
963 if ntdsguid is not None:
964 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
965 else:
966 ntdsguid_line = ""
968 if dc_rid is None:
969 dc_rid = next_rid
971 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
972 "CONFIGDN": names.configdn,
973 "SCHEMADN": names.schemadn,
974 "DOMAINDN": names.domaindn,
975 "SERVERDN": names.serverdn,
976 "INVOCATIONID": invocationid,
977 "NETBIOSNAME": names.netbiosname,
978 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
979 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
980 "DOMAINSID": str(domainsid),
981 "DCRID": str(dc_rid),
982 "SAMBA_VERSION_STRING": version,
983 "NTDSGUID": ntdsguid_line,
984 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
985 domainControllerFunctionality),
986 "RIDALLOCATIONSTART": str(next_rid + 100),
987 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
989 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
990 "POLICYGUID": policyguid,
991 "POLICYGUID_DC": policyguid_dc,
992 "DNSDOMAIN": names.dnsdomain,
993 "DOMAINDN": names.domaindn})
995 # If we are setting up a subdomain, then this has been replicated in, so we
996 # don't need to add it
997 if fill == FILL_FULL:
998 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
999 "CONFIGDN": names.configdn,
1000 "SCHEMADN": names.schemadn,
1001 "DOMAINDN": names.domaindn,
1002 "SERVERDN": names.serverdn,
1003 "INVOCATIONID": invocationid,
1004 "NETBIOSNAME": names.netbiosname,
1005 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1006 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1007 "DOMAINSID": str(domainsid),
1008 "DCRID": str(dc_rid),
1009 "SAMBA_VERSION_STRING": version,
1010 "NTDSGUID": ntdsguid_line,
1011 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1012 domainControllerFunctionality)})
1014 # Setup fSMORoleOwner entries to point at the newly created DC entry
1015 setup_modify_ldif(samdb,
1016 setup_path("provision_self_join_modify_config.ldif"), {
1017 "CONFIGDN": names.configdn,
1018 "SCHEMADN": names.schemadn,
1019 "DEFAULTSITE": names.sitename,
1020 "NETBIOSNAME": names.netbiosname,
1021 "SERVERDN": names.serverdn,
1024 system_session_info = system_session()
1025 samdb.set_session_info(system_session_info)
1026 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1027 # modify a serverReference under cn=config when we are a subdomain, we must
1028 # be system due to ACLs
1029 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1030 "DOMAINDN": names.domaindn,
1031 "SERVERDN": names.serverdn,
1032 "NETBIOSNAME": names.netbiosname,
1035 samdb.set_session_info(admin_session_info)
1037 # This is Samba4 specific and should be replaced by the correct
1038 # DNS AD-style setup
1039 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1040 "DNSDOMAIN": names.dnsdomain,
1041 "DOMAINDN": names.domaindn,
1042 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1043 "HOSTNAME" : names.hostname,
1044 "DNSNAME" : '%s.%s' % (
1045 names.netbiosname.lower(), names.dnsdomain.lower())
1049 def getpolicypath(sysvolpath, dnsdomain, guid):
1050 """Return the physical path of policy given its guid.
1052 :param sysvolpath: Path to the sysvol folder
1053 :param dnsdomain: DNS name of the AD domain
1054 :param guid: The GUID of the policy
1055 :return: A string with the complete path to the policy folder
1057 if guid[0] != "{":
1058 guid = "{%s}" % guid
1059 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1060 return policy_path
1063 def create_gpo_struct(policy_path):
1064 if not os.path.exists(policy_path):
1065 os.makedirs(policy_path, 0775)
1066 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1067 try:
1068 f.write("[General]\r\nVersion=0")
1069 finally:
1070 f.close()
1071 p = os.path.join(policy_path, "MACHINE")
1072 if not os.path.exists(p):
1073 os.makedirs(p, 0775)
1074 p = os.path.join(policy_path, "USER")
1075 if not os.path.exists(p):
1076 os.makedirs(p, 0775)
1079 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1080 """Create the default GPO for a domain
1082 :param sysvolpath: Physical path for the sysvol folder
1083 :param dnsdomain: DNS domain name of the AD domain
1084 :param policyguid: GUID of the default domain policy
1085 :param policyguid_dc: GUID of the default domain controler policy
1087 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1088 create_gpo_struct(policy_path)
1090 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1091 create_gpo_struct(policy_path)
1094 def setup_samdb(path, session_info, provision_backend, lp, names,
1095 logger, fill, serverrole, schema, am_rodc=False):
1096 """Setup a complete SAM Database.
1098 :note: This will wipe the main SAM database file!
1101 # Also wipes the database
1102 setup_samdb_partitions(path, logger=logger, lp=lp,
1103 provision_backend=provision_backend, session_info=session_info,
1104 names=names, serverrole=serverrole, schema=schema)
1106 # Load the database, but don's load the global schema and don't connect
1107 # quite yet
1108 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1109 credentials=provision_backend.credentials, lp=lp,
1110 global_schema=False, am_rodc=am_rodc)
1112 logger.info("Pre-loading the Samba 4 and AD schema")
1114 # Load the schema from the one we computed earlier
1115 samdb.set_schema(schema)
1117 # Set the NTDS settings DN manually - in order to have it already around
1118 # before the provisioned tree exists and we connect
1119 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1121 # And now we can connect to the DB - the schema won't be loaded from the
1122 # DB
1123 samdb.connect(path)
1125 return samdb
1128 def fill_samdb(samdb, lp, names,
1129 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1130 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1131 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1132 next_rid=None, dc_rid=None):
1134 if next_rid is None:
1135 next_rid = 1000
1137 # Provision does not make much sense values larger than 1000000000
1138 # as the upper range of the rIDAvailablePool is 1073741823 and
1139 # we don't want to create a domain that cannot allocate rids.
1140 if next_rid < 1000 or next_rid > 1000000000:
1141 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1142 error += "the valid range is %u-%u. The default is %u." % (
1143 1000, 1000000000, 1000)
1144 raise ProvisioningError(error)
1146 # ATTENTION: Do NOT change these default values without discussion with the
1147 # team and/or release manager. They have a big impact on the whole program!
1148 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1150 if dom_for_fun_level is None:
1151 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1153 if dom_for_fun_level > domainControllerFunctionality:
1154 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!")
1156 domainFunctionality = dom_for_fun_level
1157 forestFunctionality = dom_for_fun_level
1159 # Set the NTDS settings DN manually - in order to have it already around
1160 # before the provisioned tree exists and we connect
1161 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1163 samdb.transaction_start()
1164 try:
1165 # Set the domain functionality levels onto the database.
1166 # Various module (the password_hash module in particular) need
1167 # to know what level of AD we are emulating.
1169 # These will be fixed into the database via the database
1170 # modifictions below, but we need them set from the start.
1171 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1172 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1173 samdb.set_opaque_integer("domainControllerFunctionality",
1174 domainControllerFunctionality)
1176 samdb.set_domain_sid(str(domainsid))
1177 samdb.set_invocation_id(invocationid)
1179 logger.info("Adding DomainDN: %s" % names.domaindn)
1181 # impersonate domain admin
1182 admin_session_info = admin_session(lp, str(domainsid))
1183 samdb.set_session_info(admin_session_info)
1184 if domainguid is not None:
1185 domainguid_line = "objectGUID: %s\n-" % domainguid
1186 else:
1187 domainguid_line = ""
1189 descr = b64encode(get_domain_descriptor(domainsid))
1190 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1191 "DOMAINDN": names.domaindn,
1192 "DOMAINSID": str(domainsid),
1193 "DESCRIPTOR": descr,
1194 "DOMAINGUID": domainguid_line
1197 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1198 "DOMAINDN": names.domaindn,
1199 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1200 "NEXTRID": str(next_rid),
1201 "DEFAULTSITE": names.sitename,
1202 "CONFIGDN": names.configdn,
1203 "POLICYGUID": policyguid,
1204 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1205 "SAMBA_VERSION_STRING": version
1208 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1209 if fill == FILL_FULL:
1210 logger.info("Adding configuration container")
1211 descr = b64encode(get_config_descriptor(domainsid))
1212 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1213 "CONFIGDN": names.configdn,
1214 "DESCRIPTOR": descr,
1217 # The LDIF here was created when the Schema object was constructed
1218 logger.info("Setting up sam.ldb schema")
1219 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1220 samdb.modify_ldif(schema.schema_dn_modify)
1221 samdb.write_prefixes_from_schema()
1222 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1223 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1224 {"SCHEMADN": names.schemadn})
1226 # Now register this container in the root of the forest
1227 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1228 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1229 "subRefs")
1231 except:
1232 samdb.transaction_cancel()
1233 raise
1234 else:
1235 samdb.transaction_commit()
1237 samdb.transaction_start()
1238 try:
1239 samdb.invocation_id = invocationid
1241 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1242 if fill == FILL_FULL:
1243 logger.info("Setting up sam.ldb configuration data")
1244 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1245 "CONFIGDN": names.configdn,
1246 "NETBIOSNAME": names.netbiosname,
1247 "DEFAULTSITE": names.sitename,
1248 "DNSDOMAIN": names.dnsdomain,
1249 "DOMAIN": names.domain,
1250 "SCHEMADN": names.schemadn,
1251 "DOMAINDN": names.domaindn,
1252 "SERVERDN": names.serverdn,
1253 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1254 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1257 logger.info("Setting up display specifiers")
1258 display_specifiers_ldif = read_ms_ldif(
1259 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1260 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1261 {"CONFIGDN": names.configdn})
1262 check_all_substituted(display_specifiers_ldif)
1263 samdb.add_ldif(display_specifiers_ldif)
1265 logger.info("Adding users container")
1266 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1267 "DOMAINDN": names.domaindn})
1268 logger.info("Modifying users container")
1269 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1270 "DOMAINDN": names.domaindn})
1271 logger.info("Adding computers container")
1272 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1273 "DOMAINDN": names.domaindn})
1274 logger.info("Modifying computers container")
1275 setup_modify_ldif(samdb,
1276 setup_path("provision_computers_modify.ldif"), {
1277 "DOMAINDN": names.domaindn})
1278 logger.info("Setting up sam.ldb data")
1279 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1280 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1281 "DOMAINDN": names.domaindn,
1282 "NETBIOSNAME": names.netbiosname,
1283 "DEFAULTSITE": names.sitename,
1284 "CONFIGDN": names.configdn,
1285 "SERVERDN": names.serverdn,
1286 "RIDAVAILABLESTART": str(next_rid + 600),
1287 "POLICYGUID_DC": policyguid_dc
1290 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1291 if fill == FILL_FULL:
1292 setup_modify_ldif(samdb,
1293 setup_path("provision_configuration_references.ldif"), {
1294 "CONFIGDN": names.configdn,
1295 "SCHEMADN": names.schemadn})
1297 logger.info("Setting up well known security principals")
1298 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1299 "CONFIGDN": names.configdn,
1302 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1303 setup_modify_ldif(samdb,
1304 setup_path("provision_basedn_references.ldif"),
1305 {"DOMAINDN": names.domaindn})
1307 logger.info("Setting up sam.ldb users and groups")
1308 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1309 "DOMAINDN": names.domaindn,
1310 "DOMAINSID": str(domainsid),
1311 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1312 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1315 logger.info("Setting up self join")
1316 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1317 invocationid=invocationid,
1318 dnspass=dnspass,
1319 machinepass=machinepass,
1320 domainsid=domainsid,
1321 next_rid=next_rid,
1322 dc_rid=dc_rid,
1323 policyguid=policyguid,
1324 policyguid_dc=policyguid_dc,
1325 domainControllerFunctionality=domainControllerFunctionality,
1326 ntdsguid=ntdsguid)
1328 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1329 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1330 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1331 assert isinstance(names.ntdsguid, str)
1332 except:
1333 samdb.transaction_cancel()
1334 raise
1335 else:
1336 samdb.transaction_commit()
1337 return samdb
1340 FILL_FULL = "FULL"
1341 FILL_SUBDOMAIN = "SUBDOMAIN"
1342 FILL_NT4SYNC = "NT4SYNC"
1343 FILL_DRS = "DRS"
1344 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1345 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)"
1348 def set_dir_acl(path, acl, lp, domsid):
1349 setntacl(lp, path, acl, domsid)
1350 for root, dirs, files in os.walk(path, topdown=False):
1351 for name in files:
1352 setntacl(lp, os.path.join(root, name), acl, domsid)
1353 for name in dirs:
1354 setntacl(lp, os.path.join(root, name), acl, domsid)
1357 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1358 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1359 folders beneath.
1361 :param sysvol: Physical path for the sysvol folder
1362 :param dnsdomain: The DNS name of the domain
1363 :param domainsid: The SID of the domain
1364 :param domaindn: The DN of the domain (ie. DC=...)
1365 :param samdb: An LDB object on the SAM db
1366 :param lp: an LP object
1369 # Set ACL for GPO root folder
1370 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1371 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1373 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1374 attrs=["cn", "nTSecurityDescriptor"],
1375 expression="", scope=ldb.SCOPE_ONELEVEL)
1377 for policy in res:
1378 acl = ndr_unpack(security.descriptor,
1379 str(policy["nTSecurityDescriptor"])).as_sddl()
1380 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1381 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1382 str(domainsid))
1385 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1386 lp):
1387 """Set the ACL for the sysvol share and the subfolders
1389 :param samdb: An LDB object on the SAM db
1390 :param netlogon: Physical path for the netlogon folder
1391 :param sysvol: Physical path for the sysvol folder
1392 :param gid: The GID of the "Domain adminstrators" group
1393 :param domainsid: The SID of the domain
1394 :param dnsdomain: The DNS name of the domain
1395 :param domaindn: The DN of the domain (ie. DC=...)
1398 try:
1399 os.chown(sysvol, -1, gid)
1400 except OSError:
1401 canchown = False
1402 else:
1403 canchown = True
1405 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1406 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1407 for root, dirs, files in os.walk(sysvol, topdown=False):
1408 for name in files:
1409 if canchown:
1410 os.chown(os.path.join(root, name), -1, gid)
1411 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1412 for name in dirs:
1413 if canchown:
1414 os.chown(os.path.join(root, name), -1, gid)
1415 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1417 # Set acls on Policy folder and policies folders
1418 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1421 def interface_ips_v4(lp):
1422 '''return only IPv4 IPs'''
1423 ips = samba.interface_ips(lp, False)
1424 ret = []
1425 for i in ips:
1426 if i.find(':') == -1:
1427 ret.append(i)
1428 return ret
1430 def interface_ips_v6(lp, linklocal=False):
1431 '''return only IPv6 IPs'''
1432 ips = samba.interface_ips(lp, False)
1433 ret = []
1434 for i in ips:
1435 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1436 ret.append(i)
1437 return ret
1440 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1441 domainsid, schema=None,
1442 targetdir=None, samdb_fill=FILL_FULL,
1443 hostip=None, hostip6=None,
1444 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1445 domainguid=None, policyguid=None, policyguid_dc=None,
1446 invocationid=None, machinepass=None, ntdsguid=None,
1447 dns_backend=None, dnspass=None,
1448 serverrole=None, dom_for_fun_level=None,
1449 am_rodc=False, lp=None):
1450 # create/adapt the group policy GUIDs
1451 # Default GUID for default policy are described at
1452 # "How Core Group Policy Works"
1453 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1454 if policyguid is None:
1455 policyguid = DEFAULT_POLICY_GUID
1456 policyguid = policyguid.upper()
1457 if policyguid_dc is None:
1458 policyguid_dc = DEFAULT_DC_POLICY_GUID
1459 policyguid_dc = policyguid_dc.upper()
1461 if invocationid is None:
1462 invocationid = str(uuid.uuid4())
1464 if krbtgtpass is None:
1465 krbtgtpass = samba.generate_random_password(128, 255)
1466 if machinepass is None:
1467 machinepass = samba.generate_random_password(128, 255)
1468 if dnspass is None:
1469 dnspass = samba.generate_random_password(128, 255)
1471 samdb = fill_samdb(samdb, lp, names, logger=logger,
1472 domainsid=domainsid, schema=schema, domainguid=domainguid,
1473 policyguid=policyguid, policyguid_dc=policyguid_dc,
1474 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1475 invocationid=invocationid, machinepass=machinepass,
1476 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1477 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1478 next_rid=next_rid, dc_rid=dc_rid)
1480 if serverrole == "domain controller":
1481 # Set up group policies (domain policy and domain controller
1482 # policy)
1483 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1484 policyguid_dc)
1485 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.wheel_gid,
1486 domainsid, names.dnsdomain, names.domaindn, lp)
1488 secretsdb_self_join(secrets_ldb, domain=names.domain,
1489 realm=names.realm, dnsdomain=names.dnsdomain,
1490 netbiosname=names.netbiosname, domainsid=domainsid,
1491 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1493 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1494 # In future, this might be determined from some configuration
1495 kerberos_enctypes = str(ENC_ALL_TYPES)
1497 try:
1498 msg = ldb.Message(ldb.Dn(samdb,
1499 samdb.searchone("distinguishedName",
1500 expression="samAccountName=%s$" % names.netbiosname,
1501 scope=ldb.SCOPE_SUBTREE)))
1502 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1503 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1504 name="msDS-SupportedEncryptionTypes")
1505 samdb.modify(msg)
1506 except ldb.LdbError, (enum, estr):
1507 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1508 # It might be that this attribute does not exist in this schema
1509 raise
1511 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1512 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1513 dnspass=dnspass, os_level=dom_for_fun_level,
1514 targetdir=targetdir, site=DEFAULTSITE)
1516 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1517 attribute="objectGUID")
1518 assert isinstance(domainguid, str)
1520 lastProvisionUSNs = get_last_provision_usn(samdb)
1521 maxUSN = get_max_usn(samdb, str(names.rootdn))
1522 if lastProvisionUSNs is not None:
1523 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1524 else:
1525 set_provision_usn(samdb, 0, maxUSN, invocationid)
1527 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1528 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1529 { 'NTDSGUID' : names.ntdsguid })
1531 # fix any dangling GUIDs from the provision
1532 logger.info("Fixing provision GUIDs")
1533 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1534 quiet=True)
1535 samdb.transaction_start()
1536 try:
1537 # a small number of GUIDs are missing because of ordering issues in the
1538 # provision code
1539 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1540 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1541 scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1542 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1543 scope=ldb.SCOPE_ONELEVEL,
1544 attrs=['ipsecOwnersReference',
1545 'ipsecFilterReference',
1546 'ipsecISAKMPReference',
1547 'ipsecNegotiationPolicyReference',
1548 'ipsecNFAReference'])
1549 except:
1550 samdb.transaction_cancel()
1551 raise
1552 else:
1553 samdb.transaction_commit()
1556 _ROLES_MAP = {
1557 "ROLE_STANDALONE": "standalone",
1558 "ROLE_DOMAIN_MEMBER": "member server",
1559 "ROLE_DOMAIN_BDC": "domain controller",
1560 "ROLE_DOMAIN_PDC": "domain controller",
1561 "dc": "domain controller",
1562 "member": "member server",
1563 "domain controller": "domain controller",
1564 "member server": "member server",
1565 "standalone": "standalone",
1569 def sanitize_server_role(role):
1570 """Sanitize a server role name.
1572 :param role: Server role
1573 :raise ValueError: If the role can not be interpreted
1574 :return: Sanitized server role (one of "member server",
1575 "domain controller", "standalone")
1577 try:
1578 return _ROLES_MAP[role]
1579 except KeyError:
1580 raise ValueError(role)
1583 def provision(logger, session_info, credentials, smbconf=None,
1584 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1585 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1586 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1587 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1588 domainguid=None, policyguid=None, policyguid_dc=None,
1589 dns_backend=None, dnspass=None,
1590 invocationid=None, machinepass=None, ntdsguid=None,
1591 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1592 serverrole=None, dom_for_fun_level=None,
1593 backend_type=None, sitename=None,
1594 ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1595 useeadb=False, am_rodc=False,
1596 lp=None, use_ntvfs=True):
1597 """Provision samba4
1599 :note: caution, this wipes all existing data!
1602 try:
1603 serverrole = sanitize_server_role(serverrole)
1604 except ValueError:
1605 raise ProvisioningError('server role (%s) should be one of "domain controller", "member server", "standalone"' % serverrole)
1607 if ldapadminpass is None:
1608 # Make a new, random password between Samba and it's LDAP server
1609 ldapadminpass = samba.generate_random_password(128, 255)
1611 if backend_type is None:
1612 backend_type = "ldb"
1614 if domainsid is None:
1615 domainsid = security.random_sid()
1616 else:
1617 domainsid = security.dom_sid(domainsid)
1619 sid_generator = "internal"
1620 if backend_type == "fedora-ds":
1621 sid_generator = "backend"
1623 root_uid = findnss_uid([root or "root"])
1624 nobody_uid = findnss_uid([nobody or "nobody"])
1625 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1626 if wheel is None:
1627 wheel_gid = findnss_gid(["wheel", "adm"])
1628 else:
1629 wheel_gid = findnss_gid([wheel])
1630 try:
1631 bind_gid = findnss_gid(["bind", "named"])
1632 except KeyError:
1633 bind_gid = None
1635 if targetdir is not None:
1636 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1637 elif smbconf is None:
1638 smbconf = samba.param.default_path()
1639 if not os.path.exists(os.path.dirname(smbconf)):
1640 os.makedirs(os.path.dirname(smbconf))
1642 server_services = []
1643 global_param = {}
1644 if dns_backend == "SAMBA_INTERNAL":
1645 server_services.append("+dns")
1647 if not use_ntvfs:
1648 server_services.append("-smb")
1649 server_services.append("+s3fs")
1650 global_param["vfs objects"] = ["acl_xattr"]
1652 if len(server_services) > 0:
1653 global_param["server services"] = server_services
1655 # only install a new smb.conf if there isn't one there already
1656 if os.path.exists(smbconf):
1657 # if Samba Team members can't figure out the weird errors
1658 # loading an empty smb.conf gives, then we need to be smarter.
1659 # Pretend it just didn't exist --abartlet
1660 f = open(smbconf, 'r')
1661 try:
1662 data = f.read().lstrip()
1663 finally:
1664 f.close()
1665 if data is None or data == "":
1666 make_smbconf(smbconf, hostname, domain, realm,
1667 targetdir, serverrole=serverrole,
1668 sid_generator=sid_generator, eadb=useeadb,
1669 lp=lp, global_param=global_param)
1670 else:
1671 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1672 serverrole=serverrole, sid_generator=sid_generator,
1673 eadb=useeadb, lp=lp, global_param=global_param)
1675 if lp is None:
1676 lp = samba.param.LoadParm()
1677 lp.load(smbconf)
1678 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1679 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1680 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1681 sitename=sitename, rootdn=rootdn)
1682 paths = provision_paths_from_lp(lp, names.dnsdomain)
1684 paths.bind_gid = bind_gid
1685 paths.wheel_gid = wheel_gid
1687 if hostip is None:
1688 logger.info("Looking up IPv4 addresses")
1689 hostips = interface_ips_v4(lp)
1690 if len(hostips) > 0:
1691 hostip = hostips[0]
1692 if len(hostips) > 1:
1693 logger.warning("More than one IPv4 address found. Using %s",
1694 hostip)
1695 if hostip == "127.0.0.1":
1696 hostip = None
1697 if hostip is None:
1698 logger.warning("No IPv4 address will be assigned")
1700 if hostip6 is None:
1701 logger.info("Looking up IPv6 addresses")
1702 hostips = interface_ips_v6(lp, linklocal=False)
1703 if hostips:
1704 hostip6 = hostips[0]
1705 if len(hostips) > 1:
1706 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1707 if hostip6 is None:
1708 logger.warning("No IPv6 address will be assigned")
1710 names.hostip = hostip
1711 names.hostip6 = hostip6
1713 if serverrole is None:
1714 serverrole = lp.get("server role")
1716 if not os.path.exists(paths.private_dir):
1717 os.mkdir(paths.private_dir)
1718 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1719 os.mkdir(os.path.join(paths.private_dir, "tls"))
1721 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1723 schema = Schema(domainsid, invocationid=invocationid,
1724 schemadn=names.schemadn)
1726 if backend_type == "ldb":
1727 provision_backend = LDBBackend(backend_type, paths=paths,
1728 lp=lp, credentials=credentials,
1729 names=names, logger=logger)
1730 elif backend_type == "existing":
1731 # If support for this is ever added back, then the URI will need to be specified again
1732 provision_backend = ExistingBackend(backend_type, paths=paths,
1733 lp=lp, credentials=credentials,
1734 names=names, logger=logger,
1735 ldap_backend_forced_uri=None)
1736 elif backend_type == "fedora-ds":
1737 provision_backend = FDSBackend(backend_type, paths=paths,
1738 lp=lp, credentials=credentials,
1739 names=names, logger=logger, domainsid=domainsid,
1740 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1741 slapd_path=slapd_path,
1742 root=root)
1743 elif backend_type == "openldap":
1744 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1745 lp=lp, credentials=credentials,
1746 names=names, logger=logger, domainsid=domainsid,
1747 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1748 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1749 else:
1750 raise ValueError("Unknown LDAP backend type selected")
1752 provision_backend.init()
1753 provision_backend.start()
1755 # only install a new shares config db if there is none
1756 if not os.path.exists(paths.shareconf):
1757 logger.info("Setting up share.ldb")
1758 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
1759 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1761 logger.info("Setting up secrets.ldb")
1762 secrets_ldb = setup_secretsdb(paths,
1763 session_info=session_info,
1764 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1766 try:
1767 logger.info("Setting up the registry")
1768 setup_registry(paths.hklm, session_info, lp=lp)
1770 logger.info("Setting up the privileges database")
1771 setup_privileges(paths.privilege, session_info, lp=lp)
1773 logger.info("Setting up idmap db")
1774 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
1776 setup_name_mappings(idmap, sid=str(domainsid),
1777 root_uid=root_uid, nobody_uid=nobody_uid,
1778 users_gid=users_gid, wheel_gid=wheel_gid)
1780 logger.info("Setting up SAM db")
1781 samdb = setup_samdb(paths.samdb, session_info,
1782 provision_backend, lp, names, logger=logger,
1783 serverrole=serverrole,
1784 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
1786 if serverrole == "domain controller":
1787 if paths.netlogon is None:
1788 raise MissingShareError("netlogon", paths.smbconf,
1789 setup_path("provision.smb.conf.dc"))
1791 if paths.sysvol is None:
1792 raise MissingShareError("sysvol", paths.smbconf,
1793 setup_path("provision.smb.conf.dc"))
1795 if not os.path.isdir(paths.netlogon):
1796 os.makedirs(paths.netlogon, 0755)
1798 if adminpass is None:
1799 adminpass = samba.generate_random_password(12, 32)
1800 adminpass_generated = True
1801 else:
1802 adminpass_generated = False
1804 if samdb_fill == FILL_FULL:
1805 provision_fill(samdb, secrets_ldb, logger, names, paths,
1806 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
1807 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
1808 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
1809 krbtgtpass=krbtgtpass, domainguid=domainguid,
1810 policyguid=policyguid, policyguid_dc=policyguid_dc,
1811 invocationid=invocationid, machinepass=machinepass,
1812 ntdsguid=ntdsguid, dns_backend=dns_backend,
1813 dnspass=dnspass, serverrole=serverrole,
1814 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1815 lp=lp)
1817 create_krb5_conf(paths.krb5conf,
1818 dnsdomain=names.dnsdomain, hostname=names.hostname,
1819 realm=names.realm)
1820 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1821 "generated at %s", paths.krb5conf)
1823 if serverrole == "domain controller":
1824 create_dns_update_list(lp, logger, paths)
1826 backend_result = provision_backend.post_setup()
1827 provision_backend.shutdown()
1829 create_phpldapadmin_config(paths.phpldapadminconfig,
1830 ldapi_url)
1831 except:
1832 secrets_ldb.transaction_cancel()
1833 raise
1835 # Now commit the secrets.ldb to disk
1836 secrets_ldb.transaction_commit()
1838 # the commit creates the dns.keytab, now chown it
1839 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1840 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1841 try:
1842 os.chmod(dns_keytab_path, 0640)
1843 os.chown(dns_keytab_path, -1, paths.bind_gid)
1844 except OSError:
1845 if not os.environ.has_key('SAMBA_SELFTEST'):
1846 logger.info("Failed to chown %s to bind gid %u",
1847 dns_keytab_path, paths.bind_gid)
1849 result = ProvisionResult()
1850 result.server_role = serverrole
1851 result.domaindn = domaindn
1852 result.paths = paths
1853 result.names = names
1854 result.lp = lp
1855 result.samdb = samdb
1856 result.idmap = idmap
1857 result.domainsid = str(domainsid)
1859 if samdb_fill == FILL_FULL:
1860 result.adminpass_generated = adminpass_generated
1861 result.adminpass = adminpass
1862 else:
1863 result.adminpass_generated = False
1864 result.adminpass = None
1866 result.backend_result = backend_result
1868 return result
1871 def provision_become_dc(smbconf=None, targetdir=None,
1872 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1873 serverdn=None, domain=None, hostname=None, domainsid=None,
1874 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1875 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1876 dns_backend=None, root=None, nobody=None, users=None, wheel=None,
1877 backup=None, serverrole=None, ldap_backend=None,
1878 ldap_backend_type=None, sitename=None, debuglevel=1):
1880 logger = logging.getLogger("provision")
1881 samba.set_debug_level(debuglevel)
1883 res = provision(logger, system_session(), None,
1884 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1885 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1886 configdn=configdn, serverdn=serverdn, domain=domain,
1887 hostname=hostname, hostip=None, domainsid=domainsid,
1888 machinepass=machinepass, serverrole="domain controller",
1889 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
1890 res.lp.set("debuglevel", str(debuglevel))
1891 return res
1894 def create_phpldapadmin_config(path, ldapi_uri):
1895 """Create a PHP LDAP admin configuration file.
1897 :param path: Path to write the configuration to.
1899 setup_file(setup_path("phpldapadmin-config.php"), path,
1900 {"S4_LDAPI_URI": ldapi_uri})
1903 def create_krb5_conf(path, dnsdomain, hostname, realm):
1904 """Write out a file containing zone statements suitable for inclusion in a
1905 named.conf file (including GSS-TSIG configuration).
1907 :param path: Path of the new named.conf file.
1908 :param dnsdomain: DNS Domain name
1909 :param hostname: Local hostname
1910 :param realm: Realm name
1912 setup_file(setup_path("krb5.conf"), path, {
1913 "DNSDOMAIN": dnsdomain,
1914 "HOSTNAME": hostname,
1915 "REALM": realm,
1919 class ProvisioningError(Exception):
1920 """A generic provision error."""
1922 def __init__(self, value):
1923 self.value = value
1925 def __str__(self):
1926 return "ProvisioningError: " + self.value
1929 class InvalidNetbiosName(Exception):
1930 """A specified name was not a valid NetBIOS name."""
1932 def __init__(self, name):
1933 super(InvalidNetbiosName, self).__init__(
1934 "The name '%r' is not a valid NetBIOS name" % name)
1937 class MissingShareError(ProvisioningError):
1939 def __init__(self, name, smbconf, smbconf_template):
1940 super(MissingShareError, self).__init__(
1941 "Existing smb.conf does not have a [%s] share, but you are "
1942 "configuring a DC. Please either remove %s or see the template "
1943 "at %s" % (name, smbconf, smbconf_template))