s4:provision: set the correct nTSecurityDescriptor on CN=Sites,CN=Configuration....
[Samba/gbeck.git] / source4 / scripting / python / samba / provision / __init__.py
blob5e80d63d4a963967a25d789ce8d6d3b67422a1c4
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_config_sites_descriptor,
84 get_domain_descriptor
86 from samba.provision.common import (
87 setup_path,
88 setup_add_ldif,
89 setup_modify_ldif,
91 from samba.provision.sambadns import (
92 setup_ad_dns,
93 create_dns_update_list
96 import samba.param
97 import samba.registry
98 from samba.schema import Schema
99 from samba.samdb import SamDB
100 from samba.dbchecker import dbcheck
103 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
104 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
105 DEFAULTSITE = "Default-First-Site-Name"
106 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
109 class ProvisionPaths(object):
111 def __init__(self):
112 self.shareconf = None
113 self.hklm = None
114 self.hkcu = None
115 self.hkcr = None
116 self.hku = None
117 self.hkpd = None
118 self.hkpt = None
119 self.samdb = None
120 self.idmapdb = None
121 self.secrets = None
122 self.keytab = None
123 self.dns_keytab = None
124 self.dns = None
125 self.winsdb = None
126 self.private_dir = None
127 self.state_dir = None
130 class ProvisionNames(object):
132 def __init__(self):
133 self.rootdn = None
134 self.domaindn = None
135 self.configdn = None
136 self.schemadn = None
137 self.ldapmanagerdn = None
138 self.dnsdomain = None
139 self.realm = None
140 self.netbiosname = None
141 self.domain = None
142 self.hostname = None
143 self.sitename = None
144 self.smbconf = None
147 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
148 lp):
149 """Get key provision parameters (realm, domain, ...) from a given provision
151 :param samdb: An LDB object connected to the sam.ldb file
152 :param secretsdb: An LDB object connected to the secrets.ldb file
153 :param idmapdb: An LDB object connected to the idmap.ldb file
154 :param paths: A list of path to provision object
155 :param smbconf: Path to the smb.conf file
156 :param lp: A LoadParm object
157 :return: A list of key provision parameters
159 names = ProvisionNames()
160 names.adminpass = None
162 # NT domain, kerberos realm, root dn, domain dn, domain dns name
163 names.domain = string.upper(lp.get("workgroup"))
164 names.realm = lp.get("realm")
165 names.dnsdomain = names.realm.lower()
166 basedn = samba.dn_from_dns_name(names.dnsdomain)
167 names.realm = string.upper(names.realm)
168 # netbiosname
169 # Get the netbiosname first (could be obtained from smb.conf in theory)
170 res = secretsdb.search(expression="(flatname=%s)" %
171 names.domain,base="CN=Primary Domains",
172 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
173 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
175 names.smbconf = smbconf
177 # That's a bit simplistic but it's ok as long as we have only 3
178 # partitions
179 current = samdb.search(expression="(objectClass=*)",
180 base="", scope=ldb.SCOPE_BASE,
181 attrs=["defaultNamingContext", "schemaNamingContext",
182 "configurationNamingContext","rootDomainNamingContext"])
184 names.configdn = current[0]["configurationNamingContext"]
185 configdn = str(names.configdn)
186 names.schemadn = current[0]["schemaNamingContext"]
187 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
188 current[0]["defaultNamingContext"][0]))):
189 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
190 "is not the same ..." % (paths.samdb,
191 str(current[0]["defaultNamingContext"][0]),
192 paths.smbconf, basedn)))
194 names.domaindn=current[0]["defaultNamingContext"]
195 names.rootdn=current[0]["rootDomainNamingContext"]
196 # default site name
197 res3 = samdb.search(expression="(objectClass=site)",
198 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
199 names.sitename = str(res3[0]["cn"])
201 # dns hostname and server dn
202 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
203 base="OU=Domain Controllers,%s" % basedn,
204 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
205 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
207 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
208 attrs=[], base=configdn)
209 names.serverdn = server_res[0].dn
211 # invocation id/objectguid
212 res5 = samdb.search(expression="(objectClass=*)",
213 base="CN=NTDS Settings,%s" % str(names.serverdn),
214 scope=ldb.SCOPE_BASE,
215 attrs=["invocationID", "objectGUID"])
216 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
217 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
219 # domain guid/sid
220 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
221 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
222 "objectSid","msDS-Behavior-Version" ])
223 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
224 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
225 if res6[0].get("msDS-Behavior-Version") is None or \
226 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
227 names.domainlevel = DS_DOMAIN_FUNCTION_2000
228 else:
229 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
231 # policy guid
232 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
233 base="CN=Policies,CN=System," + basedn,
234 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
235 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
236 # dc policy guid
237 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
238 " Policy)",
239 base="CN=Policies,CN=System," + basedn,
240 scope=ldb.SCOPE_ONELEVEL,
241 attrs=["cn","displayName"])
242 if len(res8) == 1:
243 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
244 else:
245 names.policyid_dc = None
247 res9 = idmapdb.search(expression="(cn=%s-%s)" %
248 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
249 attrs=["xidNumber", "type"])
250 if len(res9) != 1:
251 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
252 if res9[0]["type"][0] == "ID_TYPE_BOTH":
253 names.root_gid = res9[0]["xidNumber"][0]
254 else:
255 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
256 return names
259 def update_provision_usn(samdb, low, high, id, replace=False):
260 """Update the field provisionUSN in sam.ldb
262 This field is used to track range of USN modified by provision and
263 upgradeprovision.
264 This value is used afterward by next provision to figure out if
265 the field have been modified since last provision.
267 :param samdb: An LDB object connect to sam.ldb
268 :param low: The lowest USN modified by this upgrade
269 :param high: The highest USN modified by this upgrade
270 :param id: The invocation id of the samba's dc
271 :param replace: A boolean indicating if the range should replace any
272 existing one or appended (default)
275 tab = []
276 if not replace:
277 entry = samdb.search(base="@PROVISION",
278 scope=ldb.SCOPE_BASE,
279 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
280 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
281 if not re.search(';', e):
282 e = "%s;%s" % (e, id)
283 tab.append(str(e))
285 tab.append("%s-%s;%s" % (low, high, id))
286 delta = ldb.Message()
287 delta.dn = ldb.Dn(samdb, "@PROVISION")
288 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
289 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
290 entry = samdb.search(expression='provisionnerID=*',
291 base="@PROVISION", scope=ldb.SCOPE_BASE,
292 attrs=["provisionnerID"])
293 if len(entry) == 0 or len(entry[0]) == 0:
294 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
295 samdb.modify(delta)
298 def set_provision_usn(samdb, low, high, id):
299 """Set the field provisionUSN in sam.ldb
300 This field is used to track range of USN modified by provision and
301 upgradeprovision.
302 This value is used afterward by next provision to figure out if
303 the field have been modified since last provision.
305 :param samdb: An LDB object connect to sam.ldb
306 :param low: The lowest USN modified by this upgrade
307 :param high: The highest USN modified by this upgrade
308 :param id: The invocationId of the provision"""
310 tab = []
311 tab.append("%s-%s;%s" % (low, high, id))
313 delta = ldb.Message()
314 delta.dn = ldb.Dn(samdb, "@PROVISION")
315 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
316 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
317 samdb.add(delta)
320 def get_max_usn(samdb,basedn):
321 """ This function return the biggest USN present in the provision
323 :param samdb: A LDB object pointing to the sam.ldb
324 :param basedn: A string containing the base DN of the provision
325 (ie. DC=foo, DC=bar)
326 :return: The biggest USN in the provision"""
328 res = samdb.search(expression="objectClass=*",base=basedn,
329 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
330 controls=["search_options:1:2",
331 "server_sort:1:1:uSNChanged",
332 "paged_results:1:1"])
333 return res[0]["uSNChanged"]
336 def get_last_provision_usn(sam):
337 """Get USNs ranges modified by a provision or an upgradeprovision
339 :param sam: An LDB object pointing to the sam.ldb
340 :return: a dictionary which keys are invocation id and values are an array
341 of integer representing the different ranges
343 try:
344 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
345 base="@PROVISION", scope=ldb.SCOPE_BASE,
346 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
347 except ldb.LdbError, (ecode, emsg):
348 if ecode == ldb.ERR_NO_SUCH_OBJECT:
349 return None
350 raise
351 if len(entry) > 0:
352 myids = []
353 range = {}
354 p = re.compile(r'-')
355 if entry[0].get("provisionnerID"):
356 for e in entry[0]["provisionnerID"]:
357 myids.append(str(e))
358 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
359 tab1 = str(r).split(';')
360 if len(tab1) == 2:
361 id = tab1[1]
362 else:
363 id = "default"
364 if (len(myids) > 0 and id not in myids):
365 continue
366 tab2 = p.split(tab1[0])
367 if range.get(id) is None:
368 range[id] = []
369 range[id].append(tab2[0])
370 range[id].append(tab2[1])
371 return range
372 else:
373 return None
376 class ProvisionResult(object):
377 """Result of a provision.
379 :ivar server_role: The server role
380 :ivar paths: ProvisionPaths instance
381 :ivar domaindn: The domain dn, as string
384 def __init__(self):
385 self.server_role = None
386 self.paths = None
387 self.domaindn = None
388 self.lp = None
389 self.samdb = None
390 self.idmap = None
391 self.names = None
392 self.domainsid = None
393 self.adminpass_generated = None
394 self.adminpass = None
395 self.backend_result = None
397 def report_logger(self, logger):
398 """Report this provision result to a logger."""
399 logger.info(
400 "Once the above files are installed, your Samba4 server will "
401 "be ready to use")
402 if self.adminpass_generated:
403 logger.info("Admin password: %s", self.adminpass)
404 logger.info("Server Role: %s", self.server_role)
405 logger.info("Hostname: %s", self.names.hostname)
406 logger.info("NetBIOS Domain: %s", self.names.domain)
407 logger.info("DNS Domain: %s", self.names.dnsdomain)
408 logger.info("DOMAIN SID: %s", self.domainsid)
410 if self.backend_result:
411 self.backend_result.report_logger(logger)
414 def check_install(lp, session_info, credentials):
415 """Check whether the current install seems ok.
417 :param lp: Loadparm context
418 :param session_info: Session information
419 :param credentials: Credentials
421 if lp.get("realm") == "":
422 raise Exception("Realm empty")
423 samdb = Ldb(lp.samdb_url(), session_info=session_info,
424 credentials=credentials, lp=lp)
425 if len(samdb.search("(cn=Administrator)")) != 1:
426 raise ProvisioningError("No administrator account found")
429 def findnss(nssfn, names):
430 """Find a user or group from a list of possibilities.
432 :param nssfn: NSS Function to try (should raise KeyError if not found)
433 :param names: Names to check.
434 :return: Value return by first names list.
436 for name in names:
437 try:
438 return nssfn(name)
439 except KeyError:
440 pass
441 raise KeyError("Unable to find user/group in %r" % names)
444 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
445 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
448 def provision_paths_from_lp(lp, dnsdomain):
449 """Set the default paths for provisioning.
451 :param lp: Loadparm context.
452 :param dnsdomain: DNS Domain name
454 paths = ProvisionPaths()
455 paths.private_dir = lp.get("private dir")
456 paths.state_dir = lp.get("state directory")
458 # This is stored without path prefix for the "privateKeytab" attribute in
459 # "secrets_dns.ldif".
460 paths.dns_keytab = "dns.keytab"
461 paths.keytab = "secrets.keytab"
463 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
464 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
465 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
466 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
467 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
468 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
469 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
470 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
471 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
472 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
473 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
474 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
475 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
476 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
477 paths.hklm = "hklm.ldb"
478 paths.hkcr = "hkcr.ldb"
479 paths.hkcu = "hkcu.ldb"
480 paths.hku = "hku.ldb"
481 paths.hkpd = "hkpd.ldb"
482 paths.hkpt = "hkpt.ldb"
483 paths.sysvol = lp.get("path", "sysvol")
484 paths.netlogon = lp.get("path", "netlogon")
485 paths.smbconf = lp.configfile
486 return paths
489 def determine_netbios_name(hostname):
490 """Determine a netbios name from a hostname."""
491 # remove forbidden chars and force the length to be <16
492 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
493 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
496 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
497 serverrole=None, rootdn=None, domaindn=None, configdn=None,
498 schemadn=None, serverdn=None, sitename=None):
499 """Guess configuration settings to use."""
501 if hostname is None:
502 hostname = socket.gethostname().split(".")[0]
504 netbiosname = lp.get("netbios name")
505 if netbiosname is None:
506 netbiosname = determine_netbios_name(hostname)
507 netbiosname = netbiosname.upper()
508 if not valid_netbios_name(netbiosname):
509 raise InvalidNetbiosName(netbiosname)
511 if dnsdomain is None:
512 dnsdomain = lp.get("realm")
513 if dnsdomain is None or dnsdomain == "":
514 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
516 dnsdomain = dnsdomain.lower()
518 if serverrole is None:
519 serverrole = lp.get("server role")
520 if serverrole is None:
521 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
523 serverrole = serverrole.lower()
525 realm = dnsdomain.upper()
527 if lp.get("realm") == "":
528 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
530 if lp.get("realm").upper() != realm:
531 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))
533 if lp.get("server role").lower() != serverrole:
534 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))
536 if serverrole == "active directory domain controller":
537 if domain is None:
538 # This will, for better or worse, default to 'WORKGROUP'
539 domain = lp.get("workgroup")
540 domain = domain.upper()
542 if lp.get("workgroup").upper() != domain:
543 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))
545 if domaindn is None:
546 domaindn = samba.dn_from_dns_name(dnsdomain)
548 if domain == netbiosname:
549 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
550 else:
551 domain = netbiosname
552 if domaindn is None:
553 domaindn = "DC=" + netbiosname
555 if not valid_netbios_name(domain):
556 raise InvalidNetbiosName(domain)
558 if hostname.upper() == realm:
559 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
560 if netbiosname.upper() == realm:
561 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
562 if domain == realm:
563 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
565 if rootdn is None:
566 rootdn = domaindn
568 if configdn is None:
569 configdn = "CN=Configuration," + rootdn
570 if schemadn is None:
571 schemadn = "CN=Schema," + configdn
573 if sitename is None:
574 sitename = DEFAULTSITE
576 names = ProvisionNames()
577 names.rootdn = rootdn
578 names.domaindn = domaindn
579 names.configdn = configdn
580 names.schemadn = schemadn
581 names.ldapmanagerdn = "CN=Manager," + rootdn
582 names.dnsdomain = dnsdomain
583 names.domain = domain
584 names.realm = realm
585 names.netbiosname = netbiosname
586 names.hostname = hostname
587 names.sitename = sitename
588 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
589 netbiosname, sitename, configdn)
591 return names
594 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
595 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
596 global_param=None):
597 """Create a new smb.conf file based on a couple of basic settings.
599 assert smbconf is not None
601 if hostname is None:
602 hostname = socket.gethostname().split(".")[0]
604 netbiosname = determine_netbios_name(hostname)
606 if serverrole is None:
607 serverrole = "standalone server"
609 assert domain is not None
610 domain = domain.upper()
612 assert realm is not None
613 realm = realm.upper()
615 global_settings = {
616 "netbios name": netbiosname,
617 "workgroup": domain,
618 "realm": realm,
619 "server role": serverrole,
622 if lp is None:
623 lp = samba.param.LoadParm()
624 #Load non-existent file
625 if os.path.exists(smbconf):
626 lp.load(smbconf)
628 if global_param is not None:
629 for ent in global_param:
630 if global_param[ent] is not None:
631 global_settings[ent] = " ".join(global_param[ent])
633 if targetdir is not None:
634 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
635 global_settings["lock dir"] = os.path.abspath(targetdir)
636 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
637 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
639 lp.set("lock dir", os.path.abspath(targetdir))
640 lp.set("state directory", global_settings["state directory"])
641 lp.set("cache directory", global_settings["cache directory"])
643 if eadb:
644 if use_ntvfs and not lp.get("posix:eadb"):
645 if targetdir is not None:
646 privdir = os.path.join(targetdir, "private")
647 else:
648 privdir = lp.get("private dir")
649 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
650 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
651 if targetdir is not None:
652 statedir = os.path.join(targetdir, "state")
653 else:
654 statedir = lp.get("state directory")
655 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
657 shares = {}
658 if serverrole == "active directory domain controller":
659 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
660 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
661 "scripts")
662 else:
663 global_settings["passdb backend"] = "samba_dsdb"
665 f = open(smbconf, 'w')
666 try:
667 f.write("[globals]\n")
668 for key, val in global_settings.iteritems():
669 f.write("\t%s = %s\n" % (key, val))
670 f.write("\n")
672 for name, path in shares.iteritems():
673 f.write("[%s]\n" % name)
674 f.write("\tpath = %s\n" % path)
675 f.write("\tread only = no\n")
676 f.write("\n")
677 finally:
678 f.close()
679 # reload the smb.conf
680 lp.load(smbconf)
682 # and dump it without any values that are the default
683 # this ensures that any smb.conf parameters that were set
684 # on the provision/join command line are set in the resulting smb.conf
685 f = open(smbconf, mode='w')
686 try:
687 lp.dump(f, False)
688 finally:
689 f.close()
692 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
693 users_gid, root_gid):
694 """setup reasonable name mappings for sam names to unix names.
696 :param samdb: SamDB object.
697 :param idmap: IDmap db object.
698 :param sid: The domain sid.
699 :param domaindn: The domain DN.
700 :param root_uid: uid of the UNIX root user.
701 :param nobody_uid: uid of the UNIX nobody user.
702 :param users_gid: gid of the UNIX users group.
703 :param root_gid: gid of the UNIX root group.
705 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
707 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
708 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
711 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
712 provision_backend, names, schema, serverrole,
713 erase=False):
714 """Setup the partitions for the SAM database.
716 Alternatively, provision() may call this, and then populate the database.
718 :note: This will wipe the Sam Database!
720 :note: This function always removes the local SAM LDB file. The erase
721 parameter controls whether to erase the existing data, which
722 may not be stored locally but in LDAP.
725 assert session_info is not None
727 # We use options=["modules:"] to stop the modules loading - we
728 # just want to wipe and re-initialise the database, not start it up
730 try:
731 os.unlink(samdb_path)
732 except OSError:
733 pass
735 samdb = Ldb(url=samdb_path, session_info=session_info,
736 lp=lp, options=["modules:"])
738 ldap_backend_line = "# No LDAP backend"
739 if provision_backend.type != "ldb":
740 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
742 samdb.transaction_start()
743 try:
744 logger.info("Setting up sam.ldb partitions and settings")
745 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
746 "LDAP_BACKEND_LINE": ldap_backend_line
750 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
751 "BACKEND_TYPE": provision_backend.type,
752 "SERVER_ROLE": serverrole
755 logger.info("Setting up sam.ldb rootDSE")
756 setup_samdb_rootdse(samdb, names)
757 except:
758 samdb.transaction_cancel()
759 raise
760 else:
761 samdb.transaction_commit()
764 def secretsdb_self_join(secretsdb, domain,
765 netbiosname, machinepass, domainsid=None,
766 realm=None, dnsdomain=None,
767 keytab_path=None,
768 key_version_number=1,
769 secure_channel_type=SEC_CHAN_WKSTA):
770 """Add domain join-specific bits to a secrets database.
772 :param secretsdb: Ldb Handle to the secrets database
773 :param machinepass: Machine password
775 attrs = ["whenChanged",
776 "secret",
777 "priorSecret",
778 "priorChanged",
779 "krb5Keytab",
780 "privateKeytab"]
782 if realm is not None:
783 if dnsdomain is None:
784 dnsdomain = realm.lower()
785 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
786 else:
787 dnsname = None
788 shortname = netbiosname.lower()
790 # We don't need to set msg["flatname"] here, because rdn_name will handle
791 # it, and it causes problems for modifies anyway
792 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
793 msg["secureChannelType"] = [str(secure_channel_type)]
794 msg["objectClass"] = ["top", "primaryDomain"]
795 if dnsname is not None:
796 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
797 msg["realm"] = [realm]
798 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
799 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
800 msg["privateKeytab"] = ["secrets.keytab"]
802 msg["secret"] = [machinepass]
803 msg["samAccountName"] = ["%s$" % netbiosname]
804 msg["secureChannelType"] = [str(secure_channel_type)]
805 if domainsid is not None:
806 msg["objectSid"] = [ndr_pack(domainsid)]
808 # This complex expression tries to ensure that we don't have more
809 # than one record for this SID, realm or netbios domain at a time,
810 # but we don't delete the old record that we are about to modify,
811 # because that would delete the keytab and previous password.
812 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
813 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
814 scope=ldb.SCOPE_ONELEVEL)
816 for del_msg in res:
817 secretsdb.delete(del_msg.dn)
819 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
821 if len(res) == 1:
822 msg["priorSecret"] = [res[0]["secret"][0]]
823 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
825 try:
826 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
827 except KeyError:
828 pass
830 try:
831 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
832 except KeyError:
833 pass
835 for el in msg:
836 if el != 'dn':
837 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
838 secretsdb.modify(msg)
839 secretsdb.rename(res[0].dn, msg.dn)
840 else:
841 spn = [ 'HOST/%s' % shortname ]
842 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
843 # we are a domain controller then we add servicePrincipalName
844 # entries for the keytab code to update.
845 spn.extend([ 'HOST/%s' % dnsname ])
846 msg["servicePrincipalName"] = spn
848 secretsdb.add(msg)
851 def setup_secretsdb(paths, session_info, backend_credentials, lp):
852 """Setup the secrets database.
854 :note: This function does not handle exceptions and transaction on purpose,
855 it's up to the caller to do this job.
857 :param path: Path to the secrets database.
858 :param session_info: Session info.
859 :param credentials: Credentials
860 :param lp: Loadparm context
861 :return: LDB handle for the created secrets database
863 if os.path.exists(paths.secrets):
864 os.unlink(paths.secrets)
866 keytab_path = os.path.join(paths.private_dir, paths.keytab)
867 if os.path.exists(keytab_path):
868 os.unlink(keytab_path)
870 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
871 if os.path.exists(dns_keytab_path):
872 os.unlink(dns_keytab_path)
874 path = paths.secrets
876 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
877 secrets_ldb.erase()
878 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
879 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
880 secrets_ldb.transaction_start()
881 try:
882 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
884 if (backend_credentials is not None and
885 backend_credentials.authentication_requested()):
886 if backend_credentials.get_bind_dn() is not None:
887 setup_add_ldif(secrets_ldb,
888 setup_path("secrets_simple_ldap.ldif"), {
889 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
890 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
892 else:
893 setup_add_ldif(secrets_ldb,
894 setup_path("secrets_sasl_ldap.ldif"), {
895 "LDAPADMINUSER": backend_credentials.get_username(),
896 "LDAPADMINREALM": backend_credentials.get_realm(),
897 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
899 except:
900 secrets_ldb.transaction_cancel()
901 raise
902 return secrets_ldb
905 def setup_privileges(path, session_info, lp):
906 """Setup the privileges database.
908 :param path: Path to the privileges database.
909 :param session_info: Session info.
910 :param credentials: Credentials
911 :param lp: Loadparm context
912 :return: LDB handle for the created secrets database
914 if os.path.exists(path):
915 os.unlink(path)
916 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
917 privilege_ldb.erase()
918 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
921 def setup_registry(path, session_info, lp):
922 """Setup the registry.
924 :param path: Path to the registry database
925 :param session_info: Session information
926 :param credentials: Credentials
927 :param lp: Loadparm context
929 reg = samba.registry.Registry()
930 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
931 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
932 provision_reg = setup_path("provision.reg")
933 assert os.path.exists(provision_reg)
934 reg.diff_apply(provision_reg)
937 def setup_idmapdb(path, session_info, lp):
938 """Setup the idmap database.
940 :param path: path to the idmap database
941 :param session_info: Session information
942 :param credentials: Credentials
943 :param lp: Loadparm context
945 if os.path.exists(path):
946 os.unlink(path)
948 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
949 idmap_ldb.erase()
950 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
951 return idmap_ldb
954 def setup_samdb_rootdse(samdb, names):
955 """Setup the SamDB rootdse.
957 :param samdb: Sam Database handle
959 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
960 "SCHEMADN": names.schemadn,
961 "DOMAINDN": names.domaindn,
962 "ROOTDN" : names.rootdn,
963 "CONFIGDN": names.configdn,
964 "SERVERDN": names.serverdn,
968 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
969 dns_backend, dnspass, domainsid, next_rid, invocationid,
970 policyguid, policyguid_dc,
971 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
972 """Join a host to its own domain."""
973 assert isinstance(invocationid, str)
974 if ntdsguid is not None:
975 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
976 else:
977 ntdsguid_line = ""
979 if dc_rid is None:
980 dc_rid = next_rid
982 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
983 "CONFIGDN": names.configdn,
984 "SCHEMADN": names.schemadn,
985 "DOMAINDN": names.domaindn,
986 "SERVERDN": names.serverdn,
987 "INVOCATIONID": invocationid,
988 "NETBIOSNAME": names.netbiosname,
989 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
990 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
991 "DOMAINSID": str(domainsid),
992 "DCRID": str(dc_rid),
993 "SAMBA_VERSION_STRING": version,
994 "NTDSGUID": ntdsguid_line,
995 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
996 domainControllerFunctionality),
997 "RIDALLOCATIONSTART": str(next_rid + 100),
998 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1000 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1001 "POLICYGUID": policyguid,
1002 "POLICYGUID_DC": policyguid_dc,
1003 "DNSDOMAIN": names.dnsdomain,
1004 "DOMAINDN": names.domaindn})
1006 # If we are setting up a subdomain, then this has been replicated in, so we
1007 # don't need to add it
1008 if fill == FILL_FULL:
1009 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1010 "CONFIGDN": names.configdn,
1011 "SCHEMADN": names.schemadn,
1012 "DOMAINDN": names.domaindn,
1013 "SERVERDN": names.serverdn,
1014 "INVOCATIONID": invocationid,
1015 "NETBIOSNAME": names.netbiosname,
1016 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1017 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1018 "DOMAINSID": str(domainsid),
1019 "DCRID": str(dc_rid),
1020 "SAMBA_VERSION_STRING": version,
1021 "NTDSGUID": ntdsguid_line,
1022 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1023 domainControllerFunctionality)})
1025 # Setup fSMORoleOwner entries to point at the newly created DC entry
1026 setup_modify_ldif(samdb,
1027 setup_path("provision_self_join_modify_config.ldif"), {
1028 "CONFIGDN": names.configdn,
1029 "SCHEMADN": names.schemadn,
1030 "DEFAULTSITE": names.sitename,
1031 "NETBIOSNAME": names.netbiosname,
1032 "SERVERDN": names.serverdn,
1035 system_session_info = system_session()
1036 samdb.set_session_info(system_session_info)
1037 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1038 # modify a serverReference under cn=config when we are a subdomain, we must
1039 # be system due to ACLs
1040 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1041 "DOMAINDN": names.domaindn,
1042 "SERVERDN": names.serverdn,
1043 "NETBIOSNAME": names.netbiosname,
1046 samdb.set_session_info(admin_session_info)
1048 if dns_backend != "SAMBA_INTERNAL":
1049 # This is Samba4 specific and should be replaced by the correct
1050 # DNS AD-style setup
1051 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1052 "DNSDOMAIN": names.dnsdomain,
1053 "DOMAINDN": names.domaindn,
1054 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1055 "HOSTNAME" : names.hostname,
1056 "DNSNAME" : '%s.%s' % (
1057 names.netbiosname.lower(), names.dnsdomain.lower())
1061 def getpolicypath(sysvolpath, dnsdomain, guid):
1062 """Return the physical path of policy given its guid.
1064 :param sysvolpath: Path to the sysvol folder
1065 :param dnsdomain: DNS name of the AD domain
1066 :param guid: The GUID of the policy
1067 :return: A string with the complete path to the policy folder
1069 if guid[0] != "{":
1070 guid = "{%s}" % guid
1071 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1072 return policy_path
1075 def create_gpo_struct(policy_path):
1076 if not os.path.exists(policy_path):
1077 os.makedirs(policy_path, 0775)
1078 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1079 try:
1080 f.write("[General]\r\nVersion=0")
1081 finally:
1082 f.close()
1083 p = os.path.join(policy_path, "MACHINE")
1084 if not os.path.exists(p):
1085 os.makedirs(p, 0775)
1086 p = os.path.join(policy_path, "USER")
1087 if not os.path.exists(p):
1088 os.makedirs(p, 0775)
1091 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1092 """Create the default GPO for a domain
1094 :param sysvolpath: Physical path for the sysvol folder
1095 :param dnsdomain: DNS domain name of the AD domain
1096 :param policyguid: GUID of the default domain policy
1097 :param policyguid_dc: GUID of the default domain controler policy
1099 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1100 create_gpo_struct(policy_path)
1102 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1103 create_gpo_struct(policy_path)
1106 def setup_samdb(path, session_info, provision_backend, lp, names,
1107 logger, fill, serverrole, schema, am_rodc=False):
1108 """Setup a complete SAM Database.
1110 :note: This will wipe the main SAM database file!
1113 # Also wipes the database
1114 setup_samdb_partitions(path, logger=logger, lp=lp,
1115 provision_backend=provision_backend, session_info=session_info,
1116 names=names, serverrole=serverrole, schema=schema)
1118 # Load the database, but don's load the global schema and don't connect
1119 # quite yet
1120 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1121 credentials=provision_backend.credentials, lp=lp,
1122 global_schema=False, am_rodc=am_rodc)
1124 logger.info("Pre-loading the Samba 4 and AD schema")
1126 # Load the schema from the one we computed earlier
1127 samdb.set_schema(schema, write_indices_and_attributes=False)
1129 # Set the NTDS settings DN manually - in order to have it already around
1130 # before the provisioned tree exists and we connect
1131 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1133 # And now we can connect to the DB - the schema won't be loaded from the
1134 # DB
1135 samdb.connect(path)
1137 # But we have to give it one more kick to have it use the schema
1138 # during provision - it needs, now that it is connected, to write
1139 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1140 samdb.set_schema(schema, write_indices_and_attributes=True)
1142 return samdb
1145 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1146 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1147 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1148 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1150 if next_rid is None:
1151 next_rid = 1000
1153 # Provision does not make much sense values larger than 1000000000
1154 # as the upper range of the rIDAvailablePool is 1073741823 and
1155 # we don't want to create a domain that cannot allocate rids.
1156 if next_rid < 1000 or next_rid > 1000000000:
1157 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1158 error += "the valid range is %u-%u. The default is %u." % (
1159 1000, 1000000000, 1000)
1160 raise ProvisioningError(error)
1162 # ATTENTION: Do NOT change these default values without discussion with the
1163 # team and/or release manager. They have a big impact on the whole program!
1164 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1166 if dom_for_fun_level is None:
1167 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1169 if dom_for_fun_level > domainControllerFunctionality:
1170 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!")
1172 domainFunctionality = dom_for_fun_level
1173 forestFunctionality = dom_for_fun_level
1175 # Set the NTDS settings DN manually - in order to have it already around
1176 # before the provisioned tree exists and we connect
1177 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1179 samdb.transaction_start()
1180 try:
1181 # Set the domain functionality levels onto the database.
1182 # Various module (the password_hash module in particular) need
1183 # to know what level of AD we are emulating.
1185 # These will be fixed into the database via the database
1186 # modifictions below, but we need them set from the start.
1187 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1188 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1189 samdb.set_opaque_integer("domainControllerFunctionality",
1190 domainControllerFunctionality)
1192 samdb.set_domain_sid(str(domainsid))
1193 samdb.set_invocation_id(invocationid)
1195 logger.info("Adding DomainDN: %s" % names.domaindn)
1197 # impersonate domain admin
1198 admin_session_info = admin_session(lp, str(domainsid))
1199 samdb.set_session_info(admin_session_info)
1200 if domainguid is not None:
1201 domainguid_line = "objectGUID: %s\n-" % domainguid
1202 else:
1203 domainguid_line = ""
1205 descr = b64encode(get_domain_descriptor(domainsid))
1206 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1207 "DOMAINDN": names.domaindn,
1208 "DOMAINSID": str(domainsid),
1209 "DESCRIPTOR": descr,
1210 "DOMAINGUID": domainguid_line
1213 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1214 "DOMAINDN": names.domaindn,
1215 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1216 "NEXTRID": str(next_rid),
1217 "DEFAULTSITE": names.sitename,
1218 "CONFIGDN": names.configdn,
1219 "POLICYGUID": policyguid,
1220 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1221 "SAMBA_VERSION_STRING": version
1224 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1225 if fill == FILL_FULL:
1226 logger.info("Adding configuration container")
1227 descr = b64encode(get_config_descriptor(domainsid))
1228 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1229 "CONFIGDN": names.configdn,
1230 "DESCRIPTOR": descr,
1233 # The LDIF here was created when the Schema object was constructed
1234 logger.info("Setting up sam.ldb schema")
1235 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1236 samdb.modify_ldif(schema.schema_dn_modify)
1237 samdb.write_prefixes_from_schema()
1238 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1239 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1240 {"SCHEMADN": names.schemadn})
1242 # Now register this container in the root of the forest
1243 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1244 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1245 "subRefs")
1247 except:
1248 samdb.transaction_cancel()
1249 raise
1250 else:
1251 samdb.transaction_commit()
1253 samdb.transaction_start()
1254 try:
1255 samdb.invocation_id = invocationid
1257 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1258 if fill == FILL_FULL:
1259 logger.info("Setting up sam.ldb configuration data")
1260 partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
1261 sites_descr = b64encode(get_config_sites_descriptor(domainsid))
1262 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1263 "CONFIGDN": names.configdn,
1264 "NETBIOSNAME": names.netbiosname,
1265 "DEFAULTSITE": names.sitename,
1266 "DNSDOMAIN": names.dnsdomain,
1267 "DOMAIN": names.domain,
1268 "SCHEMADN": names.schemadn,
1269 "DOMAINDN": names.domaindn,
1270 "SERVERDN": names.serverdn,
1271 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1272 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1273 "PARTITIONS_DESCRIPTOR": partitions_descr,
1274 "SITES_DESCRIPTOR": sites_descr,
1277 logger.info("Setting up display specifiers")
1278 display_specifiers_ldif = read_ms_ldif(
1279 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1280 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1281 {"CONFIGDN": names.configdn})
1282 check_all_substituted(display_specifiers_ldif)
1283 samdb.add_ldif(display_specifiers_ldif)
1285 logger.info("Adding users container")
1286 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1287 "DOMAINDN": names.domaindn})
1288 logger.info("Modifying users container")
1289 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1290 "DOMAINDN": names.domaindn})
1291 logger.info("Adding computers container")
1292 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1293 "DOMAINDN": names.domaindn})
1294 logger.info("Modifying computers container")
1295 setup_modify_ldif(samdb,
1296 setup_path("provision_computers_modify.ldif"), {
1297 "DOMAINDN": names.domaindn})
1298 logger.info("Setting up sam.ldb data")
1299 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1300 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1301 "DOMAINDN": names.domaindn,
1302 "NETBIOSNAME": names.netbiosname,
1303 "DEFAULTSITE": names.sitename,
1304 "CONFIGDN": names.configdn,
1305 "SERVERDN": names.serverdn,
1306 "RIDAVAILABLESTART": str(next_rid + 600),
1307 "POLICYGUID_DC": policyguid_dc
1310 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1311 if fill == FILL_FULL:
1312 setup_modify_ldif(samdb,
1313 setup_path("provision_configuration_references.ldif"), {
1314 "CONFIGDN": names.configdn,
1315 "SCHEMADN": names.schemadn})
1317 logger.info("Setting up well known security principals")
1318 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1319 "CONFIGDN": names.configdn,
1322 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1323 setup_modify_ldif(samdb,
1324 setup_path("provision_basedn_references.ldif"),
1325 {"DOMAINDN": names.domaindn})
1327 logger.info("Setting up sam.ldb users and groups")
1328 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1329 "DOMAINDN": names.domaindn,
1330 "DOMAINSID": str(domainsid),
1331 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1332 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1335 logger.info("Setting up self join")
1336 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1337 invocationid=invocationid,
1338 dns_backend=dns_backend,
1339 dnspass=dnspass,
1340 machinepass=machinepass,
1341 domainsid=domainsid,
1342 next_rid=next_rid,
1343 dc_rid=dc_rid,
1344 policyguid=policyguid,
1345 policyguid_dc=policyguid_dc,
1346 domainControllerFunctionality=domainControllerFunctionality,
1347 ntdsguid=ntdsguid)
1349 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1350 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1351 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1352 assert isinstance(names.ntdsguid, str)
1353 except:
1354 samdb.transaction_cancel()
1355 raise
1356 else:
1357 samdb.transaction_commit()
1358 return samdb
1361 FILL_FULL = "FULL"
1362 FILL_SUBDOMAIN = "SUBDOMAIN"
1363 FILL_NT4SYNC = "NT4SYNC"
1364 FILL_DRS = "DRS"
1365 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1366 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)"
1369 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb):
1370 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1371 for root, dirs, files in os.walk(path, topdown=False):
1372 for name in files:
1373 setntacl(lp, os.path.join(root, name), acl, domsid,
1374 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1375 for name in dirs:
1376 setntacl(lp, os.path.join(root, name), acl, domsid,
1377 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1380 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1381 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1382 folders beneath.
1384 :param sysvol: Physical path for the sysvol folder
1385 :param dnsdomain: The DNS name of the domain
1386 :param domainsid: The SID of the domain
1387 :param domaindn: The DN of the domain (ie. DC=...)
1388 :param samdb: An LDB object on the SAM db
1389 :param lp: an LP object
1392 # Set ACL for GPO root folder
1393 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1394 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1395 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1397 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1398 attrs=["cn", "nTSecurityDescriptor"],
1399 expression="", scope=ldb.SCOPE_ONELEVEL)
1401 for policy in res:
1402 acl = ndr_unpack(security.descriptor,
1403 str(policy["nTSecurityDescriptor"])).as_sddl()
1404 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1405 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1406 str(domainsid), use_ntvfs,
1407 passdb=passdb)
1410 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1411 domaindn, lp, use_ntvfs):
1412 """Set the ACL for the sysvol share and the subfolders
1414 :param samdb: An LDB object on the SAM db
1415 :param netlogon: Physical path for the netlogon folder
1416 :param sysvol: Physical path for the sysvol folder
1417 :param uid: The UID of the "Administrator" user
1418 :param gid: The GID of the "Domain adminstrators" group
1419 :param domainsid: The SID of the domain
1420 :param dnsdomain: The DNS name of the domain
1421 :param domaindn: The DN of the domain (ie. DC=...)
1423 s4_passdb = None
1425 if not use_ntvfs:
1426 # This will ensure that the smbd code we are running when setting ACLs
1427 # is initialised with the smb.conf
1428 s3conf = s3param.get_context()
1429 s3conf.load(lp.configfile)
1430 # ensure we are using the right samba_dsdb passdb backend, no matter what
1431 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1432 passdb.reload_static_pdb()
1434 # ensure that we init the samba_dsdb backend, so the domain sid is
1435 # marked in secrets.tdb
1436 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1438 # now ensure everything matches correctly, to avoid wierd issues
1439 if passdb.get_global_sam_sid() != domainsid:
1440 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))
1442 domain_info = s4_passdb.domain_info()
1443 if domain_info["dom_sid"] != domainsid:
1444 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))
1446 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1447 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()))
1450 try:
1451 if use_ntvfs:
1452 os.chown(sysvol, -1, gid)
1453 except OSError:
1454 canchown = False
1455 else:
1456 canchown = True
1458 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1459 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1460 for root, dirs, files in os.walk(sysvol, topdown=False):
1461 for name in files:
1462 if use_ntvfs and canchown:
1463 os.chown(os.path.join(root, name), -1, gid)
1464 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1465 for name in dirs:
1466 if use_ntvfs and canchown:
1467 os.chown(os.path.join(root, name), -1, gid)
1468 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1470 # Set acls on Policy folder and policies folders
1471 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1473 def acl_type(direct_db_access):
1474 if direct_db_access:
1475 return "DB"
1476 else:
1477 return "VFS"
1479 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1480 fsacl = getntacl(lp, path, direct_db_access=direct_db_access)
1481 fsacl_sddl = fsacl.as_sddl(domainsid)
1482 if fsacl_sddl != acl:
1483 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))
1485 for root, dirs, files in os.walk(path, topdown=False):
1486 for name in files:
1487 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1488 if fsacl is None:
1489 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1490 fsacl_sddl = fsacl.as_sddl(domainsid)
1491 if fsacl_sddl != acl:
1492 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))
1494 for name in dirs:
1495 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1496 if fsacl is None:
1497 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1498 fsacl_sddl = fsacl.as_sddl(domainsid)
1499 if fsacl_sddl != acl:
1500 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))
1503 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1504 direct_db_access):
1505 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1506 folders beneath.
1508 :param sysvol: Physical path for the sysvol folder
1509 :param dnsdomain: The DNS name of the domain
1510 :param domainsid: The SID of the domain
1511 :param domaindn: The DN of the domain (ie. DC=...)
1512 :param samdb: An LDB object on the SAM db
1513 :param lp: an LP object
1516 # Set ACL for GPO root folder
1517 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1518 fsacl = getntacl(lp, root_policy_path, direct_db_access=direct_db_access)
1519 if fsacl is None:
1520 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1521 fsacl_sddl = fsacl.as_sddl(domainsid)
1522 if fsacl_sddl != POLICIES_ACL:
1523 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))
1524 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1525 attrs=["cn", "nTSecurityDescriptor"],
1526 expression="", scope=ldb.SCOPE_ONELEVEL)
1528 for policy in res:
1529 acl = ndr_unpack(security.descriptor,
1530 str(policy["nTSecurityDescriptor"])).as_sddl()
1531 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1532 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1533 domainsid, direct_db_access)
1536 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1537 lp):
1538 """Set the ACL for the sysvol share and the subfolders
1540 :param samdb: An LDB object on the SAM db
1541 :param netlogon: Physical path for the netlogon folder
1542 :param sysvol: Physical path for the sysvol folder
1543 :param uid: The UID of the "Administrator" user
1544 :param gid: The GID of the "Domain adminstrators" group
1545 :param domainsid: The SID of the domain
1546 :param dnsdomain: The DNS name of the domain
1547 :param domaindn: The DN of the domain (ie. DC=...)
1550 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1551 s3conf = s3param.get_context()
1552 s3conf.load(lp.configfile)
1553 # ensure we are using the right samba_dsdb passdb backend, no matter what
1554 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1555 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1556 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1558 # now ensure everything matches correctly, to avoid wierd issues
1559 if passdb.get_global_sam_sid() != domainsid:
1560 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))
1562 domain_info = s4_passdb.domain_info()
1563 if domain_info["dom_sid"] != domainsid:
1564 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))
1566 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1567 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()))
1569 # Ensure we can read this directly, and via the smbd VFS
1570 for direct_db_access in [True, False]:
1571 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1572 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1573 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access)
1574 if fsacl is None:
1575 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1576 fsacl_sddl = fsacl.as_sddl(domainsid)
1577 if fsacl_sddl != SYSVOL_ACL:
1578 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))
1580 # Check acls on Policy folder and policies folders
1581 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1582 direct_db_access)
1585 def interface_ips_v4(lp):
1586 """return only IPv4 IPs"""
1587 ips = samba.interface_ips(lp, False)
1588 ret = []
1589 for i in ips:
1590 if i.find(':') == -1:
1591 ret.append(i)
1592 return ret
1595 def interface_ips_v6(lp, linklocal=False):
1596 """return only IPv6 IPs"""
1597 ips = samba.interface_ips(lp, False)
1598 ret = []
1599 for i in ips:
1600 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1601 ret.append(i)
1602 return ret
1605 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1606 domainsid, schema=None,
1607 targetdir=None, samdb_fill=FILL_FULL,
1608 hostip=None, hostip6=None,
1609 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1610 domainguid=None, policyguid=None, policyguid_dc=None,
1611 invocationid=None, machinepass=None, ntdsguid=None,
1612 dns_backend=None, dnspass=None,
1613 serverrole=None, dom_for_fun_level=None,
1614 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1615 # create/adapt the group policy GUIDs
1616 # Default GUID for default policy are described at
1617 # "How Core Group Policy Works"
1618 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1619 if policyguid is None:
1620 policyguid = DEFAULT_POLICY_GUID
1621 policyguid = policyguid.upper()
1622 if policyguid_dc is None:
1623 policyguid_dc = DEFAULT_DC_POLICY_GUID
1624 policyguid_dc = policyguid_dc.upper()
1626 if invocationid is None:
1627 invocationid = str(uuid.uuid4())
1629 if krbtgtpass is None:
1630 krbtgtpass = samba.generate_random_password(128, 255)
1631 if machinepass is None:
1632 machinepass = samba.generate_random_password(128, 255)
1633 if dnspass is None:
1634 dnspass = samba.generate_random_password(128, 255)
1636 samdb = fill_samdb(samdb, lp, names, logger=logger,
1637 domainsid=domainsid, schema=schema, domainguid=domainguid,
1638 policyguid=policyguid, policyguid_dc=policyguid_dc,
1639 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1640 invocationid=invocationid, machinepass=machinepass,
1641 dns_backend=dns_backend, dnspass=dnspass,
1642 ntdsguid=ntdsguid, serverrole=serverrole,
1643 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1644 next_rid=next_rid, dc_rid=dc_rid)
1646 if serverrole == "active directory domain controller":
1648 # Set up group policies (domain policy and domain controller
1649 # policy)
1650 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1651 policyguid_dc)
1652 if not skip_sysvolacl:
1653 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1654 paths.root_gid, domainsid, names.dnsdomain,
1655 names.domaindn, lp, use_ntvfs)
1656 else:
1657 logger.info("Setting acl on sysvol skipped")
1659 secretsdb_self_join(secrets_ldb, domain=names.domain,
1660 realm=names.realm, dnsdomain=names.dnsdomain,
1661 netbiosname=names.netbiosname, domainsid=domainsid,
1662 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1664 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1665 # In future, this might be determined from some configuration
1666 kerberos_enctypes = str(ENC_ALL_TYPES)
1668 try:
1669 msg = ldb.Message(ldb.Dn(samdb,
1670 samdb.searchone("distinguishedName",
1671 expression="samAccountName=%s$" % names.netbiosname,
1672 scope=ldb.SCOPE_SUBTREE)))
1673 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1674 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1675 name="msDS-SupportedEncryptionTypes")
1676 samdb.modify(msg)
1677 except ldb.LdbError, (enum, estr):
1678 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1679 # It might be that this attribute does not exist in this schema
1680 raise
1682 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1683 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1684 dnspass=dnspass, os_level=dom_for_fun_level,
1685 targetdir=targetdir, site=DEFAULTSITE)
1687 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1688 attribute="objectGUID")
1689 assert isinstance(domainguid, str)
1691 lastProvisionUSNs = get_last_provision_usn(samdb)
1692 maxUSN = get_max_usn(samdb, str(names.rootdn))
1693 if lastProvisionUSNs is not None:
1694 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1695 else:
1696 set_provision_usn(samdb, 0, maxUSN, invocationid)
1698 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1699 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1700 { 'NTDSGUID' : names.ntdsguid })
1702 # fix any dangling GUIDs from the provision
1703 logger.info("Fixing provision GUIDs")
1704 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1705 quiet=True)
1706 samdb.transaction_start()
1707 try:
1708 # a small number of GUIDs are missing because of ordering issues in the
1709 # provision code
1710 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1711 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1712 scope=ldb.SCOPE_BASE,
1713 attrs=['defaultObjectCategory'])
1714 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1715 scope=ldb.SCOPE_ONELEVEL,
1716 attrs=['ipsecOwnersReference',
1717 'ipsecFilterReference',
1718 'ipsecISAKMPReference',
1719 'ipsecNegotiationPolicyReference',
1720 'ipsecNFAReference'])
1721 except:
1722 samdb.transaction_cancel()
1723 raise
1724 else:
1725 samdb.transaction_commit()
1728 _ROLES_MAP = {
1729 "ROLE_STANDALONE": "standalone server",
1730 "ROLE_DOMAIN_MEMBER": "member server",
1731 "ROLE_DOMAIN_BDC": "active directory domain controller",
1732 "ROLE_DOMAIN_PDC": "active directory domain controller",
1733 "dc": "active directory domain controller",
1734 "member": "member server",
1735 "domain controller": "active directory domain controller",
1736 "active directory domain controller": "active directory domain controller",
1737 "member server": "member server",
1738 "standalone": "standalone server",
1739 "standalone server": "standalone server",
1743 def sanitize_server_role(role):
1744 """Sanitize a server role name.
1746 :param role: Server role
1747 :raise ValueError: If the role can not be interpreted
1748 :return: Sanitized server role (one of "member server",
1749 "active directory domain controller", "standalone server")
1751 try:
1752 return _ROLES_MAP[role]
1753 except KeyError:
1754 raise ValueError(role)
1757 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1758 maxuid, maxgid):
1759 """Create AD entries for the fake ypserver.
1761 This is needed for being able to manipulate posix attrs via ADUC.
1763 samdb.transaction_start()
1764 try:
1765 logger.info("Setting up fake yp server settings")
1766 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1767 "DOMAINDN": domaindn,
1768 "NETBIOSNAME": netbiosname,
1769 "NISDOMAIN": nisdomain,
1771 except:
1772 samdb.transaction_cancel()
1773 raise
1774 else:
1775 samdb.transaction_commit()
1778 def provision(logger, session_info, credentials, smbconf=None,
1779 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1780 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1781 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1782 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1783 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1784 dns_backend=None, dns_forwarder=None, dnspass=None,
1785 invocationid=None, machinepass=None, ntdsguid=None,
1786 root=None, nobody=None, users=None, backup=None, aci=None,
1787 serverrole=None, dom_for_fun_level=None, backend_type=None,
1788 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1789 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1790 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1791 """Provision samba4
1793 :note: caution, this wipes all existing data!
1796 try:
1797 serverrole = sanitize_server_role(serverrole)
1798 except ValueError:
1799 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1801 if ldapadminpass is None:
1802 # Make a new, random password between Samba and it's LDAP server
1803 ldapadminpass = samba.generate_random_password(128, 255)
1805 if backend_type is None:
1806 backend_type = "ldb"
1808 if domainsid is None:
1809 domainsid = security.random_sid()
1810 else:
1811 domainsid = security.dom_sid(domainsid)
1813 root_uid = findnss_uid([root or "root"])
1814 nobody_uid = findnss_uid([nobody or "nobody"])
1815 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1816 root_gid = pwd.getpwuid(root_uid).pw_gid
1818 try:
1819 bind_gid = findnss_gid(["bind", "named"])
1820 except KeyError:
1821 bind_gid = None
1823 if targetdir is not None:
1824 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1825 elif smbconf is None:
1826 smbconf = samba.param.default_path()
1827 if not os.path.exists(os.path.dirname(smbconf)):
1828 os.makedirs(os.path.dirname(smbconf))
1830 server_services = []
1831 global_param = {}
1832 if use_rfc2307:
1833 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1835 if dns_backend != "SAMBA_INTERNAL":
1836 server_services.append("-dns")
1837 else:
1838 if dns_forwarder is not None:
1839 global_param["dns forwarder"] = [dns_forwarder]
1841 if use_ntvfs:
1842 server_services.append("+smb")
1843 server_services.append("-s3fs")
1844 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1846 if len(server_services) > 0:
1847 global_param["server services"] = server_services
1849 # only install a new smb.conf if there isn't one there already
1850 if os.path.exists(smbconf):
1851 # if Samba Team members can't figure out the weird errors
1852 # loading an empty smb.conf gives, then we need to be smarter.
1853 # Pretend it just didn't exist --abartlet
1854 f = open(smbconf, 'r')
1855 try:
1856 data = f.read().lstrip()
1857 finally:
1858 f.close()
1859 if data is None or data == "":
1860 make_smbconf(smbconf, hostname, domain, realm,
1861 targetdir, serverrole=serverrole,
1862 eadb=useeadb, use_ntvfs=use_ntvfs,
1863 lp=lp, global_param=global_param)
1864 else:
1865 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1866 serverrole=serverrole,
1867 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1869 if lp is None:
1870 lp = samba.param.LoadParm()
1871 lp.load(smbconf)
1872 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1873 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1874 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1875 sitename=sitename, rootdn=rootdn)
1876 paths = provision_paths_from_lp(lp, names.dnsdomain)
1878 paths.bind_gid = bind_gid
1879 paths.root_uid = root_uid;
1880 paths.root_gid = root_gid
1882 if hostip is None:
1883 logger.info("Looking up IPv4 addresses")
1884 hostips = interface_ips_v4(lp)
1885 if len(hostips) > 0:
1886 hostip = hostips[0]
1887 if len(hostips) > 1:
1888 logger.warning("More than one IPv4 address found. Using %s",
1889 hostip)
1890 if hostip == "127.0.0.1":
1891 hostip = None
1892 if hostip is None:
1893 logger.warning("No IPv4 address will be assigned")
1895 if hostip6 is None:
1896 logger.info("Looking up IPv6 addresses")
1897 hostips = interface_ips_v6(lp, linklocal=False)
1898 if hostips:
1899 hostip6 = hostips[0]
1900 if len(hostips) > 1:
1901 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1902 if hostip6 is None:
1903 logger.warning("No IPv6 address will be assigned")
1905 names.hostip = hostip
1906 names.hostip6 = hostip6
1908 if serverrole is None:
1909 serverrole = lp.get("server role")
1911 if not os.path.exists(paths.private_dir):
1912 os.mkdir(paths.private_dir)
1913 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1914 os.mkdir(os.path.join(paths.private_dir, "tls"))
1915 if not os.path.exists(paths.state_dir):
1916 os.mkdir(paths.state_dir)
1918 if paths.sysvol and not os.path.exists(paths.sysvol):
1919 os.makedirs(paths.sysvol, 0775)
1921 if not use_ntvfs and serverrole == "active directory domain controller":
1922 s3conf = s3param.get_context()
1923 s3conf.load(lp.configfile)
1925 if paths.sysvol is None:
1926 raise MissingShareError("sysvol", paths.smbconf)
1928 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1929 try:
1930 try:
1931 smbd.set_simple_acl(file.name, 0755, root_gid)
1932 except Exception:
1933 if not smbd.have_posix_acls():
1934 # This clue is only strictly correct for RPM and
1935 # Debian-like Linux systems, but hopefully other users
1936 # will get enough clue from it.
1937 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.")
1939 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1940 try:
1941 smbd.chown(file.name, root_uid, root_gid)
1942 except Exception:
1943 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1944 finally:
1945 file.close()
1947 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1949 schema = Schema(domainsid, invocationid=invocationid,
1950 schemadn=names.schemadn)
1952 if backend_type == "ldb":
1953 provision_backend = LDBBackend(backend_type, paths=paths,
1954 lp=lp, credentials=credentials,
1955 names=names, logger=logger)
1956 elif backend_type == "existing":
1957 # If support for this is ever added back, then the URI will need to be
1958 # specified again
1959 provision_backend = ExistingBackend(backend_type, paths=paths,
1960 lp=lp, credentials=credentials,
1961 names=names, logger=logger,
1962 ldap_backend_forced_uri=None)
1963 elif backend_type == "fedora-ds":
1964 provision_backend = FDSBackend(backend_type, paths=paths,
1965 lp=lp, credentials=credentials,
1966 names=names, logger=logger, domainsid=domainsid,
1967 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1968 slapd_path=slapd_path,
1969 root=root)
1970 elif backend_type == "openldap":
1971 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1972 lp=lp, credentials=credentials,
1973 names=names, logger=logger, domainsid=domainsid,
1974 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1975 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1976 else:
1977 raise ValueError("Unknown LDAP backend type selected")
1979 provision_backend.init()
1980 provision_backend.start()
1982 # only install a new shares config db if there is none
1983 if not os.path.exists(paths.shareconf):
1984 logger.info("Setting up share.ldb")
1985 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
1986 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1988 logger.info("Setting up secrets.ldb")
1989 secrets_ldb = setup_secretsdb(paths,
1990 session_info=session_info,
1991 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1993 try:
1994 logger.info("Setting up the registry")
1995 setup_registry(paths.hklm, session_info, lp=lp)
1997 logger.info("Setting up the privileges database")
1998 setup_privileges(paths.privilege, session_info, lp=lp)
2000 logger.info("Setting up idmap db")
2001 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2003 setup_name_mappings(idmap, sid=str(domainsid),
2004 root_uid=root_uid, nobody_uid=nobody_uid,
2005 users_gid=users_gid, root_gid=root_gid)
2007 logger.info("Setting up SAM db")
2008 samdb = setup_samdb(paths.samdb, session_info,
2009 provision_backend, lp, names, logger=logger,
2010 serverrole=serverrole,
2011 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2013 if serverrole == "active directory domain controller":
2014 if paths.netlogon is None:
2015 raise MissingShareError("netlogon", paths.smbconf)
2017 if paths.sysvol is None:
2018 raise MissingShareError("sysvol", paths.smbconf)
2020 if not os.path.isdir(paths.netlogon):
2021 os.makedirs(paths.netlogon, 0755)
2023 if adminpass is None:
2024 adminpass = samba.generate_random_password(12, 32)
2025 adminpass_generated = True
2026 else:
2027 adminpass_generated = False
2029 if samdb_fill == FILL_FULL:
2030 provision_fill(samdb, secrets_ldb, logger, names, paths,
2031 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2032 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2033 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2034 krbtgtpass=krbtgtpass, domainguid=domainguid,
2035 policyguid=policyguid, policyguid_dc=policyguid_dc,
2036 invocationid=invocationid, machinepass=machinepass,
2037 ntdsguid=ntdsguid, dns_backend=dns_backend,
2038 dnspass=dnspass, serverrole=serverrole,
2039 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2040 lp=lp, use_ntvfs=use_ntvfs,
2041 skip_sysvolacl=skip_sysvolacl)
2043 create_krb5_conf(paths.krb5conf,
2044 dnsdomain=names.dnsdomain, hostname=names.hostname,
2045 realm=names.realm)
2046 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2047 "generated at %s", paths.krb5conf)
2049 if serverrole == "active directory domain controller":
2050 create_dns_update_list(lp, logger, paths)
2052 backend_result = provision_backend.post_setup()
2053 provision_backend.shutdown()
2055 except:
2056 secrets_ldb.transaction_cancel()
2057 raise
2059 # Now commit the secrets.ldb to disk
2060 secrets_ldb.transaction_commit()
2062 # the commit creates the dns.keytab, now chown it
2063 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2064 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2065 try:
2066 os.chmod(dns_keytab_path, 0640)
2067 os.chown(dns_keytab_path, -1, paths.bind_gid)
2068 except OSError:
2069 if not os.environ.has_key('SAMBA_SELFTEST'):
2070 logger.info("Failed to chown %s to bind gid %u",
2071 dns_keytab_path, paths.bind_gid)
2073 result = ProvisionResult()
2074 result.server_role = serverrole
2075 result.domaindn = domaindn
2076 result.paths = paths
2077 result.names = names
2078 result.lp = lp
2079 result.samdb = samdb
2080 result.idmap = idmap
2081 result.domainsid = str(domainsid)
2083 if samdb_fill == FILL_FULL:
2084 result.adminpass_generated = adminpass_generated
2085 result.adminpass = adminpass
2086 else:
2087 result.adminpass_generated = False
2088 result.adminpass = None
2090 result.backend_result = backend_result
2092 if use_rfc2307:
2093 provision_fake_ypserver(logger=logger, samdb=samdb,
2094 domaindn=names.domaindn, netbiosname=names.netbiosname,
2095 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2097 return result
2100 def provision_become_dc(smbconf=None, targetdir=None,
2101 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2102 serverdn=None, domain=None, hostname=None, domainsid=None,
2103 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2104 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2105 dns_backend=None, root=None, nobody=None, users=None,
2106 backup=None, serverrole=None, ldap_backend=None,
2107 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2109 logger = logging.getLogger("provision")
2110 samba.set_debug_level(debuglevel)
2112 res = provision(logger, system_session(), None,
2113 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2114 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2115 configdn=configdn, serverdn=serverdn, domain=domain,
2116 hostname=hostname, hostip=None, domainsid=domainsid,
2117 machinepass=machinepass,
2118 serverrole="active directory domain controller",
2119 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2120 use_ntvfs=use_ntvfs)
2121 res.lp.set("debuglevel", str(debuglevel))
2122 return res
2125 def create_krb5_conf(path, dnsdomain, hostname, realm):
2126 """Write out a file containing zone statements suitable for inclusion in a
2127 named.conf file (including GSS-TSIG configuration).
2129 :param path: Path of the new named.conf file.
2130 :param dnsdomain: DNS Domain name
2131 :param hostname: Local hostname
2132 :param realm: Realm name
2134 setup_file(setup_path("krb5.conf"), path, {
2135 "DNSDOMAIN": dnsdomain,
2136 "HOSTNAME": hostname,
2137 "REALM": realm,
2141 class ProvisioningError(Exception):
2142 """A generic provision error."""
2144 def __init__(self, value):
2145 self.value = value
2147 def __str__(self):
2148 return "ProvisioningError: " + self.value
2151 class InvalidNetbiosName(Exception):
2152 """A specified name was not a valid NetBIOS name."""
2154 def __init__(self, name):
2155 super(InvalidNetbiosName, self).__init__(
2156 "The name '%r' is not a valid NetBIOS name" % name)
2159 class MissingShareError(ProvisioningError):
2161 def __init__(self, name, smbconf):
2162 super(MissingShareError, self).__init__(
2163 "Existing smb.conf does not have a [%s] share, but you are "
2164 "configuring a DC. Please remove %s or add the share manually." %
2165 (name, smbconf))