s4-provision: set 'dcerpc endpoint servers' but not 'vfs objects'
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / provision / __init__.py
blobdb202eff4798f56721c4ba5327940d4267a30db7
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, use_ntvfs=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:
627 if use_ntvfs and not lp.get("posix:eadb"):
628 if targetdir is not None:
629 privdir = os.path.join(targetdir, "private")
630 else:
631 privdir = lp.get("private dir")
632 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
633 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
634 if targetdir is not None:
635 statedir = os.path.join(targetdir, "state")
636 else:
637 statedir = lp.get("state dir")
638 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
640 if global_param is not None:
641 for ent in global_param:
642 if global_param[ent] is not None:
643 global_settings[ent] = " ".join(global_param[ent])
645 if targetdir is not None:
646 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
647 global_settings["lock dir"] = os.path.abspath(targetdir)
648 global_settings["state directory"] = os.path.abspath(targetdir)
649 global_settings["cache directory"] = os.path.abspath(targetdir)
651 lp.set("lock dir", os.path.abspath(targetdir))
652 lp.set("state directory", os.path.abspath(targetdir))
653 lp.set("cache directory", os.path.abspath(targetdir))
655 shares = {}
656 if serverrole == "domain controller":
657 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
658 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
659 "scripts")
661 f = open(smbconf, 'w')
662 try:
663 f.write("[globals]\n")
664 for key, val in global_settings.iteritems():
665 f.write("\t%s = %s\n" % (key, val))
666 f.write("\n")
668 for name, path in shares.iteritems():
669 f.write("[%s]\n" % name)
670 f.write("\tpath = %s\n" % path)
671 f.write("\tread only = no\n")
672 f.write("\n")
673 finally:
674 f.close()
675 # reload the smb.conf
676 lp.load(smbconf)
678 # and dump it without any values that are the default
679 # this ensures that any smb.conf parameters that were set
680 # on the provision/join command line are set in the resulting smb.conf
681 f = open(smbconf, mode='w')
682 try:
683 lp.dump(f, False)
684 finally:
685 f.close()
688 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
689 users_gid, wheel_gid):
690 """setup reasonable name mappings for sam names to unix names.
692 :param samdb: SamDB object.
693 :param idmap: IDmap db object.
694 :param sid: The domain sid.
695 :param domaindn: The domain DN.
696 :param root_uid: uid of the UNIX root user.
697 :param nobody_uid: uid of the UNIX nobody user.
698 :param users_gid: gid of the UNIX users group.
699 :param wheel_gid: gid of the UNIX wheel group.
701 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
702 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
704 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
705 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
708 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
709 provision_backend, names, schema, serverrole,
710 erase=False):
711 """Setup the partitions for the SAM database.
713 Alternatively, provision() may call this, and then populate the database.
715 :note: This will wipe the Sam Database!
717 :note: This function always removes the local SAM LDB file. The erase
718 parameter controls whether to erase the existing data, which
719 may not be stored locally but in LDAP.
722 assert session_info is not None
724 # We use options=["modules:"] to stop the modules loading - we
725 # just want to wipe and re-initialise the database, not start it up
727 try:
728 os.unlink(samdb_path)
729 except OSError:
730 pass
732 samdb = Ldb(url=samdb_path, session_info=session_info,
733 lp=lp, options=["modules:"])
735 ldap_backend_line = "# No LDAP backend"
736 if provision_backend.type != "ldb":
737 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
739 samdb.transaction_start()
740 try:
741 logger.info("Setting up sam.ldb partitions and settings")
742 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
743 "LDAP_BACKEND_LINE": ldap_backend_line
747 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
748 "BACKEND_TYPE": provision_backend.type,
749 "SERVER_ROLE": serverrole
752 logger.info("Setting up sam.ldb rootDSE")
753 setup_samdb_rootdse(samdb, names)
754 except:
755 samdb.transaction_cancel()
756 raise
757 else:
758 samdb.transaction_commit()
761 def secretsdb_self_join(secretsdb, domain,
762 netbiosname, machinepass, domainsid=None,
763 realm=None, dnsdomain=None,
764 keytab_path=None,
765 key_version_number=1,
766 secure_channel_type=SEC_CHAN_WKSTA):
767 """Add domain join-specific bits to a secrets database.
769 :param secretsdb: Ldb Handle to the secrets database
770 :param machinepass: Machine password
772 attrs = ["whenChanged",
773 "secret",
774 "priorSecret",
775 "priorChanged",
776 "krb5Keytab",
777 "privateKeytab"]
779 if realm is not None:
780 if dnsdomain is None:
781 dnsdomain = realm.lower()
782 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
783 else:
784 dnsname = None
785 shortname = netbiosname.lower()
787 # We don't need to set msg["flatname"] here, because rdn_name will handle
788 # it, and it causes problems for modifies anyway
789 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
790 msg["secureChannelType"] = [str(secure_channel_type)]
791 msg["objectClass"] = ["top", "primaryDomain"]
792 if dnsname is not None:
793 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
794 msg["realm"] = [realm]
795 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
796 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
797 msg["privateKeytab"] = ["secrets.keytab"]
799 msg["secret"] = [machinepass]
800 msg["samAccountName"] = ["%s$" % netbiosname]
801 msg["secureChannelType"] = [str(secure_channel_type)]
802 if domainsid is not None:
803 msg["objectSid"] = [ndr_pack(domainsid)]
805 # This complex expression tries to ensure that we don't have more
806 # than one record for this SID, realm or netbios domain at a time,
807 # but we don't delete the old record that we are about to modify,
808 # because that would delete the keytab and previous password.
809 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
810 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
811 scope=ldb.SCOPE_ONELEVEL)
813 for del_msg in res:
814 secretsdb.delete(del_msg.dn)
816 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
818 if len(res) == 1:
819 msg["priorSecret"] = [res[0]["secret"][0]]
820 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
822 try:
823 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
824 except KeyError:
825 pass
827 try:
828 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
829 except KeyError:
830 pass
832 for el in msg:
833 if el != 'dn':
834 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
835 secretsdb.modify(msg)
836 secretsdb.rename(res[0].dn, msg.dn)
837 else:
838 spn = [ 'HOST/%s' % shortname ]
839 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
840 # we are a domain controller then we add servicePrincipalName
841 # entries for the keytab code to update.
842 spn.extend([ 'HOST/%s' % dnsname ])
843 msg["servicePrincipalName"] = spn
845 secretsdb.add(msg)
848 def setup_secretsdb(paths, session_info, backend_credentials, lp):
849 """Setup the secrets database.
851 :note: This function does not handle exceptions and transaction on purpose,
852 it's up to the caller to do this job.
854 :param path: Path to the secrets database.
855 :param session_info: Session info.
856 :param credentials: Credentials
857 :param lp: Loadparm context
858 :return: LDB handle for the created secrets database
860 if os.path.exists(paths.secrets):
861 os.unlink(paths.secrets)
863 keytab_path = os.path.join(paths.private_dir, paths.keytab)
864 if os.path.exists(keytab_path):
865 os.unlink(keytab_path)
867 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
868 if os.path.exists(dns_keytab_path):
869 os.unlink(dns_keytab_path)
871 path = paths.secrets
873 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
874 secrets_ldb.erase()
875 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
876 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
877 secrets_ldb.transaction_start()
878 try:
879 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
881 if (backend_credentials is not None and
882 backend_credentials.authentication_requested()):
883 if backend_credentials.get_bind_dn() is not None:
884 setup_add_ldif(secrets_ldb,
885 setup_path("secrets_simple_ldap.ldif"), {
886 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
887 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
889 else:
890 setup_add_ldif(secrets_ldb,
891 setup_path("secrets_sasl_ldap.ldif"), {
892 "LDAPADMINUSER": backend_credentials.get_username(),
893 "LDAPADMINREALM": backend_credentials.get_realm(),
894 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
896 except:
897 secrets_ldb.transaction_cancel()
898 raise
899 return secrets_ldb
902 def setup_privileges(path, session_info, lp):
903 """Setup the privileges database.
905 :param path: Path to the privileges database.
906 :param session_info: Session info.
907 :param credentials: Credentials
908 :param lp: Loadparm context
909 :return: LDB handle for the created secrets database
911 if os.path.exists(path):
912 os.unlink(path)
913 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
914 privilege_ldb.erase()
915 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
918 def setup_registry(path, session_info, lp):
919 """Setup the registry.
921 :param path: Path to the registry database
922 :param session_info: Session information
923 :param credentials: Credentials
924 :param lp: Loadparm context
926 reg = samba.registry.Registry()
927 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
928 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
929 provision_reg = setup_path("provision.reg")
930 assert os.path.exists(provision_reg)
931 reg.diff_apply(provision_reg)
934 def setup_idmapdb(path, session_info, lp):
935 """Setup the idmap database.
937 :param path: path to the idmap database
938 :param session_info: Session information
939 :param credentials: Credentials
940 :param lp: Loadparm context
942 if os.path.exists(path):
943 os.unlink(path)
945 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
946 idmap_ldb.erase()
947 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
948 return idmap_ldb
951 def setup_samdb_rootdse(samdb, names):
952 """Setup the SamDB rootdse.
954 :param samdb: Sam Database handle
956 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
957 "SCHEMADN": names.schemadn,
958 "DOMAINDN": names.domaindn,
959 "ROOTDN" : names.rootdn,
960 "CONFIGDN": names.configdn,
961 "SERVERDN": names.serverdn,
965 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
966 dnspass, domainsid, next_rid, invocationid, policyguid, policyguid_dc,
967 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
968 """Join a host to its own domain."""
969 assert isinstance(invocationid, str)
970 if ntdsguid is not None:
971 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
972 else:
973 ntdsguid_line = ""
975 if dc_rid is None:
976 dc_rid = next_rid
978 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
979 "CONFIGDN": names.configdn,
980 "SCHEMADN": names.schemadn,
981 "DOMAINDN": names.domaindn,
982 "SERVERDN": names.serverdn,
983 "INVOCATIONID": invocationid,
984 "NETBIOSNAME": names.netbiosname,
985 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
986 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
987 "DOMAINSID": str(domainsid),
988 "DCRID": str(dc_rid),
989 "SAMBA_VERSION_STRING": version,
990 "NTDSGUID": ntdsguid_line,
991 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
992 domainControllerFunctionality),
993 "RIDALLOCATIONSTART": str(next_rid + 100),
994 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
996 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
997 "POLICYGUID": policyguid,
998 "POLICYGUID_DC": policyguid_dc,
999 "DNSDOMAIN": names.dnsdomain,
1000 "DOMAINDN": names.domaindn})
1002 # If we are setting up a subdomain, then this has been replicated in, so we
1003 # don't need to add it
1004 if fill == FILL_FULL:
1005 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1006 "CONFIGDN": names.configdn,
1007 "SCHEMADN": names.schemadn,
1008 "DOMAINDN": names.domaindn,
1009 "SERVERDN": names.serverdn,
1010 "INVOCATIONID": invocationid,
1011 "NETBIOSNAME": names.netbiosname,
1012 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1013 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1014 "DOMAINSID": str(domainsid),
1015 "DCRID": str(dc_rid),
1016 "SAMBA_VERSION_STRING": version,
1017 "NTDSGUID": ntdsguid_line,
1018 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1019 domainControllerFunctionality)})
1021 # Setup fSMORoleOwner entries to point at the newly created DC entry
1022 setup_modify_ldif(samdb,
1023 setup_path("provision_self_join_modify_config.ldif"), {
1024 "CONFIGDN": names.configdn,
1025 "SCHEMADN": names.schemadn,
1026 "DEFAULTSITE": names.sitename,
1027 "NETBIOSNAME": names.netbiosname,
1028 "SERVERDN": names.serverdn,
1031 system_session_info = system_session()
1032 samdb.set_session_info(system_session_info)
1033 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1034 # modify a serverReference under cn=config when we are a subdomain, we must
1035 # be system due to ACLs
1036 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1037 "DOMAINDN": names.domaindn,
1038 "SERVERDN": names.serverdn,
1039 "NETBIOSNAME": names.netbiosname,
1042 samdb.set_session_info(admin_session_info)
1044 # This is Samba4 specific and should be replaced by the correct
1045 # DNS AD-style setup
1046 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1047 "DNSDOMAIN": names.dnsdomain,
1048 "DOMAINDN": names.domaindn,
1049 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1050 "HOSTNAME" : names.hostname,
1051 "DNSNAME" : '%s.%s' % (
1052 names.netbiosname.lower(), names.dnsdomain.lower())
1056 def getpolicypath(sysvolpath, dnsdomain, guid):
1057 """Return the physical path of policy given its guid.
1059 :param sysvolpath: Path to the sysvol folder
1060 :param dnsdomain: DNS name of the AD domain
1061 :param guid: The GUID of the policy
1062 :return: A string with the complete path to the policy folder
1064 if guid[0] != "{":
1065 guid = "{%s}" % guid
1066 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1067 return policy_path
1070 def create_gpo_struct(policy_path):
1071 if not os.path.exists(policy_path):
1072 os.makedirs(policy_path, 0775)
1073 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1074 try:
1075 f.write("[General]\r\nVersion=0")
1076 finally:
1077 f.close()
1078 p = os.path.join(policy_path, "MACHINE")
1079 if not os.path.exists(p):
1080 os.makedirs(p, 0775)
1081 p = os.path.join(policy_path, "USER")
1082 if not os.path.exists(p):
1083 os.makedirs(p, 0775)
1086 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1087 """Create the default GPO for a domain
1089 :param sysvolpath: Physical path for the sysvol folder
1090 :param dnsdomain: DNS domain name of the AD domain
1091 :param policyguid: GUID of the default domain policy
1092 :param policyguid_dc: GUID of the default domain controler policy
1094 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1095 create_gpo_struct(policy_path)
1097 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1098 create_gpo_struct(policy_path)
1101 def setup_samdb(path, session_info, provision_backend, lp, names,
1102 logger, fill, serverrole, schema, am_rodc=False):
1103 """Setup a complete SAM Database.
1105 :note: This will wipe the main SAM database file!
1108 # Also wipes the database
1109 setup_samdb_partitions(path, logger=logger, lp=lp,
1110 provision_backend=provision_backend, session_info=session_info,
1111 names=names, serverrole=serverrole, schema=schema)
1113 # Load the database, but don's load the global schema and don't connect
1114 # quite yet
1115 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1116 credentials=provision_backend.credentials, lp=lp,
1117 global_schema=False, am_rodc=am_rodc)
1119 logger.info("Pre-loading the Samba 4 and AD schema")
1121 # Load the schema from the one we computed earlier
1122 samdb.set_schema(schema)
1124 # Set the NTDS settings DN manually - in order to have it already around
1125 # before the provisioned tree exists and we connect
1126 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1128 # And now we can connect to the DB - the schema won't be loaded from the
1129 # DB
1130 samdb.connect(path)
1132 return samdb
1135 def fill_samdb(samdb, lp, names,
1136 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1137 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1138 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1139 next_rid=None, dc_rid=None):
1141 if next_rid is None:
1142 next_rid = 1000
1144 # Provision does not make much sense values larger than 1000000000
1145 # as the upper range of the rIDAvailablePool is 1073741823 and
1146 # we don't want to create a domain that cannot allocate rids.
1147 if next_rid < 1000 or next_rid > 1000000000:
1148 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1149 error += "the valid range is %u-%u. The default is %u." % (
1150 1000, 1000000000, 1000)
1151 raise ProvisioningError(error)
1153 # ATTENTION: Do NOT change these default values without discussion with the
1154 # team and/or release manager. They have a big impact on the whole program!
1155 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1157 if dom_for_fun_level is None:
1158 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1160 if dom_for_fun_level > domainControllerFunctionality:
1161 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!")
1163 domainFunctionality = dom_for_fun_level
1164 forestFunctionality = dom_for_fun_level
1166 # Set the NTDS settings DN manually - in order to have it already around
1167 # before the provisioned tree exists and we connect
1168 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1170 samdb.transaction_start()
1171 try:
1172 # Set the domain functionality levels onto the database.
1173 # Various module (the password_hash module in particular) need
1174 # to know what level of AD we are emulating.
1176 # These will be fixed into the database via the database
1177 # modifictions below, but we need them set from the start.
1178 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1179 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1180 samdb.set_opaque_integer("domainControllerFunctionality",
1181 domainControllerFunctionality)
1183 samdb.set_domain_sid(str(domainsid))
1184 samdb.set_invocation_id(invocationid)
1186 logger.info("Adding DomainDN: %s" % names.domaindn)
1188 # impersonate domain admin
1189 admin_session_info = admin_session(lp, str(domainsid))
1190 samdb.set_session_info(admin_session_info)
1191 if domainguid is not None:
1192 domainguid_line = "objectGUID: %s\n-" % domainguid
1193 else:
1194 domainguid_line = ""
1196 descr = b64encode(get_domain_descriptor(domainsid))
1197 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1198 "DOMAINDN": names.domaindn,
1199 "DOMAINSID": str(domainsid),
1200 "DESCRIPTOR": descr,
1201 "DOMAINGUID": domainguid_line
1204 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1205 "DOMAINDN": names.domaindn,
1206 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1207 "NEXTRID": str(next_rid),
1208 "DEFAULTSITE": names.sitename,
1209 "CONFIGDN": names.configdn,
1210 "POLICYGUID": policyguid,
1211 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1212 "SAMBA_VERSION_STRING": version
1215 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1216 if fill == FILL_FULL:
1217 logger.info("Adding configuration container")
1218 descr = b64encode(get_config_descriptor(domainsid))
1219 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1220 "CONFIGDN": names.configdn,
1221 "DESCRIPTOR": descr,
1224 # The LDIF here was created when the Schema object was constructed
1225 logger.info("Setting up sam.ldb schema")
1226 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1227 samdb.modify_ldif(schema.schema_dn_modify)
1228 samdb.write_prefixes_from_schema()
1229 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1230 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1231 {"SCHEMADN": names.schemadn})
1233 # Now register this container in the root of the forest
1234 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1235 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1236 "subRefs")
1238 except:
1239 samdb.transaction_cancel()
1240 raise
1241 else:
1242 samdb.transaction_commit()
1244 samdb.transaction_start()
1245 try:
1246 samdb.invocation_id = invocationid
1248 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1249 if fill == FILL_FULL:
1250 logger.info("Setting up sam.ldb configuration data")
1251 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1252 "CONFIGDN": names.configdn,
1253 "NETBIOSNAME": names.netbiosname,
1254 "DEFAULTSITE": names.sitename,
1255 "DNSDOMAIN": names.dnsdomain,
1256 "DOMAIN": names.domain,
1257 "SCHEMADN": names.schemadn,
1258 "DOMAINDN": names.domaindn,
1259 "SERVERDN": names.serverdn,
1260 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1261 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1264 logger.info("Setting up display specifiers")
1265 display_specifiers_ldif = read_ms_ldif(
1266 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1267 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1268 {"CONFIGDN": names.configdn})
1269 check_all_substituted(display_specifiers_ldif)
1270 samdb.add_ldif(display_specifiers_ldif)
1272 logger.info("Adding users container")
1273 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1274 "DOMAINDN": names.domaindn})
1275 logger.info("Modifying users container")
1276 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1277 "DOMAINDN": names.domaindn})
1278 logger.info("Adding computers container")
1279 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1280 "DOMAINDN": names.domaindn})
1281 logger.info("Modifying computers container")
1282 setup_modify_ldif(samdb,
1283 setup_path("provision_computers_modify.ldif"), {
1284 "DOMAINDN": names.domaindn})
1285 logger.info("Setting up sam.ldb data")
1286 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1287 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1288 "DOMAINDN": names.domaindn,
1289 "NETBIOSNAME": names.netbiosname,
1290 "DEFAULTSITE": names.sitename,
1291 "CONFIGDN": names.configdn,
1292 "SERVERDN": names.serverdn,
1293 "RIDAVAILABLESTART": str(next_rid + 600),
1294 "POLICYGUID_DC": policyguid_dc
1297 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1298 if fill == FILL_FULL:
1299 setup_modify_ldif(samdb,
1300 setup_path("provision_configuration_references.ldif"), {
1301 "CONFIGDN": names.configdn,
1302 "SCHEMADN": names.schemadn})
1304 logger.info("Setting up well known security principals")
1305 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1306 "CONFIGDN": names.configdn,
1309 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1310 setup_modify_ldif(samdb,
1311 setup_path("provision_basedn_references.ldif"),
1312 {"DOMAINDN": names.domaindn})
1314 logger.info("Setting up sam.ldb users and groups")
1315 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1316 "DOMAINDN": names.domaindn,
1317 "DOMAINSID": str(domainsid),
1318 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1319 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1322 logger.info("Setting up self join")
1323 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1324 invocationid=invocationid,
1325 dnspass=dnspass,
1326 machinepass=machinepass,
1327 domainsid=domainsid,
1328 next_rid=next_rid,
1329 dc_rid=dc_rid,
1330 policyguid=policyguid,
1331 policyguid_dc=policyguid_dc,
1332 domainControllerFunctionality=domainControllerFunctionality,
1333 ntdsguid=ntdsguid)
1335 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1336 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1337 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1338 assert isinstance(names.ntdsguid, str)
1339 except:
1340 samdb.transaction_cancel()
1341 raise
1342 else:
1343 samdb.transaction_commit()
1344 return samdb
1347 FILL_FULL = "FULL"
1348 FILL_SUBDOMAIN = "SUBDOMAIN"
1349 FILL_NT4SYNC = "NT4SYNC"
1350 FILL_DRS = "DRS"
1351 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1352 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)"
1355 def set_dir_acl(path, acl, lp, domsid):
1356 setntacl(lp, path, acl, domsid)
1357 for root, dirs, files in os.walk(path, topdown=False):
1358 for name in files:
1359 setntacl(lp, os.path.join(root, name), acl, domsid)
1360 for name in dirs:
1361 setntacl(lp, os.path.join(root, name), acl, domsid)
1364 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1365 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1366 folders beneath.
1368 :param sysvol: Physical path for the sysvol folder
1369 :param dnsdomain: The DNS name of the domain
1370 :param domainsid: The SID of the domain
1371 :param domaindn: The DN of the domain (ie. DC=...)
1372 :param samdb: An LDB object on the SAM db
1373 :param lp: an LP object
1376 # Set ACL for GPO root folder
1377 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1378 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1380 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1381 attrs=["cn", "nTSecurityDescriptor"],
1382 expression="", scope=ldb.SCOPE_ONELEVEL)
1384 for policy in res:
1385 acl = ndr_unpack(security.descriptor,
1386 str(policy["nTSecurityDescriptor"])).as_sddl()
1387 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1388 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1389 str(domainsid))
1392 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1393 lp):
1394 """Set the ACL for the sysvol share and the subfolders
1396 :param samdb: An LDB object on the SAM db
1397 :param netlogon: Physical path for the netlogon folder
1398 :param sysvol: Physical path for the sysvol folder
1399 :param gid: The GID of the "Domain adminstrators" group
1400 :param domainsid: The SID of the domain
1401 :param dnsdomain: The DNS name of the domain
1402 :param domaindn: The DN of the domain (ie. DC=...)
1405 try:
1406 os.chown(sysvol, -1, gid)
1407 except OSError:
1408 canchown = False
1409 else:
1410 canchown = True
1412 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1413 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1414 for root, dirs, files in os.walk(sysvol, topdown=False):
1415 for name in files:
1416 if canchown:
1417 os.chown(os.path.join(root, name), -1, gid)
1418 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1419 for name in dirs:
1420 if canchown:
1421 os.chown(os.path.join(root, name), -1, gid)
1422 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1424 # Set acls on Policy folder and policies folders
1425 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1428 def interface_ips_v4(lp):
1429 '''return only IPv4 IPs'''
1430 ips = samba.interface_ips(lp, False)
1431 ret = []
1432 for i in ips:
1433 if i.find(':') == -1:
1434 ret.append(i)
1435 return ret
1437 def interface_ips_v6(lp, linklocal=False):
1438 '''return only IPv6 IPs'''
1439 ips = samba.interface_ips(lp, False)
1440 ret = []
1441 for i in ips:
1442 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1443 ret.append(i)
1444 return ret
1447 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1448 domainsid, schema=None,
1449 targetdir=None, samdb_fill=FILL_FULL,
1450 hostip=None, hostip6=None,
1451 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1452 domainguid=None, policyguid=None, policyguid_dc=None,
1453 invocationid=None, machinepass=None, ntdsguid=None,
1454 dns_backend=None, dnspass=None,
1455 serverrole=None, dom_for_fun_level=None,
1456 am_rodc=False, lp=None):
1457 # create/adapt the group policy GUIDs
1458 # Default GUID for default policy are described at
1459 # "How Core Group Policy Works"
1460 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1461 if policyguid is None:
1462 policyguid = DEFAULT_POLICY_GUID
1463 policyguid = policyguid.upper()
1464 if policyguid_dc is None:
1465 policyguid_dc = DEFAULT_DC_POLICY_GUID
1466 policyguid_dc = policyguid_dc.upper()
1468 if invocationid is None:
1469 invocationid = str(uuid.uuid4())
1471 if krbtgtpass is None:
1472 krbtgtpass = samba.generate_random_password(128, 255)
1473 if machinepass is None:
1474 machinepass = samba.generate_random_password(128, 255)
1475 if dnspass is None:
1476 dnspass = samba.generate_random_password(128, 255)
1478 samdb = fill_samdb(samdb, lp, names, logger=logger,
1479 domainsid=domainsid, schema=schema, domainguid=domainguid,
1480 policyguid=policyguid, policyguid_dc=policyguid_dc,
1481 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1482 invocationid=invocationid, machinepass=machinepass,
1483 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1484 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1485 next_rid=next_rid, dc_rid=dc_rid)
1487 if serverrole == "domain controller":
1488 # Set up group policies (domain policy and domain controller
1489 # policy)
1490 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1491 policyguid_dc)
1492 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.wheel_gid,
1493 domainsid, names.dnsdomain, names.domaindn, lp)
1495 secretsdb_self_join(secrets_ldb, domain=names.domain,
1496 realm=names.realm, dnsdomain=names.dnsdomain,
1497 netbiosname=names.netbiosname, domainsid=domainsid,
1498 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1500 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1501 # In future, this might be determined from some configuration
1502 kerberos_enctypes = str(ENC_ALL_TYPES)
1504 try:
1505 msg = ldb.Message(ldb.Dn(samdb,
1506 samdb.searchone("distinguishedName",
1507 expression="samAccountName=%s$" % names.netbiosname,
1508 scope=ldb.SCOPE_SUBTREE)))
1509 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1510 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1511 name="msDS-SupportedEncryptionTypes")
1512 samdb.modify(msg)
1513 except ldb.LdbError, (enum, estr):
1514 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1515 # It might be that this attribute does not exist in this schema
1516 raise
1518 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1519 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1520 dnspass=dnspass, os_level=dom_for_fun_level,
1521 targetdir=targetdir, site=DEFAULTSITE)
1523 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1524 attribute="objectGUID")
1525 assert isinstance(domainguid, str)
1527 lastProvisionUSNs = get_last_provision_usn(samdb)
1528 maxUSN = get_max_usn(samdb, str(names.rootdn))
1529 if lastProvisionUSNs is not None:
1530 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1531 else:
1532 set_provision_usn(samdb, 0, maxUSN, invocationid)
1534 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1535 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1536 { 'NTDSGUID' : names.ntdsguid })
1538 # fix any dangling GUIDs from the provision
1539 logger.info("Fixing provision GUIDs")
1540 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1541 quiet=True)
1542 samdb.transaction_start()
1543 try:
1544 # a small number of GUIDs are missing because of ordering issues in the
1545 # provision code
1546 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1547 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1548 scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1549 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1550 scope=ldb.SCOPE_ONELEVEL,
1551 attrs=['ipsecOwnersReference',
1552 'ipsecFilterReference',
1553 'ipsecISAKMPReference',
1554 'ipsecNegotiationPolicyReference',
1555 'ipsecNFAReference'])
1556 except:
1557 samdb.transaction_cancel()
1558 raise
1559 else:
1560 samdb.transaction_commit()
1563 _ROLES_MAP = {
1564 "ROLE_STANDALONE": "standalone",
1565 "ROLE_DOMAIN_MEMBER": "member server",
1566 "ROLE_DOMAIN_BDC": "domain controller",
1567 "ROLE_DOMAIN_PDC": "domain controller",
1568 "dc": "domain controller",
1569 "member": "member server",
1570 "domain controller": "domain controller",
1571 "member server": "member server",
1572 "standalone": "standalone",
1576 def sanitize_server_role(role):
1577 """Sanitize a server role name.
1579 :param role: Server role
1580 :raise ValueError: If the role can not be interpreted
1581 :return: Sanitized server role (one of "member server",
1582 "domain controller", "standalone")
1584 try:
1585 return _ROLES_MAP[role]
1586 except KeyError:
1587 raise ValueError(role)
1590 def provision(logger, session_info, credentials, smbconf=None,
1591 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1592 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1593 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1594 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1595 domainguid=None, policyguid=None, policyguid_dc=None,
1596 dns_backend=None, dnspass=None,
1597 invocationid=None, machinepass=None, ntdsguid=None,
1598 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1599 serverrole=None, dom_for_fun_level=None,
1600 backend_type=None, sitename=None,
1601 ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1602 useeadb=False, am_rodc=False,
1603 lp=None, use_ntvfs=True):
1604 """Provision samba4
1606 :note: caution, this wipes all existing data!
1609 try:
1610 serverrole = sanitize_server_role(serverrole)
1611 except ValueError:
1612 raise ProvisioningError('server role (%s) should be one of "domain controller", "member server", "standalone"' % serverrole)
1614 if ldapadminpass is None:
1615 # Make a new, random password between Samba and it's LDAP server
1616 ldapadminpass = samba.generate_random_password(128, 255)
1618 if backend_type is None:
1619 backend_type = "ldb"
1621 if domainsid is None:
1622 domainsid = security.random_sid()
1623 else:
1624 domainsid = security.dom_sid(domainsid)
1626 sid_generator = "internal"
1627 if backend_type == "fedora-ds":
1628 sid_generator = "backend"
1630 root_uid = findnss_uid([root or "root"])
1631 nobody_uid = findnss_uid([nobody or "nobody"])
1632 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1633 if wheel is None:
1634 wheel_gid = findnss_gid(["wheel", "adm"])
1635 else:
1636 wheel_gid = findnss_gid([wheel])
1637 try:
1638 bind_gid = findnss_gid(["bind", "named"])
1639 except KeyError:
1640 bind_gid = None
1642 if targetdir is not None:
1643 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1644 elif smbconf is None:
1645 smbconf = samba.param.default_path()
1646 if not os.path.exists(os.path.dirname(smbconf)):
1647 os.makedirs(os.path.dirname(smbconf))
1649 server_services = []
1650 global_param = {}
1651 if dns_backend == "SAMBA_INTERNAL":
1652 server_services.append("+dns")
1654 if not use_ntvfs:
1655 server_services.append("-smb")
1656 server_services.append("+s3fs")
1657 global_param["dcerpc endpoint servers"] = ["-winreg", "-srvsvc"]
1659 if len(server_services) > 0:
1660 global_param["server services"] = server_services
1662 # only install a new smb.conf if there isn't one there already
1663 if os.path.exists(smbconf):
1664 # if Samba Team members can't figure out the weird errors
1665 # loading an empty smb.conf gives, then we need to be smarter.
1666 # Pretend it just didn't exist --abartlet
1667 f = open(smbconf, 'r')
1668 try:
1669 data = f.read().lstrip()
1670 finally:
1671 f.close()
1672 if data is None or data == "":
1673 make_smbconf(smbconf, hostname, domain, realm,
1674 targetdir, serverrole=serverrole,
1675 sid_generator=sid_generator, eadb=useeadb, use_ntvfs=use_ntvfs,
1676 lp=lp, global_param=global_param)
1677 else:
1678 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1679 serverrole=serverrole, sid_generator=sid_generator,
1680 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1682 if lp is None:
1683 lp = samba.param.LoadParm()
1684 lp.load(smbconf)
1685 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1686 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1687 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1688 sitename=sitename, rootdn=rootdn)
1689 paths = provision_paths_from_lp(lp, names.dnsdomain)
1691 paths.bind_gid = bind_gid
1692 paths.wheel_gid = wheel_gid
1694 if hostip is None:
1695 logger.info("Looking up IPv4 addresses")
1696 hostips = interface_ips_v4(lp)
1697 if len(hostips) > 0:
1698 hostip = hostips[0]
1699 if len(hostips) > 1:
1700 logger.warning("More than one IPv4 address found. Using %s",
1701 hostip)
1702 if hostip == "127.0.0.1":
1703 hostip = None
1704 if hostip is None:
1705 logger.warning("No IPv4 address will be assigned")
1707 if hostip6 is None:
1708 logger.info("Looking up IPv6 addresses")
1709 hostips = interface_ips_v6(lp, linklocal=False)
1710 if hostips:
1711 hostip6 = hostips[0]
1712 if len(hostips) > 1:
1713 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1714 if hostip6 is None:
1715 logger.warning("No IPv6 address will be assigned")
1717 names.hostip = hostip
1718 names.hostip6 = hostip6
1720 if serverrole is None:
1721 serverrole = lp.get("server role")
1723 if not os.path.exists(paths.private_dir):
1724 os.mkdir(paths.private_dir)
1725 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1726 os.mkdir(os.path.join(paths.private_dir, "tls"))
1728 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1730 schema = Schema(domainsid, invocationid=invocationid,
1731 schemadn=names.schemadn)
1733 if backend_type == "ldb":
1734 provision_backend = LDBBackend(backend_type, paths=paths,
1735 lp=lp, credentials=credentials,
1736 names=names, logger=logger)
1737 elif backend_type == "existing":
1738 # If support for this is ever added back, then the URI will need to be specified again
1739 provision_backend = ExistingBackend(backend_type, paths=paths,
1740 lp=lp, credentials=credentials,
1741 names=names, logger=logger,
1742 ldap_backend_forced_uri=None)
1743 elif backend_type == "fedora-ds":
1744 provision_backend = FDSBackend(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,
1749 root=root)
1750 elif backend_type == "openldap":
1751 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1752 lp=lp, credentials=credentials,
1753 names=names, logger=logger, domainsid=domainsid,
1754 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1755 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1756 else:
1757 raise ValueError("Unknown LDAP backend type selected")
1759 provision_backend.init()
1760 provision_backend.start()
1762 # only install a new shares config db if there is none
1763 if not os.path.exists(paths.shareconf):
1764 logger.info("Setting up share.ldb")
1765 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
1766 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1768 logger.info("Setting up secrets.ldb")
1769 secrets_ldb = setup_secretsdb(paths,
1770 session_info=session_info,
1771 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1773 try:
1774 logger.info("Setting up the registry")
1775 setup_registry(paths.hklm, session_info, lp=lp)
1777 logger.info("Setting up the privileges database")
1778 setup_privileges(paths.privilege, session_info, lp=lp)
1780 logger.info("Setting up idmap db")
1781 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
1783 setup_name_mappings(idmap, sid=str(domainsid),
1784 root_uid=root_uid, nobody_uid=nobody_uid,
1785 users_gid=users_gid, wheel_gid=wheel_gid)
1787 logger.info("Setting up SAM db")
1788 samdb = setup_samdb(paths.samdb, session_info,
1789 provision_backend, lp, names, logger=logger,
1790 serverrole=serverrole,
1791 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
1793 if serverrole == "domain controller":
1794 if paths.netlogon is None:
1795 raise MissingShareError("netlogon", paths.smbconf,
1796 setup_path("provision.smb.conf.dc"))
1798 if paths.sysvol is None:
1799 raise MissingShareError("sysvol", paths.smbconf,
1800 setup_path("provision.smb.conf.dc"))
1802 if not os.path.isdir(paths.netlogon):
1803 os.makedirs(paths.netlogon, 0755)
1805 if adminpass is None:
1806 adminpass = samba.generate_random_password(12, 32)
1807 adminpass_generated = True
1808 else:
1809 adminpass_generated = False
1811 if samdb_fill == FILL_FULL:
1812 provision_fill(samdb, secrets_ldb, logger, names, paths,
1813 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
1814 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
1815 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
1816 krbtgtpass=krbtgtpass, domainguid=domainguid,
1817 policyguid=policyguid, policyguid_dc=policyguid_dc,
1818 invocationid=invocationid, machinepass=machinepass,
1819 ntdsguid=ntdsguid, dns_backend=dns_backend,
1820 dnspass=dnspass, serverrole=serverrole,
1821 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1822 lp=lp)
1824 create_krb5_conf(paths.krb5conf,
1825 dnsdomain=names.dnsdomain, hostname=names.hostname,
1826 realm=names.realm)
1827 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1828 "generated at %s", paths.krb5conf)
1830 if serverrole == "domain controller":
1831 create_dns_update_list(lp, logger, paths)
1833 backend_result = provision_backend.post_setup()
1834 provision_backend.shutdown()
1836 create_phpldapadmin_config(paths.phpldapadminconfig,
1837 ldapi_url)
1838 except:
1839 secrets_ldb.transaction_cancel()
1840 raise
1842 # Now commit the secrets.ldb to disk
1843 secrets_ldb.transaction_commit()
1845 # the commit creates the dns.keytab, now chown it
1846 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1847 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1848 try:
1849 os.chmod(dns_keytab_path, 0640)
1850 os.chown(dns_keytab_path, -1, paths.bind_gid)
1851 except OSError:
1852 if not os.environ.has_key('SAMBA_SELFTEST'):
1853 logger.info("Failed to chown %s to bind gid %u",
1854 dns_keytab_path, paths.bind_gid)
1856 result = ProvisionResult()
1857 result.server_role = serverrole
1858 result.domaindn = domaindn
1859 result.paths = paths
1860 result.names = names
1861 result.lp = lp
1862 result.samdb = samdb
1863 result.idmap = idmap
1864 result.domainsid = str(domainsid)
1866 if samdb_fill == FILL_FULL:
1867 result.adminpass_generated = adminpass_generated
1868 result.adminpass = adminpass
1869 else:
1870 result.adminpass_generated = False
1871 result.adminpass = None
1873 result.backend_result = backend_result
1875 return result
1878 def provision_become_dc(smbconf=None, targetdir=None,
1879 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1880 serverdn=None, domain=None, hostname=None, domainsid=None,
1881 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1882 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1883 dns_backend=None, root=None, nobody=None, users=None, wheel=None,
1884 backup=None, serverrole=None, ldap_backend=None,
1885 ldap_backend_type=None, sitename=None, debuglevel=1):
1887 logger = logging.getLogger("provision")
1888 samba.set_debug_level(debuglevel)
1890 res = provision(logger, system_session(), None,
1891 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1892 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1893 configdn=configdn, serverdn=serverdn, domain=domain,
1894 hostname=hostname, hostip=None, domainsid=domainsid,
1895 machinepass=machinepass, serverrole="domain controller",
1896 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
1897 res.lp.set("debuglevel", str(debuglevel))
1898 return res
1901 def create_phpldapadmin_config(path, ldapi_uri):
1902 """Create a PHP LDAP admin configuration file.
1904 :param path: Path to write the configuration to.
1906 setup_file(setup_path("phpldapadmin-config.php"), path,
1907 {"S4_LDAPI_URI": ldapi_uri})
1910 def create_krb5_conf(path, dnsdomain, hostname, realm):
1911 """Write out a file containing zone statements suitable for inclusion in a
1912 named.conf file (including GSS-TSIG configuration).
1914 :param path: Path of the new named.conf file.
1915 :param dnsdomain: DNS Domain name
1916 :param hostname: Local hostname
1917 :param realm: Realm name
1919 setup_file(setup_path("krb5.conf"), path, {
1920 "DNSDOMAIN": dnsdomain,
1921 "HOSTNAME": hostname,
1922 "REALM": realm,
1926 class ProvisioningError(Exception):
1927 """A generic provision error."""
1929 def __init__(self, value):
1930 self.value = value
1932 def __str__(self):
1933 return "ProvisioningError: " + self.value
1936 class InvalidNetbiosName(Exception):
1937 """A specified name was not a valid NetBIOS name."""
1939 def __init__(self, name):
1940 super(InvalidNetbiosName, self).__init__(
1941 "The name '%r' is not a valid NetBIOS name" % name)
1944 class MissingShareError(ProvisioningError):
1946 def __init__(self, name, smbconf, smbconf_template):
1947 super(MissingShareError, self).__init__(
1948 "Existing smb.conf does not have a [%s] share, but you are "
1949 "configuring a DC. Please either remove %s or see the template "
1950 "at %s" % (name, smbconf, smbconf_template))