provision: Add --backend-store-size option
[Samba.git] / python / samba / provision / __init__.py
blob6c345a43100bf4120239fb814a10ee0d9e4ed448
1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba AD 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 samba.compat import urllib_quote
30 from samba.compat import string_types
31 from samba.compat import binary_type
32 from base64 import b64encode
33 import errno
34 import os
35 import stat
36 import re
37 import pwd
38 import grp
39 import logging
40 import time
41 import uuid
42 import socket
43 import tempfile
44 import samba.dsdb
46 import ldb
48 from samba.auth import system_session, admin_session
49 import samba
50 from samba import auth
51 from samba.samba3 import smbd, passdb
52 from samba.samba3 import param as s3param
53 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
54 from samba import (
55 Ldb,
56 MAX_NETBIOS_NAME_LEN,
57 check_all_substituted,
58 is_valid_netbios_char,
59 setup_file,
60 substitute_var,
61 valid_netbios_name,
62 version,
63 is_heimdal_built,
65 from samba.dcerpc import security, misc
66 from samba.dcerpc.misc import (
67 SEC_CHAN_BDC,
68 SEC_CHAN_WKSTA,
70 from samba.dsdb import (
71 DS_DOMAIN_FUNCTION_2003,
72 DS_DOMAIN_FUNCTION_2008_R2,
73 ENC_ALL_TYPES,
75 from samba.idmap import IDmapDB
76 from samba.ms_display_specifiers import read_ms_ldif
77 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
78 from samba.ndr import ndr_pack, ndr_unpack
79 from samba.provision.backend import (
80 FDSBackend,
81 LDBBackend,
82 OpenLDAPBackend,
84 from samba.descriptor import (
85 get_empty_descriptor,
86 get_config_descriptor,
87 get_config_partitions_descriptor,
88 get_config_sites_descriptor,
89 get_config_ntds_quotas_descriptor,
90 get_config_delete_protected1_descriptor,
91 get_config_delete_protected1wd_descriptor,
92 get_config_delete_protected2_descriptor,
93 get_domain_descriptor,
94 get_domain_infrastructure_descriptor,
95 get_domain_builtin_descriptor,
96 get_domain_computers_descriptor,
97 get_domain_users_descriptor,
98 get_domain_controllers_descriptor,
99 get_domain_delete_protected1_descriptor,
100 get_domain_delete_protected2_descriptor,
101 get_dns_partition_descriptor,
102 get_dns_forest_microsoft_dns_descriptor,
103 get_dns_domain_microsoft_dns_descriptor,
104 get_managed_service_accounts_descriptor,
106 from samba.provision.common import (
107 setup_path,
108 setup_add_ldif,
109 setup_modify_ldif,
110 FILL_FULL,
111 FILL_SUBDOMAIN,
112 FILL_NT4SYNC,
113 FILL_DRS
115 from samba.provision.sambadns import (
116 get_dnsadmins_sid,
117 setup_ad_dns,
118 create_dns_update_list
121 import samba.param
122 import samba.registry
123 from samba.schema import Schema
124 from samba.samdb import SamDB
125 from samba.dbchecker import dbcheck
126 from samba.provision.kerberos import create_kdc_conf
127 from samba.samdb import get_default_backend_store
129 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
130 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
131 DEFAULTSITE = "Default-First-Site-Name"
132 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
134 DEFAULT_MIN_PWD_LENGTH = 7
137 class ProvisionPaths(object):
139 def __init__(self):
140 self.shareconf = None
141 self.hklm = None
142 self.hkcu = None
143 self.hkcr = None
144 self.hku = None
145 self.hkpd = None
146 self.hkpt = None
147 self.samdb = None
148 self.idmapdb = None
149 self.secrets = None
150 self.keytab = None
151 self.dns_keytab = None
152 self.dns = None
153 self.winsdb = None
154 self.private_dir = None
155 self.binddns_dir = None
156 self.state_dir = None
159 class ProvisionNames(object):
161 def __init__(self):
162 self.ncs = None
163 self.rootdn = None
164 self.domaindn = None
165 self.configdn = None
166 self.schemadn = None
167 self.dnsforestdn = None
168 self.dnsdomaindn = None
169 self.ldapmanagerdn = None
170 self.dnsdomain = None
171 self.realm = None
172 self.netbiosname = None
173 self.domain = None
174 self.hostname = None
175 self.sitename = None
176 self.smbconf = None
177 self.domainsid = None
178 self.forestsid = None
179 self.domainguid = None
180 self.name_map = {}
183 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
184 lp):
185 """Get key provision parameters (realm, domain, ...) from a given provision
187 :param samdb: An LDB object connected to the sam.ldb file
188 :param secretsdb: An LDB object connected to the secrets.ldb file
189 :param idmapdb: An LDB object connected to the idmap.ldb file
190 :param paths: A list of path to provision object
191 :param smbconf: Path to the smb.conf file
192 :param lp: A LoadParm object
193 :return: A list of key provision parameters
195 names = ProvisionNames()
196 names.adminpass = None
198 # NT domain, kerberos realm, root dn, domain dn, domain dns name
199 names.domain = lp.get("workgroup").upper()
200 names.realm = lp.get("realm")
201 names.dnsdomain = names.realm.lower()
202 basedn = samba.dn_from_dns_name(names.dnsdomain)
203 names.realm = names.realm.upper()
204 # netbiosname
205 # Get the netbiosname first (could be obtained from smb.conf in theory)
206 res = secretsdb.search(expression="(flatname=%s)" %
207 names.domain, base="CN=Primary Domains",
208 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
209 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$", "")
211 names.smbconf = smbconf
213 # That's a bit simplistic but it's ok as long as we have only 3
214 # partitions
215 current = samdb.search(expression="(objectClass=*)",
216 base="", scope=ldb.SCOPE_BASE,
217 attrs=["defaultNamingContext", "schemaNamingContext",
218 "configurationNamingContext", "rootDomainNamingContext",
219 "namingContexts"])
221 names.configdn = str(current[0]["configurationNamingContext"][0])
222 names.schemadn = str(current[0]["schemaNamingContext"][0])
223 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
224 current[0]["defaultNamingContext"][0].decode('utf8')))):
225 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
226 "is not the same ..." % (paths.samdb,
227 str(current[0]["defaultNamingContext"][0].decode('utf8')),
228 paths.smbconf, basedn)))
230 names.domaindn = str(current[0]["defaultNamingContext"][0])
231 names.rootdn = str(current[0]["rootDomainNamingContext"][0])
232 names.ncs = current[0]["namingContexts"]
233 names.dnsforestdn = None
234 names.dnsdomaindn = None
236 for i in range(0, len(names.ncs)):
237 nc = str(names.ncs[i])
239 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
240 if nc == dnsforestdn:
241 names.dnsforestdn = dnsforestdn
242 continue
244 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
245 if nc == dnsdomaindn:
246 names.dnsdomaindn = dnsdomaindn
247 continue
249 # default site name
250 res3 = samdb.search(expression="(objectClass=site)",
251 base="CN=Sites," + str(names.configdn), scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
252 names.sitename = str(res3[0]["cn"])
254 # dns hostname and server dn
255 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
256 base="OU=Domain Controllers,%s" % basedn,
257 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
258 if len(res4) == 0:
259 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
261 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
263 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
264 attrs=[], base=names.configdn)
265 names.serverdn = str(server_res[0].dn)
267 # invocation id/objectguid
268 res5 = samdb.search(expression="(objectClass=*)",
269 base="CN=NTDS Settings,%s" % str(names.serverdn),
270 scope=ldb.SCOPE_BASE,
271 attrs=["invocationID", "objectGUID"])
272 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
273 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
275 # domain guid/sid
276 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
277 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
278 "objectSid", "msDS-Behavior-Version"])
279 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
280 names.domainsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
281 names.forestsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
282 if res6[0].get("msDS-Behavior-Version") is None or \
283 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
284 names.domainlevel = DS_DOMAIN_FUNCTION_2000
285 else:
286 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
288 # policy guid
289 res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
290 base="CN=Policies,CN=System," + basedn,
291 scope=ldb.SCOPE_ONELEVEL, attrs=["cn", "displayName"])
292 names.policyid = str(res7[0]["cn"]).replace("{", "").replace("}", "")
293 # dc policy guid
294 res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
295 base="CN=Policies,CN=System," + basedn,
296 scope=ldb.SCOPE_ONELEVEL,
297 attrs=["cn", "displayName"])
298 if len(res8) == 1:
299 names.policyid_dc = str(res8[0]["cn"]).replace("{", "").replace("}", "")
300 else:
301 names.policyid_dc = None
303 res9 = idmapdb.search(expression="(cn=%s-%s)" %
304 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
305 attrs=["xidNumber", "type"])
306 if len(res9) != 1:
307 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
308 if str(res9[0]["type"][0]) == "ID_TYPE_BOTH":
309 names.root_gid = int(res9[0]["xidNumber"][0])
310 else:
311 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
313 res10 = samdb.search(expression="(samaccountname=dns)",
314 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
315 controls=["search_options:1:2"])
316 if (len(res10) > 0):
317 has_legacy_dns_account = True
318 else:
319 has_legacy_dns_account = False
321 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
322 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
323 controls=["search_options:1:2"])
324 if (len(res11) > 0):
325 has_dns_account = True
326 else:
327 has_dns_account = False
329 if names.dnsdomaindn is not None:
330 if has_dns_account:
331 names.dns_backend = 'BIND9_DLZ'
332 else:
333 names.dns_backend = 'SAMBA_INTERNAL'
334 elif has_dns_account or has_legacy_dns_account:
335 names.dns_backend = 'BIND9_FLATFILE'
336 else:
337 names.dns_backend = 'NONE'
339 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
340 names.name_map['DnsAdmins'] = str(dns_admins_sid)
342 return names
345 def update_provision_usn(samdb, low, high, id, replace=False):
346 """Update the field provisionUSN in sam.ldb
348 This field is used to track range of USN modified by provision and
349 upgradeprovision.
350 This value is used afterward by next provision to figure out if
351 the field have been modified since last provision.
353 :param samdb: An LDB object connect to sam.ldb
354 :param low: The lowest USN modified by this upgrade
355 :param high: The highest USN modified by this upgrade
356 :param id: The invocation id of the samba's dc
357 :param replace: A boolean indicating if the range should replace any
358 existing one or appended (default)
361 tab = []
362 if not replace:
363 entry = samdb.search(base="@PROVISION",
364 scope=ldb.SCOPE_BASE,
365 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
366 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
367 if not re.search(';', str(e)):
368 e = "%s;%s" % (str(e), id)
369 tab.append(str(e))
371 tab.append("%s-%s;%s" % (low, high, id))
372 delta = ldb.Message()
373 delta.dn = ldb.Dn(samdb, "@PROVISION")
374 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
375 ldb.MessageElement(tab,
376 ldb.FLAG_MOD_REPLACE,
377 LAST_PROVISION_USN_ATTRIBUTE)
378 entry = samdb.search(expression='provisionnerID=*',
379 base="@PROVISION", scope=ldb.SCOPE_BASE,
380 attrs=["provisionnerID"])
381 if len(entry) == 0 or len(entry[0]) == 0:
382 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
383 samdb.modify(delta)
386 def set_provision_usn(samdb, low, high, id):
387 """Set the field provisionUSN in sam.ldb
388 This field is used to track range of USN modified by provision and
389 upgradeprovision.
390 This value is used afterward by next provision to figure out if
391 the field have been modified since last provision.
393 :param samdb: An LDB object connect to sam.ldb
394 :param low: The lowest USN modified by this upgrade
395 :param high: The highest USN modified by this upgrade
396 :param id: The invocationId of the provision"""
398 tab = []
399 tab.append("%s-%s;%s" % (low, high, id))
401 delta = ldb.Message()
402 delta.dn = ldb.Dn(samdb, "@PROVISION")
403 delta[LAST_PROVISION_USN_ATTRIBUTE] = \
404 ldb.MessageElement(tab,
405 ldb.FLAG_MOD_ADD,
406 LAST_PROVISION_USN_ATTRIBUTE)
407 samdb.add(delta)
410 def get_max_usn(samdb, basedn):
411 """ This function return the biggest USN present in the provision
413 :param samdb: A LDB object pointing to the sam.ldb
414 :param basedn: A string containing the base DN of the provision
415 (ie. DC=foo, DC=bar)
416 :return: The biggest USN in the provision"""
418 res = samdb.search(expression="objectClass=*", base=basedn,
419 scope=ldb.SCOPE_SUBTREE, attrs=["uSNChanged"],
420 controls=["search_options:1:2",
421 "server_sort:1:1:uSNChanged",
422 "paged_results:1:1"])
423 return res[0]["uSNChanged"]
426 def get_last_provision_usn(sam):
427 """Get USNs ranges modified by a provision or an upgradeprovision
429 :param sam: An LDB object pointing to the sam.ldb
430 :return: a dictionary which keys are invocation id and values are an array
431 of integer representing the different ranges
433 try:
434 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
435 base="@PROVISION", scope=ldb.SCOPE_BASE,
436 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
437 except ldb.LdbError as e1:
438 (ecode, emsg) = e1.args
439 if ecode == ldb.ERR_NO_SUCH_OBJECT:
440 return None
441 raise
442 if len(entry) > 0:
443 myids = []
444 range = {}
445 p = re.compile(r'-')
446 if entry[0].get("provisionnerID"):
447 for e in entry[0]["provisionnerID"]:
448 myids.append(str(e))
449 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
450 tab1 = str(r).split(';')
451 if len(tab1) == 2:
452 id = tab1[1]
453 else:
454 id = "default"
455 if (len(myids) > 0 and id not in myids):
456 continue
457 tab2 = p.split(tab1[0])
458 if range.get(id) is None:
459 range[id] = []
460 range[id].append(tab2[0])
461 range[id].append(tab2[1])
462 return range
463 else:
464 return None
467 class ProvisionResult(object):
468 """Result of a provision.
470 :ivar server_role: The server role
471 :ivar paths: ProvisionPaths instance
472 :ivar domaindn: The domain dn, as string
475 def __init__(self):
476 self.server_role = None
477 self.paths = None
478 self.domaindn = None
479 self.lp = None
480 self.samdb = None
481 self.idmap = None
482 self.names = None
483 self.domainsid = None
484 self.adminpass_generated = None
485 self.adminpass = None
486 self.backend_result = None
488 def report_logger(self, logger):
489 """Report this provision result to a logger."""
490 logger.info(
491 "Once the above files are installed, your Samba AD server will "
492 "be ready to use")
493 if self.adminpass_generated:
494 logger.info("Admin password: %s", self.adminpass)
495 logger.info("Server Role: %s", self.server_role)
496 logger.info("Hostname: %s", self.names.hostname)
497 logger.info("NetBIOS Domain: %s", self.names.domain)
498 logger.info("DNS Domain: %s", self.names.dnsdomain)
499 logger.info("DOMAIN SID: %s", self.domainsid)
501 if self.backend_result:
502 self.backend_result.report_logger(logger)
505 def check_install(lp, session_info, credentials):
506 """Check whether the current install seems ok.
508 :param lp: Loadparm context
509 :param session_info: Session information
510 :param credentials: Credentials
512 if lp.get("realm") == "":
513 raise Exception("Realm empty")
514 samdb = Ldb(lp.samdb_url(), session_info=session_info,
515 credentials=credentials, lp=lp)
516 if len(samdb.search("(cn=Administrator)")) != 1:
517 raise ProvisioningError("No administrator account found")
520 def findnss(nssfn, names):
521 """Find a user or group from a list of possibilities.
523 :param nssfn: NSS Function to try (should raise KeyError if not found)
524 :param names: Names to check.
525 :return: Value return by first names list.
527 for name in names:
528 try:
529 return nssfn(name)
530 except KeyError:
531 pass
532 raise KeyError("Unable to find user/group in %r" % names)
535 def findnss_uid(names):
536 return findnss(pwd.getpwnam, names)[2]
539 def findnss_gid(names):
540 return findnss(grp.getgrnam, names)[2]
543 def get_root_uid(root, logger):
544 try:
545 root_uid = findnss_uid(root)
546 except KeyError as e:
547 logger.info(e)
548 logger.info("Assuming root user has UID zero")
549 root_uid = 0
550 return root_uid
553 def provision_paths_from_lp(lp, dnsdomain):
554 """Set the default paths for provisioning.
556 :param lp: Loadparm context.
557 :param dnsdomain: DNS Domain name
559 paths = ProvisionPaths()
560 paths.private_dir = lp.get("private dir")
561 paths.binddns_dir = lp.get("binddns dir")
562 paths.state_dir = lp.get("state directory")
564 # This is stored without path prefix for the "privateKeytab" attribute in
565 # "secrets_dns.ldif".
566 paths.dns_keytab = "dns.keytab"
567 paths.keytab = "secrets.keytab"
569 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
570 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
571 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
572 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
573 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
574 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
575 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
576 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
577 paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
578 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
579 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
580 paths.encrypted_secrets_key_path = os.path.join(
581 paths.private_dir,
582 "encrypted_secrets.key")
584 paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
585 paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
586 paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
587 paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
589 paths.hklm = "hklm.ldb"
590 paths.hkcr = "hkcr.ldb"
591 paths.hkcu = "hkcu.ldb"
592 paths.hku = "hku.ldb"
593 paths.hkpd = "hkpd.ldb"
594 paths.hkpt = "hkpt.ldb"
595 paths.sysvol = lp.get("path", "sysvol")
596 paths.netlogon = lp.get("path", "netlogon")
597 paths.smbconf = lp.configfile
598 return paths
601 def determine_netbios_name(hostname):
602 """Determine a netbios name from a hostname."""
603 # remove forbidden chars and force the length to be <16
604 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
605 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
608 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
609 serverrole=None, rootdn=None, domaindn=None, configdn=None,
610 schemadn=None, serverdn=None, sitename=None,
611 domain_names_forced=False):
612 """Guess configuration settings to use."""
614 if hostname is None:
615 hostname = socket.gethostname().split(".")[0]
617 netbiosname = lp.get("netbios name")
618 if netbiosname is None:
619 netbiosname = determine_netbios_name(hostname)
620 netbiosname = netbiosname.upper()
621 if not valid_netbios_name(netbiosname):
622 raise InvalidNetbiosName(netbiosname)
624 if dnsdomain is None:
625 dnsdomain = lp.get("realm")
626 if dnsdomain is None or dnsdomain == "":
627 raise ProvisioningError(
628 "guess_names: 'realm' not specified in supplied %s!" %
629 lp.configfile)
631 dnsdomain = dnsdomain.lower()
633 if serverrole is None:
634 serverrole = lp.get("server role")
635 if serverrole is None:
636 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
638 serverrole = serverrole.lower()
640 realm = dnsdomain.upper()
642 if lp.get("realm") == "":
643 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
645 if lp.get("realm").upper() != realm:
646 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(), lp.configfile, realm))
648 if lp.get("server role").lower() != serverrole:
649 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))
651 if serverrole == "active directory domain controller":
652 if domain is None:
653 # This will, for better or worse, default to 'WORKGROUP'
654 domain = lp.get("workgroup")
655 domain = domain.upper()
657 if lp.get("workgroup").upper() != domain:
658 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))
660 if domaindn is None:
661 domaindn = samba.dn_from_dns_name(dnsdomain)
663 if domain == netbiosname:
664 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
665 else:
666 domain = netbiosname
667 if domaindn is None:
668 domaindn = "DC=" + netbiosname
670 if not valid_netbios_name(domain):
671 raise InvalidNetbiosName(domain)
673 if hostname.upper() == realm:
674 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
675 if netbiosname.upper() == realm:
676 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
677 if domain == realm and not domain_names_forced:
678 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
680 if serverrole != "active directory domain controller":
682 # This is the code path for a domain member
683 # where we provision the database as if we where
684 # on a domain controller, so we should not use
685 # the same dnsdomain as the domain controllers
686 # of our primary domain.
688 # This will be important if we start doing
689 # SID/name filtering and reject the local
690 # sid and names if they come from a domain
691 # controller.
693 realm = netbiosname
694 dnsdomain = netbiosname.lower()
696 if rootdn is None:
697 rootdn = domaindn
699 if configdn is None:
700 configdn = "CN=Configuration," + rootdn
701 if schemadn is None:
702 schemadn = "CN=Schema," + configdn
704 if sitename is None:
705 sitename = DEFAULTSITE
707 names = ProvisionNames()
708 names.rootdn = rootdn
709 names.domaindn = domaindn
710 names.configdn = configdn
711 names.schemadn = schemadn
712 names.ldapmanagerdn = "CN=Manager," + rootdn
713 names.dnsdomain = dnsdomain
714 names.domain = domain
715 names.realm = realm
716 names.netbiosname = netbiosname
717 names.hostname = hostname
718 names.sitename = sitename
719 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
720 netbiosname, sitename, configdn)
722 return names
725 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
726 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
727 global_param=None):
728 """Create a new smb.conf file based on a couple of basic settings.
730 assert smbconf is not None
732 if hostname is None:
733 hostname = socket.gethostname().split(".")[0]
735 netbiosname = determine_netbios_name(hostname)
737 if serverrole is None:
738 serverrole = "standalone server"
740 assert domain is not None
741 domain = domain.upper()
743 assert realm is not None
744 realm = realm.upper()
746 global_settings = {
747 "netbios name": netbiosname,
748 "workgroup": domain,
749 "realm": realm,
750 "server role": serverrole,
753 if lp is None:
754 lp = samba.param.LoadParm()
755 # Load non-existent file
756 if os.path.exists(smbconf):
757 lp.load(smbconf)
759 if global_param is not None:
760 for ent in global_param:
761 if global_param[ent] is not None:
762 global_settings[ent] = " ".join(global_param[ent])
764 if targetdir is not None:
765 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
766 global_settings["lock dir"] = os.path.abspath(targetdir)
767 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
768 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
769 global_settings["binddns dir"] = os.path.abspath(os.path.join(targetdir, "bind-dns"))
771 lp.set("lock dir", os.path.abspath(targetdir))
772 lp.set("state directory", global_settings["state directory"])
773 lp.set("cache directory", global_settings["cache directory"])
774 lp.set("binddns dir", global_settings["binddns dir"])
776 if eadb:
777 if use_ntvfs:
778 if targetdir is not None:
779 privdir = os.path.join(targetdir, "private")
780 lp.set("posix:eadb",
781 os.path.abspath(os.path.join(privdir, "eadb.tdb")))
782 elif not lp.get("posix:eadb"):
783 privdir = lp.get("private dir")
784 lp.set("posix:eadb",
785 os.path.abspath(os.path.join(privdir, "eadb.tdb")))
786 else:
787 if targetdir is not None:
788 statedir = os.path.join(targetdir, "state")
789 lp.set("xattr_tdb:file",
790 os.path.abspath(os.path.join(statedir, "xattr.tdb")))
791 elif not lp.get("xattr_tdb:file"):
792 statedir = lp.get("state directory")
793 lp.set("xattr_tdb:file",
794 os.path.abspath(os.path.join(statedir, "xattr.tdb")))
796 shares = {}
797 if serverrole == "active directory domain controller":
798 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
799 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
800 "scripts")
801 else:
802 global_settings["passdb backend"] = "samba_dsdb"
804 f = open(smbconf, 'w')
805 try:
806 f.write("[globals]\n")
807 for key, val in global_settings.items():
808 f.write("\t%s = %s\n" % (key, val))
809 f.write("\n")
811 for name, path in shares.items():
812 f.write("[%s]\n" % name)
813 f.write("\tpath = %s\n" % path)
814 f.write("\tread only = no\n")
815 f.write("\n")
816 finally:
817 f.close()
818 # reload the smb.conf
819 lp.load(smbconf)
821 # and dump it without any values that are the default
822 # this ensures that any smb.conf parameters that were set
823 # on the provision/join command line are set in the resulting smb.conf
824 lp.dump(False, smbconf)
827 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
828 users_gid, root_gid):
829 """setup reasonable name mappings for sam names to unix names.
831 :param samdb: SamDB object.
832 :param idmap: IDmap db object.
833 :param sid: The domain sid.
834 :param domaindn: The domain DN.
835 :param root_uid: uid of the UNIX root user.
836 :param nobody_uid: uid of the UNIX nobody user.
837 :param users_gid: gid of the UNIX users group.
838 :param root_gid: gid of the UNIX root group.
840 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
842 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
843 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
846 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
847 provision_backend, names, serverrole,
848 erase=False, plaintext_secrets=False,
849 backend_store=None,backend_store_size=None):
850 """Setup the partitions for the SAM database.
852 Alternatively, provision() may call this, and then populate the database.
854 :note: This will wipe the Sam Database!
856 :note: This function always removes the local SAM LDB file. The erase
857 parameter controls whether to erase the existing data, which
858 may not be stored locally but in LDAP.
861 assert session_info is not None
863 # We use options=["modules:"] to stop the modules loading - we
864 # just want to wipe and re-initialise the database, not start it up
866 try:
867 os.unlink(samdb_path)
868 except OSError:
869 pass
871 samdb = Ldb(url=samdb_path, session_info=session_info,
872 lp=lp, options=["modules:"])
874 ldap_backend_line = "# No LDAP backend"
875 if provision_backend.type != "ldb":
876 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
878 required_features = None
879 if not plaintext_secrets:
880 required_features = "requiredFeatures: encryptedSecrets"
882 if backend_store is None:
883 backend_store = get_default_backend_store()
884 backend_store_line = "backendStore: %s" % backend_store
886 if backend_store == "mdb":
887 if required_features is not None:
888 required_features += "\n"
889 else:
890 required_features = ""
891 required_features += "requiredFeatures: lmdbLevelOne"
893 if required_features is None:
894 required_features = "# No required features"
896 samdb.transaction_start()
897 try:
898 logger.info("Setting up sam.ldb partitions and settings")
899 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
900 "LDAP_BACKEND_LINE": ldap_backend_line,
901 "BACKEND_STORE": backend_store_line
904 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
905 "BACKEND_TYPE": provision_backend.type,
906 "SERVER_ROLE": serverrole,
907 "REQUIRED_FEATURES": required_features
910 logger.info("Setting up sam.ldb rootDSE")
911 setup_samdb_rootdse(samdb, names)
912 except:
913 samdb.transaction_cancel()
914 raise
915 else:
916 samdb.transaction_commit()
919 def secretsdb_self_join(secretsdb, domain,
920 netbiosname, machinepass, domainsid=None,
921 realm=None, dnsdomain=None,
922 keytab_path=None,
923 key_version_number=1,
924 secure_channel_type=SEC_CHAN_WKSTA):
925 """Add domain join-specific bits to a secrets database.
927 :param secretsdb: Ldb Handle to the secrets database
928 :param machinepass: Machine password
930 attrs = ["whenChanged",
931 "secret",
932 "priorSecret",
933 "priorChanged",
934 "krb5Keytab",
935 "privateKeytab"]
937 if realm is not None:
938 if dnsdomain is None:
939 dnsdomain = realm.lower()
940 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
941 else:
942 dnsname = None
943 shortname = netbiosname.lower()
945 # We don't need to set msg["flatname"] here, because rdn_name will handle
946 # it, and it causes problems for modifies anyway
947 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
948 msg["secureChannelType"] = [str(secure_channel_type)]
949 msg["objectClass"] = ["top", "primaryDomain"]
950 if dnsname is not None:
951 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
952 msg["realm"] = [realm]
953 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
954 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
955 msg["privateKeytab"] = ["secrets.keytab"]
957 msg["secret"] = [machinepass.encode('utf-8')]
958 msg["samAccountName"] = ["%s$" % netbiosname]
959 msg["secureChannelType"] = [str(secure_channel_type)]
960 if domainsid is not None:
961 msg["objectSid"] = [ndr_pack(domainsid)]
963 # This complex expression tries to ensure that we don't have more
964 # than one record for this SID, realm or netbios domain at a time,
965 # but we don't delete the old record that we are about to modify,
966 # because that would delete the keytab and previous password.
967 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
968 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
969 scope=ldb.SCOPE_ONELEVEL)
971 for del_msg in res:
972 secretsdb.delete(del_msg.dn)
974 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
976 if len(res) == 1:
977 msg["priorSecret"] = [res[0]["secret"][0]]
978 try:
979 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
980 except KeyError:
981 pass
983 try:
984 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
985 except KeyError:
986 pass
988 try:
989 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
990 except KeyError:
991 pass
993 for el in msg:
994 if el != 'dn':
995 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
996 secretsdb.modify(msg)
997 secretsdb.rename(res[0].dn, msg.dn)
998 else:
999 spn = ['HOST/%s' % shortname]
1000 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
1001 # we are a domain controller then we add servicePrincipalName
1002 # entries for the keytab code to update.
1003 spn.extend(['HOST/%s' % dnsname])
1004 msg["servicePrincipalName"] = spn
1006 secretsdb.add(msg)
1009 def setup_secretsdb(paths, session_info, backend_credentials, lp):
1010 """Setup the secrets database.
1012 :note: This function does not handle exceptions and transaction on purpose,
1013 it's up to the caller to do this job.
1015 :param path: Path to the secrets database.
1016 :param session_info: Session info.
1017 :param credentials: Credentials
1018 :param lp: Loadparm context
1019 :return: LDB handle for the created secrets database
1021 if os.path.exists(paths.secrets):
1022 os.unlink(paths.secrets)
1024 keytab_path = os.path.join(paths.private_dir, paths.keytab)
1025 if os.path.exists(keytab_path):
1026 os.unlink(keytab_path)
1028 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1029 if os.path.exists(bind_dns_keytab_path):
1030 os.unlink(bind_dns_keytab_path)
1032 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1033 if os.path.exists(dns_keytab_path):
1034 os.unlink(dns_keytab_path)
1036 path = paths.secrets
1038 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1039 secrets_ldb.erase()
1040 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1041 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1042 secrets_ldb.transaction_start()
1043 try:
1044 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1046 if (backend_credentials is not None and
1047 backend_credentials.authentication_requested()):
1048 if backend_credentials.get_bind_dn() is not None:
1049 setup_add_ldif(secrets_ldb,
1050 setup_path("secrets_simple_ldap.ldif"), {
1051 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
1052 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1054 else:
1055 setup_add_ldif(secrets_ldb,
1056 setup_path("secrets_sasl_ldap.ldif"), {
1057 "LDAPADMINUSER": backend_credentials.get_username(),
1058 "LDAPADMINREALM": backend_credentials.get_realm(),
1059 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1061 except:
1062 secrets_ldb.transaction_cancel()
1063 raise
1064 return secrets_ldb
1067 def setup_privileges(path, session_info, lp):
1068 """Setup the privileges database.
1070 :param path: Path to the privileges database.
1071 :param session_info: Session info.
1072 :param credentials: Credentials
1073 :param lp: Loadparm context
1074 :return: LDB handle for the created secrets database
1076 if os.path.exists(path):
1077 os.unlink(path)
1078 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1079 privilege_ldb.erase()
1080 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1083 def setup_encrypted_secrets_key(path):
1084 """Setup the encrypted secrets key file.
1086 Any existing key file will be deleted and a new random key generated.
1088 :param path: Path to the secrets key file.
1091 if os.path.exists(path):
1092 os.unlink(path)
1094 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1095 mode = stat.S_IRUSR | stat.S_IWUSR
1097 umask_original = os.umask(0)
1098 try:
1099 fd = os.open(path, flags, mode)
1100 finally:
1101 os.umask(umask_original)
1103 with os.fdopen(fd, 'wb') as f:
1104 key = samba.generate_random_bytes(16)
1105 f.write(key)
1108 def setup_registry(path, session_info, lp):
1109 """Setup the registry.
1111 :param path: Path to the registry database
1112 :param session_info: Session information
1113 :param credentials: Credentials
1114 :param lp: Loadparm context
1116 reg = samba.registry.Registry()
1117 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1118 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1119 provision_reg = setup_path("provision.reg")
1120 assert os.path.exists(provision_reg)
1121 reg.diff_apply(provision_reg)
1124 def setup_idmapdb(path, session_info, lp):
1125 """Setup the idmap database.
1127 :param path: path to the idmap database
1128 :param session_info: Session information
1129 :param credentials: Credentials
1130 :param lp: Loadparm context
1132 if os.path.exists(path):
1133 os.unlink(path)
1135 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1136 idmap_ldb.erase()
1137 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1138 return idmap_ldb
1141 def setup_samdb_rootdse(samdb, names):
1142 """Setup the SamDB rootdse.
1144 :param samdb: Sam Database handle
1146 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1147 "SCHEMADN": names.schemadn,
1148 "DOMAINDN": names.domaindn,
1149 "ROOTDN": names.rootdn,
1150 "CONFIGDN": names.configdn,
1151 "SERVERDN": names.serverdn,
1155 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1156 dns_backend, dnspass, domainsid, next_rid, invocationid,
1157 policyguid, policyguid_dc,
1158 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1159 """Join a host to its own domain."""
1160 assert isinstance(invocationid, str)
1161 if ntdsguid is not None:
1162 ntdsguid_line = "objectGUID: %s\n" % ntdsguid
1163 else:
1164 ntdsguid_line = ""
1166 if dc_rid is None:
1167 dc_rid = next_rid
1169 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1170 "CONFIGDN": names.configdn,
1171 "SCHEMADN": names.schemadn,
1172 "DOMAINDN": names.domaindn,
1173 "SERVERDN": names.serverdn,
1174 "INVOCATIONID": invocationid,
1175 "NETBIOSNAME": names.netbiosname,
1176 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1177 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1178 "DOMAINSID": str(domainsid),
1179 "DCRID": str(dc_rid),
1180 "SAMBA_VERSION_STRING": version,
1181 "NTDSGUID": ntdsguid_line,
1182 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1183 domainControllerFunctionality),
1184 "RIDALLOCATIONSTART": str(next_rid + 100),
1185 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1187 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1188 "POLICYGUID": policyguid,
1189 "POLICYGUID_DC": policyguid_dc,
1190 "DNSDOMAIN": names.dnsdomain,
1191 "DOMAINDN": names.domaindn})
1193 # If we are setting up a subdomain, then this has been replicated in, so we
1194 # don't need to add it
1195 if fill == FILL_FULL:
1196 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1197 "CONFIGDN": names.configdn,
1198 "SCHEMADN": names.schemadn,
1199 "DOMAINDN": names.domaindn,
1200 "SERVERDN": names.serverdn,
1201 "INVOCATIONID": invocationid,
1202 "NETBIOSNAME": names.netbiosname,
1203 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1204 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1205 "DOMAINSID": str(domainsid),
1206 "DCRID": str(dc_rid),
1207 "SAMBA_VERSION_STRING": version,
1208 "NTDSGUID": ntdsguid_line,
1209 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1210 domainControllerFunctionality)})
1212 # Setup fSMORoleOwner entries to point at the newly created DC entry
1213 setup_modify_ldif(samdb,
1214 setup_path("provision_self_join_modify_schema.ldif"), {
1215 "SCHEMADN": names.schemadn,
1216 "SERVERDN": names.serverdn,
1218 controls=["provision:0", "relax:0"])
1219 setup_modify_ldif(samdb,
1220 setup_path("provision_self_join_modify_config.ldif"), {
1221 "CONFIGDN": names.configdn,
1222 "DEFAULTSITE": names.sitename,
1223 "NETBIOSNAME": names.netbiosname,
1224 "SERVERDN": names.serverdn,
1227 system_session_info = system_session()
1228 samdb.set_session_info(system_session_info)
1229 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1230 # modify a serverReference under cn=config when we are a subdomain, we must
1231 # be system due to ACLs
1232 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1233 "DOMAINDN": names.domaindn,
1234 "SERVERDN": names.serverdn,
1235 "NETBIOSNAME": names.netbiosname,
1238 samdb.set_session_info(admin_session_info)
1240 if dns_backend != "SAMBA_INTERNAL":
1241 # This is Samba4 specific and should be replaced by the correct
1242 # DNS AD-style setup
1243 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1244 "DNSDOMAIN": names.dnsdomain,
1245 "DOMAINDN": names.domaindn,
1246 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
1247 "HOSTNAME": names.hostname,
1248 "DNSNAME": '%s.%s' % (
1249 names.netbiosname.lower(), names.dnsdomain.lower())
1253 def getpolicypath(sysvolpath, dnsdomain, guid):
1254 """Return the physical path of policy given its guid.
1256 :param sysvolpath: Path to the sysvol folder
1257 :param dnsdomain: DNS name of the AD domain
1258 :param guid: The GUID of the policy
1259 :return: A string with the complete path to the policy folder
1261 if guid[0] != "{":
1262 guid = "{%s}" % guid
1263 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1264 return policy_path
1267 def create_gpo_struct(policy_path):
1268 if not os.path.exists(policy_path):
1269 os.makedirs(policy_path, 0o775)
1270 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1271 try:
1272 f.write("[General]\r\nVersion=0")
1273 finally:
1274 f.close()
1275 p = os.path.join(policy_path, "MACHINE")
1276 if not os.path.exists(p):
1277 os.makedirs(p, 0o775)
1278 p = os.path.join(policy_path, "USER")
1279 if not os.path.exists(p):
1280 os.makedirs(p, 0o775)
1283 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1284 """Create the default GPO for a domain
1286 :param sysvolpath: Physical path for the sysvol folder
1287 :param dnsdomain: DNS domain name of the AD domain
1288 :param policyguid: GUID of the default domain policy
1289 :param policyguid_dc: GUID of the default domain controler policy
1291 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid)
1292 create_gpo_struct(policy_path)
1294 policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid_dc)
1295 create_gpo_struct(policy_path)
1298 # Default the database size to 8Gb
1299 DEFAULT_BACKEND_SIZE = 8 * 1024 * 1024 *1024
1301 def setup_samdb(path, session_info, provision_backend, lp, names,
1302 logger, fill, serverrole, schema, am_rodc=False,
1303 plaintext_secrets=False, backend_store=None,
1304 backend_store_size=None):
1305 """Setup a complete SAM Database.
1307 :note: This will wipe the main SAM database file!
1310 # Also wipes the database
1311 setup_samdb_partitions(path, logger=logger, lp=lp,
1312 provision_backend=provision_backend, session_info=session_info,
1313 names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1314 backend_store=backend_store,
1315 backend_store_size=backend_store_size)
1317 options = []
1318 if backend_store == "mdb":
1319 if backend_store_size:
1320 store_size = backend_store_size
1321 else :
1322 # If no lmdb map size provided default to the default of
1323 # 8 GiB
1324 store_size = DEFAULT_BACKEND_SIZE
1325 options = ["lmdb_env_size:" + str(store_size)]
1327 # Load the database, but don's load the global schema and don't connect
1328 # quite yet
1329 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1330 credentials=provision_backend.credentials, lp=lp,
1331 global_schema=False, am_rodc=am_rodc, options=options)
1333 logger.info("Pre-loading the Samba 4 and AD schema")
1335 # Load the schema from the one we computed earlier
1336 samdb.set_schema(schema, write_indices_and_attributes=False)
1338 # Set the NTDS settings DN manually - in order to have it already around
1339 # before the provisioned tree exists and we connect
1340 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1342 # And now we can connect to the DB - the schema won't be loaded from the
1343 # DB
1344 try:
1345 samdb.connect(path, options=options)
1346 except ldb.LdbError as e2:
1347 (num, string_error) = e2.args
1348 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1349 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1350 else:
1351 raise
1353 # But we have to give it one more kick to have it use the schema
1354 # during provision - it needs, now that it is connected, to write
1355 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1356 samdb.set_schema(schema, write_indices_and_attributes=True)
1358 return samdb
1361 def fill_samdb(samdb, lp, names, logger, policyguid,
1362 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1363 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1364 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
1365 backend_store=None,
1366 backend_store_size=None):
1368 if next_rid is None:
1369 next_rid = 1000
1371 # Provision does not make much sense values larger than 1000000000
1372 # as the upper range of the rIDAvailablePool is 1073741823 and
1373 # we don't want to create a domain that cannot allocate rids.
1374 if next_rid < 1000 or next_rid > 1000000000:
1375 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1376 error += "the valid range is %u-%u. The default is %u." % (
1377 1000, 1000000000, 1000)
1378 raise ProvisioningError(error)
1380 # ATTENTION: Do NOT change these default values without discussion with the
1381 # team and/or release manager. They have a big impact on the whole program!
1382 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1384 if dom_for_fun_level is None:
1385 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1387 if dom_for_fun_level > domainControllerFunctionality:
1388 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!")
1390 domainFunctionality = dom_for_fun_level
1391 forestFunctionality = dom_for_fun_level
1393 # Set the NTDS settings DN manually - in order to have it already around
1394 # before the provisioned tree exists and we connect
1395 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1397 # Set the domain functionality levels onto the database.
1398 # Various module (the password_hash module in particular) need
1399 # to know what level of AD we are emulating.
1401 # These will be fixed into the database via the database
1402 # modifictions below, but we need them set from the start.
1403 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1404 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1405 samdb.set_opaque_integer("domainControllerFunctionality",
1406 domainControllerFunctionality)
1408 samdb.set_domain_sid(str(names.domainsid))
1409 samdb.set_invocation_id(invocationid)
1411 logger.info("Adding DomainDN: %s" % names.domaindn)
1413 # impersonate domain admin
1414 admin_session_info = admin_session(lp, str(names.domainsid))
1415 samdb.set_session_info(admin_session_info)
1416 if names.domainguid is not None:
1417 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1418 else:
1419 domainguid_line = ""
1421 descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
1422 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1423 "DOMAINDN": names.domaindn,
1424 "DOMAINSID": str(names.domainsid),
1425 "DESCRIPTOR": descr,
1426 "DOMAINGUID": domainguid_line
1429 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1430 "DOMAINDN": names.domaindn,
1431 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1432 "NEXTRID": str(next_rid),
1433 "DEFAULTSITE": names.sitename,
1434 "CONFIGDN": names.configdn,
1435 "POLICYGUID": policyguid,
1436 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1437 "SAMBA_VERSION_STRING": version,
1438 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1441 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1442 if fill == FILL_FULL:
1443 logger.info("Adding configuration container")
1444 descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
1445 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1446 "CONFIGDN": names.configdn,
1447 "DESCRIPTOR": descr,
1450 # The LDIF here was created when the Schema object was constructed
1451 ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1452 schema_controls = [
1453 "provision:0",
1454 "relax:0",
1455 ignore_checks_oid
1458 logger.info("Setting up sam.ldb schema")
1459 samdb.add_ldif(schema.schema_dn_add, controls=schema_controls)
1460 samdb.modify_ldif(schema.schema_dn_modify, controls=schema_controls)
1461 samdb.write_prefixes_from_schema()
1462 samdb.add_ldif(schema.schema_data, controls=schema_controls)
1463 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1464 {"SCHEMADN": names.schemadn},
1465 controls=schema_controls)
1467 # Now register this container in the root of the forest
1468 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1469 msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD,
1470 "subRefs")
1472 samdb.invocation_id = invocationid
1474 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1475 if fill == FILL_FULL:
1476 logger.info("Setting up sam.ldb configuration data")
1478 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
1479 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
1480 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
1481 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1482 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1483 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1485 if "2008" in schema.base_schema:
1486 # exclude 2012-specific changes if we're using a 2008 schema
1487 incl_2012 = "#"
1488 else:
1489 incl_2012 = ""
1491 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1492 "CONFIGDN": names.configdn,
1493 "NETBIOSNAME": names.netbiosname,
1494 "DEFAULTSITE": names.sitename,
1495 "DNSDOMAIN": names.dnsdomain,
1496 "DOMAIN": names.domain,
1497 "SCHEMADN": names.schemadn,
1498 "DOMAINDN": names.domaindn,
1499 "SERVERDN": names.serverdn,
1500 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1501 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1502 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1503 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1504 "SERVICES_DESCRIPTOR": protected1_descr,
1505 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1506 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1507 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1508 "PARTITIONS_DESCRIPTOR": partitions_descr,
1509 "SITES_DESCRIPTOR": sites_descr,
1512 setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1513 "CONFIGDN": names.configdn,
1514 "INC2012": incl_2012,
1517 logger.info("Setting up display specifiers")
1518 display_specifiers_ldif = read_ms_ldif(
1519 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1520 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1521 {"CONFIGDN": names.configdn})
1522 check_all_substituted(display_specifiers_ldif)
1523 samdb.add_ldif(display_specifiers_ldif)
1525 logger.info("Modifying display specifiers and extended rights")
1526 setup_modify_ldif(samdb,
1527 setup_path("provision_configuration_modify.ldif"), {
1528 "CONFIGDN": names.configdn,
1529 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1532 logger.info("Adding users container")
1533 users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
1534 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1535 "DOMAINDN": names.domaindn,
1536 "USERS_DESCRIPTOR": users_desc
1538 logger.info("Modifying users container")
1539 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1540 "DOMAINDN": names.domaindn})
1541 logger.info("Adding computers container")
1542 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
1543 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1544 "DOMAINDN": names.domaindn,
1545 "COMPUTERS_DESCRIPTOR": computers_desc
1547 logger.info("Modifying computers container")
1548 setup_modify_ldif(samdb,
1549 setup_path("provision_computers_modify.ldif"), {
1550 "DOMAINDN": names.domaindn})
1551 logger.info("Setting up sam.ldb data")
1552 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
1553 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1554 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1555 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
1556 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
1557 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1558 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1559 "DOMAINDN": names.domaindn,
1560 "NETBIOSNAME": names.netbiosname,
1561 "DEFAULTSITE": names.sitename,
1562 "CONFIGDN": names.configdn,
1563 "SERVERDN": names.serverdn,
1564 "RIDAVAILABLESTART": str(next_rid + 600),
1565 "POLICYGUID_DC": policyguid_dc,
1566 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1567 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1568 "SYSTEM_DESCRIPTOR": system_desc,
1569 "BUILTIN_DESCRIPTOR": builtin_desc,
1570 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1573 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1574 if fill == FILL_FULL:
1575 managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
1576 setup_modify_ldif(samdb,
1577 setup_path("provision_configuration_references.ldif"), {
1578 "CONFIGDN": names.configdn,
1579 "SCHEMADN": names.schemadn})
1581 logger.info("Setting up well known security principals")
1582 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1583 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1584 "CONFIGDN": names.configdn,
1585 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1586 }, controls=["relax:0", "provision:0"])
1588 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1589 setup_modify_ldif(samdb,
1590 setup_path("provision_basedn_references.ldif"), {
1591 "DOMAINDN": names.domaindn,
1592 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1595 logger.info("Setting up sam.ldb users and groups")
1596 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1597 "DOMAINDN": names.domaindn,
1598 "DOMAINSID": str(names.domainsid),
1599 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
1600 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
1601 }, controls=["relax:0", "provision:0"])
1603 logger.info("Setting up self join")
1604 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1605 invocationid=invocationid,
1606 dns_backend=dns_backend,
1607 dnspass=dnspass,
1608 machinepass=machinepass,
1609 domainsid=names.domainsid,
1610 next_rid=next_rid,
1611 dc_rid=dc_rid,
1612 policyguid=policyguid,
1613 policyguid_dc=policyguid_dc,
1614 domainControllerFunctionality=domainControllerFunctionality,
1615 ntdsguid=ntdsguid)
1617 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1618 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1619 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE).decode('utf8')
1620 assert isinstance(names.ntdsguid, string_types)
1622 return samdb
1625 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1626 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)"
1627 SYSVOL_SERVICE = "sysvol"
1630 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1631 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1632 for root, dirs, files in os.walk(path, topdown=False):
1633 for name in files:
1634 setntacl(lp, os.path.join(root, name), acl, domsid,
1635 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1636 for name in dirs:
1637 setntacl(lp, os.path.join(root, name), acl, domsid,
1638 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1641 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1642 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1643 folders beneath.
1645 :param sysvol: Physical path for the sysvol folder
1646 :param dnsdomain: The DNS name of the domain
1647 :param domainsid: The SID of the domain
1648 :param domaindn: The DN of the domain (ie. DC=...)
1649 :param samdb: An LDB object on the SAM db
1650 :param lp: an LP object
1653 # Set ACL for GPO root folder
1654 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1655 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1656 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1658 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1659 attrs=["cn", "nTSecurityDescriptor"],
1660 expression="", scope=ldb.SCOPE_ONELEVEL)
1662 for policy in res:
1663 acl = ndr_unpack(security.descriptor,
1664 policy["nTSecurityDescriptor"][0]).as_sddl()
1665 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1666 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1667 str(domainsid), use_ntvfs,
1668 passdb=passdb)
1671 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1672 domaindn, lp, use_ntvfs):
1673 """Set the ACL for the sysvol share and the subfolders
1675 :param samdb: An LDB object on the SAM db
1676 :param netlogon: Physical path for the netlogon folder
1677 :param sysvol: Physical path for the sysvol folder
1678 :param uid: The UID of the "Administrator" user
1679 :param gid: The GID of the "Domain adminstrators" group
1680 :param domainsid: The SID of the domain
1681 :param dnsdomain: The DNS name of the domain
1682 :param domaindn: The DN of the domain (ie. DC=...)
1684 s4_passdb = None
1686 if not use_ntvfs:
1687 s3conf = s3param.get_context()
1688 s3conf.load(lp.configfile)
1690 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1691 try:
1692 try:
1693 smbd.set_simple_acl(file.name, 0o755, gid)
1694 except OSError:
1695 if not smbd.have_posix_acls():
1696 # This clue is only strictly correct for RPM and
1697 # Debian-like Linux systems, but hopefully other users
1698 # will get enough clue from it.
1699 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1700 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1702 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1703 "Try the mounting the filesystem with the 'acl' option.")
1704 try:
1705 smbd.chown(file.name, uid, gid)
1706 except OSError:
1707 raise ProvisioningError("Unable to chown a file on your filesystem. "
1708 "You may not be running provision as root.")
1709 finally:
1710 file.close()
1712 # This will ensure that the smbd code we are running when setting ACLs
1713 # is initialised with the smb.conf
1714 s3conf = s3param.get_context()
1715 s3conf.load(lp.configfile)
1716 # ensure we are using the right samba_dsdb passdb backend, no matter what
1717 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1718 passdb.reload_static_pdb()
1720 # ensure that we init the samba_dsdb backend, so the domain sid is
1721 # marked in secrets.tdb
1722 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1724 # now ensure everything matches correctly, to avoid wierd issues
1725 if passdb.get_global_sam_sid() != domainsid:
1726 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))
1728 domain_info = s4_passdb.domain_info()
1729 if domain_info["dom_sid"] != domainsid:
1730 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))
1732 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1733 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()))
1735 try:
1736 if use_ntvfs:
1737 os.chown(sysvol, -1, gid)
1738 except OSError:
1739 canchown = False
1740 else:
1741 canchown = True
1743 # use admin sid dn as user dn, since admin should own most of the files,
1744 # the operation will be much faster
1745 userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
1747 flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1748 auth.AUTH_SESSION_INFO_AUTHENTICATED |
1749 auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
1751 session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
1752 session_info_flags=flags)
1754 def _setntacl(path):
1755 """A helper to reuse args"""
1756 return setntacl(
1757 lp, path, SYSVOL_ACL, str(domainsid),
1758 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb,
1759 service=SYSVOL_SERVICE, session_info=session_info)
1761 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1762 _setntacl(sysvol)
1763 for root, dirs, files in os.walk(sysvol, topdown=False):
1764 for name in files:
1765 if use_ntvfs and canchown:
1766 os.chown(os.path.join(root, name), -1, gid)
1767 _setntacl(os.path.join(root, name))
1768 for name in dirs:
1769 if use_ntvfs and canchown:
1770 os.chown(os.path.join(root, name), -1, gid)
1771 _setntacl(os.path.join(root, name))
1773 # Set acls on Policy folder and policies folders
1774 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1777 def acl_type(direct_db_access):
1778 if direct_db_access:
1779 return "DB"
1780 else:
1781 return "VFS"
1784 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1785 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1786 fsacl_sddl = fsacl.as_sddl(domainsid)
1787 if fsacl_sddl != acl:
1788 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))
1790 for root, dirs, files in os.walk(path, topdown=False):
1791 for name in files:
1792 fsacl = getntacl(lp, os.path.join(root, name),
1793 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1794 if fsacl is None:
1795 raise ProvisioningError('%s ACL on GPO file %s not found!' %
1796 (acl_type(direct_db_access),
1797 os.path.join(root, name)))
1798 fsacl_sddl = fsacl.as_sddl(domainsid)
1799 if fsacl_sddl != acl:
1800 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))
1802 for name in dirs:
1803 fsacl = getntacl(lp, os.path.join(root, name),
1804 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1805 if fsacl is None:
1806 raise ProvisioningError('%s ACL on GPO directory %s not found!'
1807 % (acl_type(direct_db_access),
1808 os.path.join(root, name)))
1809 fsacl_sddl = fsacl.as_sddl(domainsid)
1810 if fsacl_sddl != acl:
1811 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))
1814 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1815 direct_db_access):
1816 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1817 folders beneath.
1819 :param sysvol: Physical path for the sysvol folder
1820 :param dnsdomain: The DNS name of the domain
1821 :param domainsid: The SID of the domain
1822 :param domaindn: The DN of the domain (ie. DC=...)
1823 :param samdb: An LDB object on the SAM db
1824 :param lp: an LP object
1827 # Set ACL for GPO root folder
1828 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1829 fsacl = getntacl(lp, root_policy_path,
1830 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1831 if fsacl is None:
1832 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1833 fsacl_sddl = fsacl.as_sddl(domainsid)
1834 if fsacl_sddl != POLICIES_ACL:
1835 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))
1836 res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1837 attrs=["cn", "nTSecurityDescriptor"],
1838 expression="", scope=ldb.SCOPE_ONELEVEL)
1840 for policy in res:
1841 acl = ndr_unpack(security.descriptor,
1842 policy["nTSecurityDescriptor"][0]).as_sddl()
1843 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1844 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1845 domainsid, direct_db_access)
1848 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1849 lp):
1850 """Set the ACL for the sysvol share and the subfolders
1852 :param samdb: An LDB object on the SAM db
1853 :param netlogon: Physical path for the netlogon folder
1854 :param sysvol: Physical path for the sysvol folder
1855 :param uid: The UID of the "Administrator" user
1856 :param gid: The GID of the "Domain adminstrators" group
1857 :param domainsid: The SID of the domain
1858 :param dnsdomain: The DNS name of the domain
1859 :param domaindn: The DN of the domain (ie. DC=...)
1862 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1863 s3conf = s3param.get_context()
1864 s3conf.load(lp.configfile)
1865 # ensure we are using the right samba_dsdb passdb backend, no matter what
1866 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1867 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1868 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1870 # now ensure everything matches correctly, to avoid wierd issues
1871 if passdb.get_global_sam_sid() != domainsid:
1872 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))
1874 domain_info = s4_passdb.domain_info()
1875 if domain_info["dom_sid"] != domainsid:
1876 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))
1878 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1879 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()))
1881 # Ensure we can read this directly, and via the smbd VFS
1882 for direct_db_access in [True, False]:
1883 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1884 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1885 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1886 if fsacl is None:
1887 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1888 fsacl_sddl = fsacl.as_sddl(domainsid)
1889 if fsacl_sddl != SYSVOL_ACL:
1890 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))
1892 # Check acls on Policy folder and policies folders
1893 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1894 direct_db_access)
1897 def interface_ips_v4(lp, all_interfaces=False):
1898 """return only IPv4 IPs"""
1899 ips = samba.interface_ips(lp, all_interfaces)
1900 ret = []
1901 for i in ips:
1902 if i.find(':') == -1:
1903 ret.append(i)
1904 return ret
1907 def interface_ips_v6(lp):
1908 """return only IPv6 IPs"""
1909 ips = samba.interface_ips(lp, False)
1910 ret = []
1911 for i in ips:
1912 if i.find(':') != -1:
1913 ret.append(i)
1914 return ret
1917 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1918 schema=None,
1919 targetdir=None, samdb_fill=FILL_FULL,
1920 hostip=None, hostip6=None,
1921 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1922 domainguid=None, policyguid=None, policyguid_dc=None,
1923 invocationid=None, machinepass=None, ntdsguid=None,
1924 dns_backend=None, dnspass=None,
1925 serverrole=None, dom_for_fun_level=None,
1926 am_rodc=False, lp=None, use_ntvfs=False,
1927 skip_sysvolacl=False, backend_store=None,
1928 backend_store_size=None):
1929 # create/adapt the group policy GUIDs
1930 # Default GUID for default policy are described at
1931 # "How Core Group Policy Works"
1932 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1933 if policyguid is None:
1934 policyguid = DEFAULT_POLICY_GUID
1935 policyguid = policyguid.upper()
1936 if policyguid_dc is None:
1937 policyguid_dc = DEFAULT_DC_POLICY_GUID
1938 policyguid_dc = policyguid_dc.upper()
1940 if invocationid is None:
1941 invocationid = str(uuid.uuid4())
1943 if krbtgtpass is None:
1944 krbtgtpass = samba.generate_random_machine_password(128, 255)
1945 if machinepass is None:
1946 machinepass = samba.generate_random_machine_password(128, 255)
1947 if dnspass is None:
1948 dnspass = samba.generate_random_password(128, 255)
1950 samdb.transaction_start()
1951 try:
1952 samdb = fill_samdb(samdb, lp, names, logger=logger,
1953 schema=schema,
1954 policyguid=policyguid, policyguid_dc=policyguid_dc,
1955 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1956 invocationid=invocationid, machinepass=machinepass,
1957 dns_backend=dns_backend, dnspass=dnspass,
1958 ntdsguid=ntdsguid, serverrole=serverrole,
1959 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1960 next_rid=next_rid, dc_rid=dc_rid,
1961 backend_store=backend_store,
1962 backend_store_size=backend_store_size)
1964 # Set up group policies (domain policy and domain controller
1965 # policy)
1966 if serverrole == "active directory domain controller":
1967 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1968 policyguid_dc)
1969 except:
1970 samdb.transaction_cancel()
1971 raise
1972 else:
1973 samdb.transaction_commit()
1975 if serverrole == "active directory domain controller":
1976 # Continue setting up sysvol for GPO. This appears to require being
1977 # outside a transaction.
1978 if not skip_sysvolacl:
1979 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1980 paths.root_gid, names.domainsid, names.dnsdomain,
1981 names.domaindn, lp, use_ntvfs)
1982 else:
1983 logger.info("Setting acl on sysvol skipped")
1985 secretsdb_self_join(secrets_ldb, domain=names.domain,
1986 realm=names.realm, dnsdomain=names.dnsdomain,
1987 netbiosname=names.netbiosname, domainsid=names.domainsid,
1988 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1990 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1991 # In future, this might be determined from some configuration
1992 kerberos_enctypes = str(ENC_ALL_TYPES)
1994 try:
1995 msg = ldb.Message(ldb.Dn(samdb,
1996 samdb.searchone("distinguishedName",
1997 expression="samAccountName=%s$" % names.netbiosname,
1998 scope=ldb.SCOPE_SUBTREE).decode('utf8')))
1999 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
2000 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
2001 name="msDS-SupportedEncryptionTypes")
2002 samdb.modify(msg)
2003 except ldb.LdbError as e:
2004 (enum, estr) = e.args
2005 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
2006 # It might be that this attribute does not exist in this schema
2007 raise
2009 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
2010 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
2011 dnspass=dnspass, os_level=dom_for_fun_level,
2012 targetdir=targetdir, fill_level=samdb_fill,
2013 backend_store=backend_store)
2015 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
2016 attribute="objectGUID").decode('utf8')
2017 assert isinstance(domainguid, string_types)
2019 lastProvisionUSNs = get_last_provision_usn(samdb)
2020 maxUSN = get_max_usn(samdb, str(names.rootdn))
2021 if lastProvisionUSNs is not None:
2022 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
2023 else:
2024 set_provision_usn(samdb, 0, maxUSN, invocationid)
2026 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
2027 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
2028 {'NTDSGUID': names.ntdsguid})
2030 # fix any dangling GUIDs from the provision
2031 logger.info("Fixing provision GUIDs")
2032 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
2033 quiet=True)
2034 samdb.transaction_start()
2035 try:
2036 # a small number of GUIDs are missing because of ordering issues in the
2037 # provision code
2038 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
2039 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
2040 scope=ldb.SCOPE_BASE,
2041 attrs=['defaultObjectCategory'])
2042 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
2043 scope=ldb.SCOPE_ONELEVEL,
2044 attrs=['ipsecOwnersReference',
2045 'ipsecFilterReference',
2046 'ipsecISAKMPReference',
2047 'ipsecNegotiationPolicyReference',
2048 'ipsecNFAReference'])
2049 if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
2050 attrs=['attributeId', 'governsId']) != 0:
2051 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
2052 except:
2053 samdb.transaction_cancel()
2054 raise
2055 else:
2056 samdb.transaction_commit()
2059 _ROLES_MAP = {
2060 "ROLE_STANDALONE": "standalone server",
2061 "ROLE_DOMAIN_MEMBER": "member server",
2062 "ROLE_DOMAIN_BDC": "active directory domain controller",
2063 "ROLE_DOMAIN_PDC": "active directory domain controller",
2064 "dc": "active directory domain controller",
2065 "member": "member server",
2066 "domain controller": "active directory domain controller",
2067 "active directory domain controller": "active directory domain controller",
2068 "member server": "member server",
2069 "standalone": "standalone server",
2070 "standalone server": "standalone server",
2074 def sanitize_server_role(role):
2075 """Sanitize a server role name.
2077 :param role: Server role
2078 :raise ValueError: If the role can not be interpreted
2079 :return: Sanitized server role (one of "member server",
2080 "active directory domain controller", "standalone server")
2082 try:
2083 return _ROLES_MAP[role]
2084 except KeyError:
2085 raise ValueError(role)
2088 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
2089 maxuid, maxgid):
2090 """Create AD entries for the fake ypserver.
2092 This is needed for being able to manipulate posix attrs via ADUC.
2094 samdb.transaction_start()
2095 try:
2096 logger.info("Setting up fake yp server settings")
2097 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2098 "DOMAINDN": domaindn,
2099 "NETBIOSNAME": netbiosname,
2100 "NISDOMAIN": nisdomain,
2102 except:
2103 samdb.transaction_cancel()
2104 raise
2105 else:
2106 samdb.transaction_commit()
2109 def directory_create_or_exists(path, mode=0o755):
2110 if not os.path.exists(path):
2111 try:
2112 os.mkdir(path, mode)
2113 except OSError as e:
2114 if e.errno in [errno.EEXIST]:
2115 pass
2116 else:
2117 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2120 def determine_host_ip(logger, lp, hostip=None):
2121 if hostip is None:
2122 logger.info("Looking up IPv4 addresses")
2123 hostips = interface_ips_v4(lp)
2124 if len(hostips) > 0:
2125 hostip = hostips[0]
2126 if len(hostips) > 1:
2127 logger.warning("More than one IPv4 address found. Using %s",
2128 hostip)
2129 if hostip == "127.0.0.1":
2130 hostip = None
2131 if hostip is None:
2132 logger.warning("No IPv4 address will be assigned")
2134 return hostip
2137 def determine_host_ip6(logger, lp, hostip6=None):
2138 if hostip6 is None:
2139 logger.info("Looking up IPv6 addresses")
2140 hostips = interface_ips_v6(lp)
2141 if hostips:
2142 hostip6 = hostips[0]
2143 if len(hostips) > 1:
2144 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2145 if hostip6 is None:
2146 logger.warning("No IPv6 address will be assigned")
2148 return hostip6
2151 def provision(logger, session_info, smbconf=None,
2152 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2153 domaindn=None, schemadn=None, configdn=None, serverdn=None,
2154 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2155 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2156 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2157 dns_backend=None, dns_forwarder=None, dnspass=None,
2158 invocationid=None, machinepass=None, ntdsguid=None,
2159 root=None, nobody=None, users=None, backup=None, aci=None,
2160 serverrole=None, dom_for_fun_level=None, backend_type=None,
2161 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
2162 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2163 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2164 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
2165 ldap_backend_extra_port=None, base_schema="2012_R2",
2166 plaintext_secrets=False, backend_store=None,
2167 backend_store_size=None):
2168 """Provision samba4
2170 :note: caution, this wipes all existing data!
2173 try:
2174 serverrole = sanitize_server_role(serverrole)
2175 except ValueError:
2176 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2178 if ldapadminpass is None:
2179 # Make a new, random password between Samba and it's LDAP server
2180 ldapadminpass = samba.generate_random_password(128, 255)
2182 if backend_type is None:
2183 backend_type = "ldb"
2184 if backend_store is None:
2185 backend_store = get_default_backend_store()
2187 if domainsid is None:
2188 domainsid = security.random_sid()
2190 root_uid = get_root_uid([root or "root"], logger)
2191 nobody_uid = findnss_uid([nobody or "nobody"])
2192 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2193 root_gid = pwd.getpwuid(root_uid).pw_gid
2195 try:
2196 bind_gid = findnss_gid(["bind", "named"])
2197 except KeyError:
2198 bind_gid = None
2200 if targetdir is not None:
2201 smbconf = os.path.join(targetdir, "etc", "smb.conf")
2202 elif smbconf is None:
2203 smbconf = samba.param.default_path()
2204 if not os.path.exists(os.path.dirname(smbconf)):
2205 os.makedirs(os.path.dirname(smbconf))
2207 server_services = []
2208 global_param = {}
2209 if use_rfc2307:
2210 global_param["idmap_ldb:use rfc2307"] = ["yes"]
2212 if dns_backend != "SAMBA_INTERNAL":
2213 server_services.append("-dns")
2214 else:
2215 if dns_forwarder is not None:
2216 global_param["dns forwarder"] = [dns_forwarder]
2218 if use_ntvfs:
2219 server_services.append("+smb")
2220 server_services.append("-s3fs")
2221 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2223 if len(server_services) > 0:
2224 global_param["server services"] = server_services
2226 # only install a new smb.conf if there isn't one there already
2227 if os.path.exists(smbconf):
2228 # if Samba Team members can't figure out the weird errors
2229 # loading an empty smb.conf gives, then we need to be smarter.
2230 # Pretend it just didn't exist --abartlet
2231 f = open(smbconf, 'r')
2232 try:
2233 data = f.read().lstrip()
2234 finally:
2235 f.close()
2236 if data is None or data == "":
2237 make_smbconf(smbconf, hostname, domain, realm,
2238 targetdir, serverrole=serverrole,
2239 eadb=useeadb, use_ntvfs=use_ntvfs,
2240 lp=lp, global_param=global_param)
2241 else:
2242 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2243 serverrole=serverrole,
2244 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2246 if lp is None:
2247 lp = samba.param.LoadParm()
2248 lp.load(smbconf)
2249 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2250 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2251 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2252 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2253 paths = provision_paths_from_lp(lp, names.dnsdomain)
2255 paths.bind_gid = bind_gid
2256 paths.root_uid = root_uid
2257 paths.root_gid = root_gid
2259 hostip = determine_host_ip(logger, lp, hostip)
2260 hostip6 = determine_host_ip6(logger, lp, hostip6)
2261 names.hostip = hostip
2262 names.hostip6 = hostip6
2263 names.domainguid = domainguid
2264 names.domainsid = domainsid
2265 names.forestsid = domainsid
2267 if serverrole is None:
2268 serverrole = lp.get("server role")
2270 directory_create_or_exists(paths.private_dir, 0o700)
2271 directory_create_or_exists(paths.binddns_dir, 0o770)
2272 directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2273 directory_create_or_exists(paths.state_dir)
2274 if not plaintext_secrets:
2275 setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2277 if paths.sysvol and not os.path.exists(paths.sysvol):
2278 os.makedirs(paths.sysvol, 0o775)
2280 ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="")
2282 schema = Schema(domainsid, invocationid=invocationid,
2283 schemadn=names.schemadn, base_schema=base_schema)
2285 if backend_type == "ldb":
2286 provision_backend = LDBBackend(backend_type, paths=paths,
2287 lp=lp,
2288 names=names, logger=logger)
2289 elif backend_type == "fedora-ds":
2290 provision_backend = FDSBackend(backend_type, paths=paths,
2291 lp=lp,
2292 names=names, logger=logger, domainsid=domainsid,
2293 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2294 slapd_path=slapd_path,
2295 root=root)
2296 elif backend_type == "openldap":
2297 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2298 lp=lp,
2299 names=names, logger=logger, domainsid=domainsid,
2300 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2301 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2302 ldap_backend_extra_port=ldap_backend_extra_port,
2303 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2304 ldap_backend_forced_uri=ldap_backend_forced_uri)
2305 else:
2306 raise ValueError("Unknown LDAP backend type selected")
2308 provision_backend.init()
2309 provision_backend.start()
2311 # only install a new shares config db if there is none
2312 if not os.path.exists(paths.shareconf):
2313 logger.info("Setting up share.ldb")
2314 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2315 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2317 logger.info("Setting up secrets.ldb")
2318 secrets_ldb = setup_secretsdb(paths,
2319 session_info=session_info,
2320 backend_credentials=provision_backend.credentials, lp=lp)
2322 try:
2323 logger.info("Setting up the registry")
2324 setup_registry(paths.hklm, session_info, lp=lp)
2326 logger.info("Setting up the privileges database")
2327 setup_privileges(paths.privilege, session_info, lp=lp)
2329 logger.info("Setting up idmap db")
2330 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2332 setup_name_mappings(idmap, sid=str(domainsid),
2333 root_uid=root_uid, nobody_uid=nobody_uid,
2334 users_gid=users_gid, root_gid=root_gid)
2336 logger.info("Setting up SAM db")
2337 samdb = setup_samdb(paths.samdb, session_info,
2338 provision_backend, lp, names, logger=logger,
2339 serverrole=serverrole,
2340 schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2341 plaintext_secrets=plaintext_secrets,
2342 backend_store=backend_store,
2343 backend_store_size=backend_store_size)
2345 if serverrole == "active directory domain controller":
2346 if paths.netlogon is None:
2347 raise MissingShareError("netlogon", paths.smbconf)
2349 if paths.sysvol is None:
2350 raise MissingShareError("sysvol", paths.smbconf)
2352 if not os.path.isdir(paths.netlogon):
2353 os.makedirs(paths.netlogon, 0o755)
2355 if adminpass is None:
2356 adminpass = samba.generate_random_password(12, 32)
2357 adminpass_generated = True
2358 else:
2359 if isinstance(adminpass, binary_type):
2360 adminpass = adminpass.decode('utf-8')
2361 adminpass_generated = False
2363 if samdb_fill == FILL_FULL:
2364 provision_fill(samdb, secrets_ldb, logger, names, paths,
2365 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2366 hostip=hostip, hostip6=hostip6,
2367 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2368 krbtgtpass=krbtgtpass,
2369 policyguid=policyguid, policyguid_dc=policyguid_dc,
2370 invocationid=invocationid, machinepass=machinepass,
2371 ntdsguid=ntdsguid, dns_backend=dns_backend,
2372 dnspass=dnspass, serverrole=serverrole,
2373 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2374 lp=lp, use_ntvfs=use_ntvfs,
2375 skip_sysvolacl=skip_sysvolacl,
2376 backend_store=backend_store,
2377 backend_store_size=backend_store_size)
2379 if not is_heimdal_built():
2380 create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2381 logger.info("The Kerberos KDC configuration for Samba AD is "
2382 "located at %s", paths.kdcconf)
2384 create_krb5_conf(paths.krb5conf,
2385 dnsdomain=names.dnsdomain, hostname=names.hostname,
2386 realm=names.realm)
2387 logger.info("A Kerberos configuration suitable for Samba AD has been "
2388 "generated at %s", paths.krb5conf)
2389 logger.info("Merge the contents of this file with your system "
2390 "krb5.conf or replace it with this one. Do not create a "
2391 "symlink!")
2393 if serverrole == "active directory domain controller":
2394 create_dns_update_list(lp, logger, paths)
2396 backend_result = provision_backend.post_setup()
2397 provision_backend.shutdown()
2399 except:
2400 secrets_ldb.transaction_cancel()
2401 raise
2403 # Now commit the secrets.ldb to disk
2404 secrets_ldb.transaction_commit()
2406 # the commit creates the dns.keytab in the private directory
2407 private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2408 bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2410 if os.path.isfile(private_dns_keytab_path):
2411 if os.path.isfile(bind_dns_keytab_path):
2412 try:
2413 os.unlink(bind_dns_keytab_path)
2414 except OSError as e:
2415 logger.error("Failed to remove %s: %s" %
2416 (bind_dns_keytab_path, e.strerror))
2418 # link the dns.keytab to the bind-dns directory
2419 try:
2420 os.link(private_dns_keytab_path, bind_dns_keytab_path)
2421 except OSError as e:
2422 logger.error("Failed to create link %s -> %s: %s" %
2423 (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2425 # chown the dns.keytab in the bind-dns directory
2426 if paths.bind_gid is not None:
2427 try:
2428 os.chmod(paths.binddns_dir, 0o770)
2429 os.chown(paths.binddns_dir, -1, paths.bind_gid)
2430 except OSError:
2431 if 'SAMBA_SELFTEST' not in os.environ:
2432 logger.info("Failed to chown %s to bind gid %u",
2433 paths.binddns_dir, paths.bind_gid)
2435 try:
2436 os.chmod(bind_dns_keytab_path, 0o640)
2437 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2438 except OSError:
2439 if 'SAMBA_SELFTEST' not in os.environ:
2440 logger.info("Failed to chown %s to bind gid %u",
2441 bind_dns_keytab_path, paths.bind_gid)
2443 result = ProvisionResult()
2444 result.server_role = serverrole
2445 result.domaindn = domaindn
2446 result.paths = paths
2447 result.names = names
2448 result.lp = lp
2449 result.samdb = samdb
2450 result.idmap = idmap
2451 result.domainsid = str(domainsid)
2453 if samdb_fill == FILL_FULL:
2454 result.adminpass_generated = adminpass_generated
2455 result.adminpass = adminpass
2456 else:
2457 result.adminpass_generated = False
2458 result.adminpass = None
2460 result.backend_result = backend_result
2462 if use_rfc2307:
2463 provision_fake_ypserver(logger=logger, samdb=samdb,
2464 domaindn=names.domaindn, netbiosname=names.netbiosname,
2465 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2467 return result
2470 def provision_become_dc(smbconf=None, targetdir=None,
2471 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2472 serverdn=None, domain=None, hostname=None, domainsid=None,
2473 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2474 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2475 dns_backend=None, root=None, nobody=None, users=None,
2476 backup=None, serverrole=None, ldap_backend=None,
2477 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2479 logger = logging.getLogger("provision")
2480 samba.set_debug_level(debuglevel)
2482 res = provision(logger, system_session(),
2483 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2484 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2485 configdn=configdn, serverdn=serverdn, domain=domain,
2486 hostname=hostname, hostip=None, domainsid=domainsid,
2487 machinepass=machinepass,
2488 serverrole="active directory domain controller",
2489 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2490 use_ntvfs=use_ntvfs)
2491 res.lp.set("debuglevel", str(debuglevel))
2492 return res
2495 def create_krb5_conf(path, dnsdomain, hostname, realm):
2496 """Write out a file containing a valid krb5.conf file
2498 :param path: Path of the new krb5.conf file.
2499 :param dnsdomain: DNS Domain name
2500 :param hostname: Local hostname
2501 :param realm: Realm name
2503 setup_file(setup_path("krb5.conf"), path, {
2504 "DNSDOMAIN": dnsdomain,
2505 "HOSTNAME": hostname,
2506 "REALM": realm,
2510 class ProvisioningError(Exception):
2511 """A generic provision error."""
2513 def __init__(self, value):
2514 self.value = value
2516 def __str__(self):
2517 return "ProvisioningError: " + self.value
2520 class InvalidNetbiosName(Exception):
2521 """A specified name was not a valid NetBIOS name."""
2523 def __init__(self, name):
2524 super(InvalidNetbiosName, self).__init__(
2525 "The name '%r' is not a valid NetBIOS name" % name)
2528 class MissingShareError(ProvisioningError):
2530 def __init__(self, name, smbconf):
2531 super(MissingShareError, self).__init__(
2532 "Existing smb.conf does not have a [%s] share, but you are "
2533 "configuring a DC. Please remove %s or add the share manually." %
2534 (name, smbconf))