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 base64
import b64encode
45 from samba
.auth
import system_session
, admin_session
46 from samba
.auth_util
import system_session_unix
48 from samba
import auth
49 from samba
.samba3
import smbd
, passdb
50 from samba
.samba3
import param
as s3param
54 check_all_substituted
,
55 is_valid_netbios_char
,
62 from samba
.dcerpc
import security
, misc
63 from samba
.dcerpc
.misc
import (
67 from samba
.dsdb
import (
68 DS_DOMAIN_FUNCTION_2000
,
69 DS_DOMAIN_FUNCTION_2008
,
70 DS_DOMAIN_FUNCTION_2008_R2
,
71 DS_DOMAIN_FUNCTION_2012
,
72 DS_DOMAIN_FUNCTION_2012_R2
,
73 DS_DOMAIN_FUNCTION_2016
,
76 from samba
.gkdi
import (
80 from samba
.idmap
import IDmapDB
81 from samba
.ms_display_specifiers
import read_ms_ldif
82 from samba
.ntacls
import setntacl
, getntacl
, dsacl2fsacl
83 from samba
.nt_time
import nt_now
84 from samba
.ndr
import ndr_pack
, ndr_unpack
85 from samba
.provision
.backend
import (
88 from samba
.descriptor
import (
89 get_deletedobjects_descriptor
,
90 get_config_descriptor
,
91 get_config_partitions_descriptor
,
92 get_config_sites_descriptor
,
93 get_config_ntds_quotas_descriptor
,
94 get_config_delete_protected1_descriptor
,
95 get_config_delete_protected1wd_descriptor
,
96 get_config_delete_protected2_descriptor
,
97 get_domain_descriptor
,
98 get_domain_infrastructure_descriptor
,
99 get_domain_builtin_descriptor
,
100 get_domain_computers_descriptor
,
101 get_domain_users_descriptor
,
102 get_domain_controllers_descriptor
,
103 get_domain_delete_protected1_descriptor
,
104 get_domain_delete_protected2_descriptor
,
105 get_managed_service_accounts_descriptor
,
107 from samba
.provision
.common
import (
115 from samba
.provision
.sambadns
import (
118 create_dns_dir_keytab_link
,
119 create_dns_update_list
123 import samba
.registry
124 from samba
.schema
import Schema
125 from samba
.samdb
import SamDB
126 from samba
.dbchecker
import dbcheck
127 from samba
.provision
.kerberos
import create_kdc_conf
128 from samba
.samdb
import get_default_backend_store
129 from samba
import functional_level
131 DEFAULT_POLICY_GUID
= "31B2F340-016D-11D2-945F-00C04FB984F9"
132 DEFAULT_DC_POLICY_GUID
= "6AC1786C-016F-11D2-945F-00C04FB984F9"
133 DEFAULTSITE
= "Default-First-Site-Name"
134 LAST_PROVISION_USN_ATTRIBUTE
= "lastProvisionUSN"
136 DEFAULT_MIN_PWD_LENGTH
= 7
139 class ProvisionPaths(object):
142 self
.shareconf
= None
153 self
.dns_keytab
= None
156 self
.private_dir
= None
157 self
.binddns_dir
= None
158 self
.state_dir
= None
161 class ProvisionNames(object):
169 self
.dnsforestdn
= None
170 self
.dnsdomaindn
= None
171 self
.ldapmanagerdn
= None
172 self
.dnsdomain
= None
174 self
.netbiosname
= None
179 self
.domainsid
= None
180 self
.forestsid
= None
181 self
.domainguid
= None
185 def find_provision_key_parameters(samdb
, secretsdb
, idmapdb
, paths
, smbconf
,
187 """Get key provision parameters (realm, domain, ...) from a given provision
189 :param samdb: An LDB object connected to the sam.ldb file
190 :param secretsdb: An LDB object connected to the secrets.ldb file
191 :param idmapdb: An LDB object connected to the idmap.ldb file
192 :param paths: A list of path to provision object
193 :param smbconf: Path to the smb.conf file
194 :param lp: A LoadParm object
195 :return: A list of key provision parameters
197 names
= ProvisionNames()
198 names
.adminpass
= None
200 # NT domain, kerberos realm, root dn, domain dn, domain dns name
201 names
.domain
= lp
.get("workgroup").upper()
202 names
.realm
= lp
.get("realm")
203 names
.dnsdomain
= names
.realm
.lower()
204 basedn
= samba
.dn_from_dns_name(names
.dnsdomain
)
205 names
.realm
= names
.realm
.upper()
207 # Get the netbiosname first (could be obtained from smb.conf in theory)
208 res
= secretsdb
.search(expression
="(flatname=%s)" %
209 names
.domain
, base
="CN=Primary Domains",
210 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
211 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$", "")
213 names
.smbconf
= smbconf
215 # That's a bit simplistic but it's ok as long as we have only 3
217 current
= samdb
.search(expression
="(objectClass=*)",
218 base
="", scope
=ldb
.SCOPE_BASE
,
219 attrs
=["defaultNamingContext", "schemaNamingContext",
220 "configurationNamingContext", "rootDomainNamingContext",
223 names
.configdn
= str(current
[0]["configurationNamingContext"][0])
224 names
.schemadn
= str(current
[0]["schemaNamingContext"][0])
225 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
,
226 current
[0]["defaultNamingContext"][0].decode('utf8')))):
227 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
228 "is not the same ..." % (paths
.samdb
,
229 str(current
[0]["defaultNamingContext"][0].decode('utf8')),
230 paths
.smbconf
, basedn
)))
232 names
.domaindn
= str(current
[0]["defaultNamingContext"][0])
233 names
.rootdn
= str(current
[0]["rootDomainNamingContext"][0])
234 names
.ncs
= current
[0]["namingContexts"]
235 names
.dnsforestdn
= None
236 names
.dnsdomaindn
= None
238 for i
in range(0, len(names
.ncs
)):
239 nc
= str(names
.ncs
[i
])
241 dnsforestdn
= "DC=ForestDnsZones,%s" % (str(names
.rootdn
))
242 if nc
== dnsforestdn
:
243 names
.dnsforestdn
= dnsforestdn
246 dnsdomaindn
= "DC=DomainDnsZones,%s" % (str(names
.domaindn
))
247 if nc
== dnsdomaindn
:
248 names
.dnsdomaindn
= dnsdomaindn
252 res3
= samdb
.search(expression
="(objectClass=site)",
253 base
="CN=Sites," + str(names
.configdn
), scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn"])
254 names
.sitename
= str(res3
[0]["cn"])
256 # dns hostname and server dn
257 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
258 base
="OU=Domain Controllers,%s" % basedn
,
259 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
261 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names
.netbiosname
, basedn
))
263 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("." + names
.dnsdomain
, "")
265 server_res
= samdb
.search(expression
="serverReference=%s" % res4
[0].dn
,
266 attrs
=[], base
=names
.configdn
)
267 names
.serverdn
= str(server_res
[0].dn
)
269 # invocation id/objectguid
270 res5
= samdb
.search(expression
="(objectClass=*)",
271 base
="CN=NTDS Settings,%s" % str(names
.serverdn
),
272 scope
=ldb
.SCOPE_BASE
,
273 attrs
=["invocationID", "objectGUID"])
274 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
275 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
278 res6
= samdb
.search(expression
="(objectClass=*)", base
=basedn
,
279 scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID",
280 "objectSid", "msDS-Behavior-Version"])
281 names
.domainguid
= str(ndr_unpack(misc
.GUID
, res6
[0]["objectGUID"][0]))
282 names
.domainsid
= ndr_unpack(security
.dom_sid
, res6
[0]["objectSid"][0])
283 names
.forestsid
= ndr_unpack(security
.dom_sid
, res6
[0]["objectSid"][0])
284 if res6
[0].get("msDS-Behavior-Version") is None or \
285 int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
286 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
288 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
291 res7
= samdb
.search(expression
="(name={%s})" % DEFAULT_POLICY_GUID
,
292 base
="CN=Policies,CN=System," + basedn
,
293 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn", "displayName"])
294 names
.policyid
= str(res7
[0]["cn"]).replace("{", "").replace("}", "")
296 res8
= samdb
.search(expression
="(name={%s})" % DEFAULT_DC_POLICY_GUID
,
297 base
="CN=Policies,CN=System," + basedn
,
298 scope
=ldb
.SCOPE_ONELEVEL
,
299 attrs
=["cn", "displayName"])
301 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{", "").replace("}", "")
303 names
.policyid_dc
= None
305 res9
= idmapdb
.search(expression
="(cn=%s-%s)" %
306 (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
),
307 attrs
=["xidNumber", "type"])
309 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
))
310 if str(res9
[0]["type"][0]) == "ID_TYPE_BOTH":
311 names
.root_gid
= int(res9
[0]["xidNumber"][0])
313 names
.root_gid
= pwd
.getpwuid(int(res9
[0]["xidNumber"][0])).pw_gid
315 res10
= samdb
.search(expression
="(samaccountname=dns)",
316 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["dn"],
317 controls
=["search_options:1:2"])
319 has_legacy_dns_account
= True
321 has_legacy_dns_account
= False
323 res11
= samdb
.search(expression
="(samaccountname=dns-%s)" % names
.netbiosname
,
324 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["dn"],
325 controls
=["search_options:1:2"])
327 has_dns_account
= True
329 has_dns_account
= False
331 if names
.dnsdomaindn
is not None:
333 names
.dns_backend
= 'BIND9_DLZ'
335 names
.dns_backend
= 'SAMBA_INTERNAL'
336 elif has_dns_account
or has_legacy_dns_account
:
337 names
.dns_backend
= 'BIND9_FLATFILE'
339 names
.dns_backend
= 'NONE'
341 dns_admins_sid
= get_dnsadmins_sid(samdb
, names
.domaindn
)
342 names
.name_map
['DnsAdmins'] = str(dns_admins_sid
)
347 def update_provision_usn(samdb
, low
, high
, id, replace
=False):
348 """Update the field provisionUSN in sam.ldb
350 This field is used to track range of USN modified by provision and
352 This value is used afterward by next provision to figure out if
353 the field have been modified since last provision.
355 :param samdb: An LDB object connect to sam.ldb
356 :param low: The lowest USN modified by this upgrade
357 :param high: The highest USN modified by this upgrade
358 :param id: The invocation id of the samba's dc
359 :param replace: A boolean indicating if the range should replace any
360 existing one or appended (default)
365 entry
= samdb
.search(base
="@PROVISION",
366 scope
=ldb
.SCOPE_BASE
,
367 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "dn"])
368 for e
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
369 if not re
.search(';', str(e
)):
370 e
= "%s;%s" % (str(e
), id)
373 tab
.append("%s-%s;%s" % (low
, high
, id))
374 delta
= ldb
.Message()
375 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
376 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = \
377 ldb
.MessageElement(tab
,
378 ldb
.FLAG_MOD_REPLACE
,
379 LAST_PROVISION_USN_ATTRIBUTE
)
380 entry
= samdb
.search(expression
='provisionnerID=*',
381 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
382 attrs
=["provisionnerID"])
383 if len(entry
) == 0 or len(entry
[0]) == 0:
384 delta
["provisionnerID"] = ldb
.MessageElement(id, ldb
.FLAG_MOD_ADD
, "provisionnerID")
388 def set_provision_usn(samdb
, low
, high
, id):
389 """Set the field provisionUSN in sam.ldb
390 This field is used to track range of USN modified by provision and
392 This value is used afterward by next provision to figure out if
393 the field have been modified since last provision.
395 :param samdb: An LDB object connect to sam.ldb
396 :param low: The lowest USN modified by this upgrade
397 :param high: The highest USN modified by this upgrade
398 :param id: The invocationId of the provision"""
401 tab
.append("%s-%s;%s" % (low
, high
, id))
403 delta
= ldb
.Message()
404 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
405 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = \
406 ldb
.MessageElement(tab
,
408 LAST_PROVISION_USN_ATTRIBUTE
)
412 def get_max_usn(samdb
, basedn
):
413 """ This function return the biggest USN present in the provision
415 :param samdb: A LDB object pointing to the sam.ldb
416 :param basedn: A string containing the base DN of the provision
418 :return: The biggest USN in the provision"""
420 res
= samdb
.search(expression
="objectClass=*", base
=basedn
,
421 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["uSNChanged"],
422 controls
=["search_options:1:2",
423 "server_sort:1:1:uSNChanged",
424 "paged_results:1:1"])
425 return res
[0]["uSNChanged"]
428 def get_last_provision_usn(sam
):
429 """Get USNs ranges modified by a provision or an upgradeprovision
431 :param sam: An LDB object pointing to the sam.ldb
432 :return: a dictionary which keys are invocation id and values are an array
433 of integer representing the different ranges
436 entry
= sam
.search(expression
="%s=*" % LAST_PROVISION_USN_ATTRIBUTE
,
437 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
438 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "provisionnerID"])
439 except ldb
.LdbError
as e1
:
440 (ecode
, emsg
) = e1
.args
441 if ecode
== ldb
.ERR_NO_SUCH_OBJECT
:
448 if entry
[0].get("provisionnerID"):
449 for e
in entry
[0]["provisionnerID"]:
451 for r
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
452 tab1
= str(r
).split(';')
457 if (len(myids
) > 0 and id not in myids
):
459 tab2
= p
.split(tab1
[0])
460 if range.get(id) is None:
462 range[id].append(tab2
[0])
463 range[id].append(tab2
[1])
469 class ProvisionResult(object):
470 """Result of a provision.
472 :ivar server_role: The server role
473 :ivar paths: ProvisionPaths instance
474 :ivar domaindn: The domain dn, as string
478 self
.server_role
= None
485 self
.domainsid
= None
486 self
.adminpass_generated
= None
487 self
.adminpass
= None
488 self
.backend_result
= None
490 def report_logger(self
, logger
):
491 """Report this provision result to a logger."""
493 "Once the above files are installed, your Samba AD server will "
495 if self
.adminpass_generated
:
496 logger
.info("Admin password: %s", self
.adminpass
)
497 logger
.info("Server Role: %s", self
.server_role
)
498 logger
.info("Hostname: %s", self
.names
.hostname
)
499 logger
.info("NetBIOS Domain: %s", self
.names
.domain
)
500 logger
.info("DNS Domain: %s", self
.names
.dnsdomain
)
501 logger
.info("DOMAIN SID: %s", self
.domainsid
)
503 if self
.backend_result
:
504 self
.backend_result
.report_logger(logger
)
507 def findnss(nssfn
, names
):
508 """Find a user or group from a list of possibilities.
510 :param nssfn: NSS Function to try (should raise KeyError if not found)
511 :param names: Names to check.
512 :return: Value return by first names list.
519 raise KeyError("Unable to find user/group in %r" % names
)
522 def findnss_uid(names
):
523 return findnss(pwd
.getpwnam
, names
)[2]
526 def findnss_gid(names
):
527 return findnss(grp
.getgrnam
, names
)[2]
530 def get_root_uid(root
, logger
):
532 root_uid
= findnss_uid(root
)
533 except KeyError as e
:
535 logger
.info("Assuming root user has UID zero")
540 def provision_paths_from_lp(lp
, dnsdomain
):
541 """Set the default paths for provisioning.
543 :param lp: Loadparm context.
544 :param dnsdomain: DNS Domain name
546 paths
= ProvisionPaths()
547 paths
.private_dir
= lp
.get("private dir")
548 paths
.binddns_dir
= lp
.get("binddns dir")
549 paths
.state_dir
= lp
.get("state directory")
551 # This is stored without path prefix for the "privateKeytab" attribute in
552 # "secrets_dns.ldif".
553 paths
.dns_keytab
= "dns.keytab"
554 paths
.keytab
= "secrets.keytab"
556 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
557 paths
.samdb
= os
.path
.join(paths
.private_dir
, "sam.ldb")
558 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, "idmap.ldb")
559 paths
.secrets
= os
.path
.join(paths
.private_dir
, "secrets.ldb")
560 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
561 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
562 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
563 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
564 paths
.kdcconf
= os
.path
.join(paths
.private_dir
, "kdc.conf")
565 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
566 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
567 paths
.encrypted_secrets_key_path
= os
.path
.join(
569 "encrypted_secrets.key")
571 paths
.dns
= os
.path
.join(paths
.binddns_dir
, "dns", dnsdomain
+ ".zone")
572 paths
.namedconf
= os
.path
.join(paths
.binddns_dir
, "named.conf")
573 paths
.namedconf_update
= os
.path
.join(paths
.binddns_dir
, "named.conf.update")
574 paths
.namedtxt
= os
.path
.join(paths
.binddns_dir
, "named.txt")
576 paths
.hklm
= "hklm.ldb"
577 paths
.hkcr
= "hkcr.ldb"
578 paths
.hkcu
= "hkcu.ldb"
579 paths
.hku
= "hku.ldb"
580 paths
.hkpd
= "hkpd.ldb"
581 paths
.hkpt
= "hkpt.ldb"
582 paths
.sysvol
= lp
.get("path", "sysvol")
583 paths
.netlogon
= lp
.get("path", "netlogon")
584 paths
.smbconf
= lp
.configfile
588 def determine_netbios_name(hostname
):
589 """Determine a netbios name from a hostname."""
590 # remove forbidden chars and force the length to be <16
591 netbiosname
= "".join([x
for x
in hostname
if is_valid_netbios_char(x
)])
592 return netbiosname
[:MAX_NETBIOS_NAME_LEN
].upper()
595 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
596 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
597 schemadn
=None, serverdn
=None, sitename
=None,
598 domain_names_forced
=False):
599 """Guess configuration settings to use."""
602 hostname
= socket
.gethostname().split(".")[0]
604 netbiosname
= lp
.get("netbios name")
605 if netbiosname
is None:
606 netbiosname
= determine_netbios_name(hostname
)
607 netbiosname
= netbiosname
.upper()
608 if not valid_netbios_name(netbiosname
):
609 raise InvalidNetbiosName(netbiosname
)
611 if dnsdomain
is None:
612 dnsdomain
= lp
.get("realm")
613 if dnsdomain
is None or dnsdomain
== "":
614 raise ProvisioningError(
615 "guess_names: 'realm' not specified in supplied %s!" %
618 dnsdomain
= dnsdomain
.lower()
620 if serverrole
is None:
621 serverrole
= lp
.get("server role")
622 if serverrole
is None:
623 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
625 serverrole
= serverrole
.lower()
627 realm
= dnsdomain
.upper()
629 if lp
.get("realm") == "":
630 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
632 if lp
.get("realm").upper() != realm
:
633 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
))
635 if lp
.get("server role").lower() != serverrole
:
636 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
))
638 if serverrole
== "active directory domain controller":
640 # This will, for better or worse, default to 'WORKGROUP'
641 domain
= lp
.get("workgroup")
642 domain
= domain
.upper()
644 if lp
.get("workgroup").upper() != domain
:
645 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
))
648 domaindn
= samba
.dn_from_dns_name(dnsdomain
)
650 if domain
== netbiosname
:
651 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain
, netbiosname
))
655 domaindn
= "DC=" + netbiosname
657 if not valid_netbios_name(domain
):
658 raise InvalidNetbiosName(domain
)
660 if hostname
.upper() == realm
:
661 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
662 if netbiosname
.upper() == realm
:
663 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm
, netbiosname
))
664 if domain
== realm
and not domain_names_forced
:
665 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
667 if serverrole
!= "active directory domain controller":
669 # This is the code path for a domain member
670 # where we provision the database as if we were
671 # on a domain controller, so we should not use
672 # the same dnsdomain as the domain controllers
673 # of our primary domain.
675 # This will be important if we start doing
676 # SID/name filtering and reject the local
677 # sid and names if they come from a domain
681 dnsdomain
= netbiosname
.lower()
687 configdn
= "CN=Configuration," + rootdn
689 schemadn
= "CN=Schema," + configdn
692 sitename
= DEFAULTSITE
695 serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
696 netbiosname
, sitename
, configdn
)
698 names
= ProvisionNames()
699 names
.rootdn
= rootdn
700 names
.domaindn
= domaindn
701 names
.configdn
= configdn
702 names
.schemadn
= schemadn
703 names
.ldapmanagerdn
= "CN=Manager," + rootdn
704 names
.dnsdomain
= dnsdomain
705 names
.domain
= domain
707 names
.netbiosname
= netbiosname
708 names
.hostname
= hostname
709 names
.sitename
= sitename
710 names
.serverdn
= serverdn
715 def make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
716 serverrole
=None, eadb
=False, use_ntvfs
=False, lp
=None,
718 """Create a new smb.conf file based on a couple of basic settings.
720 assert smbconf
is not None
723 hostname
= socket
.gethostname().split(".")[0]
725 netbiosname
= determine_netbios_name(hostname
)
727 if serverrole
is None:
728 serverrole
= "standalone server"
730 assert domain
is not None
731 domain
= domain
.upper()
733 assert realm
is not None
734 realm
= realm
.upper()
737 "netbios name": netbiosname
,
740 "server role": serverrole
,
744 lp
= samba
.param
.LoadParm()
745 # Load non-existent file
746 if os
.path
.exists(smbconf
):
749 if global_param
is not None:
750 for ent
in global_param
:
751 if global_param
[ent
] is not None:
752 global_settings
[ent
] = " ".join(global_param
[ent
])
754 if targetdir
is not None:
755 global_settings
["private dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "private"))
756 global_settings
["lock dir"] = os
.path
.abspath(targetdir
)
757 global_settings
["state directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "state"))
758 global_settings
["cache directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "cache"))
759 global_settings
["binddns dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "bind-dns"))
761 lp
.set("lock dir", os
.path
.abspath(targetdir
))
762 lp
.set("state directory", global_settings
["state directory"])
763 lp
.set("cache directory", global_settings
["cache directory"])
764 lp
.set("binddns dir", global_settings
["binddns dir"])
768 if targetdir
is not None:
769 privdir
= os
.path
.join(targetdir
, "private")
771 os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
772 elif not lp
.get("posix:eadb"):
773 privdir
= lp
.get("private dir")
775 os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
777 if targetdir
is not None:
778 statedir
= os
.path
.join(targetdir
, "state")
779 lp
.set("xattr_tdb:file",
780 os
.path
.abspath(os
.path
.join(statedir
, "xattr.tdb")))
781 elif not lp
.get("xattr_tdb:file"):
782 statedir
= lp
.get("state directory")
783 lp
.set("xattr_tdb:file",
784 os
.path
.abspath(os
.path
.join(statedir
, "xattr.tdb")))
787 if serverrole
== "active directory domain controller":
788 shares
["sysvol"] = os
.path
.join(lp
.get("state directory"), "sysvol")
789 shares
["netlogon"] = os
.path
.join(shares
["sysvol"], realm
.lower(),
792 global_settings
["passdb backend"] = "samba_dsdb"
794 f
= open(smbconf
, 'w')
796 f
.write("[globals]\n")
797 for key
, val
in global_settings
.items():
798 f
.write("\t%s = %s\n" % (key
, val
))
801 for name
, path
in shares
.items():
802 f
.write("[%s]\n" % name
)
803 f
.write("\tpath = %s\n" % path
)
804 f
.write("\tread only = no\n")
808 # reload the smb.conf
811 # and dump it without any values that are the default
812 # this ensures that any smb.conf parameters that were set
813 # on the provision/join command line are set in the resulting smb.conf
814 lp
.dump(False, smbconf
)
817 def setup_name_mappings(idmap
, sid
, root_uid
, nobody_uid
,
819 """setup reasonable name mappings for sam names to unix names.
821 :param samdb: SamDB object.
822 :param idmap: IDmap db object.
823 :param sid: The domain sid.
824 :param domaindn: The domain DN.
825 :param root_uid: uid of the UNIX root user.
826 :param nobody_uid: uid of the UNIX nobody user.
827 :param users_gid: gid of the UNIX users group.
829 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
831 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
832 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
835 def setup_samdb_partitions(samdb_path
, logger
, lp
, session_info
,
836 provision_backend
, names
, serverrole
,
837 plaintext_secrets
=False,
839 """Setup the partitions for the SAM database.
841 Alternatively, provision() may call this, and then populate the database.
843 :note: This will wipe the Sam Database!
845 :note: This function always removes the local SAM LDB file. The erase
846 parameter controls whether to erase the existing data, which
847 may not be stored locally but in LDAP.
850 assert session_info
is not None
852 # We use options=["modules:"] to stop the modules loading - we
853 # just want to wipe and re-initialise the database, not start it up
856 os
.unlink(samdb_path
)
860 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
861 lp
=lp
, options
=["modules:"])
863 ldap_backend_line
= "# No LDAP backend"
864 if provision_backend
.type != "ldb":
865 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldap_uri
867 required_features
= None
868 if not plaintext_secrets
:
869 required_features
= "requiredFeatures: encryptedSecrets"
871 if backend_store
is None:
872 backend_store
= get_default_backend_store()
873 backend_store_line
= "backendStore: %s" % backend_store
875 if backend_store
== "mdb":
876 if required_features
is not None:
877 required_features
+= "\n"
879 required_features
= ""
880 required_features
+= "requiredFeatures: lmdbLevelOne"
882 if required_features
is None:
883 required_features
= "# No required features"
885 samdb
.transaction_start()
887 logger
.info("Setting up sam.ldb partitions and settings")
888 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
889 "LDAP_BACKEND_LINE": ldap_backend_line
,
890 "BACKEND_STORE": backend_store_line
893 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
894 "BACKEND_TYPE": provision_backend
.type,
895 "SERVER_ROLE": serverrole
,
896 "REQUIRED_FEATURES": required_features
899 logger
.info("Setting up sam.ldb rootDSE")
900 setup_samdb_rootdse(samdb
, names
)
902 samdb
.transaction_cancel()
905 samdb
.transaction_commit()
908 def secretsdb_self_join(secretsdb
, domain
,
909 netbiosname
, machinepass
, domainsid
=None,
910 realm
=None, dnsdomain
=None,
911 key_version_number
=1,
912 secure_channel_type
=SEC_CHAN_WKSTA
):
913 """Add domain join-specific bits to a secrets database.
915 :param secretsdb: Ldb Handle to the secrets database
916 :param machinepass: Machine password
918 attrs
= ["whenChanged",
925 if realm
is not None:
926 if dnsdomain
is None:
927 dnsdomain
= realm
.lower()
928 dnsname
= '%s.%s' % (netbiosname
.lower(), dnsdomain
.lower())
931 shortname
= netbiosname
.lower()
933 # We don't need to set msg["flatname"] here, because rdn_name will handle
934 # it, and it causes problems for modifies anyway
935 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
936 msg
["secureChannelType"] = [str(secure_channel_type
)]
937 msg
["objectClass"] = ["top", "primaryDomain"]
938 if dnsname
is not None:
939 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
940 msg
["realm"] = [realm
]
941 msg
["saltPrincipal"] = ["host/%s@%s" % (dnsname
, realm
.upper())]
942 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
943 msg
["privateKeytab"] = ["secrets.keytab"]
945 msg
["secret"] = [machinepass
.encode('utf-8')]
946 msg
["samAccountName"] = ["%s$" % netbiosname
]
947 msg
["secureChannelType"] = [str(secure_channel_type
)]
948 if domainsid
is not None:
949 msg
["objectSid"] = [ndr_pack(domainsid
)]
951 # This complex expression tries to ensure that we don't have more
952 # than one record for this SID, realm or netbios domain at a time,
953 # but we don't delete the old record that we are about to modify,
954 # because that would delete the keytab and previous password.
955 res
= secretsdb
.search(base
="cn=Primary Domains", attrs
=attrs
,
956 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain
, realm
, str(domainsid
), str(msg
.dn
))),
957 scope
=ldb
.SCOPE_ONELEVEL
)
960 secretsdb
.delete(del_msg
.dn
)
962 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
965 msg
["priorSecret"] = [res
[0]["secret"][0]]
967 msg
["priorWhenChanged"] = [res
[0]["whenChanged"][0]]
972 msg
["privateKeytab"] = [res
[0]["privateKeytab"][0]]
977 msg
["krb5Keytab"] = [res
[0]["krb5Keytab"][0]]
983 msg
[el
].set_flags(ldb
.FLAG_MOD_REPLACE
)
984 secretsdb
.modify(msg
)
985 secretsdb
.rename(res
[0].dn
, msg
.dn
)
987 spn
= ['HOST/%s' % shortname
]
988 if secure_channel_type
== SEC_CHAN_BDC
and dnsname
is not None:
989 # if we are a domain controller then we add servicePrincipalName
990 # entries for the keytab code to update.
991 spn
.extend(['HOST/%s' % dnsname
])
992 msg
["servicePrincipalName"] = spn
997 def setup_secretsdb(paths
, session_info
, lp
):
998 """Setup the secrets database.
1000 :note: This function does not handle exceptions and transaction on purpose,
1001 it's up to the caller to do this job.
1003 :param path: Path to the secrets database.
1004 :param session_info: Session info.
1005 :param lp: Loadparm context
1006 :return: LDB handle for the created secrets database
1008 if os
.path
.exists(paths
.secrets
):
1009 os
.unlink(paths
.secrets
)
1011 keytab_path
= os
.path
.join(paths
.private_dir
, paths
.keytab
)
1012 if os
.path
.exists(keytab_path
):
1013 os
.unlink(keytab_path
)
1015 bind_dns_keytab_path
= os
.path
.join(paths
.binddns_dir
, paths
.dns_keytab
)
1016 if os
.path
.exists(bind_dns_keytab_path
):
1017 os
.unlink(bind_dns_keytab_path
)
1019 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
1020 if os
.path
.exists(dns_keytab_path
):
1021 os
.unlink(dns_keytab_path
)
1023 path
= paths
.secrets
1025 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
1027 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
1028 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
1029 secrets_ldb
.transaction_start()
1031 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
1033 secrets_ldb
.transaction_cancel()
1038 def setup_privileges(path
, session_info
, lp
):
1039 """Setup the privileges database.
1041 :param path: Path to the privileges database.
1042 :param session_info: Session info.
1043 :param lp: Loadparm context
1044 :return: LDB handle for the created secrets database
1046 if os
.path
.exists(path
):
1048 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
1049 privilege_ldb
.erase()
1050 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1053 def setup_encrypted_secrets_key(path
):
1054 """Setup the encrypted secrets key file.
1056 Any existing key file will be deleted and a new random key generated.
1058 :param path: Path to the secrets key file.
1061 if os
.path
.exists(path
):
1064 flags
= os
.O_WRONLY | os
.O_CREAT | os
.O_EXCL
1065 mode
= stat
.S_IRUSR | stat
.S_IWUSR
1067 umask_original
= os
.umask(0)
1069 fd
= os
.open(path
, flags
, mode
)
1071 os
.umask(umask_original
)
1073 with os
.fdopen(fd
, 'wb') as f
:
1074 key
= samba
.generate_random_bytes(16)
1078 def setup_registry(path
, session_info
, lp
):
1079 """Setup the registry.
1081 :param path: Path to the registry database
1082 :param session_info: Session information
1083 :param lp: Loadparm context
1085 reg
= samba
.registry
.Registry()
1086 hive
= samba
.registry
.open_ldb(path
, session_info
=session_info
, lp_ctx
=lp
)
1087 reg
.mount_hive(hive
, samba
.registry
.HKEY_LOCAL_MACHINE
)
1088 provision_reg
= setup_path("provision.reg")
1089 assert os
.path
.exists(provision_reg
)
1090 reg
.diff_apply(provision_reg
)
1093 def setup_idmapdb(path
, session_info
, lp
):
1094 """Setup the idmap database.
1096 :param path: path to the idmap database
1097 :param session_info: Session information
1098 :param lp: Loadparm context
1100 if os
.path
.exists(path
):
1103 idmap_ldb
= IDmapDB(path
, session_info
=session_info
, lp
=lp
)
1105 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
1109 def setup_samdb_rootdse(samdb
, names
):
1110 """Setup the SamDB rootdse.
1112 :param samdb: Sam Database handle
1114 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
1115 "SCHEMADN": names
.schemadn
,
1116 "DOMAINDN": names
.domaindn
,
1117 "ROOTDN": names
.rootdn
,
1118 "CONFIGDN": names
.configdn
,
1119 "SERVERDN": names
.serverdn
,
1123 def setup_self_join(samdb
, admin_session_info
, names
, fill
, machinepass
,
1124 dns_backend
, dnspass
, domainsid
, next_rid
, invocationid
,
1125 policyguid
, policyguid_dc
,
1126 domainControllerFunctionality
, ntdsguid
=None, dc_rid
=None):
1127 """Join a host to its own domain."""
1128 assert isinstance(invocationid
, str)
1129 if ntdsguid
is not None:
1130 ntdsguid_line
= "objectGUID: %s\n" % ntdsguid
1137 # Some clients/applications (like exchange) make use of
1138 # the operatingSystemVersion attribute in order to
1139 # find if a DC is good enough.
1141 # So we better use a value matching a Windows DC
1142 # with the same domainControllerFunctionality level
1143 operatingSystemVersion
= samba
.dsdb
.dc_operatingSystemVersion(domainControllerFunctionality
)
1145 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
1146 "CONFIGDN": names
.configdn
,
1147 "SCHEMADN": names
.schemadn
,
1148 "DOMAINDN": names
.domaindn
,
1149 "SERVERDN": names
.serverdn
,
1150 "INVOCATIONID": invocationid
,
1151 "NETBIOSNAME": names
.netbiosname
,
1152 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1153 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')).decode('utf8'),
1154 "DOMAINSID": str(domainsid
),
1155 "DCRID": str(dc_rid
),
1156 "OPERATING_SYSTEM": "Samba-%s" % version
,
1157 "OPERATING_SYSTEM_VERSION": operatingSystemVersion
,
1158 "NTDSGUID": ntdsguid_line
,
1159 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1160 domainControllerFunctionality
),
1161 "RIDALLOCATIONSTART": str(next_rid
+ 100),
1162 "RIDALLOCATIONEND": str(next_rid
+ 100 + 499)})
1164 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
1165 "POLICYGUID": policyguid
,
1166 "POLICYGUID_DC": policyguid_dc
,
1167 "DNSDOMAIN": names
.dnsdomain
,
1168 "DOMAINDN": names
.domaindn
})
1170 # If we are setting up a subdomain, then this has been replicated in, so we
1171 # don't need to add it
1172 if fill
== FILL_FULL
:
1173 setup_add_ldif(samdb
, setup_path("provision_self_join_config.ldif"), {
1174 "CONFIGDN": names
.configdn
,
1175 "SCHEMADN": names
.schemadn
,
1176 "DOMAINDN": names
.domaindn
,
1177 "SERVERDN": names
.serverdn
,
1178 "INVOCATIONID": invocationid
,
1179 "NETBIOSNAME": names
.netbiosname
,
1180 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1181 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')).decode('utf8'),
1182 "DOMAINSID": str(domainsid
),
1183 "DCRID": str(dc_rid
),
1184 "SAMBA_VERSION_STRING": version
,
1185 "NTDSGUID": ntdsguid_line
,
1186 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1187 domainControllerFunctionality
)})
1189 # Setup fSMORoleOwner entries to point at the newly created DC entry
1190 setup_modify_ldif(samdb
,
1191 setup_path("provision_self_join_modify_schema.ldif"), {
1192 "SCHEMADN": names
.schemadn
,
1193 "SERVERDN": names
.serverdn
,
1195 controls
=["provision:0", "relax:0"])
1196 setup_modify_ldif(samdb
,
1197 setup_path("provision_self_join_modify_config.ldif"), {
1198 "CONFIGDN": names
.configdn
,
1199 "DEFAULTSITE": names
.sitename
,
1200 "NETBIOSNAME": names
.netbiosname
,
1201 "SERVERDN": names
.serverdn
,
1204 system_session_info
= system_session()
1205 samdb
.set_session_info(system_session_info
)
1206 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1207 # modify a serverReference under cn=config when we are a subdomain, we must
1208 # be system due to ACLs
1209 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
1210 "DOMAINDN": names
.domaindn
,
1211 "SERVERDN": names
.serverdn
,
1212 "NETBIOSNAME": names
.netbiosname
,
1215 samdb
.set_session_info(admin_session_info
)
1217 if dns_backend
!= "SAMBA_INTERNAL":
1218 # This is Samba4 specific and should be replaced by the correct
1219 # DNS AD-style setup
1220 setup_add_ldif(samdb
, setup_path("provision_dns_add_samba.ldif"), {
1221 "DNSDOMAIN": names
.dnsdomain
,
1222 "DOMAINDN": names
.domaindn
,
1223 "DNSPASS_B64": b64encode(dnspass
.encode('utf-16-le')).decode('utf8'),
1224 "HOSTNAME": names
.hostname
,
1225 "DNSNAME": '%s.%s' % (
1226 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
1230 def getpolicypath(sysvolpath
, dnsdomain
, guid
):
1231 """Return the physical path of policy given its guid.
1233 :param sysvolpath: Path to the sysvol folder
1234 :param dnsdomain: DNS name of the AD domain
1235 :param guid: The GUID of the policy
1236 :return: A string with the complete path to the policy folder
1239 guid
= "{%s}" % guid
1240 policy_path
= os
.path
.join(sysvolpath
, dnsdomain
, "Policies", guid
)
1244 def create_gpo_struct(policy_path
):
1245 if not os
.path
.exists(policy_path
):
1246 os
.makedirs(policy_path
, 0o775)
1247 f
= open(os
.path
.join(policy_path
, "GPT.INI"), 'w')
1249 f
.write("[General]\r\nVersion=0")
1252 p
= os
.path
.join(policy_path
, "MACHINE")
1253 if not os
.path
.exists(p
):
1254 os
.makedirs(p
, 0o775)
1255 p
= os
.path
.join(policy_path
, "USER")
1256 if not os
.path
.exists(p
):
1257 os
.makedirs(p
, 0o775)
1260 def create_default_gpo(sysvolpath
, dnsdomain
, policyguid
, policyguid_dc
):
1261 """Create the default GPO for a domain
1263 :param sysvolpath: Physical path for the sysvol folder
1264 :param dnsdomain: DNS domain name of the AD domain
1265 :param policyguid: GUID of the default domain policy
1266 :param policyguid_dc: GUID of the default domain controller policy
1268 policy_path
= getpolicypath(sysvolpath
, dnsdomain
, policyguid
)
1269 create_gpo_struct(policy_path
)
1271 policy_path
= getpolicypath(sysvolpath
, dnsdomain
, policyguid_dc
)
1272 create_gpo_struct(policy_path
)
1275 # Default the database size to 8Gb
1276 DEFAULT_BACKEND_SIZE
= 8 * 1024 * 1024 *1024
1278 def setup_samdb(path
, session_info
, provision_backend
, lp
, names
,
1279 logger
, serverrole
, schema
, am_rodc
=False,
1280 plaintext_secrets
=False, backend_store
=None,
1281 backend_store_size
=None, batch_mode
=False):
1282 """Setup a complete SAM Database.
1284 :note: This will wipe the main SAM database file!
1287 # Also wipes the database
1288 setup_samdb_partitions(path
, logger
=logger
, lp
=lp
,
1289 provision_backend
=provision_backend
, session_info
=session_info
,
1290 names
=names
, serverrole
=serverrole
, plaintext_secrets
=plaintext_secrets
,
1291 backend_store
=backend_store
)
1293 store_size
= DEFAULT_BACKEND_SIZE
1294 if backend_store_size
:
1295 store_size
= backend_store_size
1298 if backend_store
== "mdb":
1299 options
.append("lmdb_env_size:" + str(store_size
))
1301 options
.append("batch_mode:1")
1303 # Estimate the number of index records in the transaction_index_cache
1304 # Numbers chosen give the prime 202481 for the default backend size,
1305 # which works well for a 100,000 user database
1306 cache_size
= int(store_size
/ 42423) + 1
1307 options
.append("transaction_index_cache_size:" + str(cache_size
))
1309 # Load the database, but don's load the global schema and don't connect
1311 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
1313 global_schema
=False, am_rodc
=am_rodc
, options
=options
)
1315 logger
.info("Pre-loading the Samba 4 and AD schema")
1317 # Load the schema from the one we computed earlier
1318 samdb
.set_schema(schema
, write_indices_and_attributes
=False)
1320 # Set the NTDS settings DN manually - in order to have it already around
1321 # before the provisioned tree exists and we connect
1322 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1324 # And now we can connect to the DB - the schema won't be loaded from the
1327 samdb
.connect(path
, options
=options
)
1328 except ldb
.LdbError
as e2
:
1329 (num
, string_error
) = e2
.args
1330 if (num
== ldb
.ERR_INSUFFICIENT_ACCESS_RIGHTS
):
1331 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path
)
1335 # But we have to give it one more kick to have it use the schema
1336 # during provision - it needs, now that it is connected, to write
1337 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1338 samdb
.set_schema(schema
, write_indices_and_attributes
=True)
1343 def fill_samdb(samdb
, lp
, names
, logger
, policyguid
,
1344 policyguid_dc
, fill
, adminpass
, krbtgtpass
, machinepass
, dns_backend
,
1345 dnspass
, invocationid
, ntdsguid
,
1346 dom_for_fun_level
=None, schema
=None, next_rid
=None, dc_rid
=None):
1348 if next_rid
is None:
1351 # Provision does not make much sense values larger than 1000000000
1352 # as the upper range of the rIDAvailablePool is 1073741823 and
1353 # we don't want to create a domain that cannot allocate rids.
1354 if next_rid
< 1000 or next_rid
> 1000000000:
1355 error
= "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid
)
1356 error
+= "the valid range is %u-%u. The default is %u." % (
1357 1000, 1000000000, 1000)
1358 raise ProvisioningError(error
)
1360 domainControllerFunctionality
= functional_level
.dc_level_from_lp(lp
)
1362 # ATTENTION: Do NOT change these default values without discussion with the
1363 # team and/or release manager. They have a big impact on the whole program!
1364 if dom_for_fun_level
is None:
1365 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2008_R2
1367 if dom_for_fun_level
> domainControllerFunctionality
:
1368 level
= functional_level
.level_to_string(domainControllerFunctionality
)
1369 raise ProvisioningError(f
"You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level ({level}). This won't work!")
1371 domainFunctionality
= dom_for_fun_level
1372 forestFunctionality
= dom_for_fun_level
1374 # Set the NTDS settings DN manually - in order to have it already around
1375 # before the provisioned tree exists and we connect
1376 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1378 # Set the domain functionality levels onto the database.
1379 # Various module (the password_hash module in particular) need
1380 # to know what level of AD we are emulating.
1382 # These will be fixed into the database via the database
1383 # modifictions below, but we need them set from the start.
1384 samdb
.set_opaque("domainFunctionality", domainFunctionality
)
1385 samdb
.set_opaque("forestFunctionality", forestFunctionality
)
1386 samdb
.set_opaque("domainControllerFunctionality",
1387 domainControllerFunctionality
)
1389 samdb
.set_domain_sid(str(names
.domainsid
))
1390 samdb
.set_invocation_id(invocationid
)
1392 logger
.info("Adding DomainDN: %s" % names
.domaindn
)
1394 # impersonate domain admin
1395 admin_session_info
= admin_session(lp
, str(names
.domainsid
))
1396 samdb
.set_session_info(admin_session_info
)
1397 if names
.domainguid
is not None:
1398 domainguid_line
= "objectGUID: %s\n-" % names
.domainguid
1400 domainguid_line
= ""
1402 descr
= b64encode(get_domain_descriptor(names
.domainsid
)).decode('utf8')
1403 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
1404 "DOMAINDN": names
.domaindn
,
1405 "DOMAINSID": str(names
.domainsid
),
1406 "DESCRIPTOR": descr
,
1407 "DOMAINGUID": domainguid_line
1410 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
1411 "DOMAINDN": names
.domaindn
,
1412 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1413 "NEXTRID": str(next_rid
),
1414 "DEFAULTSITE": names
.sitename
,
1415 "CONFIGDN": names
.configdn
,
1416 "POLICYGUID": policyguid
,
1417 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1418 "SAMBA_VERSION_STRING": version
,
1419 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH
)
1422 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1423 if fill
== FILL_FULL
:
1424 logger
.info("Adding configuration container")
1425 descr
= b64encode(get_config_descriptor(names
.domainsid
)).decode('utf8')
1426 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
1427 "CONFIGDN": names
.configdn
,
1428 "DESCRIPTOR": descr
,
1431 # The LDIF here was created when the Schema object was constructed
1432 ignore_checks_oid
= "local_oid:%s:0" % samba
.dsdb
.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1439 logger
.info("Setting up sam.ldb schema")
1440 samdb
.add_ldif(schema
.schema_dn_add
, controls
=schema_controls
)
1441 samdb
.modify_ldif(schema
.schema_dn_modify
, controls
=schema_controls
)
1442 samdb
.write_prefixes_from_schema()
1443 samdb
.add_ldif(schema
.schema_data
, controls
=schema_controls
)
1444 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
1445 {"SCHEMADN": names
.schemadn
},
1446 controls
=schema_controls
)
1448 # Now register this container in the root of the forest
1449 msg
= ldb
.Message(ldb
.Dn(samdb
, names
.domaindn
))
1450 msg
["subRefs"] = ldb
.MessageElement(names
.configdn
, ldb
.FLAG_MOD_ADD
,
1453 deletedobjects_descr
= b64encode(get_deletedobjects_descriptor(names
.domainsid
)).decode('utf8')
1455 samdb
.invocation_id
= invocationid
1457 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1458 if fill
== FILL_FULL
:
1459 logger
.info("Setting up sam.ldb configuration data")
1461 partitions_descr
= b64encode(get_config_partitions_descriptor(names
.domainsid
)).decode('utf8')
1462 sites_descr
= b64encode(get_config_sites_descriptor(names
.domainsid
)).decode('utf8')
1463 ntdsquotas_descr
= b64encode(get_config_ntds_quotas_descriptor(names
.domainsid
)).decode('utf8')
1464 protected1_descr
= b64encode(get_config_delete_protected1_descriptor(names
.domainsid
)).decode('utf8')
1465 protected1wd_descr
= b64encode(get_config_delete_protected1wd_descriptor(names
.domainsid
)).decode('utf8')
1466 protected2_descr
= b64encode(get_config_delete_protected2_descriptor(names
.domainsid
)).decode('utf8')
1468 if "2008" in schema
.base_schema
:
1469 # exclude 2012-specific changes if we're using a 2008 schema
1474 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
1475 "CONFIGDN": names
.configdn
,
1476 "NETBIOSNAME": names
.netbiosname
,
1477 "DEFAULTSITE": names
.sitename
,
1478 "DNSDOMAIN": names
.dnsdomain
,
1479 "DOMAIN": names
.domain
,
1480 "SCHEMADN": names
.schemadn
,
1481 "DOMAINDN": names
.domaindn
,
1482 "SERVERDN": names
.serverdn
,
1483 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
1484 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1485 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr
,
1486 "DELETEDOBJECTS_DESCRIPTOR": deletedobjects_descr
,
1487 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr
,
1488 "SERVICES_DESCRIPTOR": protected1_descr
,
1489 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr
,
1490 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr
,
1491 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr
,
1492 "PARTITIONS_DESCRIPTOR": partitions_descr
,
1493 "SITES_DESCRIPTOR": sites_descr
,
1496 setup_add_ldif(samdb
, setup_path("extended-rights.ldif"), {
1497 "CONFIGDN": names
.configdn
,
1498 "INC2012": incl_2012
,
1501 logger
.info("Setting up display specifiers")
1502 display_specifiers_ldif
= read_ms_ldif(
1503 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1504 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
,
1505 {"CONFIGDN": names
.configdn
})
1506 check_all_substituted(display_specifiers_ldif
)
1507 samdb
.add_ldif(display_specifiers_ldif
)
1509 logger
.info("Modifying display specifiers and extended rights")
1510 setup_modify_ldif(samdb
,
1511 setup_path("provision_configuration_modify.ldif"), {
1512 "CONFIGDN": names
.configdn
,
1513 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1516 logger
.info("Adding users container")
1517 users_desc
= b64encode(get_domain_users_descriptor(names
.domainsid
)).decode('utf8')
1518 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
1519 "DOMAINDN": names
.domaindn
,
1520 "USERS_DESCRIPTOR": users_desc
1522 logger
.info("Modifying users container")
1523 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
1524 "DOMAINDN": names
.domaindn
})
1525 logger
.info("Adding computers container")
1526 computers_desc
= b64encode(get_domain_computers_descriptor(names
.domainsid
)).decode('utf8')
1527 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
1528 "DOMAINDN": names
.domaindn
,
1529 "COMPUTERS_DESCRIPTOR": computers_desc
1531 logger
.info("Modifying computers container")
1532 setup_modify_ldif(samdb
,
1533 setup_path("provision_computers_modify.ldif"), {
1534 "DOMAINDN": names
.domaindn
})
1535 logger
.info("Setting up sam.ldb data")
1536 infrastructure_desc
= b64encode(get_domain_infrastructure_descriptor(names
.domainsid
)).decode('utf8')
1537 lostandfound_desc
= b64encode(get_domain_delete_protected2_descriptor(names
.domainsid
)).decode('utf8')
1538 system_desc
= b64encode(get_domain_delete_protected1_descriptor(names
.domainsid
)).decode('utf8')
1539 builtin_desc
= b64encode(get_domain_builtin_descriptor(names
.domainsid
)).decode('utf8')
1540 controllers_desc
= b64encode(get_domain_controllers_descriptor(names
.domainsid
)).decode('utf8')
1541 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1542 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1543 "DOMAINDN": names
.domaindn
,
1544 "NETBIOSNAME": names
.netbiosname
,
1545 "DEFAULTSITE": names
.sitename
,
1546 "CONFIGDN": names
.configdn
,
1547 "SERVERDN": names
.serverdn
,
1548 "RIDAVAILABLESTART": str(next_rid
+ 600),
1549 "POLICYGUID_DC": policyguid_dc
,
1550 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc
,
1551 "DELETEDOBJECTS_DESCRIPTOR": deletedobjects_descr
,
1552 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc
,
1553 "SYSTEM_DESCRIPTOR": system_desc
,
1554 "BUILTIN_DESCRIPTOR": builtin_desc
,
1555 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc
,
1558 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1559 if fill
== FILL_FULL
:
1560 managedservice_descr
= b64encode(get_managed_service_accounts_descriptor(names
.domainsid
)).decode('utf8')
1561 setup_modify_ldif(samdb
,
1562 setup_path("provision_configuration_references.ldif"), {
1563 "CONFIGDN": names
.configdn
,
1564 "SCHEMADN": names
.schemadn
})
1566 logger
.info("Setting up well known security principals")
1567 protected1wd_descr
= b64encode(get_config_delete_protected1wd_descriptor(names
.domainsid
)).decode('utf8')
1568 setup_add_ldif(samdb
, setup_path("provision_well_known_sec_princ.ldif"), {
1569 "CONFIGDN": names
.configdn
,
1570 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr
,
1571 }, controls
=["relax:0", "provision:0"])
1573 if fill
== FILL_FULL
or fill
== FILL_SUBDOMAIN
:
1574 setup_modify_ldif(samdb
,
1575 setup_path("provision_basedn_references.ldif"), {
1576 "DOMAINDN": names
.domaindn
,
1577 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1580 logger
.info("Setting up sam.ldb users and groups")
1581 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1582 "DOMAINDN": names
.domaindn
,
1583 "DOMAINSID": str(names
.domainsid
),
1584 "ADMINPASS_B64": b64encode(adminpass
.encode('utf-16-le')).decode('utf8'),
1585 "KRBTGTPASS_B64": b64encode(krbtgtpass
.encode('utf-16-le')).decode('utf8')
1586 }, controls
=["relax:0", "provision:0"])
1588 logger
.info("Setting up self join")
1589 setup_self_join(samdb
, admin_session_info
, names
=names
, fill
=fill
,
1590 invocationid
=invocationid
,
1591 dns_backend
=dns_backend
,
1593 machinepass
=machinepass
,
1594 domainsid
=names
.domainsid
,
1597 policyguid
=policyguid
,
1598 policyguid_dc
=policyguid_dc
,
1599 domainControllerFunctionality
=domainControllerFunctionality
,
1602 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1603 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1604 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
).decode('utf8')
1605 assert isinstance(names
.ntdsguid
, str)
1610 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;SO)(A;OICI;FA;;;SY)(A;OICI;0x1200a9;;;AU)"
1611 POLICIES_ACL
= "O:LAG:BAD:P(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;SO)(A;OICI;FA;;;SY)(A;OICI;0x1200a9;;;AU)(A;OICI;0x1301bf;;;PA)"
1612 SYSVOL_SERVICE
= "sysvol"
1615 def set_dir_acl(path
, acl
, lp
, domsid
, use_ntvfs
, passdb
, service
=SYSVOL_SERVICE
):
1616 session_info
= system_session_unix()
1617 setntacl(lp
, path
, acl
, domsid
, session_info
, use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1618 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1620 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
, session_info
,
1621 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1623 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
, session_info
,
1624 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1627 def set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
):
1628 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1631 :param sysvol: Physical path for the sysvol folder
1632 :param dnsdomain: The DNS name of the domain
1633 :param domainsid: The SID of the domain
1634 :param domaindn: The DN of the domain (ie. DC=...)
1635 :param samdb: An LDB object on the SAM db
1636 :param lp: an LP object
1639 # Set ACL for GPO root folder
1640 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1641 session_info
= system_session_unix()
1643 setntacl(lp
, root_policy_path
, POLICIES_ACL
, str(domainsid
), session_info
,
1644 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=SYSVOL_SERVICE
)
1646 res
= samdb
.search(base
="CN=Policies,CN=System,%s" %(domaindn),
1647 attrs
=["cn", "nTSecurityDescriptor"],
1648 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1651 acl
= ndr_unpack(security
.descriptor
,
1652 policy
["nTSecurityDescriptor"][0]).as_sddl()
1653 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1654 set_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1655 str(domainsid
), use_ntvfs
,
1659 def setsysvolacl(samdb
, sysvol
, uid
, gid
, domainsid
, dnsdomain
,
1660 domaindn
, lp
, use_ntvfs
):
1661 """Set the ACL for the sysvol share and the subfolders
1663 :param samdb: An LDB object on the SAM db
1664 :param sysvol: Physical path for the sysvol folder
1665 :param uid: The UID of the "Administrator" user
1666 :param gid: The GID of the "Domain administrators" group
1667 :param domainsid: The SID of the domain
1668 :param dnsdomain: The DNS name of the domain
1669 :param domaindn: The DN of the domain (ie. DC=...)
1674 s3conf
= s3param
.get_context()
1675 s3conf
.load(lp
.configfile
)
1677 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(sysvol
))
1680 smbd
.set_simple_acl(file.name
, 0o755, system_session_unix(), gid
)
1682 if not smbd
.have_posix_acls():
1683 # This clue is only strictly correct for RPM and
1684 # Debian-like Linux systems, but hopefully other users
1685 # will get enough clue from it.
1686 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1687 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1689 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1690 "Try the mounting the filesystem with the 'acl' option.")
1692 smbd
.chown(file.name
, uid
, gid
, system_session_unix())
1694 raise ProvisioningError("Unable to chown a file on your filesystem. "
1695 "You may not be running provision as root.")
1699 # This will ensure that the smbd code we are running when setting ACLs
1700 # is initialised with the smb.conf
1701 s3conf
= s3param
.get_context()
1702 s3conf
.load(lp
.configfile
)
1703 # ensure we are using the right samba_dsdb passdb backend, no matter what
1704 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1705 passdb
.reload_static_pdb()
1707 # ensure that we init the samba_dsdb backend, so the domain sid is
1708 # marked in secrets.tdb
1709 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1711 # now ensure everything matches correctly, to avoid weird issues
1712 if passdb
.get_global_sam_sid() != domainsid
:
1713 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
))
1715 domain_info
= s4_passdb
.domain_info()
1716 if domain_info
["dom_sid"] != domainsid
:
1717 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
))
1719 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1720 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()))
1724 os
.chown(sysvol
, -1, gid
)
1730 # use admin sid dn as user dn, since admin should own most of the files,
1731 # the operation will be much faster
1732 userdn
= '<SID={}-{}>'.format(domainsid
, security
.DOMAIN_RID_ADMINISTRATOR
)
1734 flags
= (auth
.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1735 auth
.AUTH_SESSION_INFO_AUTHENTICATED |
1736 auth
.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
)
1738 session_info
= auth
.user_session(samdb
, lp_ctx
=lp
, dn
=userdn
,
1739 session_info_flags
=flags
)
1740 auth
.session_info_set_unix(session_info
,
1742 user_name
="Administrator",
1746 def _setntacl(path
):
1747 """A helper to reuse args"""
1749 lp
, path
, SYSVOL_ACL
, str(domainsid
), session_info
,
1750 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=s4_passdb
,
1751 service
=SYSVOL_SERVICE
)
1753 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1755 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1757 if use_ntvfs
and canchown
:
1758 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1759 _setntacl(os
.path
.join(root
, name
))
1761 if use_ntvfs
and canchown
:
1762 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1763 _setntacl(os
.path
.join(root
, name
))
1765 # Set acls on Policy folder and policies folders
1766 set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
=s4_passdb
)
1769 def acl_type(direct_db_access
):
1770 if direct_db_access
:
1776 def check_dir_acl(path
, acl
, lp
, domainsid
, direct_db_access
):
1777 session_info
= system_session_unix()
1778 fsacl
= getntacl(lp
, path
, session_info
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1779 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1780 if fsacl_sddl
!= acl
:
1781 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
))
1783 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1785 fsacl
= getntacl(lp
, os
.path
.join(root
, name
), session_info
,
1786 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1788 raise ProvisioningError('%s ACL on GPO file %s not found!' %
1789 (acl_type(direct_db_access
),
1790 os
.path
.join(root
, name
)))
1791 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1792 if fsacl_sddl
!= acl
:
1793 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
))
1796 fsacl
= getntacl(lp
, os
.path
.join(root
, name
), session_info
,
1797 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1799 raise ProvisioningError('%s ACL on GPO directory %s not found!'
1800 % (acl_type(direct_db_access
),
1801 os
.path
.join(root
, name
)))
1802 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1803 if fsacl_sddl
!= acl
:
1804 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
))
1807 def check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1809 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1812 :param sysvol: Physical path for the sysvol folder
1813 :param dnsdomain: The DNS name of the domain
1814 :param domainsid: The SID of the domain
1815 :param domaindn: The DN of the domain (ie. DC=...)
1816 :param samdb: An LDB object on the SAM db
1817 :param lp: an LP object
1820 # Set ACL for GPO root folder
1821 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1822 session_info
= system_session_unix()
1823 fsacl
= getntacl(lp
, root_policy_path
, session_info
,
1824 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1826 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access
), root_policy_path
))
1827 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1828 if fsacl_sddl
!= POLICIES_ACL
:
1829 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
))
1830 res
= samdb
.search(base
="CN=Policies,CN=System,%s" %(domaindn),
1831 attrs
=["cn", "nTSecurityDescriptor"],
1832 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1835 acl
= ndr_unpack(security
.descriptor
,
1836 policy
["nTSecurityDescriptor"][0]).as_sddl()
1837 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1838 check_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1839 domainsid
, direct_db_access
)
1842 def checksysvolacl(samdb
, netlogon
, sysvol
, domainsid
, dnsdomain
, domaindn
,
1844 """Set the ACL for the sysvol share and the subfolders
1846 :param samdb: An LDB object on the SAM db
1847 :param netlogon: Physical path for the netlogon folder
1848 :param sysvol: Physical path for the sysvol folder
1849 :param uid: The UID of the "Administrator" user
1850 :param gid: The GID of the "Domain administrators" group
1851 :param domainsid: The SID of the domain
1852 :param dnsdomain: The DNS name of the domain
1853 :param domaindn: The DN of the domain (ie. DC=...)
1856 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1857 s3conf
= s3param
.get_context()
1858 s3conf
.load(lp
.configfile
)
1859 # ensure we are using the right samba_dsdb passdb backend, no matter what
1860 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1861 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1862 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1864 # now ensure everything matches correctly, to avoid weird issues
1865 if passdb
.get_global_sam_sid() != domainsid
:
1866 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
))
1868 domain_info
= s4_passdb
.domain_info()
1869 if domain_info
["dom_sid"] != domainsid
:
1870 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
))
1872 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1873 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()))
1875 # Ensure we can read this directly, and via the smbd VFS
1876 session_info
= system_session_unix()
1877 for direct_db_access
in [True, False]:
1878 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1879 for dir_path
in [os
.path
.join(sysvol
, dnsdomain
), netlogon
]:
1880 fsacl
= getntacl(lp
, dir_path
, session_info
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1882 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access
), dir_path
))
1883 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1884 if fsacl_sddl
!= SYSVOL_ACL
:
1885 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
))
1887 # Check acls on Policy folder and policies folders
1888 check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1892 def interface_ips_v4(lp
, all_interfaces
=False):
1893 """return only IPv4 IPs"""
1894 ips
= samba
.interface_ips(lp
, all_interfaces
)
1897 if i
.find(':') == -1:
1902 def interface_ips_v6(lp
):
1903 """return only IPv6 IPs"""
1904 ips
= samba
.interface_ips(lp
, False)
1907 if i
.find(':') != -1:
1912 def provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
1914 samdb_fill
=FILL_FULL
,
1915 hostip
=None, hostip6
=None,
1916 next_rid
=1000, dc_rid
=None, adminpass
=None, krbtgtpass
=None,
1917 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1918 invocationid
=None, machinepass
=None, ntdsguid
=None,
1919 dns_backend
=None, dnspass
=None,
1920 serverrole
=None, dom_for_fun_level
=None,
1921 lp
=None, use_ntvfs
=False,
1922 skip_sysvolacl
=False):
1923 # create/adapt the group policy GUIDs
1924 # Default GUID for default policy are described at
1925 # "How Core Group Policy Works"
1926 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1927 if policyguid
is None:
1928 policyguid
= DEFAULT_POLICY_GUID
1929 policyguid
= policyguid
.upper()
1930 if policyguid_dc
is None:
1931 policyguid_dc
= DEFAULT_DC_POLICY_GUID
1932 policyguid_dc
= policyguid_dc
.upper()
1934 if invocationid
is None:
1935 invocationid
= str(uuid
.uuid4())
1937 if krbtgtpass
is None:
1938 # Note that the machinepass value is ignored
1939 # as the backend (password_hash.c) will generate its
1940 # own random values for the krbtgt keys
1941 krbtgtpass
= samba
.generate_random_machine_password(128, 255)
1942 if machinepass
is None:
1943 machinepass
= samba
.generate_random_machine_password(120, 120)
1945 dnspass
= samba
.generate_random_password(120, 120)
1947 samdb
.transaction_start()
1949 samdb
= fill_samdb(samdb
, lp
, names
, logger
=logger
,
1951 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1952 fill
=samdb_fill
, adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1953 invocationid
=invocationid
, machinepass
=machinepass
,
1954 dns_backend
=dns_backend
, dnspass
=dnspass
,
1956 dom_for_fun_level
=dom_for_fun_level
,
1957 next_rid
=next_rid
, dc_rid
=dc_rid
)
1959 # Set up group policies (domain policy and domain controller
1961 if serverrole
== "active directory domain controller":
1962 create_default_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
,
1965 samdb
.transaction_cancel()
1968 samdb
.transaction_commit()
1970 if serverrole
== "active directory domain controller":
1971 # Continue setting up sysvol for GPO. This appears to require being
1972 # outside a transaction.
1973 if not skip_sysvolacl
:
1974 setsysvolacl(samdb
, paths
.sysvol
, paths
.root_uid
,
1975 paths
.root_gid
, names
.domainsid
, names
.dnsdomain
,
1976 names
.domaindn
, lp
, use_ntvfs
)
1978 logger
.info("Setting acl on sysvol skipped")
1980 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1981 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1982 netbiosname
=names
.netbiosname
, domainsid
=names
.domainsid
,
1983 machinepass
=machinepass
, secure_channel_type
=SEC_CHAN_BDC
)
1985 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1986 # In future, this might be determined from some configuration
1987 kerberos_enctypes
= str(ENC_ALL_TYPES
)
1990 msg
= ldb
.Message(ldb
.Dn(samdb
,
1991 samdb
.searchone("distinguishedName",
1992 expression
="samAccountName=%s$" % names
.netbiosname
,
1993 scope
=ldb
.SCOPE_SUBTREE
).decode('utf8')))
1994 msg
["msDS-SupportedEncryptionTypes"] = ldb
.MessageElement(
1995 elements
=kerberos_enctypes
, flags
=ldb
.FLAG_MOD_REPLACE
,
1996 name
="msDS-SupportedEncryptionTypes")
1998 except ldb
.LdbError
as e
:
1999 (enum
, estr
) = e
.args
2000 if enum
!= ldb
.ERR_NO_SUCH_ATTRIBUTE
:
2001 # It might be that this attribute does not exist in this schema
2004 setup_ad_dns(samdb
, secrets_ldb
, names
, paths
, logger
,
2005 hostip
=hostip
, hostip6
=hostip6
, dns_backend
=dns_backend
,
2006 dnspass
=dnspass
, os_level
=dom_for_fun_level
,
2007 fill_level
=samdb_fill
)
2009 domainguid
= samdb
.searchone(basedn
=samdb
.get_default_basedn(),
2010 attribute
="objectGUID").decode('utf8')
2011 assert isinstance(domainguid
, str)
2013 lastProvisionUSNs
= get_last_provision_usn(samdb
)
2014 maxUSN
= get_max_usn(samdb
, str(names
.rootdn
))
2015 if lastProvisionUSNs
is not None:
2016 update_provision_usn(samdb
, 0, maxUSN
, invocationid
, 1)
2018 set_provision_usn(samdb
, 0, maxUSN
, invocationid
)
2020 logger
.info("Setting up sam.ldb rootDSE marking as synchronized")
2021 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"),
2022 {'NTDSGUID': names
.ntdsguid
})
2024 # fix any dangling GUIDs from the provision
2025 logger
.info("Fixing provision GUIDs")
2026 chk
= dbcheck(samdb
, samdb_schema
=samdb
, verbose
=False, fix
=True, yes
=True,
2028 samdb
.transaction_start()
2030 # a small number of GUIDs are missing because of ordering issues in the
2032 for schema_obj
in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
2033 chk
.check_database(DN
="%s,%s" % (schema_obj
, names
.schemadn
),
2034 scope
=ldb
.SCOPE_BASE
,
2035 attrs
=['defaultObjectCategory'])
2036 chk
.check_database(DN
="CN=IP Security,CN=System,%s" % names
.domaindn
,
2037 scope
=ldb
.SCOPE_ONELEVEL
,
2038 attrs
=['ipsecOwnersReference',
2039 'ipsecFilterReference',
2040 'ipsecISAKMPReference',
2041 'ipsecNegotiationPolicyReference',
2042 'ipsecNFAReference'])
2043 if chk
.check_database(DN
=names
.schemadn
, scope
=ldb
.SCOPE_SUBTREE
,
2044 attrs
=['attributeId', 'governsId']) != 0:
2045 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
2047 samdb
.transaction_cancel()
2050 samdb
.transaction_commit()
2054 "ROLE_STANDALONE": "standalone server",
2055 "ROLE_DOMAIN_MEMBER": "member server",
2056 "ROLE_DOMAIN_BDC": "active directory domain controller",
2057 "ROLE_DOMAIN_PDC": "active directory domain controller",
2058 "dc": "active directory domain controller",
2059 "member": "member server",
2060 "domain controller": "active directory domain controller",
2061 "active directory domain controller": "active directory domain controller",
2062 "member server": "member server",
2063 "standalone": "standalone server",
2064 "standalone server": "standalone server",
2068 def sanitize_server_role(role
):
2069 """Sanitize a server role name.
2071 :param role: Server role
2072 :raise ValueError: If the role can not be interpreted
2073 :return: Sanitized server role (one of "member server",
2074 "active directory domain controller", "standalone server")
2077 return _ROLES_MAP
[role
]
2079 raise ValueError(role
)
2082 def provision_fake_ypserver(logger
, samdb
, domaindn
, netbiosname
, nisdomain
):
2083 """Create AD entries for the fake ypserver.
2085 This is needed for being able to manipulate posix attrs via ADUC.
2087 samdb
.transaction_start()
2089 logger
.info("Setting up fake yp server settings")
2090 setup_add_ldif(samdb
, setup_path("ypServ30.ldif"), {
2091 "DOMAINDN": domaindn
,
2092 "NETBIOSNAME": netbiosname
,
2093 "NISDOMAIN": nisdomain
,
2096 samdb
.transaction_cancel()
2099 samdb
.transaction_commit()
2102 def directory_create_or_exists(path
, mode
=0o755):
2103 if not os
.path
.exists(path
):
2105 os
.mkdir(path
, mode
)
2106 except OSError as e
:
2107 if e
.errno
in [errno
.EEXIST
]:
2110 raise ProvisioningError("Failed to create directory %s: %s" % (path
, e
.strerror
))
2113 def determine_host_ip(logger
, lp
, hostip
=None):
2115 logger
.info("Looking up IPv4 addresses")
2116 hostips
= interface_ips_v4(lp
)
2117 if len(hostips
) > 0:
2119 if len(hostips
) > 1:
2120 logger
.warning("More than one IPv4 address found. Using %s",
2122 if hostip
== "127.0.0.1":
2125 logger
.warning("No IPv4 address will be assigned")
2130 def determine_host_ip6(logger
, lp
, hostip6
=None):
2132 logger
.info("Looking up IPv6 addresses")
2133 hostips
= interface_ips_v6(lp
)
2135 hostip6
= hostips
[0]
2136 if len(hostips
) > 1:
2137 logger
.warning("More than one IPv6 address found. Using %s", hostip6
)
2139 logger
.warning("No IPv6 address will be assigned")
2144 def provision(logger
, session_info
, smbconf
=None,
2145 targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None, rootdn
=None,
2146 domaindn
=None, schemadn
=None, configdn
=None, serverdn
=None,
2147 domain
=None, hostname
=None, hostip
=None, hostip6
=None, domainsid
=None,
2148 next_rid
=1000, dc_rid
=None, adminpass
=None, ldapadminpass
=None,
2149 krbtgtpass
=None, domainguid
=None, policyguid
=None, policyguid_dc
=None,
2150 dns_backend
=None, dns_forwarder
=None, dnspass
=None,
2151 invocationid
=None, machinepass
=None, ntdsguid
=None,
2152 root
=None, nobody
=None, users
=None,
2153 sitename
=None, serverrole
=None, dom_for_fun_level
=None,
2154 useeadb
=False, am_rodc
=False, lp
=None, use_ntvfs
=False,
2155 use_rfc2307
=False, skip_sysvolacl
=True,
2156 base_schema
="2019", adprep_level
=DS_DOMAIN_FUNCTION_2016
,
2157 plaintext_secrets
=False, backend_store
=None,
2158 backend_store_size
=None, batch_mode
=False):
2161 :note: caution, this wipes all existing data!
2165 serverrole
= sanitize_server_role(serverrole
)
2167 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole
)
2169 if dom_for_fun_level
is None:
2170 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2008_R2
2172 if base_schema
in ["2008_R2", "2008_R2_old"]:
2173 max_adprep_level
= DS_DOMAIN_FUNCTION_2008_R2
2174 elif base_schema
in ["2012"]:
2175 max_adprep_level
= DS_DOMAIN_FUNCTION_2012
2176 elif base_schema
in ["2012_R2"]:
2177 max_adprep_level
= DS_DOMAIN_FUNCTION_2012_R2
2179 max_adprep_level
= DS_DOMAIN_FUNCTION_2016
2181 if max_adprep_level
< dom_for_fun_level
:
2182 raise ProvisioningError('dom_for_fun_level[%u] incompatible with base_schema[%s]' %
2183 (dom_for_fun_level
, base_schema
))
2185 if adprep_level
is not None and max_adprep_level
< adprep_level
:
2186 raise ProvisioningError('base_schema[%s] incompatible with adprep_level[%u]' %
2187 (base_schema
, adprep_level
))
2189 if adprep_level
is not None and adprep_level
< dom_for_fun_level
:
2190 raise ProvisioningError('dom_for_fun_level[%u] incompatible with adprep_level[%u]' %
2191 (dom_for_fun_level
, adprep_level
))
2193 if ldapadminpass
is None:
2194 # Make a new, random password between Samba and it's LDAP server
2195 ldapadminpass
= samba
.generate_random_password(128, 255)
2197 if backend_store
is None:
2198 backend_store
= get_default_backend_store()
2200 if domainsid
is None:
2201 domainsid
= security
.random_sid()
2203 root_uid
= get_root_uid([root
or "root"], logger
)
2204 nobody_uid
= findnss_uid([nobody
or "nobody"])
2205 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
2206 root_gid
= pwd
.getpwuid(root_uid
).pw_gid
2209 bind_gid
= findnss_gid(["bind", "named"])
2213 if targetdir
is not None:
2214 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
2215 elif smbconf
is None:
2216 smbconf
= samba
.param
.default_path()
2217 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
2218 os
.makedirs(os
.path
.dirname(smbconf
))
2220 server_services
= []
2223 global_param
["idmap_ldb:use rfc2307"] = ["yes"]
2225 if dns_backend
!= "SAMBA_INTERNAL":
2226 server_services
.append("-dns")
2228 if dns_forwarder
is not None:
2229 global_param
["dns forwarder"] = [dns_forwarder
]
2232 server_services
.append("+smb")
2233 server_services
.append("-s3fs")
2234 global_param
["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2236 if len(server_services
) > 0:
2237 global_param
["server services"] = server_services
2239 # only install a new smb.conf if there isn't one there already
2240 if os
.path
.exists(smbconf
):
2241 # if Samba Team members can't figure out the weird errors
2242 # loading an empty smb.conf gives, then we need to be smarter.
2243 # Pretend it just didn't exist --abartlet
2244 f
= open(smbconf
, 'r')
2246 data
= f
.read().lstrip()
2249 if data
is None or data
== "":
2250 make_smbconf(smbconf
, hostname
, domain
, realm
,
2251 targetdir
, serverrole
=serverrole
,
2252 eadb
=useeadb
, use_ntvfs
=use_ntvfs
,
2253 lp
=lp
, global_param
=global_param
)
2255 make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
2256 serverrole
=serverrole
,
2257 eadb
=useeadb
, use_ntvfs
=use_ntvfs
, lp
=lp
, global_param
=global_param
)
2260 lp
= samba
.param
.LoadParm()
2262 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
2263 dnsdomain
=realm
, serverrole
=serverrole
, domaindn
=domaindn
,
2264 configdn
=configdn
, schemadn
=schemadn
, serverdn
=serverdn
,
2265 sitename
=sitename
, rootdn
=rootdn
, domain_names_forced
=(samdb_fill
== FILL_DRS
))
2266 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
2268 paths
.bind_gid
= bind_gid
2269 paths
.root_uid
= root_uid
2270 paths
.root_gid
= root_gid
2272 hostip
= determine_host_ip(logger
, lp
, hostip
)
2273 hostip6
= determine_host_ip6(logger
, lp
, hostip6
)
2274 names
.hostip
= hostip
2275 names
.hostip6
= hostip6
2276 names
.domainguid
= domainguid
2277 names
.domainsid
= domainsid
2278 names
.forestsid
= domainsid
2280 if serverrole
is None:
2281 serverrole
= lp
.get("server role")
2283 directory_create_or_exists(paths
.private_dir
, 0o700)
2284 directory_create_or_exists(paths
.binddns_dir
, 0o770)
2285 directory_create_or_exists(os
.path
.join(paths
.private_dir
, "tls"))
2286 directory_create_or_exists(paths
.state_dir
)
2287 if not plaintext_secrets
:
2288 setup_encrypted_secrets_key(paths
.encrypted_secrets_key_path
)
2290 if paths
.sysvol
and not os
.path
.exists(paths
.sysvol
):
2291 os
.makedirs(paths
.sysvol
, 0o775)
2293 schema
= Schema(domainsid
, invocationid
=invocationid
,
2294 schemadn
=names
.schemadn
, base_schema
=base_schema
)
2296 provision_backend
= LDBBackend(paths
=paths
,
2298 names
=names
, logger
=logger
)
2300 provision_backend
.init()
2301 provision_backend
.start()
2303 # only install a new shares config db if there is none
2304 if not os
.path
.exists(paths
.shareconf
):
2305 logger
.info("Setting up share.ldb")
2306 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
, lp
=lp
)
2307 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
2309 logger
.info("Setting up secrets.ldb")
2310 secrets_ldb
= setup_secretsdb(paths
,
2311 session_info
=session_info
, lp
=lp
)
2314 logger
.info("Setting up the registry")
2315 setup_registry(paths
.hklm
, session_info
, lp
=lp
)
2317 logger
.info("Setting up the privileges database")
2318 setup_privileges(paths
.privilege
, session_info
, lp
=lp
)
2320 logger
.info("Setting up idmap db")
2321 idmap
= setup_idmapdb(paths
.idmapdb
, session_info
=session_info
, lp
=lp
)
2323 setup_name_mappings(idmap
, sid
=str(domainsid
),
2324 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
2325 users_gid
=users_gid
)
2327 logger
.info("Setting up SAM db")
2328 samdb
= setup_samdb(paths
.samdb
, session_info
,
2329 provision_backend
, lp
, names
, logger
=logger
,
2330 serverrole
=serverrole
,
2331 schema
=schema
, am_rodc
=am_rodc
,
2332 plaintext_secrets
=plaintext_secrets
,
2333 backend_store
=backend_store
,
2334 backend_store_size
=backend_store_size
,
2335 batch_mode
=batch_mode
)
2337 if serverrole
== "active directory domain controller":
2338 if paths
.netlogon
is None:
2339 raise MissingShareError("netlogon", paths
.smbconf
)
2341 if paths
.sysvol
is None:
2342 raise MissingShareError("sysvol", paths
.smbconf
)
2344 if not os
.path
.isdir(paths
.netlogon
):
2345 os
.makedirs(paths
.netlogon
, 0o755)
2347 if adminpass
is None:
2348 adminpass
= samba
.generate_random_password(12, 32)
2349 adminpass_generated
= True
2351 if isinstance(adminpass
, bytes
):
2352 adminpass
= adminpass
.decode('utf-8')
2353 adminpass_generated
= False
2355 if samdb_fill
== FILL_FULL
:
2356 provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
2357 schema
=schema
, samdb_fill
=samdb_fill
,
2358 hostip
=hostip
, hostip6
=hostip6
,
2359 next_rid
=next_rid
, dc_rid
=dc_rid
, adminpass
=adminpass
,
2360 krbtgtpass
=krbtgtpass
,
2361 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
2362 invocationid
=invocationid
, machinepass
=machinepass
,
2363 ntdsguid
=ntdsguid
, dns_backend
=dns_backend
,
2364 dnspass
=dnspass
, serverrole
=serverrole
,
2365 dom_for_fun_level
=dom_for_fun_level
,
2366 lp
=lp
, use_ntvfs
=use_ntvfs
,
2367 skip_sysvolacl
=skip_sysvolacl
)
2369 if adprep_level
is not None:
2370 updates_allowed_overridden
= False
2371 if lp
.get("dsdb:schema update allowed") is None:
2372 lp
.set("dsdb:schema update allowed", "yes")
2373 print("Temporarily overriding 'dsdb:schema update allowed' setting")
2374 updates_allowed_overridden
= True
2376 samdb
.transaction_start()
2378 from samba
.forest_update
import ForestUpdate
2379 forest
= ForestUpdate(samdb
, fix
=True)
2381 forest
.check_updates_iterator([11, 54, 79, 80, 81, 82, 83])
2382 forest
.check_updates_functional_level(adprep_level
,
2383 DS_DOMAIN_FUNCTION_2008_R2
,
2384 update_revision
=True)
2386 samdb
.transaction_commit()
2387 except Exception as e
:
2388 samdb
.transaction_cancel()
2391 samdb
.transaction_start()
2393 from samba
.domain_update
import DomainUpdate
2397 fix
=True).check_updates_functional_level(
2399 DS_DOMAIN_FUNCTION_2008
,
2400 update_revision
=True,
2403 samdb
.transaction_commit()
2404 except Exception as e
:
2405 samdb
.transaction_cancel()
2408 if updates_allowed_overridden
:
2409 lp
.set("dsdb:schema update allowed", "no")
2411 current_time
= nt_now()
2412 # We want the GKDI key to be instantly available for use
2413 use_start_time
= current_time \
2414 - KEY_CYCLE_DURATION
- MAX_CLOCK_SKEW
2415 gkdi_root_key_dn
= samdb
.new_gkdi_root_key(current_time
=current_time
,
2416 use_start_time
=use_start_time
)
2417 logger
.info("gkdi/gmsa root key added with guid "
2418 f
"{gkdi_root_key_dn.get_rdn_value()}")
2420 if not is_heimdal_built():
2421 create_kdc_conf(paths
.kdcconf
, realm
, domain
, os
.path
.dirname(lp
.get("log file")))
2422 logger
.info("The Kerberos KDC configuration for Samba AD is "
2423 "located at %s", paths
.kdcconf
)
2425 create_krb5_conf(paths
.krb5conf
,
2426 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
2428 logger
.info("A Kerberos configuration suitable for Samba AD has been "
2429 "generated at %s", paths
.krb5conf
)
2430 logger
.info("Merge the contents of this file with your system "
2431 "krb5.conf or replace it with this one. Do not create a "
2434 if serverrole
== "active directory domain controller":
2435 create_dns_update_list(paths
)
2437 backend_result
= provision_backend
.post_setup()
2438 provision_backend
.shutdown()
2441 secrets_ldb
.transaction_cancel()
2444 # Now commit the secrets.ldb to disk
2445 secrets_ldb
.transaction_commit()
2447 # the commit creates the dns.keytab in the private directory
2448 create_dns_dir_keytab_link(logger
, paths
)
2450 result
= ProvisionResult()
2451 result
.server_role
= serverrole
2452 result
.domaindn
= domaindn
2453 result
.paths
= paths
2454 result
.names
= names
2456 result
.samdb
= samdb
2457 result
.idmap
= idmap
2458 result
.domainsid
= str(domainsid
)
2460 if samdb_fill
== FILL_FULL
:
2461 result
.adminpass_generated
= adminpass_generated
2462 result
.adminpass
= adminpass
2464 result
.adminpass_generated
= False
2465 result
.adminpass
= None
2467 result
.backend_result
= backend_result
2470 provision_fake_ypserver(logger
=logger
, samdb
=samdb
,
2471 domaindn
=names
.domaindn
, netbiosname
=names
.netbiosname
,
2472 nisdomain
=names
.domain
.lower())
2477 def provision_become_dc(smbconf
=None, targetdir
=None, realm
=None,
2478 rootdn
=None, domaindn
=None, schemadn
=None,
2479 configdn
=None, serverdn
=None, domain
=None,
2480 hostname
=None, domainsid
=None,
2481 machinepass
=None, dnspass
=None,
2482 dns_backend
=None, sitename
=None, debuglevel
=1,
2485 logger
= logging
.getLogger("provision")
2486 samba
.set_debug_level(debuglevel
)
2488 res
= provision(logger
, system_session(),
2489 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
2490 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
2491 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
2492 hostname
=hostname
, hostip
=None, domainsid
=domainsid
,
2493 machinepass
=machinepass
,
2494 serverrole
="active directory domain controller",
2495 sitename
=sitename
, dns_backend
=dns_backend
, dnspass
=dnspass
,
2496 use_ntvfs
=use_ntvfs
)
2497 res
.lp
.set("debuglevel", str(debuglevel
))
2501 def create_krb5_conf(path
, dnsdomain
, hostname
, realm
):
2502 """Write out a file containing a valid krb5.conf file
2504 :param path: Path of the new krb5.conf file.
2505 :param dnsdomain: DNS Domain name
2506 :param hostname: Local hostname
2507 :param realm: Realm name
2509 setup_file(setup_path("krb5.conf"), path
, {
2510 "DNSDOMAIN": dnsdomain
,
2511 "HOSTNAME": hostname
,
2516 class ProvisioningError(Exception):
2517 """A generic provision error."""
2519 def __init__(self
, value
):
2523 return "ProvisioningError: " + self
.value
2526 class InvalidNetbiosName(Exception):
2527 """A specified name was not a valid NetBIOS name."""
2529 def __init__(self
, name
):
2531 "The name '%r' is not a valid NetBIOS name" % name
)
2534 class MissingShareError(ProvisioningError
):
2536 def __init__(self
, name
, smbconf
):
2538 "Existing smb.conf does not have a [%s] share, but you are "
2539 "configuring a DC. Please remove %s or add the share manually." %