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
.idmap
import IDmapDB
77 from samba
.ms_display_specifiers
import read_ms_ldif
78 from samba
.ntacls
import setntacl
, getntacl
, dsacl2fsacl
79 from samba
.ndr
import ndr_pack
, ndr_unpack
80 from samba
.provision
.backend
import (
83 from samba
.descriptor
import (
84 get_deletedobjects_descriptor
,
85 get_config_descriptor
,
86 get_config_partitions_descriptor
,
87 get_config_sites_descriptor
,
88 get_config_ntds_quotas_descriptor
,
89 get_config_delete_protected1_descriptor
,
90 get_config_delete_protected1wd_descriptor
,
91 get_config_delete_protected2_descriptor
,
92 get_domain_descriptor
,
93 get_domain_infrastructure_descriptor
,
94 get_domain_builtin_descriptor
,
95 get_domain_computers_descriptor
,
96 get_domain_users_descriptor
,
97 get_domain_controllers_descriptor
,
98 get_domain_delete_protected1_descriptor
,
99 get_domain_delete_protected2_descriptor
,
100 get_managed_service_accounts_descriptor
,
102 from samba
.provision
.common
import (
110 from samba
.provision
.sambadns
import (
113 create_dns_dir_keytab_link
,
114 create_dns_update_list
118 import samba
.registry
119 from samba
.schema
import Schema
120 from samba
.samdb
import SamDB
121 from samba
.dbchecker
import dbcheck
122 from samba
.provision
.kerberos
import create_kdc_conf
123 from samba
.samdb
import get_default_backend_store
124 from samba
import functional_level
126 DEFAULT_POLICY_GUID
= "31B2F340-016D-11D2-945F-00C04FB984F9"
127 DEFAULT_DC_POLICY_GUID
= "6AC1786C-016F-11D2-945F-00C04FB984F9"
128 DEFAULTSITE
= "Default-First-Site-Name"
129 LAST_PROVISION_USN_ATTRIBUTE
= "lastProvisionUSN"
131 DEFAULT_MIN_PWD_LENGTH
= 7
134 class ProvisionPaths(object):
137 self
.shareconf
= None
148 self
.dns_keytab
= None
151 self
.private_dir
= None
152 self
.binddns_dir
= None
153 self
.state_dir
= None
156 class ProvisionNames(object):
164 self
.dnsforestdn
= None
165 self
.dnsdomaindn
= None
166 self
.ldapmanagerdn
= None
167 self
.dnsdomain
= None
169 self
.netbiosname
= None
174 self
.domainsid
= None
175 self
.forestsid
= None
176 self
.domainguid
= None
180 def find_provision_key_parameters(samdb
, secretsdb
, idmapdb
, paths
, smbconf
,
182 """Get key provision parameters (realm, domain, ...) from a given provision
184 :param samdb: An LDB object connected to the sam.ldb file
185 :param secretsdb: An LDB object connected to the secrets.ldb file
186 :param idmapdb: An LDB object connected to the idmap.ldb file
187 :param paths: A list of path to provision object
188 :param smbconf: Path to the smb.conf file
189 :param lp: A LoadParm object
190 :return: A list of key provision parameters
192 names
= ProvisionNames()
193 names
.adminpass
= None
195 # NT domain, kerberos realm, root dn, domain dn, domain dns name
196 names
.domain
= lp
.get("workgroup").upper()
197 names
.realm
= lp
.get("realm")
198 names
.dnsdomain
= names
.realm
.lower()
199 basedn
= samba
.dn_from_dns_name(names
.dnsdomain
)
200 names
.realm
= names
.realm
.upper()
202 # Get the netbiosname first (could be obtained from smb.conf in theory)
203 res
= secretsdb
.search(expression
="(flatname=%s)" %
204 names
.domain
, base
="CN=Primary Domains",
205 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
206 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$", "")
208 names
.smbconf
= smbconf
210 # That's a bit simplistic but it's ok as long as we have only 3
212 current
= samdb
.search(expression
="(objectClass=*)",
213 base
="", scope
=ldb
.SCOPE_BASE
,
214 attrs
=["defaultNamingContext", "schemaNamingContext",
215 "configurationNamingContext", "rootDomainNamingContext",
218 names
.configdn
= str(current
[0]["configurationNamingContext"][0])
219 names
.schemadn
= str(current
[0]["schemaNamingContext"][0])
220 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
,
221 current
[0]["defaultNamingContext"][0].decode('utf8')))):
222 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
223 "is not the same ..." % (paths
.samdb
,
224 str(current
[0]["defaultNamingContext"][0].decode('utf8')),
225 paths
.smbconf
, basedn
)))
227 names
.domaindn
= str(current
[0]["defaultNamingContext"][0])
228 names
.rootdn
= str(current
[0]["rootDomainNamingContext"][0])
229 names
.ncs
= current
[0]["namingContexts"]
230 names
.dnsforestdn
= None
231 names
.dnsdomaindn
= None
233 for i
in range(0, len(names
.ncs
)):
234 nc
= str(names
.ncs
[i
])
236 dnsforestdn
= "DC=ForestDnsZones,%s" % (str(names
.rootdn
))
237 if nc
== dnsforestdn
:
238 names
.dnsforestdn
= dnsforestdn
241 dnsdomaindn
= "DC=DomainDnsZones,%s" % (str(names
.domaindn
))
242 if nc
== dnsdomaindn
:
243 names
.dnsdomaindn
= dnsdomaindn
247 res3
= samdb
.search(expression
="(objectClass=site)",
248 base
="CN=Sites," + str(names
.configdn
), scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn"])
249 names
.sitename
= str(res3
[0]["cn"])
251 # dns hostname and server dn
252 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
253 base
="OU=Domain Controllers,%s" % basedn
,
254 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
256 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names
.netbiosname
, basedn
))
258 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("." + names
.dnsdomain
, "")
260 server_res
= samdb
.search(expression
="serverReference=%s" % res4
[0].dn
,
261 attrs
=[], base
=names
.configdn
)
262 names
.serverdn
= str(server_res
[0].dn
)
264 # invocation id/objectguid
265 res5
= samdb
.search(expression
="(objectClass=*)",
266 base
="CN=NTDS Settings,%s" % str(names
.serverdn
),
267 scope
=ldb
.SCOPE_BASE
,
268 attrs
=["invocationID", "objectGUID"])
269 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
270 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
273 res6
= samdb
.search(expression
="(objectClass=*)", base
=basedn
,
274 scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID",
275 "objectSid", "msDS-Behavior-Version"])
276 names
.domainguid
= str(ndr_unpack(misc
.GUID
, res6
[0]["objectGUID"][0]))
277 names
.domainsid
= ndr_unpack(security
.dom_sid
, res6
[0]["objectSid"][0])
278 names
.forestsid
= ndr_unpack(security
.dom_sid
, res6
[0]["objectSid"][0])
279 if res6
[0].get("msDS-Behavior-Version") is None or \
280 int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
281 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
283 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
286 res7
= samdb
.search(expression
="(name={%s})" % DEFAULT_POLICY_GUID
,
287 base
="CN=Policies,CN=System," + basedn
,
288 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn", "displayName"])
289 names
.policyid
= str(res7
[0]["cn"]).replace("{", "").replace("}", "")
291 res8
= samdb
.search(expression
="(name={%s})" % DEFAULT_DC_POLICY_GUID
,
292 base
="CN=Policies,CN=System," + basedn
,
293 scope
=ldb
.SCOPE_ONELEVEL
,
294 attrs
=["cn", "displayName"])
296 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{", "").replace("}", "")
298 names
.policyid_dc
= None
300 res9
= idmapdb
.search(expression
="(cn=%s-%s)" %
301 (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
),
302 attrs
=["xidNumber", "type"])
304 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
))
305 if str(res9
[0]["type"][0]) == "ID_TYPE_BOTH":
306 names
.root_gid
= int(res9
[0]["xidNumber"][0])
308 names
.root_gid
= pwd
.getpwuid(int(res9
[0]["xidNumber"][0])).pw_gid
310 res10
= samdb
.search(expression
="(samaccountname=dns)",
311 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["dn"],
312 controls
=["search_options:1:2"])
314 has_legacy_dns_account
= True
316 has_legacy_dns_account
= False
318 res11
= samdb
.search(expression
="(samaccountname=dns-%s)" % names
.netbiosname
,
319 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["dn"],
320 controls
=["search_options:1:2"])
322 has_dns_account
= True
324 has_dns_account
= False
326 if names
.dnsdomaindn
is not None:
328 names
.dns_backend
= 'BIND9_DLZ'
330 names
.dns_backend
= 'SAMBA_INTERNAL'
331 elif has_dns_account
or has_legacy_dns_account
:
332 names
.dns_backend
= 'BIND9_FLATFILE'
334 names
.dns_backend
= 'NONE'
336 dns_admins_sid
= get_dnsadmins_sid(samdb
, names
.domaindn
)
337 names
.name_map
['DnsAdmins'] = str(dns_admins_sid
)
342 def update_provision_usn(samdb
, low
, high
, id, replace
=False):
343 """Update the field provisionUSN in sam.ldb
345 This field is used to track range of USN modified by provision and
347 This value is used afterward by next provision to figure out if
348 the field have been modified since last provision.
350 :param samdb: An LDB object connect to sam.ldb
351 :param low: The lowest USN modified by this upgrade
352 :param high: The highest USN modified by this upgrade
353 :param id: The invocation id of the samba's dc
354 :param replace: A boolean indicating if the range should replace any
355 existing one or appended (default)
360 entry
= samdb
.search(base
="@PROVISION",
361 scope
=ldb
.SCOPE_BASE
,
362 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "dn"])
363 for e
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
364 if not re
.search(';', str(e
)):
365 e
= "%s;%s" % (str(e
), id)
368 tab
.append("%s-%s;%s" % (low
, high
, id))
369 delta
= ldb
.Message()
370 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
371 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = \
372 ldb
.MessageElement(tab
,
373 ldb
.FLAG_MOD_REPLACE
,
374 LAST_PROVISION_USN_ATTRIBUTE
)
375 entry
= samdb
.search(expression
='provisionnerID=*',
376 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
377 attrs
=["provisionnerID"])
378 if len(entry
) == 0 or len(entry
[0]) == 0:
379 delta
["provisionnerID"] = ldb
.MessageElement(id, ldb
.FLAG_MOD_ADD
, "provisionnerID")
383 def set_provision_usn(samdb
, low
, high
, id):
384 """Set the field provisionUSN in sam.ldb
385 This field is used to track range of USN modified by provision and
387 This value is used afterward by next provision to figure out if
388 the field have been modified since last provision.
390 :param samdb: An LDB object connect to sam.ldb
391 :param low: The lowest USN modified by this upgrade
392 :param high: The highest USN modified by this upgrade
393 :param id: The invocationId of the provision"""
396 tab
.append("%s-%s;%s" % (low
, high
, id))
398 delta
= ldb
.Message()
399 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
400 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = \
401 ldb
.MessageElement(tab
,
403 LAST_PROVISION_USN_ATTRIBUTE
)
407 def get_max_usn(samdb
, basedn
):
408 """ This function return the biggest USN present in the provision
410 :param samdb: A LDB object pointing to the sam.ldb
411 :param basedn: A string containing the base DN of the provision
413 :return: The biggest USN in the provision"""
415 res
= samdb
.search(expression
="objectClass=*", base
=basedn
,
416 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["uSNChanged"],
417 controls
=["search_options:1:2",
418 "server_sort:1:1:uSNChanged",
419 "paged_results:1:1"])
420 return res
[0]["uSNChanged"]
423 def get_last_provision_usn(sam
):
424 """Get USNs ranges modified by a provision or an upgradeprovision
426 :param sam: An LDB object pointing to the sam.ldb
427 :return: a dictionary which keys are invocation id and values are an array
428 of integer representing the different ranges
431 entry
= sam
.search(expression
="%s=*" % LAST_PROVISION_USN_ATTRIBUTE
,
432 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
433 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "provisionnerID"])
434 except ldb
.LdbError
as e1
:
435 (ecode
, emsg
) = e1
.args
436 if ecode
== ldb
.ERR_NO_SUCH_OBJECT
:
443 if entry
[0].get("provisionnerID"):
444 for e
in entry
[0]["provisionnerID"]:
446 for r
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
447 tab1
= str(r
).split(';')
452 if (len(myids
) > 0 and id not in myids
):
454 tab2
= p
.split(tab1
[0])
455 if range.get(id) is None:
457 range[id].append(tab2
[0])
458 range[id].append(tab2
[1])
464 class ProvisionResult(object):
465 """Result of a provision.
467 :ivar server_role: The server role
468 :ivar paths: ProvisionPaths instance
469 :ivar domaindn: The domain dn, as string
473 self
.server_role
= None
480 self
.domainsid
= None
481 self
.adminpass_generated
= None
482 self
.adminpass
= None
483 self
.backend_result
= None
485 def report_logger(self
, logger
):
486 """Report this provision result to a logger."""
488 "Once the above files are installed, your Samba AD server will "
490 if self
.adminpass_generated
:
491 logger
.info("Admin password: %s", self
.adminpass
)
492 logger
.info("Server Role: %s", self
.server_role
)
493 logger
.info("Hostname: %s", self
.names
.hostname
)
494 logger
.info("NetBIOS Domain: %s", self
.names
.domain
)
495 logger
.info("DNS Domain: %s", self
.names
.dnsdomain
)
496 logger
.info("DOMAIN SID: %s", self
.domainsid
)
498 if self
.backend_result
:
499 self
.backend_result
.report_logger(logger
)
502 def findnss(nssfn
, names
):
503 """Find a user or group from a list of possibilities.
505 :param nssfn: NSS Function to try (should raise KeyError if not found)
506 :param names: Names to check.
507 :return: Value return by first names list.
514 raise KeyError("Unable to find user/group in %r" % names
)
517 def findnss_uid(names
):
518 return findnss(pwd
.getpwnam
, names
)[2]
521 def findnss_gid(names
):
522 return findnss(grp
.getgrnam
, names
)[2]
525 def get_root_uid(root
, logger
):
527 root_uid
= findnss_uid(root
)
528 except KeyError as e
:
530 logger
.info("Assuming root user has UID zero")
535 def provision_paths_from_lp(lp
, dnsdomain
):
536 """Set the default paths for provisioning.
538 :param lp: Loadparm context.
539 :param dnsdomain: DNS Domain name
541 paths
= ProvisionPaths()
542 paths
.private_dir
= lp
.get("private dir")
543 paths
.binddns_dir
= lp
.get("binddns dir")
544 paths
.state_dir
= lp
.get("state directory")
546 # This is stored without path prefix for the "privateKeytab" attribute in
547 # "secrets_dns.ldif".
548 paths
.dns_keytab
= "dns.keytab"
549 paths
.keytab
= "secrets.keytab"
551 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
552 paths
.samdb
= os
.path
.join(paths
.private_dir
, "sam.ldb")
553 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, "idmap.ldb")
554 paths
.secrets
= os
.path
.join(paths
.private_dir
, "secrets.ldb")
555 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
556 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
557 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
558 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
559 paths
.kdcconf
= os
.path
.join(paths
.private_dir
, "kdc.conf")
560 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
561 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
562 paths
.encrypted_secrets_key_path
= os
.path
.join(
564 "encrypted_secrets.key")
566 paths
.dns
= os
.path
.join(paths
.binddns_dir
, "dns", dnsdomain
+ ".zone")
567 paths
.namedconf
= os
.path
.join(paths
.binddns_dir
, "named.conf")
568 paths
.namedconf_update
= os
.path
.join(paths
.binddns_dir
, "named.conf.update")
569 paths
.namedtxt
= os
.path
.join(paths
.binddns_dir
, "named.txt")
571 paths
.hklm
= "hklm.ldb"
572 paths
.hkcr
= "hkcr.ldb"
573 paths
.hkcu
= "hkcu.ldb"
574 paths
.hku
= "hku.ldb"
575 paths
.hkpd
= "hkpd.ldb"
576 paths
.hkpt
= "hkpt.ldb"
577 paths
.sysvol
= lp
.get("path", "sysvol")
578 paths
.netlogon
= lp
.get("path", "netlogon")
579 paths
.smbconf
= lp
.configfile
583 def determine_netbios_name(hostname
):
584 """Determine a netbios name from a hostname."""
585 # remove forbidden chars and force the length to be <16
586 netbiosname
= "".join([x
for x
in hostname
if is_valid_netbios_char(x
)])
587 return netbiosname
[:MAX_NETBIOS_NAME_LEN
].upper()
590 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
591 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
592 schemadn
=None, serverdn
=None, sitename
=None,
593 domain_names_forced
=False):
594 """Guess configuration settings to use."""
597 hostname
= socket
.gethostname().split(".")[0]
599 netbiosname
= lp
.get("netbios name")
600 if netbiosname
is None:
601 netbiosname
= determine_netbios_name(hostname
)
602 netbiosname
= netbiosname
.upper()
603 if not valid_netbios_name(netbiosname
):
604 raise InvalidNetbiosName(netbiosname
)
606 if dnsdomain
is None:
607 dnsdomain
= lp
.get("realm")
608 if dnsdomain
is None or dnsdomain
== "":
609 raise ProvisioningError(
610 "guess_names: 'realm' not specified in supplied %s!" %
613 dnsdomain
= dnsdomain
.lower()
615 if serverrole
is None:
616 serverrole
= lp
.get("server role")
617 if serverrole
is None:
618 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
620 serverrole
= serverrole
.lower()
622 realm
= dnsdomain
.upper()
624 if lp
.get("realm") == "":
625 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
627 if lp
.get("realm").upper() != realm
:
628 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
))
630 if lp
.get("server role").lower() != serverrole
:
631 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
))
633 if serverrole
== "active directory domain controller":
635 # This will, for better or worse, default to 'WORKGROUP'
636 domain
= lp
.get("workgroup")
637 domain
= domain
.upper()
639 if lp
.get("workgroup").upper() != domain
:
640 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
))
643 domaindn
= samba
.dn_from_dns_name(dnsdomain
)
645 if domain
== netbiosname
:
646 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain
, netbiosname
))
650 domaindn
= "DC=" + netbiosname
652 if not valid_netbios_name(domain
):
653 raise InvalidNetbiosName(domain
)
655 if hostname
.upper() == realm
:
656 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
657 if netbiosname
.upper() == realm
:
658 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm
, netbiosname
))
659 if domain
== realm
and not domain_names_forced
:
660 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
662 if serverrole
!= "active directory domain controller":
664 # This is the code path for a domain member
665 # where we provision the database as if we were
666 # on a domain controller, so we should not use
667 # the same dnsdomain as the domain controllers
668 # of our primary domain.
670 # This will be important if we start doing
671 # SID/name filtering and reject the local
672 # sid and names if they come from a domain
676 dnsdomain
= netbiosname
.lower()
682 configdn
= "CN=Configuration," + rootdn
684 schemadn
= "CN=Schema," + configdn
687 sitename
= DEFAULTSITE
690 serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
691 netbiosname
, sitename
, configdn
)
693 names
= ProvisionNames()
694 names
.rootdn
= rootdn
695 names
.domaindn
= domaindn
696 names
.configdn
= configdn
697 names
.schemadn
= schemadn
698 names
.ldapmanagerdn
= "CN=Manager," + rootdn
699 names
.dnsdomain
= dnsdomain
700 names
.domain
= domain
702 names
.netbiosname
= netbiosname
703 names
.hostname
= hostname
704 names
.sitename
= sitename
705 names
.serverdn
= serverdn
710 def make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
711 serverrole
=None, eadb
=False, use_ntvfs
=False, lp
=None,
713 """Create a new smb.conf file based on a couple of basic settings.
715 assert smbconf
is not None
718 hostname
= socket
.gethostname().split(".")[0]
720 netbiosname
= determine_netbios_name(hostname
)
722 if serverrole
is None:
723 serverrole
= "standalone server"
725 assert domain
is not None
726 domain
= domain
.upper()
728 assert realm
is not None
729 realm
= realm
.upper()
732 "netbios name": netbiosname
,
735 "server role": serverrole
,
739 lp
= samba
.param
.LoadParm()
740 # Load non-existent file
741 if os
.path
.exists(smbconf
):
744 if global_param
is not None:
745 for ent
in global_param
:
746 if global_param
[ent
] is not None:
747 global_settings
[ent
] = " ".join(global_param
[ent
])
749 if targetdir
is not None:
750 global_settings
["private dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "private"))
751 global_settings
["lock dir"] = os
.path
.abspath(targetdir
)
752 global_settings
["state directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "state"))
753 global_settings
["cache directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "cache"))
754 global_settings
["binddns dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "bind-dns"))
756 lp
.set("lock dir", os
.path
.abspath(targetdir
))
757 lp
.set("state directory", global_settings
["state directory"])
758 lp
.set("cache directory", global_settings
["cache directory"])
759 lp
.set("binddns dir", global_settings
["binddns dir"])
763 if targetdir
is not None:
764 privdir
= os
.path
.join(targetdir
, "private")
766 os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
767 elif not lp
.get("posix:eadb"):
768 privdir
= lp
.get("private dir")
770 os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
772 if targetdir
is not None:
773 statedir
= os
.path
.join(targetdir
, "state")
774 lp
.set("xattr_tdb:file",
775 os
.path
.abspath(os
.path
.join(statedir
, "xattr.tdb")))
776 elif not lp
.get("xattr_tdb:file"):
777 statedir
= lp
.get("state directory")
778 lp
.set("xattr_tdb:file",
779 os
.path
.abspath(os
.path
.join(statedir
, "xattr.tdb")))
782 if serverrole
== "active directory domain controller":
783 shares
["sysvol"] = os
.path
.join(lp
.get("state directory"), "sysvol")
784 shares
["netlogon"] = os
.path
.join(shares
["sysvol"], realm
.lower(),
787 global_settings
["passdb backend"] = "samba_dsdb"
789 f
= open(smbconf
, 'w')
791 f
.write("[globals]\n")
792 for key
, val
in global_settings
.items():
793 f
.write("\t%s = %s\n" % (key
, val
))
796 for name
, path
in shares
.items():
797 f
.write("[%s]\n" % name
)
798 f
.write("\tpath = %s\n" % path
)
799 f
.write("\tread only = no\n")
803 # reload the smb.conf
806 # and dump it without any values that are the default
807 # this ensures that any smb.conf parameters that were set
808 # on the provision/join command line are set in the resulting smb.conf
809 lp
.dump(False, smbconf
)
812 def setup_name_mappings(idmap
, sid
, root_uid
, nobody_uid
,
814 """setup reasonable name mappings for sam names to unix names.
816 :param samdb: SamDB object.
817 :param idmap: IDmap db object.
818 :param sid: The domain sid.
819 :param domaindn: The domain DN.
820 :param root_uid: uid of the UNIX root user.
821 :param nobody_uid: uid of the UNIX nobody user.
822 :param users_gid: gid of the UNIX users group.
824 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
826 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
827 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
830 def setup_samdb_partitions(samdb_path
, logger
, lp
, session_info
,
831 provision_backend
, names
, serverrole
,
832 plaintext_secrets
=False,
834 """Setup the partitions for the SAM database.
836 Alternatively, provision() may call this, and then populate the database.
838 :note: This will wipe the Sam Database!
840 :note: This function always removes the local SAM LDB file. The erase
841 parameter controls whether to erase the existing data, which
842 may not be stored locally but in LDAP.
845 assert session_info
is not None
847 # We use options=["modules:"] to stop the modules loading - we
848 # just want to wipe and re-initialise the database, not start it up
851 os
.unlink(samdb_path
)
855 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
856 lp
=lp
, options
=["modules:"])
858 ldap_backend_line
= "# No LDAP backend"
859 if provision_backend
.type != "ldb":
860 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldap_uri
862 required_features
= None
863 if not plaintext_secrets
:
864 required_features
= "requiredFeatures: encryptedSecrets"
866 if backend_store
is None:
867 backend_store
= get_default_backend_store()
868 backend_store_line
= "backendStore: %s" % backend_store
870 if backend_store
== "mdb":
871 if required_features
is not None:
872 required_features
+= "\n"
874 required_features
= ""
875 required_features
+= "requiredFeatures: lmdbLevelOne"
877 if required_features
is None:
878 required_features
= "# No required features"
880 samdb
.transaction_start()
882 logger
.info("Setting up sam.ldb partitions and settings")
883 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
884 "LDAP_BACKEND_LINE": ldap_backend_line
,
885 "BACKEND_STORE": backend_store_line
888 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
889 "BACKEND_TYPE": provision_backend
.type,
890 "SERVER_ROLE": serverrole
,
891 "REQUIRED_FEATURES": required_features
894 logger
.info("Setting up sam.ldb rootDSE")
895 setup_samdb_rootdse(samdb
, names
)
897 samdb
.transaction_cancel()
900 samdb
.transaction_commit()
903 def secretsdb_self_join(secretsdb
, domain
,
904 netbiosname
, machinepass
, domainsid
=None,
905 realm
=None, dnsdomain
=None,
906 key_version_number
=1,
907 secure_channel_type
=SEC_CHAN_WKSTA
):
908 """Add domain join-specific bits to a secrets database.
910 :param secretsdb: Ldb Handle to the secrets database
911 :param machinepass: Machine password
913 attrs
= ["whenChanged",
920 if realm
is not None:
921 if dnsdomain
is None:
922 dnsdomain
= realm
.lower()
923 dnsname
= '%s.%s' % (netbiosname
.lower(), dnsdomain
.lower())
926 shortname
= netbiosname
.lower()
928 # We don't need to set msg["flatname"] here, because rdn_name will handle
929 # it, and it causes problems for modifies anyway
930 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
931 msg
["secureChannelType"] = [str(secure_channel_type
)]
932 msg
["objectClass"] = ["top", "primaryDomain"]
933 if dnsname
is not None:
934 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
935 msg
["realm"] = [realm
]
936 msg
["saltPrincipal"] = ["host/%s@%s" % (dnsname
, realm
.upper())]
937 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
938 msg
["privateKeytab"] = ["secrets.keytab"]
940 msg
["secret"] = [machinepass
.encode('utf-8')]
941 msg
["samAccountName"] = ["%s$" % netbiosname
]
942 msg
["secureChannelType"] = [str(secure_channel_type
)]
943 if domainsid
is not None:
944 msg
["objectSid"] = [ndr_pack(domainsid
)]
946 # This complex expression tries to ensure that we don't have more
947 # than one record for this SID, realm or netbios domain at a time,
948 # but we don't delete the old record that we are about to modify,
949 # because that would delete the keytab and previous password.
950 res
= secretsdb
.search(base
="cn=Primary Domains", attrs
=attrs
,
951 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain
, realm
, str(domainsid
), str(msg
.dn
))),
952 scope
=ldb
.SCOPE_ONELEVEL
)
955 secretsdb
.delete(del_msg
.dn
)
957 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
960 msg
["priorSecret"] = [res
[0]["secret"][0]]
962 msg
["priorWhenChanged"] = [res
[0]["whenChanged"][0]]
967 msg
["privateKeytab"] = [res
[0]["privateKeytab"][0]]
972 msg
["krb5Keytab"] = [res
[0]["krb5Keytab"][0]]
978 msg
[el
].set_flags(ldb
.FLAG_MOD_REPLACE
)
979 secretsdb
.modify(msg
)
980 secretsdb
.rename(res
[0].dn
, msg
.dn
)
982 spn
= ['HOST/%s' % shortname
]
983 if secure_channel_type
== SEC_CHAN_BDC
and dnsname
is not None:
984 # if we are a domain controller then we add servicePrincipalName
985 # entries for the keytab code to update.
986 spn
.extend(['HOST/%s' % dnsname
])
987 msg
["servicePrincipalName"] = spn
992 def setup_secretsdb(paths
, session_info
, lp
):
993 """Setup the secrets database.
995 :note: This function does not handle exceptions and transaction on purpose,
996 it's up to the caller to do this job.
998 :param path: Path to the secrets database.
999 :param session_info: Session info.
1000 :param lp: Loadparm context
1001 :return: LDB handle for the created secrets database
1003 if os
.path
.exists(paths
.secrets
):
1004 os
.unlink(paths
.secrets
)
1006 keytab_path
= os
.path
.join(paths
.private_dir
, paths
.keytab
)
1007 if os
.path
.exists(keytab_path
):
1008 os
.unlink(keytab_path
)
1010 bind_dns_keytab_path
= os
.path
.join(paths
.binddns_dir
, paths
.dns_keytab
)
1011 if os
.path
.exists(bind_dns_keytab_path
):
1012 os
.unlink(bind_dns_keytab_path
)
1014 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
1015 if os
.path
.exists(dns_keytab_path
):
1016 os
.unlink(dns_keytab_path
)
1018 path
= paths
.secrets
1020 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
1022 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
1023 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
1024 secrets_ldb
.transaction_start()
1026 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
1028 secrets_ldb
.transaction_cancel()
1033 def setup_privileges(path
, session_info
, lp
):
1034 """Setup the privileges database.
1036 :param path: Path to the privileges database.
1037 :param session_info: Session info.
1038 :param lp: Loadparm context
1039 :return: LDB handle for the created secrets database
1041 if os
.path
.exists(path
):
1043 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
1044 privilege_ldb
.erase()
1045 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1048 def setup_encrypted_secrets_key(path
):
1049 """Setup the encrypted secrets key file.
1051 Any existing key file will be deleted and a new random key generated.
1053 :param path: Path to the secrets key file.
1056 if os
.path
.exists(path
):
1059 flags
= os
.O_WRONLY | os
.O_CREAT | os
.O_EXCL
1060 mode
= stat
.S_IRUSR | stat
.S_IWUSR
1062 umask_original
= os
.umask(0)
1064 fd
= os
.open(path
, flags
, mode
)
1066 os
.umask(umask_original
)
1068 with os
.fdopen(fd
, 'wb') as f
:
1069 key
= samba
.generate_random_bytes(16)
1073 def setup_registry(path
, session_info
, lp
):
1074 """Setup the registry.
1076 :param path: Path to the registry database
1077 :param session_info: Session information
1078 :param lp: Loadparm context
1080 reg
= samba
.registry
.Registry()
1081 hive
= samba
.registry
.open_ldb(path
, session_info
=session_info
, lp_ctx
=lp
)
1082 reg
.mount_hive(hive
, samba
.registry
.HKEY_LOCAL_MACHINE
)
1083 provision_reg
= setup_path("provision.reg")
1084 assert os
.path
.exists(provision_reg
)
1085 reg
.diff_apply(provision_reg
)
1088 def setup_idmapdb(path
, session_info
, lp
):
1089 """Setup the idmap database.
1091 :param path: path to the idmap database
1092 :param session_info: Session information
1093 :param lp: Loadparm context
1095 if os
.path
.exists(path
):
1098 idmap_ldb
= IDmapDB(path
, session_info
=session_info
, lp
=lp
)
1100 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
1104 def setup_samdb_rootdse(samdb
, names
):
1105 """Setup the SamDB rootdse.
1107 :param samdb: Sam Database handle
1109 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
1110 "SCHEMADN": names
.schemadn
,
1111 "DOMAINDN": names
.domaindn
,
1112 "ROOTDN": names
.rootdn
,
1113 "CONFIGDN": names
.configdn
,
1114 "SERVERDN": names
.serverdn
,
1118 def setup_self_join(samdb
, admin_session_info
, names
, fill
, machinepass
,
1119 dns_backend
, dnspass
, domainsid
, next_rid
, invocationid
,
1120 policyguid
, policyguid_dc
,
1121 domainControllerFunctionality
, ntdsguid
=None, dc_rid
=None):
1122 """Join a host to its own domain."""
1123 assert isinstance(invocationid
, str)
1124 if ntdsguid
is not None:
1125 ntdsguid_line
= "objectGUID: %s\n" % ntdsguid
1132 # Some clients/applications (like exchange) make use of
1133 # the operatingSystemVersion attribute in order to
1134 # find if a DC is good enough.
1136 # So we better use a value matching a Windows DC
1137 # with the same domainControllerFunctionality level
1138 operatingSystemVersion
= samba
.dsdb
.dc_operatingSystemVersion(domainControllerFunctionality
)
1140 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
1141 "CONFIGDN": names
.configdn
,
1142 "SCHEMADN": names
.schemadn
,
1143 "DOMAINDN": names
.domaindn
,
1144 "SERVERDN": names
.serverdn
,
1145 "INVOCATIONID": invocationid
,
1146 "NETBIOSNAME": names
.netbiosname
,
1147 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1148 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')).decode('utf8'),
1149 "DOMAINSID": str(domainsid
),
1150 "DCRID": str(dc_rid
),
1151 "OPERATING_SYSTEM": "Samba-%s" % version
,
1152 "OPERATING_SYSTEM_VERSION": operatingSystemVersion
,
1153 "NTDSGUID": ntdsguid_line
,
1154 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1155 domainControllerFunctionality
),
1156 "RIDALLOCATIONSTART": str(next_rid
+ 100),
1157 "RIDALLOCATIONEND": str(next_rid
+ 100 + 499)})
1159 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
1160 "POLICYGUID": policyguid
,
1161 "POLICYGUID_DC": policyguid_dc
,
1162 "DNSDOMAIN": names
.dnsdomain
,
1163 "DOMAINDN": names
.domaindn
})
1165 # If we are setting up a subdomain, then this has been replicated in, so we
1166 # don't need to add it
1167 if fill
== FILL_FULL
:
1168 setup_add_ldif(samdb
, setup_path("provision_self_join_config.ldif"), {
1169 "CONFIGDN": names
.configdn
,
1170 "SCHEMADN": names
.schemadn
,
1171 "DOMAINDN": names
.domaindn
,
1172 "SERVERDN": names
.serverdn
,
1173 "INVOCATIONID": invocationid
,
1174 "NETBIOSNAME": names
.netbiosname
,
1175 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1176 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')).decode('utf8'),
1177 "DOMAINSID": str(domainsid
),
1178 "DCRID": str(dc_rid
),
1179 "SAMBA_VERSION_STRING": version
,
1180 "NTDSGUID": ntdsguid_line
,
1181 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1182 domainControllerFunctionality
)})
1184 # Setup fSMORoleOwner entries to point at the newly created DC entry
1185 setup_modify_ldif(samdb
,
1186 setup_path("provision_self_join_modify_schema.ldif"), {
1187 "SCHEMADN": names
.schemadn
,
1188 "SERVERDN": names
.serverdn
,
1190 controls
=["provision:0", "relax:0"])
1191 setup_modify_ldif(samdb
,
1192 setup_path("provision_self_join_modify_config.ldif"), {
1193 "CONFIGDN": names
.configdn
,
1194 "DEFAULTSITE": names
.sitename
,
1195 "NETBIOSNAME": names
.netbiosname
,
1196 "SERVERDN": names
.serverdn
,
1199 system_session_info
= system_session()
1200 samdb
.set_session_info(system_session_info
)
1201 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1202 # modify a serverReference under cn=config when we are a subdomain, we must
1203 # be system due to ACLs
1204 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
1205 "DOMAINDN": names
.domaindn
,
1206 "SERVERDN": names
.serverdn
,
1207 "NETBIOSNAME": names
.netbiosname
,
1210 samdb
.set_session_info(admin_session_info
)
1212 if dns_backend
!= "SAMBA_INTERNAL":
1213 # This is Samba4 specific and should be replaced by the correct
1214 # DNS AD-style setup
1215 setup_add_ldif(samdb
, setup_path("provision_dns_add_samba.ldif"), {
1216 "DNSDOMAIN": names
.dnsdomain
,
1217 "DOMAINDN": names
.domaindn
,
1218 "DNSPASS_B64": b64encode(dnspass
.encode('utf-16-le')).decode('utf8'),
1219 "HOSTNAME": names
.hostname
,
1220 "DNSNAME": '%s.%s' % (
1221 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
1225 def getpolicypath(sysvolpath
, dnsdomain
, guid
):
1226 """Return the physical path of policy given its guid.
1228 :param sysvolpath: Path to the sysvol folder
1229 :param dnsdomain: DNS name of the AD domain
1230 :param guid: The GUID of the policy
1231 :return: A string with the complete path to the policy folder
1234 guid
= "{%s}" % guid
1235 policy_path
= os
.path
.join(sysvolpath
, dnsdomain
, "Policies", guid
)
1239 def create_gpo_struct(policy_path
):
1240 if not os
.path
.exists(policy_path
):
1241 os
.makedirs(policy_path
, 0o775)
1242 f
= open(os
.path
.join(policy_path
, "GPT.INI"), 'w')
1244 f
.write("[General]\r\nVersion=0")
1247 p
= os
.path
.join(policy_path
, "MACHINE")
1248 if not os
.path
.exists(p
):
1249 os
.makedirs(p
, 0o775)
1250 p
= os
.path
.join(policy_path
, "USER")
1251 if not os
.path
.exists(p
):
1252 os
.makedirs(p
, 0o775)
1255 def create_default_gpo(sysvolpath
, dnsdomain
, policyguid
, policyguid_dc
):
1256 """Create the default GPO for a domain
1258 :param sysvolpath: Physical path for the sysvol folder
1259 :param dnsdomain: DNS domain name of the AD domain
1260 :param policyguid: GUID of the default domain policy
1261 :param policyguid_dc: GUID of the default domain controller policy
1263 policy_path
= getpolicypath(sysvolpath
, dnsdomain
, policyguid
)
1264 create_gpo_struct(policy_path
)
1266 policy_path
= getpolicypath(sysvolpath
, dnsdomain
, policyguid_dc
)
1267 create_gpo_struct(policy_path
)
1270 # Default the database size to 8Gb
1271 DEFAULT_BACKEND_SIZE
= 8 * 1024 * 1024 *1024
1273 def setup_samdb(path
, session_info
, provision_backend
, lp
, names
,
1274 logger
, serverrole
, schema
, am_rodc
=False,
1275 plaintext_secrets
=False, backend_store
=None,
1276 backend_store_size
=None, batch_mode
=False):
1277 """Setup a complete SAM Database.
1279 :note: This will wipe the main SAM database file!
1282 # Also wipes the database
1283 setup_samdb_partitions(path
, logger
=logger
, lp
=lp
,
1284 provision_backend
=provision_backend
, session_info
=session_info
,
1285 names
=names
, serverrole
=serverrole
, plaintext_secrets
=plaintext_secrets
,
1286 backend_store
=backend_store
)
1288 store_size
= DEFAULT_BACKEND_SIZE
1289 if backend_store_size
:
1290 store_size
= backend_store_size
1293 if backend_store
== "mdb":
1294 options
.append("lmdb_env_size:" + str(store_size
))
1296 options
.append("batch_mode:1")
1298 # Estimate the number of index records in the transaction_index_cache
1299 # Numbers chosen give the prime 202481 for the default backend size,
1300 # which works well for a 100,000 user database
1301 cache_size
= int(store_size
/ 42423) + 1
1302 options
.append("transaction_index_cache_size:" + str(cache_size
))
1304 # Load the database, but don's load the global schema and don't connect
1306 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
1308 global_schema
=False, am_rodc
=am_rodc
, options
=options
)
1310 logger
.info("Pre-loading the Samba 4 and AD schema")
1312 # Load the schema from the one we computed earlier
1313 samdb
.set_schema(schema
, write_indices_and_attributes
=False)
1315 # Set the NTDS settings DN manually - in order to have it already around
1316 # before the provisioned tree exists and we connect
1317 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1319 # And now we can connect to the DB - the schema won't be loaded from the
1322 samdb
.connect(path
, options
=options
)
1323 except ldb
.LdbError
as e2
:
1324 (num
, string_error
) = e2
.args
1325 if (num
== ldb
.ERR_INSUFFICIENT_ACCESS_RIGHTS
):
1326 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path
)
1330 # But we have to give it one more kick to have it use the schema
1331 # during provision - it needs, now that it is connected, to write
1332 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1333 samdb
.set_schema(schema
, write_indices_and_attributes
=True)
1338 def fill_samdb(samdb
, lp
, names
, logger
, policyguid
,
1339 policyguid_dc
, fill
, adminpass
, krbtgtpass
, machinepass
, dns_backend
,
1340 dnspass
, invocationid
, ntdsguid
,
1341 dom_for_fun_level
=None, schema
=None, next_rid
=None, dc_rid
=None):
1343 if next_rid
is None:
1346 # Provision does not make much sense values larger than 1000000000
1347 # as the upper range of the rIDAvailablePool is 1073741823 and
1348 # we don't want to create a domain that cannot allocate rids.
1349 if next_rid
< 1000 or next_rid
> 1000000000:
1350 error
= "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid
)
1351 error
+= "the valid range is %u-%u. The default is %u." % (
1352 1000, 1000000000, 1000)
1353 raise ProvisioningError(error
)
1355 domainControllerFunctionality
= functional_level
.dc_level_from_lp(lp
)
1357 # ATTENTION: Do NOT change these default values without discussion with the
1358 # team and/or release manager. They have a big impact on the whole program!
1359 if dom_for_fun_level
is None:
1360 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2008_R2
1362 if dom_for_fun_level
> domainControllerFunctionality
:
1363 level
= functional_level
.level_to_string(domainControllerFunctionality
)
1364 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!")
1366 domainFunctionality
= dom_for_fun_level
1367 forestFunctionality
= dom_for_fun_level
1369 # Set the NTDS settings DN manually - in order to have it already around
1370 # before the provisioned tree exists and we connect
1371 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1373 # Set the domain functionality levels onto the database.
1374 # Various module (the password_hash module in particular) need
1375 # to know what level of AD we are emulating.
1377 # These will be fixed into the database via the database
1378 # modifictions below, but we need them set from the start.
1379 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
1380 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
1381 samdb
.set_opaque_integer("domainControllerFunctionality",
1382 domainControllerFunctionality
)
1384 samdb
.set_domain_sid(str(names
.domainsid
))
1385 samdb
.set_invocation_id(invocationid
)
1387 logger
.info("Adding DomainDN: %s" % names
.domaindn
)
1389 # impersonate domain admin
1390 admin_session_info
= admin_session(lp
, str(names
.domainsid
))
1391 samdb
.set_session_info(admin_session_info
)
1392 if names
.domainguid
is not None:
1393 domainguid_line
= "objectGUID: %s\n-" % names
.domainguid
1395 domainguid_line
= ""
1397 descr
= b64encode(get_domain_descriptor(names
.domainsid
)).decode('utf8')
1398 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
1399 "DOMAINDN": names
.domaindn
,
1400 "DOMAINSID": str(names
.domainsid
),
1401 "DESCRIPTOR": descr
,
1402 "DOMAINGUID": domainguid_line
1405 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
1406 "DOMAINDN": names
.domaindn
,
1407 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1408 "NEXTRID": str(next_rid
),
1409 "DEFAULTSITE": names
.sitename
,
1410 "CONFIGDN": names
.configdn
,
1411 "POLICYGUID": policyguid
,
1412 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1413 "SAMBA_VERSION_STRING": version
,
1414 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH
)
1417 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1418 if fill
== FILL_FULL
:
1419 logger
.info("Adding configuration container")
1420 descr
= b64encode(get_config_descriptor(names
.domainsid
)).decode('utf8')
1421 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
1422 "CONFIGDN": names
.configdn
,
1423 "DESCRIPTOR": descr
,
1426 # The LDIF here was created when the Schema object was constructed
1427 ignore_checks_oid
= "local_oid:%s:0" % samba
.dsdb
.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1434 logger
.info("Setting up sam.ldb schema")
1435 samdb
.add_ldif(schema
.schema_dn_add
, controls
=schema_controls
)
1436 samdb
.modify_ldif(schema
.schema_dn_modify
, controls
=schema_controls
)
1437 samdb
.write_prefixes_from_schema()
1438 samdb
.add_ldif(schema
.schema_data
, controls
=schema_controls
)
1439 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
1440 {"SCHEMADN": names
.schemadn
},
1441 controls
=schema_controls
)
1443 # Now register this container in the root of the forest
1444 msg
= ldb
.Message(ldb
.Dn(samdb
, names
.domaindn
))
1445 msg
["subRefs"] = ldb
.MessageElement(names
.configdn
, ldb
.FLAG_MOD_ADD
,
1448 deletedobjects_descr
= b64encode(get_deletedobjects_descriptor(names
.domainsid
)).decode('utf8')
1450 samdb
.invocation_id
= invocationid
1452 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1453 if fill
== FILL_FULL
:
1454 logger
.info("Setting up sam.ldb configuration data")
1456 partitions_descr
= b64encode(get_config_partitions_descriptor(names
.domainsid
)).decode('utf8')
1457 sites_descr
= b64encode(get_config_sites_descriptor(names
.domainsid
)).decode('utf8')
1458 ntdsquotas_descr
= b64encode(get_config_ntds_quotas_descriptor(names
.domainsid
)).decode('utf8')
1459 protected1_descr
= b64encode(get_config_delete_protected1_descriptor(names
.domainsid
)).decode('utf8')
1460 protected1wd_descr
= b64encode(get_config_delete_protected1wd_descriptor(names
.domainsid
)).decode('utf8')
1461 protected2_descr
= b64encode(get_config_delete_protected2_descriptor(names
.domainsid
)).decode('utf8')
1463 if "2008" in schema
.base_schema
:
1464 # exclude 2012-specific changes if we're using a 2008 schema
1469 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
1470 "CONFIGDN": names
.configdn
,
1471 "NETBIOSNAME": names
.netbiosname
,
1472 "DEFAULTSITE": names
.sitename
,
1473 "DNSDOMAIN": names
.dnsdomain
,
1474 "DOMAIN": names
.domain
,
1475 "SCHEMADN": names
.schemadn
,
1476 "DOMAINDN": names
.domaindn
,
1477 "SERVERDN": names
.serverdn
,
1478 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
1479 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1480 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr
,
1481 "DELETEDOBJECTS_DESCRIPTOR": deletedobjects_descr
,
1482 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr
,
1483 "SERVICES_DESCRIPTOR": protected1_descr
,
1484 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr
,
1485 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr
,
1486 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr
,
1487 "PARTITIONS_DESCRIPTOR": partitions_descr
,
1488 "SITES_DESCRIPTOR": sites_descr
,
1491 setup_add_ldif(samdb
, setup_path("extended-rights.ldif"), {
1492 "CONFIGDN": names
.configdn
,
1493 "INC2012": incl_2012
,
1496 logger
.info("Setting up display specifiers")
1497 display_specifiers_ldif
= read_ms_ldif(
1498 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1499 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
,
1500 {"CONFIGDN": names
.configdn
})
1501 check_all_substituted(display_specifiers_ldif
)
1502 samdb
.add_ldif(display_specifiers_ldif
)
1504 logger
.info("Modifying display specifiers and extended rights")
1505 setup_modify_ldif(samdb
,
1506 setup_path("provision_configuration_modify.ldif"), {
1507 "CONFIGDN": names
.configdn
,
1508 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1511 logger
.info("Adding users container")
1512 users_desc
= b64encode(get_domain_users_descriptor(names
.domainsid
)).decode('utf8')
1513 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
1514 "DOMAINDN": names
.domaindn
,
1515 "USERS_DESCRIPTOR": users_desc
1517 logger
.info("Modifying users container")
1518 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
1519 "DOMAINDN": names
.domaindn
})
1520 logger
.info("Adding computers container")
1521 computers_desc
= b64encode(get_domain_computers_descriptor(names
.domainsid
)).decode('utf8')
1522 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
1523 "DOMAINDN": names
.domaindn
,
1524 "COMPUTERS_DESCRIPTOR": computers_desc
1526 logger
.info("Modifying computers container")
1527 setup_modify_ldif(samdb
,
1528 setup_path("provision_computers_modify.ldif"), {
1529 "DOMAINDN": names
.domaindn
})
1530 logger
.info("Setting up sam.ldb data")
1531 infrastructure_desc
= b64encode(get_domain_infrastructure_descriptor(names
.domainsid
)).decode('utf8')
1532 lostandfound_desc
= b64encode(get_domain_delete_protected2_descriptor(names
.domainsid
)).decode('utf8')
1533 system_desc
= b64encode(get_domain_delete_protected1_descriptor(names
.domainsid
)).decode('utf8')
1534 builtin_desc
= b64encode(get_domain_builtin_descriptor(names
.domainsid
)).decode('utf8')
1535 controllers_desc
= b64encode(get_domain_controllers_descriptor(names
.domainsid
)).decode('utf8')
1536 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1537 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1538 "DOMAINDN": names
.domaindn
,
1539 "NETBIOSNAME": names
.netbiosname
,
1540 "DEFAULTSITE": names
.sitename
,
1541 "CONFIGDN": names
.configdn
,
1542 "SERVERDN": names
.serverdn
,
1543 "RIDAVAILABLESTART": str(next_rid
+ 600),
1544 "POLICYGUID_DC": policyguid_dc
,
1545 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc
,
1546 "DELETEDOBJECTS_DESCRIPTOR": deletedobjects_descr
,
1547 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc
,
1548 "SYSTEM_DESCRIPTOR": system_desc
,
1549 "BUILTIN_DESCRIPTOR": builtin_desc
,
1550 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc
,
1553 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1554 if fill
== FILL_FULL
:
1555 managedservice_descr
= b64encode(get_managed_service_accounts_descriptor(names
.domainsid
)).decode('utf8')
1556 setup_modify_ldif(samdb
,
1557 setup_path("provision_configuration_references.ldif"), {
1558 "CONFIGDN": names
.configdn
,
1559 "SCHEMADN": names
.schemadn
})
1561 logger
.info("Setting up well known security principals")
1562 protected1wd_descr
= b64encode(get_config_delete_protected1wd_descriptor(names
.domainsid
)).decode('utf8')
1563 setup_add_ldif(samdb
, setup_path("provision_well_known_sec_princ.ldif"), {
1564 "CONFIGDN": names
.configdn
,
1565 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr
,
1566 }, controls
=["relax:0", "provision:0"])
1568 if fill
== FILL_FULL
or fill
== FILL_SUBDOMAIN
:
1569 setup_modify_ldif(samdb
,
1570 setup_path("provision_basedn_references.ldif"), {
1571 "DOMAINDN": names
.domaindn
,
1572 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1575 logger
.info("Setting up sam.ldb users and groups")
1576 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1577 "DOMAINDN": names
.domaindn
,
1578 "DOMAINSID": str(names
.domainsid
),
1579 "ADMINPASS_B64": b64encode(adminpass
.encode('utf-16-le')).decode('utf8'),
1580 "KRBTGTPASS_B64": b64encode(krbtgtpass
.encode('utf-16-le')).decode('utf8')
1581 }, controls
=["relax:0", "provision:0"])
1583 logger
.info("Setting up self join")
1584 setup_self_join(samdb
, admin_session_info
, names
=names
, fill
=fill
,
1585 invocationid
=invocationid
,
1586 dns_backend
=dns_backend
,
1588 machinepass
=machinepass
,
1589 domainsid
=names
.domainsid
,
1592 policyguid
=policyguid
,
1593 policyguid_dc
=policyguid_dc
,
1594 domainControllerFunctionality
=domainControllerFunctionality
,
1597 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1598 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1599 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
).decode('utf8')
1600 assert isinstance(names
.ntdsguid
, str)
1605 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;SO)(A;OICI;FA;;;SY)(A;OICI;0x1200a9;;;AU)"
1606 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)"
1607 SYSVOL_SERVICE
= "sysvol"
1610 def set_dir_acl(path
, acl
, lp
, domsid
, use_ntvfs
, passdb
, service
=SYSVOL_SERVICE
):
1611 session_info
= system_session_unix()
1612 setntacl(lp
, path
, acl
, domsid
, session_info
, use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1613 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1615 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
, session_info
,
1616 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1618 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
, session_info
,
1619 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1622 def set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
):
1623 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1626 :param sysvol: Physical path for the sysvol folder
1627 :param dnsdomain: The DNS name of the domain
1628 :param domainsid: The SID of the domain
1629 :param domaindn: The DN of the domain (ie. DC=...)
1630 :param samdb: An LDB object on the SAM db
1631 :param lp: an LP object
1634 # Set ACL for GPO root folder
1635 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1636 session_info
= system_session_unix()
1638 setntacl(lp
, root_policy_path
, POLICIES_ACL
, str(domainsid
), session_info
,
1639 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=SYSVOL_SERVICE
)
1641 res
= samdb
.search(base
="CN=Policies,CN=System,%s" %(domaindn),
1642 attrs
=["cn", "nTSecurityDescriptor"],
1643 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1646 acl
= ndr_unpack(security
.descriptor
,
1647 policy
["nTSecurityDescriptor"][0]).as_sddl()
1648 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1649 set_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1650 str(domainsid
), use_ntvfs
,
1654 def setsysvolacl(samdb
, sysvol
, uid
, gid
, domainsid
, dnsdomain
,
1655 domaindn
, lp
, use_ntvfs
):
1656 """Set the ACL for the sysvol share and the subfolders
1658 :param samdb: An LDB object on the SAM db
1659 :param sysvol: Physical path for the sysvol folder
1660 :param uid: The UID of the "Administrator" user
1661 :param gid: The GID of the "Domain administrators" group
1662 :param domainsid: The SID of the domain
1663 :param dnsdomain: The DNS name of the domain
1664 :param domaindn: The DN of the domain (ie. DC=...)
1669 s3conf
= s3param
.get_context()
1670 s3conf
.load(lp
.configfile
)
1672 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(sysvol
))
1675 smbd
.set_simple_acl(file.name
, 0o755, system_session_unix(), gid
)
1677 if not smbd
.have_posix_acls():
1678 # This clue is only strictly correct for RPM and
1679 # Debian-like Linux systems, but hopefully other users
1680 # will get enough clue from it.
1681 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1682 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1684 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1685 "Try the mounting the filesystem with the 'acl' option.")
1687 smbd
.chown(file.name
, uid
, gid
, system_session_unix())
1689 raise ProvisioningError("Unable to chown a file on your filesystem. "
1690 "You may not be running provision as root.")
1694 # This will ensure that the smbd code we are running when setting ACLs
1695 # is initialised with the smb.conf
1696 s3conf
= s3param
.get_context()
1697 s3conf
.load(lp
.configfile
)
1698 # ensure we are using the right samba_dsdb passdb backend, no matter what
1699 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1700 passdb
.reload_static_pdb()
1702 # ensure that we init the samba_dsdb backend, so the domain sid is
1703 # marked in secrets.tdb
1704 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1706 # now ensure everything matches correctly, to avoid weird issues
1707 if passdb
.get_global_sam_sid() != domainsid
:
1708 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
))
1710 domain_info
= s4_passdb
.domain_info()
1711 if domain_info
["dom_sid"] != domainsid
:
1712 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
))
1714 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1715 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()))
1719 os
.chown(sysvol
, -1, gid
)
1725 # use admin sid dn as user dn, since admin should own most of the files,
1726 # the operation will be much faster
1727 userdn
= '<SID={}-{}>'.format(domainsid
, security
.DOMAIN_RID_ADMINISTRATOR
)
1729 flags
= (auth
.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1730 auth
.AUTH_SESSION_INFO_AUTHENTICATED |
1731 auth
.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
)
1733 session_info
= auth
.user_session(samdb
, lp_ctx
=lp
, dn
=userdn
,
1734 session_info_flags
=flags
)
1735 auth
.session_info_set_unix(session_info
,
1737 user_name
="Administrator",
1741 def _setntacl(path
):
1742 """A helper to reuse args"""
1744 lp
, path
, SYSVOL_ACL
, str(domainsid
), session_info
,
1745 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=s4_passdb
,
1746 service
=SYSVOL_SERVICE
)
1748 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1750 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1752 if use_ntvfs
and canchown
:
1753 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1754 _setntacl(os
.path
.join(root
, name
))
1756 if use_ntvfs
and canchown
:
1757 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1758 _setntacl(os
.path
.join(root
, name
))
1760 # Set acls on Policy folder and policies folders
1761 set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
=s4_passdb
)
1764 def acl_type(direct_db_access
):
1765 if direct_db_access
:
1771 def check_dir_acl(path
, acl
, lp
, domainsid
, direct_db_access
):
1772 session_info
= system_session_unix()
1773 fsacl
= getntacl(lp
, path
, session_info
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1774 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1775 if fsacl_sddl
!= acl
:
1776 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
))
1778 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1780 fsacl
= getntacl(lp
, os
.path
.join(root
, name
), session_info
,
1781 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1783 raise ProvisioningError('%s ACL on GPO file %s not found!' %
1784 (acl_type(direct_db_access
),
1785 os
.path
.join(root
, name
)))
1786 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1787 if fsacl_sddl
!= acl
:
1788 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
))
1791 fsacl
= getntacl(lp
, os
.path
.join(root
, name
), session_info
,
1792 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1794 raise ProvisioningError('%s ACL on GPO directory %s not found!'
1795 % (acl_type(direct_db_access
),
1796 os
.path
.join(root
, name
)))
1797 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1798 if fsacl_sddl
!= acl
:
1799 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
))
1802 def check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1804 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1807 :param sysvol: Physical path for the sysvol folder
1808 :param dnsdomain: The DNS name of the domain
1809 :param domainsid: The SID of the domain
1810 :param domaindn: The DN of the domain (ie. DC=...)
1811 :param samdb: An LDB object on the SAM db
1812 :param lp: an LP object
1815 # Set ACL for GPO root folder
1816 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1817 session_info
= system_session_unix()
1818 fsacl
= getntacl(lp
, root_policy_path
, session_info
,
1819 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1821 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access
), root_policy_path
))
1822 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1823 if fsacl_sddl
!= POLICIES_ACL
:
1824 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
))
1825 res
= samdb
.search(base
="CN=Policies,CN=System,%s" %(domaindn),
1826 attrs
=["cn", "nTSecurityDescriptor"],
1827 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1830 acl
= ndr_unpack(security
.descriptor
,
1831 policy
["nTSecurityDescriptor"][0]).as_sddl()
1832 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1833 check_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1834 domainsid
, direct_db_access
)
1837 def checksysvolacl(samdb
, netlogon
, sysvol
, domainsid
, dnsdomain
, domaindn
,
1839 """Set the ACL for the sysvol share and the subfolders
1841 :param samdb: An LDB object on the SAM db
1842 :param netlogon: Physical path for the netlogon folder
1843 :param sysvol: Physical path for the sysvol folder
1844 :param uid: The UID of the "Administrator" user
1845 :param gid: The GID of the "Domain administrators" group
1846 :param domainsid: The SID of the domain
1847 :param dnsdomain: The DNS name of the domain
1848 :param domaindn: The DN of the domain (ie. DC=...)
1851 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1852 s3conf
= s3param
.get_context()
1853 s3conf
.load(lp
.configfile
)
1854 # ensure we are using the right samba_dsdb passdb backend, no matter what
1855 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1856 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1857 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1859 # now ensure everything matches correctly, to avoid weird issues
1860 if passdb
.get_global_sam_sid() != domainsid
:
1861 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
))
1863 domain_info
= s4_passdb
.domain_info()
1864 if domain_info
["dom_sid"] != domainsid
:
1865 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
))
1867 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1868 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()))
1870 # Ensure we can read this directly, and via the smbd VFS
1871 session_info
= system_session_unix()
1872 for direct_db_access
in [True, False]:
1873 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1874 for dir_path
in [os
.path
.join(sysvol
, dnsdomain
), netlogon
]:
1875 fsacl
= getntacl(lp
, dir_path
, session_info
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1877 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access
), dir_path
))
1878 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1879 if fsacl_sddl
!= SYSVOL_ACL
:
1880 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
))
1882 # Check acls on Policy folder and policies folders
1883 check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1887 def interface_ips_v4(lp
, all_interfaces
=False):
1888 """return only IPv4 IPs"""
1889 ips
= samba
.interface_ips(lp
, all_interfaces
)
1892 if i
.find(':') == -1:
1897 def interface_ips_v6(lp
):
1898 """return only IPv6 IPs"""
1899 ips
= samba
.interface_ips(lp
, False)
1902 if i
.find(':') != -1:
1907 def provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
1909 samdb_fill
=FILL_FULL
,
1910 hostip
=None, hostip6
=None,
1911 next_rid
=1000, dc_rid
=None, adminpass
=None, krbtgtpass
=None,
1912 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1913 invocationid
=None, machinepass
=None, ntdsguid
=None,
1914 dns_backend
=None, dnspass
=None,
1915 serverrole
=None, dom_for_fun_level
=None,
1916 lp
=None, use_ntvfs
=False,
1917 skip_sysvolacl
=False):
1918 # create/adapt the group policy GUIDs
1919 # Default GUID for default policy are described at
1920 # "How Core Group Policy Works"
1921 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1922 if policyguid
is None:
1923 policyguid
= DEFAULT_POLICY_GUID
1924 policyguid
= policyguid
.upper()
1925 if policyguid_dc
is None:
1926 policyguid_dc
= DEFAULT_DC_POLICY_GUID
1927 policyguid_dc
= policyguid_dc
.upper()
1929 if invocationid
is None:
1930 invocationid
= str(uuid
.uuid4())
1932 if krbtgtpass
is None:
1933 # Note that the machinepass value is ignored
1934 # as the backend (password_hash.c) will generate its
1935 # own random values for the krbtgt keys
1936 krbtgtpass
= samba
.generate_random_machine_password(128, 255)
1937 if machinepass
is None:
1938 machinepass
= samba
.generate_random_machine_password(120, 120)
1940 dnspass
= samba
.generate_random_password(120, 120)
1942 samdb
.transaction_start()
1944 samdb
= fill_samdb(samdb
, lp
, names
, logger
=logger
,
1946 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1947 fill
=samdb_fill
, adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1948 invocationid
=invocationid
, machinepass
=machinepass
,
1949 dns_backend
=dns_backend
, dnspass
=dnspass
,
1951 dom_for_fun_level
=dom_for_fun_level
,
1952 next_rid
=next_rid
, dc_rid
=dc_rid
)
1954 # Set up group policies (domain policy and domain controller
1956 if serverrole
== "active directory domain controller":
1957 create_default_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
,
1960 samdb
.transaction_cancel()
1963 samdb
.transaction_commit()
1965 if serverrole
== "active directory domain controller":
1966 # Continue setting up sysvol for GPO. This appears to require being
1967 # outside a transaction.
1968 if not skip_sysvolacl
:
1969 setsysvolacl(samdb
, paths
.sysvol
, paths
.root_uid
,
1970 paths
.root_gid
, names
.domainsid
, names
.dnsdomain
,
1971 names
.domaindn
, lp
, use_ntvfs
)
1973 logger
.info("Setting acl on sysvol skipped")
1975 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1976 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1977 netbiosname
=names
.netbiosname
, domainsid
=names
.domainsid
,
1978 machinepass
=machinepass
, secure_channel_type
=SEC_CHAN_BDC
)
1980 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1981 # In future, this might be determined from some configuration
1982 kerberos_enctypes
= str(ENC_ALL_TYPES
)
1985 msg
= ldb
.Message(ldb
.Dn(samdb
,
1986 samdb
.searchone("distinguishedName",
1987 expression
="samAccountName=%s$" % names
.netbiosname
,
1988 scope
=ldb
.SCOPE_SUBTREE
).decode('utf8')))
1989 msg
["msDS-SupportedEncryptionTypes"] = ldb
.MessageElement(
1990 elements
=kerberos_enctypes
, flags
=ldb
.FLAG_MOD_REPLACE
,
1991 name
="msDS-SupportedEncryptionTypes")
1993 except ldb
.LdbError
as e
:
1994 (enum
, estr
) = e
.args
1995 if enum
!= ldb
.ERR_NO_SUCH_ATTRIBUTE
:
1996 # It might be that this attribute does not exist in this schema
1999 setup_ad_dns(samdb
, secrets_ldb
, names
, paths
, logger
,
2000 hostip
=hostip
, hostip6
=hostip6
, dns_backend
=dns_backend
,
2001 dnspass
=dnspass
, os_level
=dom_for_fun_level
,
2002 fill_level
=samdb_fill
)
2004 domainguid
= samdb
.searchone(basedn
=samdb
.get_default_basedn(),
2005 attribute
="objectGUID").decode('utf8')
2006 assert isinstance(domainguid
, str)
2008 lastProvisionUSNs
= get_last_provision_usn(samdb
)
2009 maxUSN
= get_max_usn(samdb
, str(names
.rootdn
))
2010 if lastProvisionUSNs
is not None:
2011 update_provision_usn(samdb
, 0, maxUSN
, invocationid
, 1)
2013 set_provision_usn(samdb
, 0, maxUSN
, invocationid
)
2015 logger
.info("Setting up sam.ldb rootDSE marking as synchronized")
2016 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"),
2017 {'NTDSGUID': names
.ntdsguid
})
2019 # fix any dangling GUIDs from the provision
2020 logger
.info("Fixing provision GUIDs")
2021 chk
= dbcheck(samdb
, samdb_schema
=samdb
, verbose
=False, fix
=True, yes
=True,
2023 samdb
.transaction_start()
2025 # a small number of GUIDs are missing because of ordering issues in the
2027 for schema_obj
in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
2028 chk
.check_database(DN
="%s,%s" % (schema_obj
, names
.schemadn
),
2029 scope
=ldb
.SCOPE_BASE
,
2030 attrs
=['defaultObjectCategory'])
2031 chk
.check_database(DN
="CN=IP Security,CN=System,%s" % names
.domaindn
,
2032 scope
=ldb
.SCOPE_ONELEVEL
,
2033 attrs
=['ipsecOwnersReference',
2034 'ipsecFilterReference',
2035 'ipsecISAKMPReference',
2036 'ipsecNegotiationPolicyReference',
2037 'ipsecNFAReference'])
2038 if chk
.check_database(DN
=names
.schemadn
, scope
=ldb
.SCOPE_SUBTREE
,
2039 attrs
=['attributeId', 'governsId']) != 0:
2040 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
2042 samdb
.transaction_cancel()
2045 samdb
.transaction_commit()
2049 "ROLE_STANDALONE": "standalone server",
2050 "ROLE_DOMAIN_MEMBER": "member server",
2051 "ROLE_DOMAIN_BDC": "active directory domain controller",
2052 "ROLE_DOMAIN_PDC": "active directory domain controller",
2053 "dc": "active directory domain controller",
2054 "member": "member server",
2055 "domain controller": "active directory domain controller",
2056 "active directory domain controller": "active directory domain controller",
2057 "member server": "member server",
2058 "standalone": "standalone server",
2059 "standalone server": "standalone server",
2063 def sanitize_server_role(role
):
2064 """Sanitize a server role name.
2066 :param role: Server role
2067 :raise ValueError: If the role can not be interpreted
2068 :return: Sanitized server role (one of "member server",
2069 "active directory domain controller", "standalone server")
2072 return _ROLES_MAP
[role
]
2074 raise ValueError(role
)
2077 def provision_fake_ypserver(logger
, samdb
, domaindn
, netbiosname
, nisdomain
):
2078 """Create AD entries for the fake ypserver.
2080 This is needed for being able to manipulate posix attrs via ADUC.
2082 samdb
.transaction_start()
2084 logger
.info("Setting up fake yp server settings")
2085 setup_add_ldif(samdb
, setup_path("ypServ30.ldif"), {
2086 "DOMAINDN": domaindn
,
2087 "NETBIOSNAME": netbiosname
,
2088 "NISDOMAIN": nisdomain
,
2091 samdb
.transaction_cancel()
2094 samdb
.transaction_commit()
2097 def directory_create_or_exists(path
, mode
=0o755):
2098 if not os
.path
.exists(path
):
2100 os
.mkdir(path
, mode
)
2101 except OSError as e
:
2102 if e
.errno
in [errno
.EEXIST
]:
2105 raise ProvisioningError("Failed to create directory %s: %s" % (path
, e
.strerror
))
2108 def determine_host_ip(logger
, lp
, hostip
=None):
2110 logger
.info("Looking up IPv4 addresses")
2111 hostips
= interface_ips_v4(lp
)
2112 if len(hostips
) > 0:
2114 if len(hostips
) > 1:
2115 logger
.warning("More than one IPv4 address found. Using %s",
2117 if hostip
== "127.0.0.1":
2120 logger
.warning("No IPv4 address will be assigned")
2125 def determine_host_ip6(logger
, lp
, hostip6
=None):
2127 logger
.info("Looking up IPv6 addresses")
2128 hostips
= interface_ips_v6(lp
)
2130 hostip6
= hostips
[0]
2131 if len(hostips
) > 1:
2132 logger
.warning("More than one IPv6 address found. Using %s", hostip6
)
2134 logger
.warning("No IPv6 address will be assigned")
2139 def provision(logger
, session_info
, smbconf
=None,
2140 targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None, rootdn
=None,
2141 domaindn
=None, schemadn
=None, configdn
=None, serverdn
=None,
2142 domain
=None, hostname
=None, hostip
=None, hostip6
=None, domainsid
=None,
2143 next_rid
=1000, dc_rid
=None, adminpass
=None, ldapadminpass
=None,
2144 krbtgtpass
=None, domainguid
=None, policyguid
=None, policyguid_dc
=None,
2145 dns_backend
=None, dns_forwarder
=None, dnspass
=None,
2146 invocationid
=None, machinepass
=None, ntdsguid
=None,
2147 root
=None, nobody
=None, users
=None,
2148 sitename
=None, serverrole
=None, dom_for_fun_level
=None,
2149 useeadb
=False, am_rodc
=False, lp
=None, use_ntvfs
=False,
2150 use_rfc2307
=False, skip_sysvolacl
=True,
2151 base_schema
="2019", adprep_level
=DS_DOMAIN_FUNCTION_2016
,
2152 plaintext_secrets
=False, backend_store
=None,
2153 backend_store_size
=None, batch_mode
=False):
2156 :note: caution, this wipes all existing data!
2160 serverrole
= sanitize_server_role(serverrole
)
2162 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole
)
2164 if dom_for_fun_level
is None:
2165 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2008_R2
2167 if base_schema
in ["2008_R2", "2008_R2_old"]:
2168 max_adprep_level
= DS_DOMAIN_FUNCTION_2008_R2
2169 elif base_schema
in ["2012"]:
2170 max_adprep_level
= DS_DOMAIN_FUNCTION_2012
2171 elif base_schema
in ["2012_R2"]:
2172 max_adprep_level
= DS_DOMAIN_FUNCTION_2012_R2
2174 max_adprep_level
= DS_DOMAIN_FUNCTION_2016
2176 if max_adprep_level
< dom_for_fun_level
:
2177 raise ProvisioningError('dom_for_fun_level[%u] incompatible with base_schema[%s]' %
2178 (dom_for_fun_level
, base_schema
))
2180 if adprep_level
is not None and max_adprep_level
< adprep_level
:
2181 raise ProvisioningError('base_schema[%s] incompatible with adprep_level[%u]' %
2182 (base_schema
, adprep_level
))
2184 if adprep_level
is not None and adprep_level
< dom_for_fun_level
:
2185 raise ProvisioningError('dom_for_fun_level[%u] incompatible with adprep_level[%u]' %
2186 (dom_for_fun_level
, adprep_level
))
2188 if ldapadminpass
is None:
2189 # Make a new, random password between Samba and it's LDAP server
2190 ldapadminpass
= samba
.generate_random_password(128, 255)
2192 if backend_store
is None:
2193 backend_store
= get_default_backend_store()
2195 if domainsid
is None:
2196 domainsid
= security
.random_sid()
2198 root_uid
= get_root_uid([root
or "root"], logger
)
2199 nobody_uid
= findnss_uid([nobody
or "nobody"])
2200 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
2201 root_gid
= pwd
.getpwuid(root_uid
).pw_gid
2204 bind_gid
= findnss_gid(["bind", "named"])
2208 if targetdir
is not None:
2209 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
2210 elif smbconf
is None:
2211 smbconf
= samba
.param
.default_path()
2212 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
2213 os
.makedirs(os
.path
.dirname(smbconf
))
2215 server_services
= []
2218 global_param
["idmap_ldb:use rfc2307"] = ["yes"]
2220 if dns_backend
!= "SAMBA_INTERNAL":
2221 server_services
.append("-dns")
2223 if dns_forwarder
is not None:
2224 global_param
["dns forwarder"] = [dns_forwarder
]
2227 server_services
.append("+smb")
2228 server_services
.append("-s3fs")
2229 global_param
["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2231 if len(server_services
) > 0:
2232 global_param
["server services"] = server_services
2234 # only install a new smb.conf if there isn't one there already
2235 if os
.path
.exists(smbconf
):
2236 # if Samba Team members can't figure out the weird errors
2237 # loading an empty smb.conf gives, then we need to be smarter.
2238 # Pretend it just didn't exist --abartlet
2239 f
= open(smbconf
, 'r')
2241 data
= f
.read().lstrip()
2244 if data
is None or data
== "":
2245 make_smbconf(smbconf
, hostname
, domain
, realm
,
2246 targetdir
, serverrole
=serverrole
,
2247 eadb
=useeadb
, use_ntvfs
=use_ntvfs
,
2248 lp
=lp
, global_param
=global_param
)
2250 make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
2251 serverrole
=serverrole
,
2252 eadb
=useeadb
, use_ntvfs
=use_ntvfs
, lp
=lp
, global_param
=global_param
)
2255 lp
= samba
.param
.LoadParm()
2257 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
2258 dnsdomain
=realm
, serverrole
=serverrole
, domaindn
=domaindn
,
2259 configdn
=configdn
, schemadn
=schemadn
, serverdn
=serverdn
,
2260 sitename
=sitename
, rootdn
=rootdn
, domain_names_forced
=(samdb_fill
== FILL_DRS
))
2261 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
2263 paths
.bind_gid
= bind_gid
2264 paths
.root_uid
= root_uid
2265 paths
.root_gid
= root_gid
2267 hostip
= determine_host_ip(logger
, lp
, hostip
)
2268 hostip6
= determine_host_ip6(logger
, lp
, hostip6
)
2269 names
.hostip
= hostip
2270 names
.hostip6
= hostip6
2271 names
.domainguid
= domainguid
2272 names
.domainsid
= domainsid
2273 names
.forestsid
= domainsid
2275 if serverrole
is None:
2276 serverrole
= lp
.get("server role")
2278 directory_create_or_exists(paths
.private_dir
, 0o700)
2279 directory_create_or_exists(paths
.binddns_dir
, 0o770)
2280 directory_create_or_exists(os
.path
.join(paths
.private_dir
, "tls"))
2281 directory_create_or_exists(paths
.state_dir
)
2282 if not plaintext_secrets
:
2283 setup_encrypted_secrets_key(paths
.encrypted_secrets_key_path
)
2285 if paths
.sysvol
and not os
.path
.exists(paths
.sysvol
):
2286 os
.makedirs(paths
.sysvol
, 0o775)
2288 schema
= Schema(domainsid
, invocationid
=invocationid
,
2289 schemadn
=names
.schemadn
, base_schema
=base_schema
)
2291 provision_backend
= LDBBackend(paths
=paths
,
2293 names
=names
, logger
=logger
)
2295 provision_backend
.init()
2296 provision_backend
.start()
2298 # only install a new shares config db if there is none
2299 if not os
.path
.exists(paths
.shareconf
):
2300 logger
.info("Setting up share.ldb")
2301 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
, lp
=lp
)
2302 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
2304 logger
.info("Setting up secrets.ldb")
2305 secrets_ldb
= setup_secretsdb(paths
,
2306 session_info
=session_info
, lp
=lp
)
2309 logger
.info("Setting up the registry")
2310 setup_registry(paths
.hklm
, session_info
, lp
=lp
)
2312 logger
.info("Setting up the privileges database")
2313 setup_privileges(paths
.privilege
, session_info
, lp
=lp
)
2315 logger
.info("Setting up idmap db")
2316 idmap
= setup_idmapdb(paths
.idmapdb
, session_info
=session_info
, lp
=lp
)
2318 setup_name_mappings(idmap
, sid
=str(domainsid
),
2319 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
2320 users_gid
=users_gid
)
2322 logger
.info("Setting up SAM db")
2323 samdb
= setup_samdb(paths
.samdb
, session_info
,
2324 provision_backend
, lp
, names
, logger
=logger
,
2325 serverrole
=serverrole
,
2326 schema
=schema
, am_rodc
=am_rodc
,
2327 plaintext_secrets
=plaintext_secrets
,
2328 backend_store
=backend_store
,
2329 backend_store_size
=backend_store_size
,
2330 batch_mode
=batch_mode
)
2332 if serverrole
== "active directory domain controller":
2333 if paths
.netlogon
is None:
2334 raise MissingShareError("netlogon", paths
.smbconf
)
2336 if paths
.sysvol
is None:
2337 raise MissingShareError("sysvol", paths
.smbconf
)
2339 if not os
.path
.isdir(paths
.netlogon
):
2340 os
.makedirs(paths
.netlogon
, 0o755)
2342 if adminpass
is None:
2343 adminpass
= samba
.generate_random_password(12, 32)
2344 adminpass_generated
= True
2346 if isinstance(adminpass
, bytes
):
2347 adminpass
= adminpass
.decode('utf-8')
2348 adminpass_generated
= False
2350 if samdb_fill
== FILL_FULL
:
2351 provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
2352 schema
=schema
, samdb_fill
=samdb_fill
,
2353 hostip
=hostip
, hostip6
=hostip6
,
2354 next_rid
=next_rid
, dc_rid
=dc_rid
, adminpass
=adminpass
,
2355 krbtgtpass
=krbtgtpass
,
2356 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
2357 invocationid
=invocationid
, machinepass
=machinepass
,
2358 ntdsguid
=ntdsguid
, dns_backend
=dns_backend
,
2359 dnspass
=dnspass
, serverrole
=serverrole
,
2360 dom_for_fun_level
=dom_for_fun_level
,
2361 lp
=lp
, use_ntvfs
=use_ntvfs
,
2362 skip_sysvolacl
=skip_sysvolacl
)
2364 if adprep_level
is not None:
2365 updates_allowed_overridden
= False
2366 if lp
.get("dsdb:schema update allowed") is None:
2367 lp
.set("dsdb:schema update allowed", "yes")
2368 print("Temporarily overriding 'dsdb:schema update allowed' setting")
2369 updates_allowed_overridden
= True
2371 samdb
.transaction_start()
2373 from samba
.forest_update
import ForestUpdate
2374 forest
= ForestUpdate(samdb
, fix
=True)
2376 forest
.check_updates_iterator([11, 54, 79, 80, 81, 82, 83])
2377 forest
.check_updates_functional_level(adprep_level
,
2378 DS_DOMAIN_FUNCTION_2008_R2
,
2379 update_revision
=True)
2381 samdb
.transaction_commit()
2382 except Exception as e
:
2383 samdb
.transaction_cancel()
2386 samdb
.transaction_start()
2388 from samba
.domain_update
import DomainUpdate
2390 DomainUpdate(samdb
, fix
=True).check_updates_functional_level(
2392 DS_DOMAIN_FUNCTION_2008
,
2393 update_revision
=True,
2396 samdb
.transaction_commit()
2397 except Exception as e
:
2398 samdb
.transaction_cancel()
2401 if updates_allowed_overridden
:
2402 lp
.set("dsdb:schema update allowed", "no")
2404 if not is_heimdal_built():
2405 create_kdc_conf(paths
.kdcconf
, realm
, domain
, os
.path
.dirname(lp
.get("log file")))
2406 logger
.info("The Kerberos KDC configuration for Samba AD is "
2407 "located at %s", paths
.kdcconf
)
2409 create_krb5_conf(paths
.krb5conf
,
2410 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
2412 logger
.info("A Kerberos configuration suitable for Samba AD has been "
2413 "generated at %s", paths
.krb5conf
)
2414 logger
.info("Merge the contents of this file with your system "
2415 "krb5.conf or replace it with this one. Do not create a "
2418 if serverrole
== "active directory domain controller":
2419 create_dns_update_list(paths
)
2421 backend_result
= provision_backend
.post_setup()
2422 provision_backend
.shutdown()
2425 secrets_ldb
.transaction_cancel()
2428 # Now commit the secrets.ldb to disk
2429 secrets_ldb
.transaction_commit()
2431 # the commit creates the dns.keytab in the private directory
2432 create_dns_dir_keytab_link(logger
, paths
)
2434 result
= ProvisionResult()
2435 result
.server_role
= serverrole
2436 result
.domaindn
= domaindn
2437 result
.paths
= paths
2438 result
.names
= names
2440 result
.samdb
= samdb
2441 result
.idmap
= idmap
2442 result
.domainsid
= str(domainsid
)
2444 if samdb_fill
== FILL_FULL
:
2445 result
.adminpass_generated
= adminpass_generated
2446 result
.adminpass
= adminpass
2448 result
.adminpass_generated
= False
2449 result
.adminpass
= None
2451 result
.backend_result
= backend_result
2454 provision_fake_ypserver(logger
=logger
, samdb
=samdb
,
2455 domaindn
=names
.domaindn
, netbiosname
=names
.netbiosname
,
2456 nisdomain
=names
.domain
.lower())
2461 def provision_become_dc(smbconf
=None, targetdir
=None, realm
=None,
2462 rootdn
=None, domaindn
=None, schemadn
=None,
2463 configdn
=None, serverdn
=None, domain
=None,
2464 hostname
=None, domainsid
=None,
2465 machinepass
=None, dnspass
=None,
2466 dns_backend
=None, sitename
=None, debuglevel
=1,
2469 logger
= logging
.getLogger("provision")
2470 samba
.set_debug_level(debuglevel
)
2472 res
= provision(logger
, system_session(),
2473 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
2474 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
2475 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
2476 hostname
=hostname
, hostip
=None, domainsid
=domainsid
,
2477 machinepass
=machinepass
,
2478 serverrole
="active directory domain controller",
2479 sitename
=sitename
, dns_backend
=dns_backend
, dnspass
=dnspass
,
2480 use_ntvfs
=use_ntvfs
)
2481 res
.lp
.set("debuglevel", str(debuglevel
))
2485 def create_krb5_conf(path
, dnsdomain
, hostname
, realm
):
2486 """Write out a file containing a valid krb5.conf file
2488 :param path: Path of the new krb5.conf file.
2489 :param dnsdomain: DNS Domain name
2490 :param hostname: Local hostname
2491 :param realm: Realm name
2493 setup_file(setup_path("krb5.conf"), path
, {
2494 "DNSDOMAIN": dnsdomain
,
2495 "HOSTNAME": hostname
,
2500 class ProvisioningError(Exception):
2501 """A generic provision error."""
2503 def __init__(self
, value
):
2507 return "ProvisioningError: " + self
.value
2510 class InvalidNetbiosName(Exception):
2511 """A specified name was not a valid NetBIOS name."""
2513 def __init__(self
, name
):
2515 "The name '%r' is not a valid NetBIOS name" % name
)
2518 class MissingShareError(ProvisioningError
):
2520 def __init__(self
, name
, smbconf
):
2522 "Existing smb.conf does not have a [%s] share, but you are "
2523 "configuring a DC. Please remove %s or add the share manually." %