s4:provision: set the correct nTSecurityDescriptor on CN=Partitions,CN=Configuration...
[Samba/gbeck.git] / source4 / scripting / python / samba / provision / __init__.py
blob63b1bd004db1550b7cd1dd8f6c8864ad45ecf9b5
1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba4 server
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 __docformat__ = "restructuredText"
29 from base64 import b64encode
30 import os
31 import re
32 import pwd
33 import grp
34 import logging
35 import time
36 import uuid
37 import socket
38 import urllib
39 import string
40 import tempfile
42 import ldb
44 from samba.auth import system_session, admin_session
45 import samba
46 from samba.samba3 import smbd, passdb
47 from samba.samba3 import param as s3param
48 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
49 from samba import (
50 Ldb,
51 MAX_NETBIOS_NAME_LEN,
52 check_all_substituted,
53 is_valid_netbios_char,
54 setup_file,
55 substitute_var,
56 valid_netbios_name,
57 version,
59 from samba.dcerpc import security, misc
60 from samba.dcerpc.misc import (
61 SEC_CHAN_BDC,
62 SEC_CHAN_WKSTA,
64 from samba.dsdb import (
65 DS_DOMAIN_FUNCTION_2003,
66 DS_DOMAIN_FUNCTION_2008_R2,
67 ENC_ALL_TYPES,
69 from samba.idmap import IDmapDB
70 from samba.ms_display_specifiers import read_ms_ldif
71 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
72 from samba.ndr import ndr_pack, ndr_unpack
73 from samba.provision.backend import (
74 ExistingBackend,
75 FDSBackend,
76 LDBBackend,
77 OpenLDAPBackend,
79 from samba.provision.descriptor import (
80 get_empty_descriptor,
81 get_config_descriptor,
82 get_config_partitions_descriptor,
83 get_domain_descriptor
85 from samba.provision.common import (
86 setup_path,
87 setup_add_ldif,
88 setup_modify_ldif,
90 from samba.provision.sambadns import (
91 setup_ad_dns,
92 create_dns_update_list
95 import samba.param
96 import samba.registry
97 from samba.schema import Schema
98 from samba.samdb import SamDB
99 from samba.dbchecker import dbcheck
102 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
103 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
104 DEFAULTSITE = "Default-First-Site-Name"
105 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
108 class ProvisionPaths(object):
110 def __init__(self):
111 self.shareconf = None
112 self.hklm = None
113 self.hkcu = None
114 self.hkcr = None
115 self.hku = None
116 self.hkpd = None
117 self.hkpt = None
118 self.samdb = None
119 self.idmapdb = None
120 self.secrets = None
121 self.keytab = None
122 self.dns_keytab = None
123 self.dns = None
124 self.winsdb = None
125 self.private_dir = None
126 self.state_dir = None
129 class ProvisionNames(object):
131 def __init__(self):
132 self.rootdn = None
133 self.domaindn = None
134 self.configdn = None
135 self.schemadn = None
136 self.ldapmanagerdn = None
137 self.dnsdomain = None
138 self.realm = None
139 self.netbiosname = None
140 self.domain = None
141 self.hostname = None
142 self.sitename = None
143 self.smbconf = None
146 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
147 lp):
148 """Get key provision parameters (realm, domain, ...) from a given provision
150 :param samdb: An LDB object connected to the sam.ldb file
151 :param secretsdb: An LDB object connected to the secrets.ldb file
152 :param idmapdb: An LDB object connected to the idmap.ldb file
153 :param paths: A list of path to provision object
154 :param smbconf: Path to the smb.conf file
155 :param lp: A LoadParm object
156 :return: A list of key provision parameters
158 names = ProvisionNames()
159 names.adminpass = None
161 # NT domain, kerberos realm, root dn, domain dn, domain dns name
162 names.domain = string.upper(lp.get("workgroup"))
163 names.realm = lp.get("realm")
164 names.dnsdomain = names.realm.lower()
165 basedn = samba.dn_from_dns_name(names.dnsdomain)
166 names.realm = string.upper(names.realm)
167 # netbiosname
168 # Get the netbiosname first (could be obtained from smb.conf in theory)
169 res = secretsdb.search(expression="(flatname=%s)" %
170 names.domain,base="CN=Primary Domains",
171 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
172 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
174 names.smbconf = smbconf
176 # That's a bit simplistic but it's ok as long as we have only 3
177 # partitions
178 current = samdb.search(expression="(objectClass=*)",
179 base="", scope=ldb.SCOPE_BASE,
180 attrs=["defaultNamingContext", "schemaNamingContext",
181 "configurationNamingContext","rootDomainNamingContext"])
183 names.configdn = current[0]["configurationNamingContext"]
184 configdn = str(names.configdn)
185 names.schemadn = current[0]["schemaNamingContext"]
186 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
187 current[0]["defaultNamingContext"][0]))):
188 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
189 "is not the same ..." % (paths.samdb,
190 str(current[0]["defaultNamingContext"][0]),
191 paths.smbconf, basedn)))
193 names.domaindn=current[0]["defaultNamingContext"]
194 names.rootdn=current[0]["rootDomainNamingContext"]
195 # default site name
196 res3 = samdb.search(expression="(objectClass=site)",
197 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
198 names.sitename = str(res3[0]["cn"])
200 # dns hostname and server dn
201 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
202 base="OU=Domain Controllers,%s" % basedn,
203 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
204 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
206 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
207 attrs=[], base=configdn)
208 names.serverdn = server_res[0].dn
210 # invocation id/objectguid
211 res5 = samdb.search(expression="(objectClass=*)",
212 base="CN=NTDS Settings,%s" % str(names.serverdn),
213 scope=ldb.SCOPE_BASE,
214 attrs=["invocationID", "objectGUID"])
215 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
216 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
218 # domain guid/sid
219 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
220 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
221 "objectSid","msDS-Behavior-Version" ])
222 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
223 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
224 if res6[0].get("msDS-Behavior-Version") is None or \
225 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
226 names.domainlevel = DS_DOMAIN_FUNCTION_2000
227 else:
228 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
230 # policy guid
231 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
232 base="CN=Policies,CN=System," + basedn,
233 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
234 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
235 # dc policy guid
236 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
237 " Policy)",
238 base="CN=Policies,CN=System," + basedn,
239 scope=ldb.SCOPE_ONELEVEL,
240 attrs=["cn","displayName"])
241 if len(res8) == 1:
242 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
243 else:
244 names.policyid_dc = None
246 res9 = idmapdb.search(expression="(cn=%s-%s)" %
247 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
248 attrs=["xidNumber", "type"])
249 if len(res9) != 1:
250 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
251 if res9[0]["type"][0] == "ID_TYPE_BOTH":
252 names.root_gid = res9[0]["xidNumber"][0]
253 else:
254 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
255 return names
258 def update_provision_usn(samdb, low, high, id, replace=False):
259 """Update the field provisionUSN in sam.ldb
261 This field is used to track range of USN modified by provision and
262 upgradeprovision.
263 This value is used afterward by next provision to figure out if
264 the field have been modified since last provision.
266 :param samdb: An LDB object connect to sam.ldb
267 :param low: The lowest USN modified by this upgrade
268 :param high: The highest USN modified by this upgrade
269 :param id: The invocation id of the samba's dc
270 :param replace: A boolean indicating if the range should replace any
271 existing one or appended (default)
274 tab = []
275 if not replace:
276 entry = samdb.search(base="@PROVISION",
277 scope=ldb.SCOPE_BASE,
278 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
279 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
280 if not re.search(';', e):
281 e = "%s;%s" % (e, id)
282 tab.append(str(e))
284 tab.append("%s-%s;%s" % (low, high, id))
285 delta = ldb.Message()
286 delta.dn = ldb.Dn(samdb, "@PROVISION")
287 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
288 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
289 entry = samdb.search(expression='provisionnerID=*',
290 base="@PROVISION", scope=ldb.SCOPE_BASE,
291 attrs=["provisionnerID"])
292 if len(entry) == 0 or len(entry[0]) == 0:
293 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
294 samdb.modify(delta)
297 def set_provision_usn(samdb, low, high, id):
298 """Set the field provisionUSN in sam.ldb
299 This field is used to track range of USN modified by provision and
300 upgradeprovision.
301 This value is used afterward by next provision to figure out if
302 the field have been modified since last provision.
304 :param samdb: An LDB object connect to sam.ldb
305 :param low: The lowest USN modified by this upgrade
306 :param high: The highest USN modified by this upgrade
307 :param id: The invocationId of the provision"""
309 tab = []
310 tab.append("%s-%s;%s" % (low, high, id))
312 delta = ldb.Message()
313 delta.dn = ldb.Dn(samdb, "@PROVISION")
314 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
315 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
316 samdb.add(delta)
319 def get_max_usn(samdb,basedn):
320 """ This function return the biggest USN present in the provision
322 :param samdb: A LDB object pointing to the sam.ldb
323 :param basedn: A string containing the base DN of the provision
324 (ie. DC=foo, DC=bar)
325 :return: The biggest USN in the provision"""
327 res = samdb.search(expression="objectClass=*",base=basedn,
328 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
329 controls=["search_options:1:2",
330 "server_sort:1:1:uSNChanged",
331 "paged_results:1:1"])
332 return res[0]["uSNChanged"]
335 def get_last_provision_usn(sam):
336 """Get USNs ranges modified by a provision or an upgradeprovision
338 :param sam: An LDB object pointing to the sam.ldb
339 :return: a dictionary which keys are invocation id and values are an array
340 of integer representing the different ranges
342 try:
343 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
344 base="@PROVISION", scope=ldb.SCOPE_BASE,
345 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
346 except ldb.LdbError, (ecode, emsg):
347 if ecode == ldb.ERR_NO_SUCH_OBJECT:
348 return None
349 raise
350 if len(entry) > 0:
351 myids = []
352 range = {}
353 p = re.compile(r'-')
354 if entry[0].get("provisionnerID"):
355 for e in entry[0]["provisionnerID"]:
356 myids.append(str(e))
357 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
358 tab1 = str(r).split(';')
359 if len(tab1) == 2:
360 id = tab1[1]
361 else:
362 id = "default"
363 if (len(myids) > 0 and id not in myids):
364 continue
365 tab2 = p.split(tab1[0])
366 if range.get(id) is None:
367 range[id] = []
368 range[id].append(tab2[0])
369 range[id].append(tab2[1])
370 return range
371 else:
372 return None
375 class ProvisionResult(object):
376 """Result of a provision.
378 :ivar server_role: The server role
379 :ivar paths: ProvisionPaths instance
380 :ivar domaindn: The domain dn, as string
383 def __init__(self):
384 self.server_role = None
385 self.paths = None
386 self.domaindn = None
387 self.lp = None
388 self.samdb = None
389 self.idmap = None
390 self.names = None
391 self.domainsid = None
392 self.adminpass_generated = None
393 self.adminpass = None
394 self.backend_result = None
396 def report_logger(self, logger):
397 """Report this provision result to a logger."""
398 logger.info(
399 "Once the above files are installed, your Samba4 server will "
400 "be ready to use")
401 if self.adminpass_generated:
402 logger.info("Admin password: %s", self.adminpass)
403 logger.info("Server Role: %s", self.server_role)
404 logger.info("Hostname: %s", self.names.hostname)
405 logger.info("NetBIOS Domain: %s", self.names.domain)
406 logger.info("DNS Domain: %s", self.names.dnsdomain)
407 logger.info("DOMAIN SID: %s", self.domainsid)
409 if self.backend_result:
410 self.backend_result.report_logger(logger)
413 def check_install(lp, session_info, credentials):
414 """Check whether the current install seems ok.
416 :param lp: Loadparm context
417 :param session_info: Session information
418 :param credentials: Credentials
420 if lp.get("realm") == "":
421 raise Exception("Realm empty")
422 samdb = Ldb(lp.samdb_url(), session_info=session_info,
423 credentials=credentials, lp=lp)
424 if len(samdb.search("(cn=Administrator)")) != 1:
425 raise ProvisioningError("No administrator account found")
428 def findnss(nssfn, names):
429 """Find a user or group from a list of possibilities.
431 :param nssfn: NSS Function to try (should raise KeyError if not found)
432 :param names: Names to check.
433 :return: Value return by first names list.
435 for name in names:
436 try:
437 return nssfn(name)
438 except KeyError:
439 pass
440 raise KeyError("Unable to find user/group in %r" % names)
443 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
444 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
447 def provision_paths_from_lp(lp, dnsdomain):
448 """Set the default paths for provisioning.
450 :param lp: Loadparm context.
451 :param dnsdomain: DNS Domain name
453 paths = ProvisionPaths()
454 paths.private_dir = lp.get("private dir")
455 paths.state_dir = lp.get("state directory")
457 # This is stored without path prefix for the "privateKeytab" attribute in
458 # "secrets_dns.ldif".
459 paths.dns_keytab = "dns.keytab"
460 paths.keytab = "secrets.keytab"
462 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
463 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
464 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
465 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
466 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
467 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
468 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
469 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
470 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
471 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
472 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
473 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
474 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
475 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
476 paths.hklm = "hklm.ldb"
477 paths.hkcr = "hkcr.ldb"
478 paths.hkcu = "hkcu.ldb"
479 paths.hku = "hku.ldb"
480 paths.hkpd = "hkpd.ldb"
481 paths.hkpt = "hkpt.ldb"
482 paths.sysvol = lp.get("path", "sysvol")
483 paths.netlogon = lp.get("path", "netlogon")
484 paths.smbconf = lp.configfile
485 return paths
488 def determine_netbios_name(hostname):
489 """Determine a netbios name from a hostname."""
490 # remove forbidden chars and force the length to be <16
491 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
492 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
495 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
496 serverrole=None, rootdn=None, domaindn=None, configdn=None,
497 schemadn=None, serverdn=None, sitename=None):
498 """Guess configuration settings to use."""
500 if hostname is None:
501 hostname = socket.gethostname().split(".")[0]
503 netbiosname = lp.get("netbios name")
504 if netbiosname is None:
505 netbiosname = determine_netbios_name(hostname)
506 netbiosname = netbiosname.upper()
507 if not valid_netbios_name(netbiosname):
508 raise InvalidNetbiosName(netbiosname)
510 if dnsdomain is None:
511 dnsdomain = lp.get("realm")
512 if dnsdomain is None or dnsdomain == "":
513 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
515 dnsdomain = dnsdomain.lower()
517 if serverrole is None:
518 serverrole = lp.get("server role")
519 if serverrole is None:
520 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
522 serverrole = serverrole.lower()
524 realm = dnsdomain.upper()
526 if lp.get("realm") == "":
527 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
529 if lp.get("realm").upper() != realm:
530 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))
532 if lp.get("server role").lower() != serverrole:
533 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))
535 if serverrole == "active directory domain controller":
536 if domain is None:
537 # This will, for better or worse, default to 'WORKGROUP'
538 domain = lp.get("workgroup")
539 domain = domain.upper()
541 if lp.get("workgroup").upper() != domain:
542 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))
544 if domaindn is None:
545 domaindn = samba.dn_from_dns_name(dnsdomain)
547 if domain == netbiosname:
548 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
549 else:
550 domain = netbiosname
551 if domaindn is None:
552 domaindn = "DC=" + netbiosname
554 if not valid_netbios_name(domain):
555 raise InvalidNetbiosName(domain)
557 if hostname.upper() == realm:
558 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
559 if netbiosname.upper() == realm:
560 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
561 if domain == realm:
562 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
564 if rootdn is None:
565 rootdn = domaindn
567 if configdn is None:
568 configdn = "CN=Configuration," + rootdn
569 if schemadn is None:
570 schemadn = "CN=Schema," + configdn
572 if sitename is None:
573 sitename = DEFAULTSITE
575 names = ProvisionNames()
576 names.rootdn = rootdn
577 names.domaindn = domaindn
578 names.configdn = configdn
579 names.schemadn = schemadn
580 names.ldapmanagerdn = "CN=Manager," + rootdn
581 names.dnsdomain = dnsdomain
582 names.domain = domain
583 names.realm = realm
584 names.netbiosname = netbiosname
585 names.hostname = hostname
586 names.sitename = sitename
587 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
588 netbiosname, sitename, configdn)
590 return names
593 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
594 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
595 global_param=None):
596 """Create a new smb.conf file based on a couple of basic settings.
598 assert smbconf is not None
600 if hostname is None:
601 hostname = socket.gethostname().split(".")[0]
603 netbiosname = determine_netbios_name(hostname)
605 if serverrole is None:
606 serverrole = "standalone server"
608 assert domain is not None
609 domain = domain.upper()
611 assert realm is not None
612 realm = realm.upper()
614 global_settings = {
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-existent file
624 if os.path.exists(smbconf):
625 lp.load(smbconf)
627 if global_param is not None:
628 for ent in global_param:
629 if global_param[ent] is not None:
630 global_settings[ent] = " ".join(global_param[ent])
632 if targetdir is not None:
633 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
634 global_settings["lock dir"] = os.path.abspath(targetdir)
635 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
636 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
638 lp.set("lock dir", os.path.abspath(targetdir))
639 lp.set("state directory", global_settings["state directory"])
640 lp.set("cache directory", global_settings["cache directory"])
642 if eadb:
643 if use_ntvfs and not lp.get("posix:eadb"):
644 if targetdir is not None:
645 privdir = os.path.join(targetdir, "private")
646 else:
647 privdir = lp.get("private dir")
648 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
649 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
650 if targetdir is not None:
651 statedir = os.path.join(targetdir, "state")
652 else:
653 statedir = lp.get("state directory")
654 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
656 shares = {}
657 if serverrole == "active directory domain controller":
658 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
659 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
660 "scripts")
661 else:
662 global_settings["passdb backend"] = "samba_dsdb"
664 f = open(smbconf, 'w')
665 try:
666 f.write("[globals]\n")
667 for key, val in global_settings.iteritems():
668 f.write("\t%s = %s\n" % (key, val))
669 f.write("\n")
671 for name, path in shares.iteritems():
672 f.write("[%s]\n" % name)
673 f.write("\tpath = %s\n" % path)
674 f.write("\tread only = no\n")
675 f.write("\n")
676 finally:
677 f.close()
678 # reload the smb.conf
679 lp.load(smbconf)
681 # and dump it without any values that are the default
682 # this ensures that any smb.conf parameters that were set
683 # on the provision/join command line are set in the resulting smb.conf
684 f = open(smbconf, mode='w')
685 try:
686 lp.dump(f, False)
687 finally:
688 f.close()
691 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
692 users_gid, root_gid):
693 """setup reasonable name mappings for sam names to unix names.
695 :param samdb: SamDB object.
696 :param idmap: IDmap db object.
697 :param sid: The domain sid.
698 :param domaindn: The domain DN.
699 :param root_uid: uid of the UNIX root user.
700 :param nobody_uid: uid of the UNIX nobody user.
701 :param users_gid: gid of the UNIX users group.
702 :param root_gid: gid of the UNIX root group.
704 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
706 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
707 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
710 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
711 provision_backend, names, schema, serverrole,
712 erase=False):
713 """Setup the partitions for the SAM database.
715 Alternatively, provision() may call this, and then populate the database.
717 :note: This will wipe the Sam Database!
719 :note: This function always removes the local SAM LDB file. The erase
720 parameter controls whether to erase the existing data, which
721 may not be stored locally but in LDAP.
724 assert session_info is not None
726 # We use options=["modules:"] to stop the modules loading - we
727 # just want to wipe and re-initialise the database, not start it up
729 try:
730 os.unlink(samdb_path)
731 except OSError:
732 pass
734 samdb = Ldb(url=samdb_path, session_info=session_info,
735 lp=lp, options=["modules:"])
737 ldap_backend_line = "# No LDAP backend"
738 if provision_backend.type != "ldb":
739 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
741 samdb.transaction_start()
742 try:
743 logger.info("Setting up sam.ldb partitions and settings")
744 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
745 "LDAP_BACKEND_LINE": ldap_backend_line
749 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
750 "BACKEND_TYPE": provision_backend.type,
751 "SERVER_ROLE": serverrole
754 logger.info("Setting up sam.ldb rootDSE")
755 setup_samdb_rootdse(samdb, names)
756 except:
757 samdb.transaction_cancel()
758 raise
759 else:
760 samdb.transaction_commit()
763 def secretsdb_self_join(secretsdb, domain,
764 netbiosname, machinepass, domainsid=None,
765 realm=None, dnsdomain=None,
766 keytab_path=None,
767 key_version_number=1,
768 secure_channel_type=SEC_CHAN_WKSTA):
769 """Add domain join-specific bits to a secrets database.
771 :param secretsdb: Ldb Handle to the secrets database
772 :param machinepass: Machine password
774 attrs = ["whenChanged",
775 "secret",
776 "priorSecret",
777 "priorChanged",
778 "krb5Keytab",
779 "privateKeytab"]
781 if realm is not None:
782 if dnsdomain is None:
783 dnsdomain = realm.lower()
784 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
785 else:
786 dnsname = None
787 shortname = netbiosname.lower()
789 # We don't need to set msg["flatname"] here, because rdn_name will handle
790 # it, and it causes problems for modifies anyway
791 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
792 msg["secureChannelType"] = [str(secure_channel_type)]
793 msg["objectClass"] = ["top", "primaryDomain"]
794 if dnsname is not None:
795 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
796 msg["realm"] = [realm]
797 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
798 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
799 msg["privateKeytab"] = ["secrets.keytab"]
801 msg["secret"] = [machinepass]
802 msg["samAccountName"] = ["%s$" % netbiosname]
803 msg["secureChannelType"] = [str(secure_channel_type)]
804 if domainsid is not None:
805 msg["objectSid"] = [ndr_pack(domainsid)]
807 # This complex expression tries to ensure that we don't have more
808 # than one record for this SID, realm or netbios domain at a time,
809 # but we don't delete the old record that we are about to modify,
810 # because that would delete the keytab and previous password.
811 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
812 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
813 scope=ldb.SCOPE_ONELEVEL)
815 for del_msg in res:
816 secretsdb.delete(del_msg.dn)
818 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
820 if len(res) == 1:
821 msg["priorSecret"] = [res[0]["secret"][0]]
822 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
824 try:
825 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
826 except KeyError:
827 pass
829 try:
830 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
831 except KeyError:
832 pass
834 for el in msg:
835 if el != 'dn':
836 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
837 secretsdb.modify(msg)
838 secretsdb.rename(res[0].dn, msg.dn)
839 else:
840 spn = [ 'HOST/%s' % shortname ]
841 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
842 # we are a domain controller then we add servicePrincipalName
843 # entries for the keytab code to update.
844 spn.extend([ 'HOST/%s' % dnsname ])
845 msg["servicePrincipalName"] = spn
847 secretsdb.add(msg)
850 def setup_secretsdb(paths, session_info, backend_credentials, lp):
851 """Setup the secrets database.
853 :note: This function does not handle exceptions and transaction on purpose,
854 it's up to the caller to do this job.
856 :param path: Path to the secrets database.
857 :param session_info: Session info.
858 :param credentials: Credentials
859 :param lp: Loadparm context
860 :return: LDB handle for the created secrets database
862 if os.path.exists(paths.secrets):
863 os.unlink(paths.secrets)
865 keytab_path = os.path.join(paths.private_dir, paths.keytab)
866 if os.path.exists(keytab_path):
867 os.unlink(keytab_path)
869 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
870 if os.path.exists(dns_keytab_path):
871 os.unlink(dns_keytab_path)
873 path = paths.secrets
875 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
876 secrets_ldb.erase()
877 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
878 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
879 secrets_ldb.transaction_start()
880 try:
881 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
883 if (backend_credentials is not None and
884 backend_credentials.authentication_requested()):
885 if backend_credentials.get_bind_dn() is not None:
886 setup_add_ldif(secrets_ldb,
887 setup_path("secrets_simple_ldap.ldif"), {
888 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
889 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
891 else:
892 setup_add_ldif(secrets_ldb,
893 setup_path("secrets_sasl_ldap.ldif"), {
894 "LDAPADMINUSER": backend_credentials.get_username(),
895 "LDAPADMINREALM": backend_credentials.get_realm(),
896 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
898 except:
899 secrets_ldb.transaction_cancel()
900 raise
901 return secrets_ldb
904 def setup_privileges(path, session_info, lp):
905 """Setup the privileges database.
907 :param path: Path to the privileges database.
908 :param session_info: Session info.
909 :param credentials: Credentials
910 :param lp: Loadparm context
911 :return: LDB handle for the created secrets database
913 if os.path.exists(path):
914 os.unlink(path)
915 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
916 privilege_ldb.erase()
917 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
920 def setup_registry(path, session_info, lp):
921 """Setup the registry.
923 :param path: Path to the registry database
924 :param session_info: Session information
925 :param credentials: Credentials
926 :param lp: Loadparm context
928 reg = samba.registry.Registry()
929 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
930 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
931 provision_reg = setup_path("provision.reg")
932 assert os.path.exists(provision_reg)
933 reg.diff_apply(provision_reg)
936 def setup_idmapdb(path, session_info, lp):
937 """Setup the idmap database.
939 :param path: path to the idmap database
940 :param session_info: Session information
941 :param credentials: Credentials
942 :param lp: Loadparm context
944 if os.path.exists(path):
945 os.unlink(path)
947 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
948 idmap_ldb.erase()
949 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
950 return idmap_ldb
953 def setup_samdb_rootdse(samdb, names):
954 """Setup the SamDB rootdse.
956 :param samdb: Sam Database handle
958 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
959 "SCHEMADN": names.schemadn,
960 "DOMAINDN": names.domaindn,
961 "ROOTDN" : names.rootdn,
962 "CONFIGDN": names.configdn,
963 "SERVERDN": names.serverdn,
967 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
968 dns_backend, dnspass, domainsid, next_rid, invocationid,
969 policyguid, policyguid_dc,
970 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
971 """Join a host to its own domain."""
972 assert isinstance(invocationid, str)
973 if ntdsguid is not None:
974 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
975 else:
976 ntdsguid_line = ""
978 if dc_rid is None:
979 dc_rid = next_rid
981 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
982 "CONFIGDN": names.configdn,
983 "SCHEMADN": names.schemadn,
984 "DOMAINDN": names.domaindn,
985 "SERVERDN": names.serverdn,
986 "INVOCATIONID": invocationid,
987 "NETBIOSNAME": names.netbiosname,
988 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
989 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
990 "DOMAINSID": str(domainsid),
991 "DCRID": str(dc_rid),
992 "SAMBA_VERSION_STRING": version,
993 "NTDSGUID": ntdsguid_line,
994 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
995 domainControllerFunctionality),
996 "RIDALLOCATIONSTART": str(next_rid + 100),
997 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
999 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1000 "POLICYGUID": policyguid,
1001 "POLICYGUID_DC": policyguid_dc,
1002 "DNSDOMAIN": names.dnsdomain,
1003 "DOMAINDN": names.domaindn})
1005 # If we are setting up a subdomain, then this has been replicated in, so we
1006 # don't need to add it
1007 if fill == FILL_FULL:
1008 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1009 "CONFIGDN": names.configdn,
1010 "SCHEMADN": names.schemadn,
1011 "DOMAINDN": names.domaindn,
1012 "SERVERDN": names.serverdn,
1013 "INVOCATIONID": invocationid,
1014 "NETBIOSNAME": names.netbiosname,
1015 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1016 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1017 "DOMAINSID": str(domainsid),
1018 "DCRID": str(dc_rid),
1019 "SAMBA_VERSION_STRING": version,
1020 "NTDSGUID": ntdsguid_line,
1021 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1022 domainControllerFunctionality)})
1024 # Setup fSMORoleOwner entries to point at the newly created DC entry
1025 setup_modify_ldif(samdb,
1026 setup_path("provision_self_join_modify_config.ldif"), {
1027 "CONFIGDN": names.configdn,
1028 "SCHEMADN": names.schemadn,
1029 "DEFAULTSITE": names.sitename,
1030 "NETBIOSNAME": names.netbiosname,
1031 "SERVERDN": names.serverdn,
1034 system_session_info = system_session()
1035 samdb.set_session_info(system_session_info)
1036 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1037 # modify a serverReference under cn=config when we are a subdomain, we must
1038 # be system due to ACLs
1039 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1040 "DOMAINDN": names.domaindn,
1041 "SERVERDN": names.serverdn,
1042 "NETBIOSNAME": names.netbiosname,
1045 samdb.set_session_info(admin_session_info)
1047 if dns_backend != "SAMBA_INTERNAL":
1048 # This is Samba4 specific and should be replaced by the correct
1049 # DNS AD-style setup
1050 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1051 "DNSDOMAIN": names.dnsdomain,
1052 "DOMAINDN": names.domaindn,
1053 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1054 "HOSTNAME" : names.hostname,
1055 "DNSNAME" : '%s.%s' % (
1056 names.netbiosname.lower(), names.dnsdomain.lower())
1060 def getpolicypath(sysvolpath, dnsdomain, guid):
1061 """Return the physical path of policy given its guid.
1063 :param sysvolpath: Path to the sysvol folder
1064 :param dnsdomain: DNS name of the AD domain
1065 :param guid: The GUID of the policy
1066 :return: A string with the complete path to the policy folder
1068 if guid[0] != "{":
1069 guid = "{%s}" % guid
1070 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1071 return policy_path
1074 def create_gpo_struct(policy_path):
1075 if not os.path.exists(policy_path):
1076 os.makedirs(policy_path, 0775)
1077 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1078 try:
1079 f.write("[General]\r\nVersion=0")
1080 finally:
1081 f.close()
1082 p = os.path.join(policy_path, "MACHINE")
1083 if not os.path.exists(p):
1084 os.makedirs(p, 0775)
1085 p = os.path.join(policy_path, "USER")
1086 if not os.path.exists(p):
1087 os.makedirs(p, 0775)
1090 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1091 """Create the default GPO for a domain
1093 :param sysvolpath: Physical path for the sysvol folder
1094 :param dnsdomain: DNS domain name of the AD domain
1095 :param policyguid: GUID of the default domain policy
1096 :param policyguid_dc: GUID of the default domain controler policy
1098 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1099 create_gpo_struct(policy_path)
1101 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1102 create_gpo_struct(policy_path)
1105 def setup_samdb(path, session_info, provision_backend, lp, names,
1106 logger, fill, serverrole, schema, am_rodc=False):
1107 """Setup a complete SAM Database.
1109 :note: This will wipe the main SAM database file!
1112 # Also wipes the database
1113 setup_samdb_partitions(path, logger=logger, lp=lp,
1114 provision_backend=provision_backend, session_info=session_info,
1115 names=names, serverrole=serverrole, schema=schema)
1117 # Load the database, but don's load the global schema and don't connect
1118 # quite yet
1119 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1120 credentials=provision_backend.credentials, lp=lp,
1121 global_schema=False, am_rodc=am_rodc)
1123 logger.info("Pre-loading the Samba 4 and AD schema")
1125 # Load the schema from the one we computed earlier
1126 samdb.set_schema(schema, write_indices_and_attributes=False)
1128 # Set the NTDS settings DN manually - in order to have it already around
1129 # before the provisioned tree exists and we connect
1130 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1132 # And now we can connect to the DB - the schema won't be loaded from the
1133 # DB
1134 samdb.connect(path)
1136 # But we have to give it one more kick to have it use the schema
1137 # during provision - it needs, now that it is connected, to write
1138 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1139 samdb.set_schema(schema, write_indices_and_attributes=True)
1141 return samdb
1144 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1145 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1146 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1147 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1149 if next_rid is None:
1150 next_rid = 1000
1152 # Provision does not make much sense values larger than 1000000000
1153 # as the upper range of the rIDAvailablePool is 1073741823 and
1154 # we don't want to create a domain that cannot allocate rids.
1155 if next_rid < 1000 or next_rid > 1000000000:
1156 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1157 error += "the valid range is %u-%u. The default is %u." % (
1158 1000, 1000000000, 1000)
1159 raise ProvisioningError(error)
1161 # ATTENTION: Do NOT change these default values without discussion with the
1162 # team and/or release manager. They have a big impact on the whole program!
1163 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1165 if dom_for_fun_level is None:
1166 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1168 if dom_for_fun_level > domainControllerFunctionality:
1169 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!")
1171 domainFunctionality = dom_for_fun_level
1172 forestFunctionality = dom_for_fun_level
1174 # Set the NTDS settings DN manually - in order to have it already around
1175 # before the provisioned tree exists and we connect
1176 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1178 samdb.transaction_start()
1179 try:
1180 # Set the domain functionality levels onto the database.
1181 # Various module (the password_hash module in particular) need
1182 # to know what level of AD we are emulating.
1184 # These will be fixed into the database via the database
1185 # modifictions below, but we need them set from the start.
1186 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1187 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1188 samdb.set_opaque_integer("domainControllerFunctionality",
1189 domainControllerFunctionality)
1191 samdb.set_domain_sid(str(domainsid))
1192 samdb.set_invocation_id(invocationid)
1194 logger.info("Adding DomainDN: %s" % names.domaindn)
1196 # impersonate domain admin
1197 admin_session_info = admin_session(lp, str(domainsid))
1198 samdb.set_session_info(admin_session_info)
1199 if domainguid is not None:
1200 domainguid_line = "objectGUID: %s\n-" % domainguid
1201 else:
1202 domainguid_line = ""
1204 descr = b64encode(get_domain_descriptor(domainsid))
1205 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1206 "DOMAINDN": names.domaindn,
1207 "DOMAINSID": str(domainsid),
1208 "DESCRIPTOR": descr,
1209 "DOMAINGUID": domainguid_line
1212 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1213 "DOMAINDN": names.domaindn,
1214 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1215 "NEXTRID": str(next_rid),
1216 "DEFAULTSITE": names.sitename,
1217 "CONFIGDN": names.configdn,
1218 "POLICYGUID": policyguid,
1219 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1220 "SAMBA_VERSION_STRING": version
1223 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1224 if fill == FILL_FULL:
1225 logger.info("Adding configuration container")
1226 descr = b64encode(get_config_descriptor(domainsid))
1227 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1228 "CONFIGDN": names.configdn,
1229 "DESCRIPTOR": descr,
1232 # The LDIF here was created when the Schema object was constructed
1233 logger.info("Setting up sam.ldb schema")
1234 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1235 samdb.modify_ldif(schema.schema_dn_modify)
1236 samdb.write_prefixes_from_schema()
1237 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1238 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1239 {"SCHEMADN": names.schemadn})
1241 # Now register this container in the root of the forest
1242 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1243 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1244 "subRefs")
1246 except:
1247 samdb.transaction_cancel()
1248 raise
1249 else:
1250 samdb.transaction_commit()
1252 samdb.transaction_start()
1253 try:
1254 samdb.invocation_id = invocationid
1256 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1257 if fill == FILL_FULL:
1258 logger.info("Setting up sam.ldb configuration data")
1259 partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
1260 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1261 "CONFIGDN": names.configdn,
1262 "NETBIOSNAME": names.netbiosname,
1263 "DEFAULTSITE": names.sitename,
1264 "DNSDOMAIN": names.dnsdomain,
1265 "DOMAIN": names.domain,
1266 "SCHEMADN": names.schemadn,
1267 "DOMAINDN": names.domaindn,
1268 "SERVERDN": names.serverdn,
1269 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1270 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1271 "PARTITIONS_DESCRIPTOR": partitions_descr,
1274 logger.info("Setting up display specifiers")
1275 display_specifiers_ldif = read_ms_ldif(
1276 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1277 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1278 {"CONFIGDN": names.configdn})
1279 check_all_substituted(display_specifiers_ldif)
1280 samdb.add_ldif(display_specifiers_ldif)
1282 logger.info("Adding users container")
1283 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1284 "DOMAINDN": names.domaindn})
1285 logger.info("Modifying users container")
1286 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1287 "DOMAINDN": names.domaindn})
1288 logger.info("Adding computers container")
1289 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1290 "DOMAINDN": names.domaindn})
1291 logger.info("Modifying computers container")
1292 setup_modify_ldif(samdb,
1293 setup_path("provision_computers_modify.ldif"), {
1294 "DOMAINDN": names.domaindn})
1295 logger.info("Setting up sam.ldb data")
1296 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1297 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1298 "DOMAINDN": names.domaindn,
1299 "NETBIOSNAME": names.netbiosname,
1300 "DEFAULTSITE": names.sitename,
1301 "CONFIGDN": names.configdn,
1302 "SERVERDN": names.serverdn,
1303 "RIDAVAILABLESTART": str(next_rid + 600),
1304 "POLICYGUID_DC": policyguid_dc
1307 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1308 if fill == FILL_FULL:
1309 setup_modify_ldif(samdb,
1310 setup_path("provision_configuration_references.ldif"), {
1311 "CONFIGDN": names.configdn,
1312 "SCHEMADN": names.schemadn})
1314 logger.info("Setting up well known security principals")
1315 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1316 "CONFIGDN": names.configdn,
1319 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1320 setup_modify_ldif(samdb,
1321 setup_path("provision_basedn_references.ldif"),
1322 {"DOMAINDN": names.domaindn})
1324 logger.info("Setting up sam.ldb users and groups")
1325 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1326 "DOMAINDN": names.domaindn,
1327 "DOMAINSID": str(domainsid),
1328 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1329 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1332 logger.info("Setting up self join")
1333 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1334 invocationid=invocationid,
1335 dns_backend=dns_backend,
1336 dnspass=dnspass,
1337 machinepass=machinepass,
1338 domainsid=domainsid,
1339 next_rid=next_rid,
1340 dc_rid=dc_rid,
1341 policyguid=policyguid,
1342 policyguid_dc=policyguid_dc,
1343 domainControllerFunctionality=domainControllerFunctionality,
1344 ntdsguid=ntdsguid)
1346 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1347 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1348 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1349 assert isinstance(names.ntdsguid, str)
1350 except:
1351 samdb.transaction_cancel()
1352 raise
1353 else:
1354 samdb.transaction_commit()
1355 return samdb
1358 FILL_FULL = "FULL"
1359 FILL_SUBDOMAIN = "SUBDOMAIN"
1360 FILL_NT4SYNC = "NT4SYNC"
1361 FILL_DRS = "DRS"
1362 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1363 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)"
1366 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb):
1367 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1368 for root, dirs, files in os.walk(path, topdown=False):
1369 for name in files:
1370 setntacl(lp, os.path.join(root, name), acl, domsid,
1371 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1372 for name in dirs:
1373 setntacl(lp, os.path.join(root, name), acl, domsid,
1374 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1377 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1378 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1379 folders beneath.
1381 :param sysvol: Physical path for the sysvol folder
1382 :param dnsdomain: The DNS name of the domain
1383 :param domainsid: The SID of the domain
1384 :param domaindn: The DN of the domain (ie. DC=...)
1385 :param samdb: An LDB object on the SAM db
1386 :param lp: an LP object
1389 # Set ACL for GPO root folder
1390 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1391 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1392 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1394 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1395 attrs=["cn", "nTSecurityDescriptor"],
1396 expression="", scope=ldb.SCOPE_ONELEVEL)
1398 for policy in res:
1399 acl = ndr_unpack(security.descriptor,
1400 str(policy["nTSecurityDescriptor"])).as_sddl()
1401 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1402 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1403 str(domainsid), use_ntvfs,
1404 passdb=passdb)
1407 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1408 domaindn, lp, use_ntvfs):
1409 """Set the ACL for the sysvol share and the subfolders
1411 :param samdb: An LDB object on the SAM db
1412 :param netlogon: Physical path for the netlogon folder
1413 :param sysvol: Physical path for the sysvol folder
1414 :param uid: The UID of the "Administrator" user
1415 :param gid: The GID of the "Domain adminstrators" group
1416 :param domainsid: The SID of the domain
1417 :param dnsdomain: The DNS name of the domain
1418 :param domaindn: The DN of the domain (ie. DC=...)
1420 s4_passdb = None
1422 if not use_ntvfs:
1423 # This will ensure that the smbd code we are running when setting ACLs
1424 # is initialised with the smb.conf
1425 s3conf = s3param.get_context()
1426 s3conf.load(lp.configfile)
1427 # ensure we are using the right samba_dsdb passdb backend, no matter what
1428 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1429 passdb.reload_static_pdb()
1431 # ensure that we init the samba_dsdb backend, so the domain sid is
1432 # marked in secrets.tdb
1433 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1435 # now ensure everything matches correctly, to avoid wierd issues
1436 if passdb.get_global_sam_sid() != domainsid:
1437 raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid))
1439 domain_info = s4_passdb.domain_info()
1440 if domain_info["dom_sid"] != domainsid:
1441 raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid))
1443 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1444 raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
1447 try:
1448 if use_ntvfs:
1449 os.chown(sysvol, -1, gid)
1450 except OSError:
1451 canchown = False
1452 else:
1453 canchown = True
1455 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1456 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1457 for root, dirs, files in os.walk(sysvol, topdown=False):
1458 for name in files:
1459 if use_ntvfs and canchown:
1460 os.chown(os.path.join(root, name), -1, gid)
1461 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1462 for name in dirs:
1463 if use_ntvfs and canchown:
1464 os.chown(os.path.join(root, name), -1, gid)
1465 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1467 # Set acls on Policy folder and policies folders
1468 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1470 def acl_type(direct_db_access):
1471 if direct_db_access:
1472 return "DB"
1473 else:
1474 return "VFS"
1476 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1477 fsacl = getntacl(lp, path, direct_db_access=direct_db_access)
1478 fsacl_sddl = fsacl.as_sddl(domainsid)
1479 if fsacl_sddl != acl:
1480 raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), path, fsacl_sddl, acl))
1482 for root, dirs, files in os.walk(path, topdown=False):
1483 for name in files:
1484 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1485 if fsacl is None:
1486 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1487 fsacl_sddl = fsacl.as_sddl(domainsid)
1488 if fsacl_sddl != acl:
1489 raise ProvisioningError('%s ACL on GPO file %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl))
1491 for name in dirs:
1492 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1493 if fsacl is None:
1494 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1495 fsacl_sddl = fsacl.as_sddl(domainsid)
1496 if fsacl_sddl != acl:
1497 raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl))
1500 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1501 direct_db_access):
1502 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1503 folders beneath.
1505 :param sysvol: Physical path for the sysvol folder
1506 :param dnsdomain: The DNS name of the domain
1507 :param domainsid: The SID of the domain
1508 :param domaindn: The DN of the domain (ie. DC=...)
1509 :param samdb: An LDB object on the SAM db
1510 :param lp: an LP object
1513 # Set ACL for GPO root folder
1514 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1515 fsacl = getntacl(lp, root_policy_path, direct_db_access=direct_db_access)
1516 if fsacl is None:
1517 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1518 fsacl_sddl = fsacl.as_sddl(domainsid)
1519 if fsacl_sddl != POLICIES_ACL:
1520 raise ProvisioningError('%s ACL on policy root %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), root_policy_path, fsacl_sddl, fsacl))
1521 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1522 attrs=["cn", "nTSecurityDescriptor"],
1523 expression="", scope=ldb.SCOPE_ONELEVEL)
1525 for policy in res:
1526 acl = ndr_unpack(security.descriptor,
1527 str(policy["nTSecurityDescriptor"])).as_sddl()
1528 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1529 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1530 domainsid, direct_db_access)
1533 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1534 lp):
1535 """Set the ACL for the sysvol share and the subfolders
1537 :param samdb: An LDB object on the SAM db
1538 :param netlogon: Physical path for the netlogon folder
1539 :param sysvol: Physical path for the sysvol folder
1540 :param uid: The UID of the "Administrator" user
1541 :param gid: The GID of the "Domain adminstrators" group
1542 :param domainsid: The SID of the domain
1543 :param dnsdomain: The DNS name of the domain
1544 :param domaindn: The DN of the domain (ie. DC=...)
1547 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1548 s3conf = s3param.get_context()
1549 s3conf.load(lp.configfile)
1550 # ensure we are using the right samba_dsdb passdb backend, no matter what
1551 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1552 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1553 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1555 # now ensure everything matches correctly, to avoid wierd issues
1556 if passdb.get_global_sam_sid() != domainsid:
1557 raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid))
1559 domain_info = s4_passdb.domain_info()
1560 if domain_info["dom_sid"] != domainsid:
1561 raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid))
1563 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1564 raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
1566 # Ensure we can read this directly, and via the smbd VFS
1567 for direct_db_access in [True, False]:
1568 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1569 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1570 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access)
1571 if fsacl is None:
1572 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1573 fsacl_sddl = fsacl.as_sddl(domainsid)
1574 if fsacl_sddl != SYSVOL_ACL:
1575 raise ProvisioningError('%s ACL on sysvol directory %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), dir_path, fsacl_sddl, SYSVOL_ACL))
1577 # Check acls on Policy folder and policies folders
1578 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1579 direct_db_access)
1582 def interface_ips_v4(lp):
1583 """return only IPv4 IPs"""
1584 ips = samba.interface_ips(lp, False)
1585 ret = []
1586 for i in ips:
1587 if i.find(':') == -1:
1588 ret.append(i)
1589 return ret
1592 def interface_ips_v6(lp, linklocal=False):
1593 """return only IPv6 IPs"""
1594 ips = samba.interface_ips(lp, False)
1595 ret = []
1596 for i in ips:
1597 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1598 ret.append(i)
1599 return ret
1602 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1603 domainsid, schema=None,
1604 targetdir=None, samdb_fill=FILL_FULL,
1605 hostip=None, hostip6=None,
1606 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1607 domainguid=None, policyguid=None, policyguid_dc=None,
1608 invocationid=None, machinepass=None, ntdsguid=None,
1609 dns_backend=None, dnspass=None,
1610 serverrole=None, dom_for_fun_level=None,
1611 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1612 # create/adapt the group policy GUIDs
1613 # Default GUID for default policy are described at
1614 # "How Core Group Policy Works"
1615 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1616 if policyguid is None:
1617 policyguid = DEFAULT_POLICY_GUID
1618 policyguid = policyguid.upper()
1619 if policyguid_dc is None:
1620 policyguid_dc = DEFAULT_DC_POLICY_GUID
1621 policyguid_dc = policyguid_dc.upper()
1623 if invocationid is None:
1624 invocationid = str(uuid.uuid4())
1626 if krbtgtpass is None:
1627 krbtgtpass = samba.generate_random_password(128, 255)
1628 if machinepass is None:
1629 machinepass = samba.generate_random_password(128, 255)
1630 if dnspass is None:
1631 dnspass = samba.generate_random_password(128, 255)
1633 samdb = fill_samdb(samdb, lp, names, logger=logger,
1634 domainsid=domainsid, schema=schema, domainguid=domainguid,
1635 policyguid=policyguid, policyguid_dc=policyguid_dc,
1636 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1637 invocationid=invocationid, machinepass=machinepass,
1638 dns_backend=dns_backend, dnspass=dnspass,
1639 ntdsguid=ntdsguid, serverrole=serverrole,
1640 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1641 next_rid=next_rid, dc_rid=dc_rid)
1643 if serverrole == "active directory domain controller":
1645 # Set up group policies (domain policy and domain controller
1646 # policy)
1647 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1648 policyguid_dc)
1649 if not skip_sysvolacl:
1650 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1651 paths.root_gid, domainsid, names.dnsdomain,
1652 names.domaindn, lp, use_ntvfs)
1653 else:
1654 logger.info("Setting acl on sysvol skipped")
1656 secretsdb_self_join(secrets_ldb, domain=names.domain,
1657 realm=names.realm, dnsdomain=names.dnsdomain,
1658 netbiosname=names.netbiosname, domainsid=domainsid,
1659 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1661 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1662 # In future, this might be determined from some configuration
1663 kerberos_enctypes = str(ENC_ALL_TYPES)
1665 try:
1666 msg = ldb.Message(ldb.Dn(samdb,
1667 samdb.searchone("distinguishedName",
1668 expression="samAccountName=%s$" % names.netbiosname,
1669 scope=ldb.SCOPE_SUBTREE)))
1670 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1671 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1672 name="msDS-SupportedEncryptionTypes")
1673 samdb.modify(msg)
1674 except ldb.LdbError, (enum, estr):
1675 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1676 # It might be that this attribute does not exist in this schema
1677 raise
1679 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1680 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1681 dnspass=dnspass, os_level=dom_for_fun_level,
1682 targetdir=targetdir, site=DEFAULTSITE)
1684 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1685 attribute="objectGUID")
1686 assert isinstance(domainguid, str)
1688 lastProvisionUSNs = get_last_provision_usn(samdb)
1689 maxUSN = get_max_usn(samdb, str(names.rootdn))
1690 if lastProvisionUSNs is not None:
1691 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1692 else:
1693 set_provision_usn(samdb, 0, maxUSN, invocationid)
1695 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1696 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1697 { 'NTDSGUID' : names.ntdsguid })
1699 # fix any dangling GUIDs from the provision
1700 logger.info("Fixing provision GUIDs")
1701 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1702 quiet=True)
1703 samdb.transaction_start()
1704 try:
1705 # a small number of GUIDs are missing because of ordering issues in the
1706 # provision code
1707 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1708 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1709 scope=ldb.SCOPE_BASE,
1710 attrs=['defaultObjectCategory'])
1711 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1712 scope=ldb.SCOPE_ONELEVEL,
1713 attrs=['ipsecOwnersReference',
1714 'ipsecFilterReference',
1715 'ipsecISAKMPReference',
1716 'ipsecNegotiationPolicyReference',
1717 'ipsecNFAReference'])
1718 except:
1719 samdb.transaction_cancel()
1720 raise
1721 else:
1722 samdb.transaction_commit()
1725 _ROLES_MAP = {
1726 "ROLE_STANDALONE": "standalone server",
1727 "ROLE_DOMAIN_MEMBER": "member server",
1728 "ROLE_DOMAIN_BDC": "active directory domain controller",
1729 "ROLE_DOMAIN_PDC": "active directory domain controller",
1730 "dc": "active directory domain controller",
1731 "member": "member server",
1732 "domain controller": "active directory domain controller",
1733 "active directory domain controller": "active directory domain controller",
1734 "member server": "member server",
1735 "standalone": "standalone server",
1736 "standalone server": "standalone server",
1740 def sanitize_server_role(role):
1741 """Sanitize a server role name.
1743 :param role: Server role
1744 :raise ValueError: If the role can not be interpreted
1745 :return: Sanitized server role (one of "member server",
1746 "active directory domain controller", "standalone server")
1748 try:
1749 return _ROLES_MAP[role]
1750 except KeyError:
1751 raise ValueError(role)
1754 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1755 maxuid, maxgid):
1756 """Create AD entries for the fake ypserver.
1758 This is needed for being able to manipulate posix attrs via ADUC.
1760 samdb.transaction_start()
1761 try:
1762 logger.info("Setting up fake yp server settings")
1763 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1764 "DOMAINDN": domaindn,
1765 "NETBIOSNAME": netbiosname,
1766 "NISDOMAIN": nisdomain,
1768 except:
1769 samdb.transaction_cancel()
1770 raise
1771 else:
1772 samdb.transaction_commit()
1775 def provision(logger, session_info, credentials, smbconf=None,
1776 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1777 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1778 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1779 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1780 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1781 dns_backend=None, dns_forwarder=None, dnspass=None,
1782 invocationid=None, machinepass=None, ntdsguid=None,
1783 root=None, nobody=None, users=None, backup=None, aci=None,
1784 serverrole=None, dom_for_fun_level=None, backend_type=None,
1785 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1786 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1787 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1788 """Provision samba4
1790 :note: caution, this wipes all existing data!
1793 try:
1794 serverrole = sanitize_server_role(serverrole)
1795 except ValueError:
1796 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1798 if ldapadminpass is None:
1799 # Make a new, random password between Samba and it's LDAP server
1800 ldapadminpass = samba.generate_random_password(128, 255)
1802 if backend_type is None:
1803 backend_type = "ldb"
1805 if domainsid is None:
1806 domainsid = security.random_sid()
1807 else:
1808 domainsid = security.dom_sid(domainsid)
1810 root_uid = findnss_uid([root or "root"])
1811 nobody_uid = findnss_uid([nobody or "nobody"])
1812 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1813 root_gid = pwd.getpwuid(root_uid).pw_gid
1815 try:
1816 bind_gid = findnss_gid(["bind", "named"])
1817 except KeyError:
1818 bind_gid = None
1820 if targetdir is not None:
1821 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1822 elif smbconf is None:
1823 smbconf = samba.param.default_path()
1824 if not os.path.exists(os.path.dirname(smbconf)):
1825 os.makedirs(os.path.dirname(smbconf))
1827 server_services = []
1828 global_param = {}
1829 if use_rfc2307:
1830 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1832 if dns_backend != "SAMBA_INTERNAL":
1833 server_services.append("-dns")
1834 else:
1835 if dns_forwarder is not None:
1836 global_param["dns forwarder"] = [dns_forwarder]
1838 if use_ntvfs:
1839 server_services.append("+smb")
1840 server_services.append("-s3fs")
1841 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1843 if len(server_services) > 0:
1844 global_param["server services"] = server_services
1846 # only install a new smb.conf if there isn't one there already
1847 if os.path.exists(smbconf):
1848 # if Samba Team members can't figure out the weird errors
1849 # loading an empty smb.conf gives, then we need to be smarter.
1850 # Pretend it just didn't exist --abartlet
1851 f = open(smbconf, 'r')
1852 try:
1853 data = f.read().lstrip()
1854 finally:
1855 f.close()
1856 if data is None or data == "":
1857 make_smbconf(smbconf, hostname, domain, realm,
1858 targetdir, serverrole=serverrole,
1859 eadb=useeadb, use_ntvfs=use_ntvfs,
1860 lp=lp, global_param=global_param)
1861 else:
1862 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1863 serverrole=serverrole,
1864 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1866 if lp is None:
1867 lp = samba.param.LoadParm()
1868 lp.load(smbconf)
1869 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1870 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1871 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1872 sitename=sitename, rootdn=rootdn)
1873 paths = provision_paths_from_lp(lp, names.dnsdomain)
1875 paths.bind_gid = bind_gid
1876 paths.root_uid = root_uid;
1877 paths.root_gid = root_gid
1879 if hostip is None:
1880 logger.info("Looking up IPv4 addresses")
1881 hostips = interface_ips_v4(lp)
1882 if len(hostips) > 0:
1883 hostip = hostips[0]
1884 if len(hostips) > 1:
1885 logger.warning("More than one IPv4 address found. Using %s",
1886 hostip)
1887 if hostip == "127.0.0.1":
1888 hostip = None
1889 if hostip is None:
1890 logger.warning("No IPv4 address will be assigned")
1892 if hostip6 is None:
1893 logger.info("Looking up IPv6 addresses")
1894 hostips = interface_ips_v6(lp, linklocal=False)
1895 if hostips:
1896 hostip6 = hostips[0]
1897 if len(hostips) > 1:
1898 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1899 if hostip6 is None:
1900 logger.warning("No IPv6 address will be assigned")
1902 names.hostip = hostip
1903 names.hostip6 = hostip6
1905 if serverrole is None:
1906 serverrole = lp.get("server role")
1908 if not os.path.exists(paths.private_dir):
1909 os.mkdir(paths.private_dir)
1910 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1911 os.mkdir(os.path.join(paths.private_dir, "tls"))
1912 if not os.path.exists(paths.state_dir):
1913 os.mkdir(paths.state_dir)
1915 if paths.sysvol and not os.path.exists(paths.sysvol):
1916 os.makedirs(paths.sysvol, 0775)
1918 if not use_ntvfs and serverrole == "active directory domain controller":
1919 s3conf = s3param.get_context()
1920 s3conf.load(lp.configfile)
1922 if paths.sysvol is None:
1923 raise MissingShareError("sysvol", paths.smbconf)
1925 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1926 try:
1927 try:
1928 smbd.set_simple_acl(file.name, 0755, root_gid)
1929 except Exception:
1930 if not smbd.have_posix_acls():
1931 # This clue is only strictly correct for RPM and
1932 # Debian-like Linux systems, but hopefully other users
1933 # will get enough clue from it.
1934 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1936 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1937 try:
1938 smbd.chown(file.name, root_uid, root_gid)
1939 except Exception:
1940 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1941 finally:
1942 file.close()
1944 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1946 schema = Schema(domainsid, invocationid=invocationid,
1947 schemadn=names.schemadn)
1949 if backend_type == "ldb":
1950 provision_backend = LDBBackend(backend_type, paths=paths,
1951 lp=lp, credentials=credentials,
1952 names=names, logger=logger)
1953 elif backend_type == "existing":
1954 # If support for this is ever added back, then the URI will need to be
1955 # specified again
1956 provision_backend = ExistingBackend(backend_type, paths=paths,
1957 lp=lp, credentials=credentials,
1958 names=names, logger=logger,
1959 ldap_backend_forced_uri=None)
1960 elif backend_type == "fedora-ds":
1961 provision_backend = FDSBackend(backend_type, paths=paths,
1962 lp=lp, credentials=credentials,
1963 names=names, logger=logger, domainsid=domainsid,
1964 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1965 slapd_path=slapd_path,
1966 root=root)
1967 elif backend_type == "openldap":
1968 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1969 lp=lp, credentials=credentials,
1970 names=names, logger=logger, domainsid=domainsid,
1971 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1972 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1973 else:
1974 raise ValueError("Unknown LDAP backend type selected")
1976 provision_backend.init()
1977 provision_backend.start()
1979 # only install a new shares config db if there is none
1980 if not os.path.exists(paths.shareconf):
1981 logger.info("Setting up share.ldb")
1982 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
1983 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1985 logger.info("Setting up secrets.ldb")
1986 secrets_ldb = setup_secretsdb(paths,
1987 session_info=session_info,
1988 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1990 try:
1991 logger.info("Setting up the registry")
1992 setup_registry(paths.hklm, session_info, lp=lp)
1994 logger.info("Setting up the privileges database")
1995 setup_privileges(paths.privilege, session_info, lp=lp)
1997 logger.info("Setting up idmap db")
1998 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2000 setup_name_mappings(idmap, sid=str(domainsid),
2001 root_uid=root_uid, nobody_uid=nobody_uid,
2002 users_gid=users_gid, root_gid=root_gid)
2004 logger.info("Setting up SAM db")
2005 samdb = setup_samdb(paths.samdb, session_info,
2006 provision_backend, lp, names, logger=logger,
2007 serverrole=serverrole,
2008 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2010 if serverrole == "active directory domain controller":
2011 if paths.netlogon is None:
2012 raise MissingShareError("netlogon", paths.smbconf)
2014 if paths.sysvol is None:
2015 raise MissingShareError("sysvol", paths.smbconf)
2017 if not os.path.isdir(paths.netlogon):
2018 os.makedirs(paths.netlogon, 0755)
2020 if adminpass is None:
2021 adminpass = samba.generate_random_password(12, 32)
2022 adminpass_generated = True
2023 else:
2024 adminpass_generated = False
2026 if samdb_fill == FILL_FULL:
2027 provision_fill(samdb, secrets_ldb, logger, names, paths,
2028 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2029 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2030 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2031 krbtgtpass=krbtgtpass, domainguid=domainguid,
2032 policyguid=policyguid, policyguid_dc=policyguid_dc,
2033 invocationid=invocationid, machinepass=machinepass,
2034 ntdsguid=ntdsguid, dns_backend=dns_backend,
2035 dnspass=dnspass, serverrole=serverrole,
2036 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2037 lp=lp, use_ntvfs=use_ntvfs,
2038 skip_sysvolacl=skip_sysvolacl)
2040 create_krb5_conf(paths.krb5conf,
2041 dnsdomain=names.dnsdomain, hostname=names.hostname,
2042 realm=names.realm)
2043 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2044 "generated at %s", paths.krb5conf)
2046 if serverrole == "active directory domain controller":
2047 create_dns_update_list(lp, logger, paths)
2049 backend_result = provision_backend.post_setup()
2050 provision_backend.shutdown()
2052 except:
2053 secrets_ldb.transaction_cancel()
2054 raise
2056 # Now commit the secrets.ldb to disk
2057 secrets_ldb.transaction_commit()
2059 # the commit creates the dns.keytab, now chown it
2060 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2061 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2062 try:
2063 os.chmod(dns_keytab_path, 0640)
2064 os.chown(dns_keytab_path, -1, paths.bind_gid)
2065 except OSError:
2066 if not os.environ.has_key('SAMBA_SELFTEST'):
2067 logger.info("Failed to chown %s to bind gid %u",
2068 dns_keytab_path, paths.bind_gid)
2070 result = ProvisionResult()
2071 result.server_role = serverrole
2072 result.domaindn = domaindn
2073 result.paths = paths
2074 result.names = names
2075 result.lp = lp
2076 result.samdb = samdb
2077 result.idmap = idmap
2078 result.domainsid = str(domainsid)
2080 if samdb_fill == FILL_FULL:
2081 result.adminpass_generated = adminpass_generated
2082 result.adminpass = adminpass
2083 else:
2084 result.adminpass_generated = False
2085 result.adminpass = None
2087 result.backend_result = backend_result
2089 if use_rfc2307:
2090 provision_fake_ypserver(logger=logger, samdb=samdb,
2091 domaindn=names.domaindn, netbiosname=names.netbiosname,
2092 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2094 return result
2097 def provision_become_dc(smbconf=None, targetdir=None,
2098 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2099 serverdn=None, domain=None, hostname=None, domainsid=None,
2100 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2101 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2102 dns_backend=None, root=None, nobody=None, users=None,
2103 backup=None, serverrole=None, ldap_backend=None,
2104 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2106 logger = logging.getLogger("provision")
2107 samba.set_debug_level(debuglevel)
2109 res = provision(logger, system_session(), None,
2110 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2111 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2112 configdn=configdn, serverdn=serverdn, domain=domain,
2113 hostname=hostname, hostip=None, domainsid=domainsid,
2114 machinepass=machinepass,
2115 serverrole="active directory domain controller",
2116 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2117 use_ntvfs=use_ntvfs)
2118 res.lp.set("debuglevel", str(debuglevel))
2119 return res
2122 def create_krb5_conf(path, dnsdomain, hostname, realm):
2123 """Write out a file containing zone statements suitable for inclusion in a
2124 named.conf file (including GSS-TSIG configuration).
2126 :param path: Path of the new named.conf file.
2127 :param dnsdomain: DNS Domain name
2128 :param hostname: Local hostname
2129 :param realm: Realm name
2131 setup_file(setup_path("krb5.conf"), path, {
2132 "DNSDOMAIN": dnsdomain,
2133 "HOSTNAME": hostname,
2134 "REALM": realm,
2138 class ProvisioningError(Exception):
2139 """A generic provision error."""
2141 def __init__(self, value):
2142 self.value = value
2144 def __str__(self):
2145 return "ProvisioningError: " + self.value
2148 class InvalidNetbiosName(Exception):
2149 """A specified name was not a valid NetBIOS name."""
2151 def __init__(self, name):
2152 super(InvalidNetbiosName, self).__init__(
2153 "The name '%r' is not a valid NetBIOS name" % name)
2156 class MissingShareError(ProvisioningError):
2158 def __init__(self, name, smbconf):
2159 super(MissingShareError, self).__init__(
2160 "Existing smb.conf does not have a [%s] share, but you are "
2161 "configuring a DC. Please remove %s or add the share manually." %
2162 (name, smbconf))