1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba AD server
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 __docformat__
= "restructuredText"
29 from samba
.compat
import urllib_quote
30 from base64
import b64encode
47 from samba
.auth
import system_session
, admin_session
49 from samba
import auth
50 from samba
.samba3
import smbd
, passdb
51 from samba
.samba3
import param
as s3param
52 from samba
.dsdb
import DS_DOMAIN_FUNCTION_2000
56 check_all_substituted
,
57 is_valid_netbios_char
,
64 from samba
.dcerpc
import security
, misc
65 from samba
.dcerpc
.misc
import (
69 from samba
.dsdb
import (
70 DS_DOMAIN_FUNCTION_2003
,
71 DS_DOMAIN_FUNCTION_2008_R2
,
74 from samba
.idmap
import IDmapDB
75 from samba
.ms_display_specifiers
import read_ms_ldif
76 from samba
.ntacls
import setntacl
, getntacl
, dsacl2fsacl
77 from samba
.ndr
import ndr_pack
, ndr_unpack
78 from samba
.provision
.backend
import (
84 from samba
.descriptor
import (
86 get_config_descriptor
,
87 get_config_partitions_descriptor
,
88 get_config_sites_descriptor
,
89 get_config_ntds_quotas_descriptor
,
90 get_config_delete_protected1_descriptor
,
91 get_config_delete_protected1wd_descriptor
,
92 get_config_delete_protected2_descriptor
,
93 get_domain_descriptor
,
94 get_domain_infrastructure_descriptor
,
95 get_domain_builtin_descriptor
,
96 get_domain_computers_descriptor
,
97 get_domain_users_descriptor
,
98 get_domain_controllers_descriptor
,
99 get_domain_delete_protected1_descriptor
,
100 get_domain_delete_protected2_descriptor
,
101 get_dns_partition_descriptor
,
102 get_dns_forest_microsoft_dns_descriptor
,
103 get_dns_domain_microsoft_dns_descriptor
,
104 get_managed_service_accounts_descriptor
,
106 from samba
.provision
.common
import (
115 from samba
.provision
.sambadns
import (
118 create_dns_update_list
122 import samba
.registry
123 from samba
.schema
import Schema
124 from samba
.samdb
import SamDB
125 from samba
.dbchecker
import dbcheck
126 from samba
.provision
.kerberos
import create_kdc_conf
127 from samba
.samdb
import get_default_backend_store
129 DEFAULT_POLICY_GUID
= "31B2F340-016D-11D2-945F-00C04FB984F9"
130 DEFAULT_DC_POLICY_GUID
= "6AC1786C-016F-11D2-945F-00C04FB984F9"
131 DEFAULTSITE
= "Default-First-Site-Name"
132 LAST_PROVISION_USN_ATTRIBUTE
= "lastProvisionUSN"
134 DEFAULT_MIN_PWD_LENGTH
= 7
137 class ProvisionPaths(object):
140 self
.shareconf
= None
151 self
.dns_keytab
= None
154 self
.private_dir
= None
155 self
.binddns_dir
= None
156 self
.state_dir
= None
159 class ProvisionNames(object):
167 self
.dnsforestdn
= None
168 self
.dnsdomaindn
= None
169 self
.ldapmanagerdn
= None
170 self
.dnsdomain
= None
172 self
.netbiosname
= None
177 self
.domainsid
= None
178 self
.forestsid
= None
179 self
.domainguid
= None
183 def find_provision_key_parameters(samdb
, secretsdb
, idmapdb
, paths
, smbconf
,
185 """Get key provision parameters (realm, domain, ...) from a given provision
187 :param samdb: An LDB object connected to the sam.ldb file
188 :param secretsdb: An LDB object connected to the secrets.ldb file
189 :param idmapdb: An LDB object connected to the idmap.ldb file
190 :param paths: A list of path to provision object
191 :param smbconf: Path to the smb.conf file
192 :param lp: A LoadParm object
193 :return: A list of key provision parameters
195 names
= ProvisionNames()
196 names
.adminpass
= None
198 # NT domain, kerberos realm, root dn, domain dn, domain dns name
199 names
.domain
= lp
.get("workgroup").upper()
200 names
.realm
= lp
.get("realm")
201 names
.dnsdomain
= names
.realm
.lower()
202 basedn
= samba
.dn_from_dns_name(names
.dnsdomain
)
203 names
.realm
= names
.realm
.upper()
205 # Get the netbiosname first (could be obtained from smb.conf in theory)
206 res
= secretsdb
.search(expression
="(flatname=%s)" %
207 names
.domain
,base
="CN=Primary Domains",
208 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
209 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
211 names
.smbconf
= smbconf
213 # That's a bit simplistic but it's ok as long as we have only 3
215 current
= samdb
.search(expression
="(objectClass=*)",
216 base
="", scope
=ldb
.SCOPE_BASE
,
217 attrs
=["defaultNamingContext", "schemaNamingContext",
218 "configurationNamingContext","rootDomainNamingContext",
221 names
.configdn
= current
[0]["configurationNamingContext"][0]
222 names
.schemadn
= current
[0]["schemaNamingContext"][0]
223 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
,
224 current
[0]["defaultNamingContext"][0].decode('utf8')))):
225 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
226 "is not the same ..." % (paths
.samdb
,
227 str(current
[0]["defaultNamingContext"][0].decode('utf8')),
228 paths
.smbconf
, basedn
)))
230 names
.domaindn
=current
[0]["defaultNamingContext"][0]
231 names
.rootdn
=current
[0]["rootDomainNamingContext"][0]
232 names
.ncs
=current
[0]["namingContexts"]
233 names
.dnsforestdn
= None
234 names
.dnsdomaindn
= None
236 for i
in range(0, len(names
.ncs
)):
239 dnsforestdn
= "DC=ForestDnsZones,%s" % (str(names
.rootdn
))
240 if nc
== dnsforestdn
:
241 names
.dnsforestdn
= dnsforestdn
244 dnsdomaindn
= "DC=DomainDnsZones,%s" % (str(names
.domaindn
))
245 if nc
== dnsdomaindn
:
246 names
.dnsdomaindn
= dnsdomaindn
250 res3
= samdb
.search(expression
="(objectClass=site)",
251 base
="CN=Sites," + names
.configdn
, scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn"])
252 names
.sitename
= str(res3
[0]["cn"])
254 # dns hostname and server dn
255 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
256 base
="OU=Domain Controllers,%s" % basedn
,
257 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
259 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names
.netbiosname
, basedn
))
261 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("." + names
.dnsdomain
, "")
263 server_res
= samdb
.search(expression
="serverReference=%s" % res4
[0].dn
,
264 attrs
=[], base
=names
.configdn
)
265 names
.serverdn
= str(server_res
[0].dn
)
267 # invocation id/objectguid
268 res5
= samdb
.search(expression
="(objectClass=*)",
269 base
="CN=NTDS Settings,%s" % str(names
.serverdn
),
270 scope
=ldb
.SCOPE_BASE
,
271 attrs
=["invocationID", "objectGUID"])
272 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
273 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
276 res6
= samdb
.search(expression
="(objectClass=*)", base
=basedn
,
277 scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID",
278 "objectSid","msDS-Behavior-Version" ])
279 names
.domainguid
= str(ndr_unpack(misc
.GUID
, res6
[0]["objectGUID"][0]))
280 names
.domainsid
= ndr_unpack( security
.dom_sid
, res6
[0]["objectSid"][0])
281 names
.forestsid
= ndr_unpack( security
.dom_sid
, res6
[0]["objectSid"][0])
282 if res6
[0].get("msDS-Behavior-Version") is None or \
283 int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
284 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
286 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
289 res7
= samdb
.search(expression
="(name={%s})" % DEFAULT_POLICY_GUID
,
290 base
="CN=Policies,CN=System," + basedn
,
291 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
292 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
294 res8
= samdb
.search(expression
="(name={%s})" % DEFAULT_DC_POLICY_GUID
,
295 base
="CN=Policies,CN=System," + basedn
,
296 scope
=ldb
.SCOPE_ONELEVEL
,
297 attrs
=["cn","displayName"])
299 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
301 names
.policyid_dc
= None
303 res9
= idmapdb
.search(expression
="(cn=%s-%s)" %
304 (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
),
305 attrs
=["xidNumber", "type"])
307 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
))
308 if res9
[0]["type"][0] == "ID_TYPE_BOTH":
309 names
.root_gid
= res9
[0]["xidNumber"][0]
311 names
.root_gid
= pwd
.getpwuid(int(res9
[0]["xidNumber"][0])).pw_gid
313 res10
= samdb
.search(expression
="(samaccountname=dns)",
314 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["dn"],
315 controls
=["search_options:1:2"])
317 has_legacy_dns_account
= True
319 has_legacy_dns_account
= False
321 res11
= samdb
.search(expression
="(samaccountname=dns-%s)" % names
.netbiosname
,
322 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["dn"],
323 controls
=["search_options:1:2"])
325 has_dns_account
= True
327 has_dns_account
= False
329 if names
.dnsdomaindn
is not None:
331 names
.dns_backend
= 'BIND9_DLZ'
333 names
.dns_backend
= 'SAMBA_INTERNAL'
334 elif has_dns_account
or has_legacy_dns_account
:
335 names
.dns_backend
= 'BIND9_FLATFILE'
337 names
.dns_backend
= 'NONE'
339 dns_admins_sid
= get_dnsadmins_sid(samdb
, names
.domaindn
)
340 names
.name_map
['DnsAdmins'] = str(dns_admins_sid
)
345 def update_provision_usn(samdb
, low
, high
, id, replace
=False):
346 """Update the field provisionUSN in sam.ldb
348 This field is used to track range of USN modified by provision and
350 This value is used afterward by next provision to figure out if
351 the field have been modified since last provision.
353 :param samdb: An LDB object connect to sam.ldb
354 :param low: The lowest USN modified by this upgrade
355 :param high: The highest USN modified by this upgrade
356 :param id: The invocation id of the samba's dc
357 :param replace: A boolean indicating if the range should replace any
358 existing one or appended (default)
363 entry
= samdb
.search(base
="@PROVISION",
364 scope
=ldb
.SCOPE_BASE
,
365 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "dn"])
366 for e
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
367 if not re
.search(';', e
):
368 e
= "%s;%s" % (e
, id)
371 tab
.append("%s-%s;%s" % (low
, high
, id))
372 delta
= ldb
.Message()
373 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
374 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
375 ldb
.FLAG_MOD_REPLACE
, LAST_PROVISION_USN_ATTRIBUTE
)
376 entry
= samdb
.search(expression
='provisionnerID=*',
377 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
378 attrs
=["provisionnerID"])
379 if len(entry
) == 0 or len(entry
[0]) == 0:
380 delta
["provisionnerID"] = ldb
.MessageElement(id, ldb
.FLAG_MOD_ADD
, "provisionnerID")
384 def set_provision_usn(samdb
, low
, high
, id):
385 """Set the field provisionUSN in sam.ldb
386 This field is used to track range of USN modified by provision and
388 This value is used afterward by next provision to figure out if
389 the field have been modified since last provision.
391 :param samdb: An LDB object connect to sam.ldb
392 :param low: The lowest USN modified by this upgrade
393 :param high: The highest USN modified by this upgrade
394 :param id: The invocationId of the provision"""
397 tab
.append("%s-%s;%s" % (low
, high
, id))
399 delta
= ldb
.Message()
400 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
401 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
402 ldb
.FLAG_MOD_ADD
, LAST_PROVISION_USN_ATTRIBUTE
)
406 def get_max_usn(samdb
,basedn
):
407 """ This function return the biggest USN present in the provision
409 :param samdb: A LDB object pointing to the sam.ldb
410 :param basedn: A string containing the base DN of the provision
412 :return: The biggest USN in the provision"""
414 res
= samdb
.search(expression
="objectClass=*",base
=basedn
,
415 scope
=ldb
.SCOPE_SUBTREE
,attrs
=["uSNChanged"],
416 controls
=["search_options:1:2",
417 "server_sort:1:1:uSNChanged",
418 "paged_results:1:1"])
419 return res
[0]["uSNChanged"]
422 def get_last_provision_usn(sam
):
423 """Get USNs ranges modified by a provision or an upgradeprovision
425 :param sam: An LDB object pointing to the sam.ldb
426 :return: a dictionary which keys are invocation id and values are an array
427 of integer representing the different ranges
430 entry
= sam
.search(expression
="%s=*" % LAST_PROVISION_USN_ATTRIBUTE
,
431 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
432 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "provisionnerID"])
433 except ldb
.LdbError
as e1
:
434 (ecode
, emsg
) = e1
.args
435 if ecode
== ldb
.ERR_NO_SUCH_OBJECT
:
442 if entry
[0].get("provisionnerID"):
443 for e
in entry
[0]["provisionnerID"]:
445 for r
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
446 tab1
= str(r
).split(';')
451 if (len(myids
) > 0 and id not in myids
):
453 tab2
= p
.split(tab1
[0])
454 if range.get(id) is None:
456 range[id].append(tab2
[0])
457 range[id].append(tab2
[1])
463 class ProvisionResult(object):
464 """Result of a provision.
466 :ivar server_role: The server role
467 :ivar paths: ProvisionPaths instance
468 :ivar domaindn: The domain dn, as string
472 self
.server_role
= None
479 self
.domainsid
= None
480 self
.adminpass_generated
= None
481 self
.adminpass
= None
482 self
.backend_result
= None
484 def report_logger(self
, logger
):
485 """Report this provision result to a logger."""
487 "Once the above files are installed, your Samba AD server will "
489 if self
.adminpass_generated
:
490 logger
.info("Admin password: %s", self
.adminpass
)
491 logger
.info("Server Role: %s", self
.server_role
)
492 logger
.info("Hostname: %s", self
.names
.hostname
)
493 logger
.info("NetBIOS Domain: %s", self
.names
.domain
)
494 logger
.info("DNS Domain: %s", self
.names
.dnsdomain
)
495 logger
.info("DOMAIN SID: %s", self
.domainsid
)
497 if self
.backend_result
:
498 self
.backend_result
.report_logger(logger
)
501 def check_install(lp
, session_info
, credentials
):
502 """Check whether the current install seems ok.
504 :param lp: Loadparm context
505 :param session_info: Session information
506 :param credentials: Credentials
508 if lp
.get("realm") == "":
509 raise Exception("Realm empty")
510 samdb
= Ldb(lp
.samdb_url(), session_info
=session_info
,
511 credentials
=credentials
, lp
=lp
)
512 if len(samdb
.search("(cn=Administrator)")) != 1:
513 raise ProvisioningError("No administrator account found")
516 def findnss(nssfn
, names
):
517 """Find a user or group from a list of possibilities.
519 :param nssfn: NSS Function to try (should raise KeyError if not found)
520 :param names: Names to check.
521 :return: Value return by first names list.
528 raise KeyError("Unable to find user/group in %r" % names
)
531 def findnss_uid(names
):
532 return findnss(pwd
.getpwnam
, names
)[2]
535 def findnss_gid(names
):
536 return findnss(grp
.getgrnam
, names
)[2]
539 def provision_paths_from_lp(lp
, dnsdomain
):
540 """Set the default paths for provisioning.
542 :param lp: Loadparm context.
543 :param dnsdomain: DNS Domain name
545 paths
= ProvisionPaths()
546 paths
.private_dir
= lp
.get("private dir")
547 paths
.binddns_dir
= lp
.get("binddns dir")
548 paths
.state_dir
= lp
.get("state directory")
550 # This is stored without path prefix for the "privateKeytab" attribute in
551 # "secrets_dns.ldif".
552 paths
.dns_keytab
= "dns.keytab"
553 paths
.keytab
= "secrets.keytab"
555 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
556 paths
.samdb
= os
.path
.join(paths
.private_dir
, "sam.ldb")
557 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, "idmap.ldb")
558 paths
.secrets
= os
.path
.join(paths
.private_dir
, "secrets.ldb")
559 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
560 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
561 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
562 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
563 paths
.kdcconf
= os
.path
.join(paths
.private_dir
, "kdc.conf")
564 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
565 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
566 paths
.encrypted_secrets_key_path
= os
.path
.join(
568 "encrypted_secrets.key")
570 paths
.dns
= os
.path
.join(paths
.binddns_dir
, "dns", dnsdomain
+ ".zone")
571 paths
.namedconf
= os
.path
.join(paths
.binddns_dir
, "named.conf")
572 paths
.namedconf_update
= os
.path
.join(paths
.binddns_dir
, "named.conf.update")
573 paths
.namedtxt
= os
.path
.join(paths
.binddns_dir
, "named.txt")
575 paths
.hklm
= "hklm.ldb"
576 paths
.hkcr
= "hkcr.ldb"
577 paths
.hkcu
= "hkcu.ldb"
578 paths
.hku
= "hku.ldb"
579 paths
.hkpd
= "hkpd.ldb"
580 paths
.hkpt
= "hkpt.ldb"
581 paths
.sysvol
= lp
.get("path", "sysvol")
582 paths
.netlogon
= lp
.get("path", "netlogon")
583 paths
.smbconf
= lp
.configfile
587 def determine_netbios_name(hostname
):
588 """Determine a netbios name from a hostname."""
589 # remove forbidden chars and force the length to be <16
590 netbiosname
= "".join([x
for x
in hostname
if is_valid_netbios_char(x
)])
591 return netbiosname
[:MAX_NETBIOS_NAME_LEN
].upper()
594 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
595 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
596 schemadn
=None, serverdn
=None, sitename
=None,
597 domain_names_forced
=False):
598 """Guess configuration settings to use."""
601 hostname
= socket
.gethostname().split(".")[0]
603 netbiosname
= lp
.get("netbios name")
604 if netbiosname
is None:
605 netbiosname
= determine_netbios_name(hostname
)
606 netbiosname
= netbiosname
.upper()
607 if not valid_netbios_name(netbiosname
):
608 raise InvalidNetbiosName(netbiosname
)
610 if dnsdomain
is None:
611 dnsdomain
= lp
.get("realm")
612 if dnsdomain
is None or dnsdomain
== "":
613 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp
.configfile
)
615 dnsdomain
= dnsdomain
.lower()
617 if serverrole
is None:
618 serverrole
= lp
.get("server role")
619 if serverrole
is None:
620 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
622 serverrole
= serverrole
.lower()
624 realm
= dnsdomain
.upper()
626 if lp
.get("realm") == "":
627 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
629 if lp
.get("realm").upper() != realm
:
630 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
))
632 if lp
.get("server role").lower() != serverrole
:
633 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
))
635 if serverrole
== "active directory domain controller":
637 # This will, for better or worse, default to 'WORKGROUP'
638 domain
= lp
.get("workgroup")
639 domain
= domain
.upper()
641 if lp
.get("workgroup").upper() != domain
:
642 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
))
645 domaindn
= samba
.dn_from_dns_name(dnsdomain
)
647 if domain
== netbiosname
:
648 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain
, netbiosname
))
652 domaindn
= "DC=" + netbiosname
654 if not valid_netbios_name(domain
):
655 raise InvalidNetbiosName(domain
)
657 if hostname
.upper() == realm
:
658 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
659 if netbiosname
.upper() == realm
:
660 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm
, netbiosname
))
661 if domain
== realm
and not domain_names_forced
:
662 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
664 if serverrole
!= "active directory domain controller":
666 # This is the code path for a domain member
667 # where we provision the database as if we where
668 # on a domain controller, so we should not use
669 # the same dnsdomain as the domain controllers
670 # of our primary domain.
672 # This will be important if we start doing
673 # SID/name filtering and reject the local
674 # sid and names if they come from a domain
678 dnsdomain
= netbiosname
.lower()
684 configdn
= "CN=Configuration," + rootdn
686 schemadn
= "CN=Schema," + configdn
689 sitename
= DEFAULTSITE
691 names
= ProvisionNames()
692 names
.rootdn
= rootdn
693 names
.domaindn
= domaindn
694 names
.configdn
= configdn
695 names
.schemadn
= schemadn
696 names
.ldapmanagerdn
= "CN=Manager," + rootdn
697 names
.dnsdomain
= dnsdomain
698 names
.domain
= domain
700 names
.netbiosname
= netbiosname
701 names
.hostname
= hostname
702 names
.sitename
= sitename
703 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
704 netbiosname
, sitename
, configdn
)
708 def make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
709 serverrole
=None, eadb
=False, use_ntvfs
=False, lp
=None,
711 """Create a new smb.conf file based on a couple of basic settings.
713 assert smbconf
is not None
716 hostname
= socket
.gethostname().split(".")[0]
718 netbiosname
= determine_netbios_name(hostname
)
720 if serverrole
is None:
721 serverrole
= "standalone server"
723 assert domain
is not None
724 domain
= domain
.upper()
726 assert realm
is not None
727 realm
= realm
.upper()
730 "netbios name": netbiosname
,
733 "server role": serverrole
,
737 lp
= samba
.param
.LoadParm()
738 #Load non-existent file
739 if os
.path
.exists(smbconf
):
742 if global_param
is not None:
743 for ent
in global_param
:
744 if global_param
[ent
] is not None:
745 global_settings
[ent
] = " ".join(global_param
[ent
])
747 if targetdir
is not None:
748 global_settings
["private dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "private"))
749 global_settings
["lock dir"] = os
.path
.abspath(targetdir
)
750 global_settings
["state directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "state"))
751 global_settings
["cache directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "cache"))
752 global_settings
["binddns dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "bind-dns"))
754 lp
.set("lock dir", os
.path
.abspath(targetdir
))
755 lp
.set("state directory", global_settings
["state directory"])
756 lp
.set("cache directory", global_settings
["cache directory"])
757 lp
.set("binddns dir", global_settings
["binddns dir"])
760 if use_ntvfs
and not lp
.get("posix:eadb"):
761 if targetdir
is not None:
762 privdir
= os
.path
.join(targetdir
, "private")
764 privdir
= lp
.get("private dir")
765 lp
.set("posix:eadb", os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
766 elif not use_ntvfs
and not lp
.get("xattr_tdb:file"):
767 if targetdir
is not None:
768 statedir
= os
.path
.join(targetdir
, "state")
770 statedir
= lp
.get("state directory")
771 lp
.set("xattr_tdb:file", os
.path
.abspath(os
.path
.join(statedir
, "xattr.tdb")))
774 if serverrole
== "active directory domain controller":
775 shares
["sysvol"] = os
.path
.join(lp
.get("state directory"), "sysvol")
776 shares
["netlogon"] = os
.path
.join(shares
["sysvol"], realm
.lower(),
779 global_settings
["passdb backend"] = "samba_dsdb"
781 f
= open(smbconf
, 'w')
783 f
.write("[globals]\n")
784 for key
, val
in global_settings
.items():
785 f
.write("\t%s = %s\n" % (key
, val
))
788 for name
, path
in shares
.items():
789 f
.write("[%s]\n" % name
)
790 f
.write("\tpath = %s\n" % path
)
791 f
.write("\tread only = no\n")
795 # reload the smb.conf
798 # and dump it without any values that are the default
799 # this ensures that any smb.conf parameters that were set
800 # on the provision/join command line are set in the resulting smb.conf
801 lp
.dump(False, smbconf
)
804 def setup_name_mappings(idmap
, sid
, root_uid
, nobody_uid
,
805 users_gid
, root_gid
):
806 """setup reasonable name mappings for sam names to unix names.
808 :param samdb: SamDB object.
809 :param idmap: IDmap db object.
810 :param sid: The domain sid.
811 :param domaindn: The domain DN.
812 :param root_uid: uid of the UNIX root user.
813 :param nobody_uid: uid of the UNIX nobody user.
814 :param users_gid: gid of the UNIX users group.
815 :param root_gid: gid of the UNIX root group.
817 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
819 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
820 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
823 def setup_samdb_partitions(samdb_path
, logger
, lp
, session_info
,
824 provision_backend
, names
, serverrole
,
825 erase
=False, plaintext_secrets
=False,
827 """Setup the partitions for the SAM database.
829 Alternatively, provision() may call this, and then populate the database.
831 :note: This will wipe the Sam Database!
833 :note: This function always removes the local SAM LDB file. The erase
834 parameter controls whether to erase the existing data, which
835 may not be stored locally but in LDAP.
838 assert session_info
is not None
840 # We use options=["modules:"] to stop the modules loading - we
841 # just want to wipe and re-initialise the database, not start it up
844 os
.unlink(samdb_path
)
848 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
849 lp
=lp
, options
=["modules:"])
851 ldap_backend_line
= "# No LDAP backend"
852 if provision_backend
.type != "ldb":
853 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldap_uri
855 required_features
= None
856 if not plaintext_secrets
:
857 required_features
= "requiredFeatures: encryptedSecrets"
859 if backend_store
is None:
860 backend_store
= get_default_backend_store()
861 backend_store_line
= "backendStore: %s" % backend_store
863 if backend_store
== "mdb":
864 if required_features
is not None:
865 required_features
+= "\n"
867 required_features
= ""
868 required_features
+= "requiredFeatures: lmdbLevelOne"
870 if required_features
is None:
871 required_features
= "# No required features"
873 samdb
.transaction_start()
875 logger
.info("Setting up sam.ldb partitions and settings")
876 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
877 "LDAP_BACKEND_LINE": ldap_backend_line
,
878 "BACKEND_STORE": backend_store_line
882 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
883 "BACKEND_TYPE": provision_backend
.type,
884 "SERVER_ROLE": serverrole
,
885 "REQUIRED_FEATURES": required_features
888 logger
.info("Setting up sam.ldb rootDSE")
889 setup_samdb_rootdse(samdb
, names
)
891 samdb
.transaction_cancel()
894 samdb
.transaction_commit()
897 def secretsdb_self_join(secretsdb
, domain
,
898 netbiosname
, machinepass
, domainsid
=None,
899 realm
=None, dnsdomain
=None,
901 key_version_number
=1,
902 secure_channel_type
=SEC_CHAN_WKSTA
):
903 """Add domain join-specific bits to a secrets database.
905 :param secretsdb: Ldb Handle to the secrets database
906 :param machinepass: Machine password
908 attrs
= ["whenChanged",
915 if realm
is not None:
916 if dnsdomain
is None:
917 dnsdomain
= realm
.lower()
918 dnsname
= '%s.%s' % (netbiosname
.lower(), dnsdomain
.lower())
921 shortname
= netbiosname
.lower()
923 # We don't need to set msg["flatname"] here, because rdn_name will handle
924 # it, and it causes problems for modifies anyway
925 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
926 msg
["secureChannelType"] = [str(secure_channel_type
)]
927 msg
["objectClass"] = ["top", "primaryDomain"]
928 if dnsname
is not None:
929 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
930 msg
["realm"] = [realm
]
931 msg
["saltPrincipal"] = ["host/%s@%s" % (dnsname
, realm
.upper())]
932 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
933 msg
["privateKeytab"] = ["secrets.keytab"]
935 msg
["secret"] = [machinepass
.encode('utf-8')]
936 msg
["samAccountName"] = ["%s$" % netbiosname
]
937 msg
["secureChannelType"] = [str(secure_channel_type
)]
938 if domainsid
is not None:
939 msg
["objectSid"] = [ndr_pack(domainsid
)]
941 # This complex expression tries to ensure that we don't have more
942 # than one record for this SID, realm or netbios domain at a time,
943 # but we don't delete the old record that we are about to modify,
944 # because that would delete the keytab and previous password.
945 res
= secretsdb
.search(base
="cn=Primary Domains", attrs
=attrs
,
946 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain
, realm
, str(domainsid
), str(msg
.dn
))),
947 scope
=ldb
.SCOPE_ONELEVEL
)
950 secretsdb
.delete(del_msg
.dn
)
952 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
955 msg
["priorSecret"] = [res
[0]["secret"][0]]
957 msg
["priorWhenChanged"] = [res
[0]["whenChanged"][0]]
962 msg
["privateKeytab"] = [res
[0]["privateKeytab"][0]]
967 msg
["krb5Keytab"] = [res
[0]["krb5Keytab"][0]]
973 msg
[el
].set_flags(ldb
.FLAG_MOD_REPLACE
)
974 secretsdb
.modify(msg
)
975 secretsdb
.rename(res
[0].dn
, msg
.dn
)
977 spn
= [ 'HOST/%s' % shortname
]
978 if secure_channel_type
== SEC_CHAN_BDC
and dnsname
is not None:
979 # we are a domain controller then we add servicePrincipalName
980 # entries for the keytab code to update.
981 spn
.extend([ 'HOST/%s' % dnsname
])
982 msg
["servicePrincipalName"] = spn
987 def setup_secretsdb(paths
, session_info
, backend_credentials
, lp
):
988 """Setup the secrets database.
990 :note: This function does not handle exceptions and transaction on purpose,
991 it's up to the caller to do this job.
993 :param path: Path to the secrets database.
994 :param session_info: Session info.
995 :param credentials: Credentials
996 :param lp: Loadparm context
997 :return: LDB handle for the created secrets database
999 if os
.path
.exists(paths
.secrets
):
1000 os
.unlink(paths
.secrets
)
1002 keytab_path
= os
.path
.join(paths
.private_dir
, paths
.keytab
)
1003 if os
.path
.exists(keytab_path
):
1004 os
.unlink(keytab_path
)
1006 bind_dns_keytab_path
= os
.path
.join(paths
.binddns_dir
, paths
.dns_keytab
)
1007 if os
.path
.exists(bind_dns_keytab_path
):
1008 os
.unlink(bind_dns_keytab_path
)
1010 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
1011 if os
.path
.exists(dns_keytab_path
):
1012 os
.unlink(dns_keytab_path
)
1014 path
= paths
.secrets
1016 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
1018 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
1019 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
1020 secrets_ldb
.transaction_start()
1022 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
1024 if (backend_credentials
is not None and
1025 backend_credentials
.authentication_requested()):
1026 if backend_credentials
.get_bind_dn() is not None:
1027 setup_add_ldif(secrets_ldb
,
1028 setup_path("secrets_simple_ldap.ldif"), {
1029 "LDAPMANAGERDN": backend_credentials
.get_bind_dn(),
1030 "LDAPMANAGERPASS_B64": b64encode(backend_credentials
.get_password()).decode('utf8')
1033 setup_add_ldif(secrets_ldb
,
1034 setup_path("secrets_sasl_ldap.ldif"), {
1035 "LDAPADMINUSER": backend_credentials
.get_username(),
1036 "LDAPADMINREALM": backend_credentials
.get_realm(),
1037 "LDAPADMINPASS_B64": b64encode(backend_credentials
.get_password()).decode('utf8')
1040 secrets_ldb
.transaction_cancel()
1045 def setup_privileges(path
, session_info
, lp
):
1046 """Setup the privileges database.
1048 :param path: Path to the privileges database.
1049 :param session_info: Session info.
1050 :param credentials: Credentials
1051 :param lp: Loadparm context
1052 :return: LDB handle for the created secrets database
1054 if os
.path
.exists(path
):
1056 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
1057 privilege_ldb
.erase()
1058 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1060 def setup_encrypted_secrets_key(path
):
1061 """Setup the encrypted secrets key file.
1063 Any existing key file will be deleted and a new random key generated.
1065 :param path: Path to the secrets key file.
1068 if os
.path
.exists(path
):
1071 flags
= os
.O_WRONLY | os
.O_CREAT | os
.O_EXCL
1072 mode
= stat
.S_IRUSR | stat
.S_IWUSR
1074 umask_original
= os
.umask(0)
1076 fd
= os
.open(path
, flags
, mode
)
1078 os
.umask(umask_original
)
1080 with os
.fdopen(fd
, 'w') as f
:
1081 key
= samba
.generate_random_bytes(16)
1085 def setup_registry(path
, session_info
, lp
):
1086 """Setup the registry.
1088 :param path: Path to the registry database
1089 :param session_info: Session information
1090 :param credentials: Credentials
1091 :param lp: Loadparm context
1093 reg
= samba
.registry
.Registry()
1094 hive
= samba
.registry
.open_ldb(path
, session_info
=session_info
, lp_ctx
=lp
)
1095 reg
.mount_hive(hive
, samba
.registry
.HKEY_LOCAL_MACHINE
)
1096 provision_reg
= setup_path("provision.reg")
1097 assert os
.path
.exists(provision_reg
)
1098 reg
.diff_apply(provision_reg
)
1101 def setup_idmapdb(path
, session_info
, lp
):
1102 """Setup the idmap database.
1104 :param path: path to the idmap database
1105 :param session_info: Session information
1106 :param credentials: Credentials
1107 :param lp: Loadparm context
1109 if os
.path
.exists(path
):
1112 idmap_ldb
= IDmapDB(path
, session_info
=session_info
, lp
=lp
)
1114 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
1118 def setup_samdb_rootdse(samdb
, names
):
1119 """Setup the SamDB rootdse.
1121 :param samdb: Sam Database handle
1123 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
1124 "SCHEMADN": names
.schemadn
,
1125 "DOMAINDN": names
.domaindn
,
1126 "ROOTDN" : names
.rootdn
,
1127 "CONFIGDN": names
.configdn
,
1128 "SERVERDN": names
.serverdn
,
1132 def setup_self_join(samdb
, admin_session_info
, names
, fill
, machinepass
,
1133 dns_backend
, dnspass
, domainsid
, next_rid
, invocationid
,
1134 policyguid
, policyguid_dc
,
1135 domainControllerFunctionality
, ntdsguid
=None, dc_rid
=None):
1136 """Join a host to its own domain."""
1137 assert isinstance(invocationid
, str)
1138 if ntdsguid
is not None:
1139 ntdsguid_line
= "objectGUID: %s\n"%ntdsguid
1146 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
1147 "CONFIGDN": names
.configdn
,
1148 "SCHEMADN": names
.schemadn
,
1149 "DOMAINDN": names
.domaindn
,
1150 "SERVERDN": names
.serverdn
,
1151 "INVOCATIONID": invocationid
,
1152 "NETBIOSNAME": names
.netbiosname
,
1153 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1154 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')).decode('utf8'),
1155 "DOMAINSID": str(domainsid
),
1156 "DCRID": str(dc_rid
),
1157 "SAMBA_VERSION_STRING": version
,
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_config.ldif"), {
1192 "CONFIGDN": names
.configdn
,
1193 "SCHEMADN": names
.schemadn
,
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 controler 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 def setup_samdb(path
, session_info
, provision_backend
, lp
, names
,
1271 logger
, fill
, serverrole
, schema
, am_rodc
=False,
1272 plaintext_secrets
=False, backend_store
=None):
1273 """Setup a complete SAM Database.
1275 :note: This will wipe the main SAM database file!
1278 # Also wipes the database
1279 setup_samdb_partitions(path
, logger
=logger
, lp
=lp
,
1280 provision_backend
=provision_backend
, session_info
=session_info
,
1281 names
=names
, serverrole
=serverrole
, plaintext_secrets
=plaintext_secrets
,
1282 backend_store
=backend_store
)
1284 # Load the database, but don's load the global schema and don't connect
1286 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
1287 credentials
=provision_backend
.credentials
, lp
=lp
,
1288 global_schema
=False, am_rodc
=am_rodc
)
1290 logger
.info("Pre-loading the Samba 4 and AD schema")
1292 # Load the schema from the one we computed earlier
1293 samdb
.set_schema(schema
, write_indices_and_attributes
=False)
1295 # Set the NTDS settings DN manually - in order to have it already around
1296 # before the provisioned tree exists and we connect
1297 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1299 # And now we can connect to the DB - the schema won't be loaded from the
1303 except ldb
.LdbError
as e2
:
1304 (num
, string_error
) = e2
.args
1305 if (num
== ldb
.ERR_INSUFFICIENT_ACCESS_RIGHTS
):
1306 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path
)
1310 # But we have to give it one more kick to have it use the schema
1311 # during provision - it needs, now that it is connected, to write
1312 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1313 samdb
.set_schema(schema
, write_indices_and_attributes
=True)
1318 def fill_samdb(samdb
, lp
, names
, logger
, policyguid
,
1319 policyguid_dc
, fill
, adminpass
, krbtgtpass
, machinepass
, dns_backend
,
1320 dnspass
, invocationid
, ntdsguid
, serverrole
, am_rodc
=False,
1321 dom_for_fun_level
=None, schema
=None, next_rid
=None, dc_rid
=None,
1322 backend_store
=None):
1324 if next_rid
is None:
1327 # Provision does not make much sense values larger than 1000000000
1328 # as the upper range of the rIDAvailablePool is 1073741823 and
1329 # we don't want to create a domain that cannot allocate rids.
1330 if next_rid
< 1000 or next_rid
> 1000000000:
1331 error
= "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid
)
1332 error
+= "the valid range is %u-%u. The default is %u." % (
1333 1000, 1000000000, 1000)
1334 raise ProvisioningError(error
)
1336 # ATTENTION: Do NOT change these default values without discussion with the
1337 # team and/or release manager. They have a big impact on the whole program!
1338 domainControllerFunctionality
= DS_DOMAIN_FUNCTION_2008_R2
1340 if dom_for_fun_level
is None:
1341 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2008_R2
1343 if dom_for_fun_level
> domainControllerFunctionality
:
1344 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008_R2). This won't work!")
1346 domainFunctionality
= dom_for_fun_level
1347 forestFunctionality
= dom_for_fun_level
1349 # Set the NTDS settings DN manually - in order to have it already around
1350 # before the provisioned tree exists and we connect
1351 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1353 # Set the domain functionality levels onto the database.
1354 # Various module (the password_hash module in particular) need
1355 # to know what level of AD we are emulating.
1357 # These will be fixed into the database via the database
1358 # modifictions below, but we need them set from the start.
1359 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
1360 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
1361 samdb
.set_opaque_integer("domainControllerFunctionality",
1362 domainControllerFunctionality
)
1364 samdb
.set_domain_sid(str(names
.domainsid
))
1365 samdb
.set_invocation_id(invocationid
)
1367 logger
.info("Adding DomainDN: %s" % names
.domaindn
)
1369 # impersonate domain admin
1370 admin_session_info
= admin_session(lp
, str(names
.domainsid
))
1371 samdb
.set_session_info(admin_session_info
)
1372 if names
.domainguid
is not None:
1373 domainguid_line
= "objectGUID: %s\n-" % names
.domainguid
1375 domainguid_line
= ""
1377 descr
= b64encode(get_domain_descriptor(names
.domainsid
)).decode('utf8')
1378 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
1379 "DOMAINDN": names
.domaindn
,
1380 "DOMAINSID": str(names
.domainsid
),
1381 "DESCRIPTOR": descr
,
1382 "DOMAINGUID": domainguid_line
1385 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
1386 "DOMAINDN": names
.domaindn
,
1387 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1388 "NEXTRID": str(next_rid
),
1389 "DEFAULTSITE": names
.sitename
,
1390 "CONFIGDN": names
.configdn
,
1391 "POLICYGUID": policyguid
,
1392 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1393 "SAMBA_VERSION_STRING": version
,
1394 "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH
)
1397 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1398 if fill
== FILL_FULL
:
1399 logger
.info("Adding configuration container")
1400 descr
= b64encode(get_config_descriptor(names
.domainsid
)).decode('utf8')
1401 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
1402 "CONFIGDN": names
.configdn
,
1403 "DESCRIPTOR": descr
,
1406 # The LDIF here was created when the Schema object was constructed
1407 ignore_checks_oid
= "local_oid:%s:0" % samba
.dsdb
.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1408 logger
.info("Setting up sam.ldb schema")
1409 samdb
.add_ldif(schema
.schema_dn_add
,
1410 controls
=["relax:0", ignore_checks_oid
])
1411 samdb
.modify_ldif(schema
.schema_dn_modify
,
1412 controls
=[ignore_checks_oid
])
1413 samdb
.write_prefixes_from_schema()
1414 samdb
.add_ldif(schema
.schema_data
, controls
=["relax:0", ignore_checks_oid
])
1415 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
1416 {"SCHEMADN": names
.schemadn
},
1417 controls
=["relax:0", ignore_checks_oid
])
1419 # Now register this container in the root of the forest
1420 msg
= ldb
.Message(ldb
.Dn(samdb
, names
.domaindn
))
1421 msg
["subRefs"] = ldb
.MessageElement(names
.configdn
, ldb
.FLAG_MOD_ADD
,
1424 samdb
.invocation_id
= invocationid
1426 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1427 if fill
== FILL_FULL
:
1428 logger
.info("Setting up sam.ldb configuration data")
1430 partitions_descr
= b64encode(get_config_partitions_descriptor(names
.domainsid
)).decode('utf8')
1431 sites_descr
= b64encode(get_config_sites_descriptor(names
.domainsid
)).decode('utf8')
1432 ntdsquotas_descr
= b64encode(get_config_ntds_quotas_descriptor(names
.domainsid
)).decode('utf8')
1433 protected1_descr
= b64encode(get_config_delete_protected1_descriptor(names
.domainsid
)).decode('utf8')
1434 protected1wd_descr
= b64encode(get_config_delete_protected1wd_descriptor(names
.domainsid
)).decode('utf8')
1435 protected2_descr
= b64encode(get_config_delete_protected2_descriptor(names
.domainsid
)).decode('utf8')
1437 if "2008" in schema
.base_schema
:
1438 # exclude 2012-specific changes if we're using a 2008 schema
1443 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
1444 "CONFIGDN": names
.configdn
,
1445 "NETBIOSNAME": names
.netbiosname
,
1446 "DEFAULTSITE": names
.sitename
,
1447 "DNSDOMAIN": names
.dnsdomain
,
1448 "DOMAIN": names
.domain
,
1449 "SCHEMADN": names
.schemadn
,
1450 "DOMAINDN": names
.domaindn
,
1451 "SERVERDN": names
.serverdn
,
1452 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
1453 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1454 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr
,
1455 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr
,
1456 "SERVICES_DESCRIPTOR": protected1_descr
,
1457 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr
,
1458 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr
,
1459 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr
,
1460 "PARTITIONS_DESCRIPTOR": partitions_descr
,
1461 "SITES_DESCRIPTOR": sites_descr
,
1464 setup_add_ldif(samdb
, setup_path("extended-rights.ldif"), {
1465 "CONFIGDN": names
.configdn
,
1466 "INC2012" : incl_2012
,
1469 logger
.info("Setting up display specifiers")
1470 display_specifiers_ldif
= read_ms_ldif(
1471 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1472 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
,
1473 {"CONFIGDN": names
.configdn
})
1474 check_all_substituted(display_specifiers_ldif
)
1475 samdb
.add_ldif(display_specifiers_ldif
)
1477 logger
.info("Modifying display specifiers and extended rights")
1478 setup_modify_ldif(samdb
,
1479 setup_path("provision_configuration_modify.ldif"), {
1480 "CONFIGDN": names
.configdn
,
1481 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1484 logger
.info("Adding users container")
1485 users_desc
= b64encode(get_domain_users_descriptor(names
.domainsid
)).decode('utf8')
1486 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
1487 "DOMAINDN": names
.domaindn
,
1488 "USERS_DESCRIPTOR": users_desc
1490 logger
.info("Modifying users container")
1491 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
1492 "DOMAINDN": names
.domaindn
})
1493 logger
.info("Adding computers container")
1494 computers_desc
= b64encode(get_domain_computers_descriptor(names
.domainsid
)).decode('utf8')
1495 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
1496 "DOMAINDN": names
.domaindn
,
1497 "COMPUTERS_DESCRIPTOR": computers_desc
1499 logger
.info("Modifying computers container")
1500 setup_modify_ldif(samdb
,
1501 setup_path("provision_computers_modify.ldif"), {
1502 "DOMAINDN": names
.domaindn
})
1503 logger
.info("Setting up sam.ldb data")
1504 infrastructure_desc
= b64encode(get_domain_infrastructure_descriptor(names
.domainsid
)).decode('utf8')
1505 lostandfound_desc
= b64encode(get_domain_delete_protected2_descriptor(names
.domainsid
)).decode('utf8')
1506 system_desc
= b64encode(get_domain_delete_protected1_descriptor(names
.domainsid
)).decode('utf8')
1507 builtin_desc
= b64encode(get_domain_builtin_descriptor(names
.domainsid
)).decode('utf8')
1508 controllers_desc
= b64encode(get_domain_controllers_descriptor(names
.domainsid
)).decode('utf8')
1509 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1510 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1511 "DOMAINDN": names
.domaindn
,
1512 "NETBIOSNAME": names
.netbiosname
,
1513 "DEFAULTSITE": names
.sitename
,
1514 "CONFIGDN": names
.configdn
,
1515 "SERVERDN": names
.serverdn
,
1516 "RIDAVAILABLESTART": str(next_rid
+ 600),
1517 "POLICYGUID_DC": policyguid_dc
,
1518 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc
,
1519 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc
,
1520 "SYSTEM_DESCRIPTOR": system_desc
,
1521 "BUILTIN_DESCRIPTOR": builtin_desc
,
1522 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc
,
1525 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1526 if fill
== FILL_FULL
:
1527 managedservice_descr
= b64encode(get_managed_service_accounts_descriptor(names
.domainsid
)).decode('utf8')
1528 setup_modify_ldif(samdb
,
1529 setup_path("provision_configuration_references.ldif"), {
1530 "CONFIGDN": names
.configdn
,
1531 "SCHEMADN": names
.schemadn
})
1533 logger
.info("Setting up well known security principals")
1534 protected1wd_descr
= b64encode(get_config_delete_protected1wd_descriptor(names
.domainsid
)).decode('utf8')
1535 setup_add_ldif(samdb
, setup_path("provision_well_known_sec_princ.ldif"), {
1536 "CONFIGDN": names
.configdn
,
1537 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr
,
1538 }, controls
=["relax:0", "provision:0"])
1540 if fill
== FILL_FULL
or fill
== FILL_SUBDOMAIN
:
1541 setup_modify_ldif(samdb
,
1542 setup_path("provision_basedn_references.ldif"), {
1543 "DOMAINDN": names
.domaindn
,
1544 "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1547 logger
.info("Setting up sam.ldb users and groups")
1548 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1549 "DOMAINDN": names
.domaindn
,
1550 "DOMAINSID": str(names
.domainsid
),
1551 "ADMINPASS_B64": b64encode(adminpass
.encode('utf-16-le')).decode('utf8'),
1552 "KRBTGTPASS_B64": b64encode(krbtgtpass
.encode('utf-16-le')).decode('utf8')
1553 }, controls
=["relax:0", "provision:0"])
1555 logger
.info("Setting up self join")
1556 setup_self_join(samdb
, admin_session_info
, names
=names
, fill
=fill
,
1557 invocationid
=invocationid
,
1558 dns_backend
=dns_backend
,
1560 machinepass
=machinepass
,
1561 domainsid
=names
.domainsid
,
1564 policyguid
=policyguid
,
1565 policyguid_dc
=policyguid_dc
,
1566 domainControllerFunctionality
=domainControllerFunctionality
,
1569 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1570 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1571 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
)
1572 assert isinstance(names
.ntdsguid
, str)
1577 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1578 POLICIES_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
1579 SYSVOL_SERVICE
="sysvol"
1581 def set_dir_acl(path
, acl
, lp
, domsid
, use_ntvfs
, passdb
, service
=SYSVOL_SERVICE
):
1582 setntacl(lp
, path
, acl
, domsid
, use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1583 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1585 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1586 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1588 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1589 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1592 def set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
):
1593 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1596 :param sysvol: Physical path for the sysvol folder
1597 :param dnsdomain: The DNS name of the domain
1598 :param domainsid: The SID of the domain
1599 :param domaindn: The DN of the domain (ie. DC=...)
1600 :param samdb: An LDB object on the SAM db
1601 :param lp: an LP object
1604 # Set ACL for GPO root folder
1605 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1606 setntacl(lp
, root_policy_path
, POLICIES_ACL
, str(domainsid
),
1607 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=SYSVOL_SERVICE
)
1609 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1610 attrs
=["cn", "nTSecurityDescriptor"],
1611 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1614 acl
= ndr_unpack(security
.descriptor
,
1615 str(policy
["nTSecurityDescriptor"])).as_sddl()
1616 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1617 set_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1618 str(domainsid
), use_ntvfs
,
1622 def setsysvolacl(samdb
, netlogon
, sysvol
, uid
, gid
, domainsid
, dnsdomain
,
1623 domaindn
, lp
, use_ntvfs
):
1624 """Set the ACL for the sysvol share and the subfolders
1626 :param samdb: An LDB object on the SAM db
1627 :param netlogon: Physical path for the netlogon folder
1628 :param sysvol: Physical path for the sysvol folder
1629 :param uid: The UID of the "Administrator" user
1630 :param gid: The GID of the "Domain adminstrators" group
1631 :param domainsid: The SID of the domain
1632 :param dnsdomain: The DNS name of the domain
1633 :param domaindn: The DN of the domain (ie. DC=...)
1638 s3conf
= s3param
.get_context()
1639 s3conf
.load(lp
.configfile
)
1641 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(sysvol
))
1644 smbd
.set_simple_acl(file.name
, 0o755, gid
)
1646 if not smbd
.have_posix_acls():
1647 # This clue is only strictly correct for RPM and
1648 # Debian-like Linux systems, but hopefully other users
1649 # will get enough clue from it.
1650 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1651 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1653 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1654 "Try the mounting the filesystem with the 'acl' option.")
1656 smbd
.chown(file.name
, uid
, gid
)
1658 raise ProvisioningError("Unable to chown a file on your filesystem. "
1659 "You may not be running provision as root.")
1663 # This will ensure that the smbd code we are running when setting ACLs
1664 # is initialised with the smb.conf
1665 s3conf
= s3param
.get_context()
1666 s3conf
.load(lp
.configfile
)
1667 # ensure we are using the right samba_dsdb passdb backend, no matter what
1668 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1669 passdb
.reload_static_pdb()
1671 # ensure that we init the samba_dsdb backend, so the domain sid is
1672 # marked in secrets.tdb
1673 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1675 # now ensure everything matches correctly, to avoid wierd issues
1676 if passdb
.get_global_sam_sid() != domainsid
:
1677 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
))
1679 domain_info
= s4_passdb
.domain_info()
1680 if domain_info
["dom_sid"] != domainsid
:
1681 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
))
1683 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1684 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()))
1689 os
.chown(sysvol
, -1, gid
)
1695 # use admin sid dn as user dn, since admin should own most of the files,
1696 # the operation will be much faster
1697 userdn
= '<SID={}-{}>'.format(domainsid
, security
.DOMAIN_RID_ADMINISTRATOR
)
1699 flags
= (auth
.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1700 auth
.AUTH_SESSION_INFO_AUTHENTICATED |
1701 auth
.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
)
1703 session_info
= auth
.user_session(samdb
, lp_ctx
=lp
, dn
=userdn
,
1704 session_info_flags
=flags
)
1706 def _setntacl(path
):
1707 """A helper to reuse args"""
1709 lp
, path
, SYSVOL_ACL
, str(domainsid
),
1710 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=s4_passdb
,
1711 service
=SYSVOL_SERVICE
, session_info
=session_info
)
1713 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1715 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1717 if use_ntvfs
and canchown
:
1718 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1719 _setntacl(os
.path
.join(root
, name
))
1721 if use_ntvfs
and canchown
:
1722 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1723 _setntacl(os
.path
.join(root
, name
))
1725 # Set acls on Policy folder and policies folders
1726 set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
=s4_passdb
)
1728 def acl_type(direct_db_access
):
1729 if direct_db_access
:
1734 def check_dir_acl(path
, acl
, lp
, domainsid
, direct_db_access
):
1735 fsacl
= getntacl(lp
, path
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1736 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1737 if fsacl_sddl
!= acl
:
1738 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
))
1740 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1742 fsacl
= getntacl(lp
, os
.path
.join(root
, name
),
1743 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1745 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1746 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1747 if fsacl_sddl
!= acl
:
1748 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
))
1751 fsacl
= getntacl(lp
, os
.path
.join(root
, name
),
1752 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1754 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1755 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1756 if fsacl_sddl
!= acl
:
1757 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
))
1760 def check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1762 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1765 :param sysvol: Physical path for the sysvol folder
1766 :param dnsdomain: The DNS name of the domain
1767 :param domainsid: The SID of the domain
1768 :param domaindn: The DN of the domain (ie. DC=...)
1769 :param samdb: An LDB object on the SAM db
1770 :param lp: an LP object
1773 # Set ACL for GPO root folder
1774 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1775 fsacl
= getntacl(lp
, root_policy_path
,
1776 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1778 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access
), root_policy_path
))
1779 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1780 if fsacl_sddl
!= POLICIES_ACL
:
1781 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
))
1782 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1783 attrs
=["cn", "nTSecurityDescriptor"],
1784 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1787 acl
= ndr_unpack(security
.descriptor
,
1788 str(policy
["nTSecurityDescriptor"])).as_sddl()
1789 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1790 check_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1791 domainsid
, direct_db_access
)
1794 def checksysvolacl(samdb
, netlogon
, sysvol
, domainsid
, dnsdomain
, domaindn
,
1796 """Set the ACL for the sysvol share and the subfolders
1798 :param samdb: An LDB object on the SAM db
1799 :param netlogon: Physical path for the netlogon folder
1800 :param sysvol: Physical path for the sysvol folder
1801 :param uid: The UID of the "Administrator" user
1802 :param gid: The GID of the "Domain adminstrators" group
1803 :param domainsid: The SID of the domain
1804 :param dnsdomain: The DNS name of the domain
1805 :param domaindn: The DN of the domain (ie. DC=...)
1808 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1809 s3conf
= s3param
.get_context()
1810 s3conf
.load(lp
.configfile
)
1811 # ensure we are using the right samba_dsdb passdb backend, no matter what
1812 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1813 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1814 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1816 # now ensure everything matches correctly, to avoid wierd issues
1817 if passdb
.get_global_sam_sid() != domainsid
:
1818 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
))
1820 domain_info
= s4_passdb
.domain_info()
1821 if domain_info
["dom_sid"] != domainsid
:
1822 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
))
1824 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1825 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()))
1827 # Ensure we can read this directly, and via the smbd VFS
1828 for direct_db_access
in [True, False]:
1829 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1830 for dir_path
in [os
.path
.join(sysvol
, dnsdomain
), netlogon
]:
1831 fsacl
= getntacl(lp
, dir_path
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1833 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access
), dir_path
))
1834 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1835 if fsacl_sddl
!= SYSVOL_ACL
:
1836 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
))
1838 # Check acls on Policy folder and policies folders
1839 check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1843 def interface_ips_v4(lp
):
1844 """return only IPv4 IPs"""
1845 ips
= samba
.interface_ips(lp
, False)
1848 if i
.find(':') == -1:
1853 def interface_ips_v6(lp
):
1854 """return only IPv6 IPs"""
1855 ips
= samba
.interface_ips(lp
, False)
1858 if i
.find(':') != -1:
1863 def provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
1865 targetdir
=None, samdb_fill
=FILL_FULL
,
1866 hostip
=None, hostip6
=None,
1867 next_rid
=1000, dc_rid
=None, adminpass
=None, krbtgtpass
=None,
1868 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1869 invocationid
=None, machinepass
=None, ntdsguid
=None,
1870 dns_backend
=None, dnspass
=None,
1871 serverrole
=None, dom_for_fun_level
=None,
1872 am_rodc
=False, lp
=None, use_ntvfs
=False,
1873 skip_sysvolacl
=False, backend_store
=None):
1874 # create/adapt the group policy GUIDs
1875 # Default GUID for default policy are described at
1876 # "How Core Group Policy Works"
1877 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1878 if policyguid
is None:
1879 policyguid
= DEFAULT_POLICY_GUID
1880 policyguid
= policyguid
.upper()
1881 if policyguid_dc
is None:
1882 policyguid_dc
= DEFAULT_DC_POLICY_GUID
1883 policyguid_dc
= policyguid_dc
.upper()
1885 if invocationid
is None:
1886 invocationid
= str(uuid
.uuid4())
1888 if krbtgtpass
is None:
1889 krbtgtpass
= samba
.generate_random_machine_password(128, 255)
1890 if machinepass
is None:
1891 machinepass
= samba
.generate_random_machine_password(128, 255)
1893 dnspass
= samba
.generate_random_password(128, 255)
1895 samdb
.transaction_start()
1897 samdb
= fill_samdb(samdb
, lp
, names
, logger
=logger
,
1899 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1900 fill
=samdb_fill
, adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1901 invocationid
=invocationid
, machinepass
=machinepass
,
1902 dns_backend
=dns_backend
, dnspass
=dnspass
,
1903 ntdsguid
=ntdsguid
, serverrole
=serverrole
,
1904 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
1905 next_rid
=next_rid
, dc_rid
=dc_rid
,
1906 backend_store
=backend_store
)
1908 # Set up group policies (domain policy and domain controller
1910 if serverrole
== "active directory domain controller":
1911 create_default_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
,
1914 samdb
.transaction_cancel()
1917 samdb
.transaction_commit()
1919 if serverrole
== "active directory domain controller":
1920 # Continue setting up sysvol for GPO. This appears to require being
1921 # outside a transaction.
1922 if not skip_sysvolacl
:
1923 setsysvolacl(samdb
, paths
.netlogon
, paths
.sysvol
, paths
.root_uid
,
1924 paths
.root_gid
, names
.domainsid
, names
.dnsdomain
,
1925 names
.domaindn
, lp
, use_ntvfs
)
1927 logger
.info("Setting acl on sysvol skipped")
1929 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1930 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1931 netbiosname
=names
.netbiosname
, domainsid
=names
.domainsid
,
1932 machinepass
=machinepass
, secure_channel_type
=SEC_CHAN_BDC
)
1934 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1935 # In future, this might be determined from some configuration
1936 kerberos_enctypes
= str(ENC_ALL_TYPES
)
1939 msg
= ldb
.Message(ldb
.Dn(samdb
,
1940 samdb
.searchone("distinguishedName",
1941 expression
="samAccountName=%s$" % names
.netbiosname
,
1942 scope
=ldb
.SCOPE_SUBTREE
).decode('utf8')))
1943 msg
["msDS-SupportedEncryptionTypes"] = ldb
.MessageElement(
1944 elements
=kerberos_enctypes
, flags
=ldb
.FLAG_MOD_REPLACE
,
1945 name
="msDS-SupportedEncryptionTypes")
1947 except ldb
.LdbError
as e
:
1948 (enum
, estr
) = e
.args
1949 if enum
!= ldb
.ERR_NO_SUCH_ATTRIBUTE
:
1950 # It might be that this attribute does not exist in this schema
1953 setup_ad_dns(samdb
, secrets_ldb
, names
, paths
, lp
, logger
,
1954 hostip
=hostip
, hostip6
=hostip6
, dns_backend
=dns_backend
,
1955 dnspass
=dnspass
, os_level
=dom_for_fun_level
,
1956 targetdir
=targetdir
, fill_level
=samdb_fill
,
1957 backend_store
=backend_store
)
1959 domainguid
= samdb
.searchone(basedn
=samdb
.get_default_basedn(),
1960 attribute
="objectGUID")
1961 assert isinstance(domainguid
, str)
1963 lastProvisionUSNs
= get_last_provision_usn(samdb
)
1964 maxUSN
= get_max_usn(samdb
, str(names
.rootdn
))
1965 if lastProvisionUSNs
is not None:
1966 update_provision_usn(samdb
, 0, maxUSN
, invocationid
, 1)
1968 set_provision_usn(samdb
, 0, maxUSN
, invocationid
)
1970 logger
.info("Setting up sam.ldb rootDSE marking as synchronized")
1971 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"),
1972 { 'NTDSGUID' : names
.ntdsguid
})
1974 # fix any dangling GUIDs from the provision
1975 logger
.info("Fixing provision GUIDs")
1976 chk
= dbcheck(samdb
, samdb_schema
=samdb
, verbose
=False, fix
=True, yes
=True,
1978 samdb
.transaction_start()
1980 # a small number of GUIDs are missing because of ordering issues in the
1982 for schema_obj
in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1983 chk
.check_database(DN
="%s,%s" % (schema_obj
, names
.schemadn
),
1984 scope
=ldb
.SCOPE_BASE
,
1985 attrs
=['defaultObjectCategory'])
1986 chk
.check_database(DN
="CN=IP Security,CN=System,%s" % names
.domaindn
,
1987 scope
=ldb
.SCOPE_ONELEVEL
,
1988 attrs
=['ipsecOwnersReference',
1989 'ipsecFilterReference',
1990 'ipsecISAKMPReference',
1991 'ipsecNegotiationPolicyReference',
1992 'ipsecNFAReference'])
1993 if chk
.check_database(DN
=names
.schemadn
, scope
=ldb
.SCOPE_SUBTREE
,
1994 attrs
=['attributeId', 'governsId']) != 0:
1995 raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
1997 samdb
.transaction_cancel()
2000 samdb
.transaction_commit()
2004 "ROLE_STANDALONE": "standalone server",
2005 "ROLE_DOMAIN_MEMBER": "member server",
2006 "ROLE_DOMAIN_BDC": "active directory domain controller",
2007 "ROLE_DOMAIN_PDC": "active directory domain controller",
2008 "dc": "active directory domain controller",
2009 "member": "member server",
2010 "domain controller": "active directory domain controller",
2011 "active directory domain controller": "active directory domain controller",
2012 "member server": "member server",
2013 "standalone": "standalone server",
2014 "standalone server": "standalone server",
2018 def sanitize_server_role(role
):
2019 """Sanitize a server role name.
2021 :param role: Server role
2022 :raise ValueError: If the role can not be interpreted
2023 :return: Sanitized server role (one of "member server",
2024 "active directory domain controller", "standalone server")
2027 return _ROLES_MAP
[role
]
2029 raise ValueError(role
)
2032 def provision_fake_ypserver(logger
, samdb
, domaindn
, netbiosname
, nisdomain
,
2034 """Create AD entries for the fake ypserver.
2036 This is needed for being able to manipulate posix attrs via ADUC.
2038 samdb
.transaction_start()
2040 logger
.info("Setting up fake yp server settings")
2041 setup_add_ldif(samdb
, setup_path("ypServ30.ldif"), {
2042 "DOMAINDN": domaindn
,
2043 "NETBIOSNAME": netbiosname
,
2044 "NISDOMAIN": nisdomain
,
2047 samdb
.transaction_cancel()
2050 samdb
.transaction_commit()
2052 def directory_create_or_exists(path
, mode
=0o755):
2053 if not os
.path
.exists(path
):
2055 os
.mkdir(path
, mode
)
2056 except OSError as e
:
2057 if e
.errno
in [errno
.EEXIST
]:
2060 raise ProvisioningError("Failed to create directory %s: %s" % (path
, e
.strerror
))
2062 def determine_host_ip(logger
, lp
, hostip
=None):
2064 logger
.info("Looking up IPv4 addresses")
2065 hostips
= interface_ips_v4(lp
)
2066 if len(hostips
) > 0:
2068 if len(hostips
) > 1:
2069 logger
.warning("More than one IPv4 address found. Using %s",
2071 if hostip
== "127.0.0.1":
2074 logger
.warning("No IPv4 address will be assigned")
2078 def determine_host_ip6(logger
, lp
, hostip6
=None):
2080 logger
.info("Looking up IPv6 addresses")
2081 hostips
= interface_ips_v6(lp
)
2083 hostip6
= hostips
[0]
2084 if len(hostips
) > 1:
2085 logger
.warning("More than one IPv6 address found. Using %s", hostip6
)
2087 logger
.warning("No IPv6 address will be assigned")
2091 def provision(logger
, session_info
, smbconf
=None,
2092 targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None, rootdn
=None,
2093 domaindn
=None, schemadn
=None, configdn
=None, serverdn
=None,
2094 domain
=None, hostname
=None, hostip
=None, hostip6
=None, domainsid
=None,
2095 next_rid
=1000, dc_rid
=None, adminpass
=None, ldapadminpass
=None,
2096 krbtgtpass
=None, domainguid
=None, policyguid
=None, policyguid_dc
=None,
2097 dns_backend
=None, dns_forwarder
=None, dnspass
=None,
2098 invocationid
=None, machinepass
=None, ntdsguid
=None,
2099 root
=None, nobody
=None, users
=None, backup
=None, aci
=None,
2100 serverrole
=None, dom_for_fun_level
=None, backend_type
=None,
2101 sitename
=None, ol_mmr_urls
=None, ol_olc
=None, slapd_path
=None,
2102 useeadb
=False, am_rodc
=False, lp
=None, use_ntvfs
=False,
2103 use_rfc2307
=False, maxuid
=None, maxgid
=None, skip_sysvolacl
=True,
2104 ldap_backend_forced_uri
=None, nosync
=False, ldap_dryrun_mode
=False,
2105 ldap_backend_extra_port
=None, base_schema
=None,
2106 plaintext_secrets
=False, backend_store
=None):
2109 :note: caution, this wipes all existing data!
2113 serverrole
= sanitize_server_role(serverrole
)
2115 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole
)
2117 if ldapadminpass
is None:
2118 # Make a new, random password between Samba and it's LDAP server
2119 ldapadminpass
= samba
.generate_random_password(128, 255)
2121 if backend_type
is None:
2122 backend_type
= "ldb"
2123 if backend_store
is None:
2124 backend_store
= get_default_backend_store()
2126 if domainsid
is None:
2127 domainsid
= security
.random_sid()
2129 root_uid
= findnss_uid([root
or "root"])
2130 nobody_uid
= findnss_uid([nobody
or "nobody"])
2131 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
2132 root_gid
= pwd
.getpwuid(root_uid
).pw_gid
2135 bind_gid
= findnss_gid(["bind", "named"])
2139 if targetdir
is not None:
2140 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
2141 elif smbconf
is None:
2142 smbconf
= samba
.param
.default_path()
2143 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
2144 os
.makedirs(os
.path
.dirname(smbconf
))
2146 server_services
= []
2149 global_param
["idmap_ldb:use rfc2307"] = ["yes"]
2151 if dns_backend
!= "SAMBA_INTERNAL":
2152 server_services
.append("-dns")
2154 if dns_forwarder
is not None:
2155 global_param
["dns forwarder"] = [dns_forwarder
]
2158 server_services
.append("+smb")
2159 server_services
.append("-s3fs")
2160 global_param
["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2162 if len(server_services
) > 0:
2163 global_param
["server services"] = server_services
2165 # only install a new smb.conf if there isn't one there already
2166 if os
.path
.exists(smbconf
):
2167 # if Samba Team members can't figure out the weird errors
2168 # loading an empty smb.conf gives, then we need to be smarter.
2169 # Pretend it just didn't exist --abartlet
2170 f
= open(smbconf
, 'r')
2172 data
= f
.read().lstrip()
2175 if data
is None or data
== "":
2176 make_smbconf(smbconf
, hostname
, domain
, realm
,
2177 targetdir
, serverrole
=serverrole
,
2178 eadb
=useeadb
, use_ntvfs
=use_ntvfs
,
2179 lp
=lp
, global_param
=global_param
)
2181 make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
2182 serverrole
=serverrole
,
2183 eadb
=useeadb
, use_ntvfs
=use_ntvfs
, lp
=lp
, global_param
=global_param
)
2186 lp
= samba
.param
.LoadParm()
2188 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
2189 dnsdomain
=realm
, serverrole
=serverrole
, domaindn
=domaindn
,
2190 configdn
=configdn
, schemadn
=schemadn
, serverdn
=serverdn
,
2191 sitename
=sitename
, rootdn
=rootdn
, domain_names_forced
=(samdb_fill
== FILL_DRS
))
2192 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
2194 paths
.bind_gid
= bind_gid
2195 paths
.root_uid
= root_uid
;
2196 paths
.root_gid
= root_gid
2198 hostip
= determine_host_ip(logger
, lp
, hostip
)
2199 hostip6
= determine_host_ip6(logger
, lp
, hostip6
)
2200 names
.hostip
= hostip
2201 names
.hostip6
= hostip6
2202 names
.domainguid
= domainguid
2203 names
.domainsid
= domainsid
2204 names
.forestsid
= domainsid
2206 if serverrole
is None:
2207 serverrole
= lp
.get("server role")
2209 directory_create_or_exists(paths
.private_dir
, 0o700)
2210 directory_create_or_exists(paths
.binddns_dir
, 0o770)
2211 directory_create_or_exists(os
.path
.join(paths
.private_dir
, "tls"))
2212 directory_create_or_exists(paths
.state_dir
)
2213 if not plaintext_secrets
:
2214 setup_encrypted_secrets_key(paths
.encrypted_secrets_key_path
)
2216 if paths
.sysvol
and not os
.path
.exists(paths
.sysvol
):
2217 os
.makedirs(paths
.sysvol
, 0o775)
2219 ldapi_url
= "ldapi://%s" % urllib_quote(paths
.s4_ldapi_path
, safe
="")
2221 schema
= Schema(domainsid
, invocationid
=invocationid
,
2222 schemadn
=names
.schemadn
, base_schema
=base_schema
)
2224 if backend_type
== "ldb":
2225 provision_backend
= LDBBackend(backend_type
, paths
=paths
,
2227 names
=names
, logger
=logger
)
2228 elif backend_type
== "existing":
2229 # If support for this is ever added back, then the URI will need to be
2231 provision_backend
= ExistingBackend(backend_type
, paths
=paths
,
2233 names
=names
, logger
=logger
,
2234 ldap_backend_forced_uri
=ldap_backend_forced_uri
)
2235 elif backend_type
== "fedora-ds":
2236 provision_backend
= FDSBackend(backend_type
, paths
=paths
,
2238 names
=names
, logger
=logger
, domainsid
=domainsid
,
2239 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
2240 slapd_path
=slapd_path
,
2242 elif backend_type
== "openldap":
2243 provision_backend
= OpenLDAPBackend(backend_type
, paths
=paths
,
2245 names
=names
, logger
=logger
, domainsid
=domainsid
,
2246 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
2247 slapd_path
=slapd_path
, ol_mmr_urls
=ol_mmr_urls
,
2248 ldap_backend_extra_port
=ldap_backend_extra_port
,
2249 ldap_dryrun_mode
=ldap_dryrun_mode
, nosync
=nosync
,
2250 ldap_backend_forced_uri
=ldap_backend_forced_uri
)
2252 raise ValueError("Unknown LDAP backend type selected")
2254 provision_backend
.init()
2255 provision_backend
.start()
2257 # only install a new shares config db if there is none
2258 if not os
.path
.exists(paths
.shareconf
):
2259 logger
.info("Setting up share.ldb")
2260 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
, lp
=lp
)
2261 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
2263 logger
.info("Setting up secrets.ldb")
2264 secrets_ldb
= setup_secretsdb(paths
,
2265 session_info
=session_info
,
2266 backend_credentials
=provision_backend
.credentials
, lp
=lp
)
2269 logger
.info("Setting up the registry")
2270 setup_registry(paths
.hklm
, session_info
, lp
=lp
)
2272 logger
.info("Setting up the privileges database")
2273 setup_privileges(paths
.privilege
, session_info
, lp
=lp
)
2275 logger
.info("Setting up idmap db")
2276 idmap
= setup_idmapdb(paths
.idmapdb
, session_info
=session_info
, lp
=lp
)
2278 setup_name_mappings(idmap
, sid
=str(domainsid
),
2279 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
2280 users_gid
=users_gid
, root_gid
=root_gid
)
2282 logger
.info("Setting up SAM db")
2283 samdb
= setup_samdb(paths
.samdb
, session_info
,
2284 provision_backend
, lp
, names
, logger
=logger
,
2285 serverrole
=serverrole
,
2286 schema
=schema
, fill
=samdb_fill
, am_rodc
=am_rodc
,
2287 plaintext_secrets
=plaintext_secrets
,
2288 backend_store
=backend_store
)
2290 if serverrole
== "active directory domain controller":
2291 if paths
.netlogon
is None:
2292 raise MissingShareError("netlogon", paths
.smbconf
)
2294 if paths
.sysvol
is None:
2295 raise MissingShareError("sysvol", paths
.smbconf
)
2297 if not os
.path
.isdir(paths
.netlogon
):
2298 os
.makedirs(paths
.netlogon
, 0o755)
2300 if adminpass
is None:
2301 adminpass
= samba
.generate_random_password(12, 32)
2302 adminpass_generated
= True
2304 adminpass
= unicode(adminpass
, 'utf-8')
2305 adminpass_generated
= False
2307 if samdb_fill
== FILL_FULL
:
2308 provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
2309 schema
=schema
, targetdir
=targetdir
, samdb_fill
=samdb_fill
,
2310 hostip
=hostip
, hostip6
=hostip6
,
2311 next_rid
=next_rid
, dc_rid
=dc_rid
, adminpass
=adminpass
,
2312 krbtgtpass
=krbtgtpass
,
2313 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
2314 invocationid
=invocationid
, machinepass
=machinepass
,
2315 ntdsguid
=ntdsguid
, dns_backend
=dns_backend
,
2316 dnspass
=dnspass
, serverrole
=serverrole
,
2317 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
2318 lp
=lp
, use_ntvfs
=use_ntvfs
,
2319 skip_sysvolacl
=skip_sysvolacl
,
2320 backend_store
=backend_store
)
2322 if not is_heimdal_built():
2323 create_kdc_conf(paths
.kdcconf
, realm
, domain
, os
.path
.dirname(lp
.get("log file")))
2324 logger
.info("The Kerberos KDC configuration for Samba AD is "
2325 "located at %s", paths
.kdcconf
)
2327 create_krb5_conf(paths
.krb5conf
,
2328 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
2330 logger
.info("A Kerberos configuration suitable for Samba AD has been "
2331 "generated at %s", paths
.krb5conf
)
2332 logger
.info("Merge the contents of this file with your system "
2333 "krb5.conf or replace it with this one. Do not create a "
2336 if serverrole
== "active directory domain controller":
2337 create_dns_update_list(lp
, logger
, paths
)
2339 backend_result
= provision_backend
.post_setup()
2340 provision_backend
.shutdown()
2343 secrets_ldb
.transaction_cancel()
2346 # Now commit the secrets.ldb to disk
2347 secrets_ldb
.transaction_commit()
2349 # the commit creates the dns.keytab in the private directory
2350 private_dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
2351 bind_dns_keytab_path
= os
.path
.join(paths
.binddns_dir
, paths
.dns_keytab
)
2353 if os
.path
.isfile(private_dns_keytab_path
):
2354 if os
.path
.isfile(bind_dns_keytab_path
):
2356 os
.unlink(bind_dns_keytab_path
)
2357 except OSError as e
:
2358 logger
.error("Failed to remove %s: %s" %
2359 (bind_dns_keytab_path
, e
.strerror
))
2361 # link the dns.keytab to the bind-dns directory
2363 os
.link(private_dns_keytab_path
, bind_dns_keytab_path
)
2364 except OSError as e
:
2365 logger
.error("Failed to create link %s -> %s: %s" %
2366 (private_dns_keytab_path
, bind_dns_keytab_path
, e
.strerror
))
2368 # chown the dns.keytab in the bind-dns directory
2369 if paths
.bind_gid
is not None:
2371 os
.chmod(paths
.binddns_dir
, 0o770)
2372 os
.chown(paths
.binddns_dir
, -1, paths
.bind_gid
)
2374 if not os
.environ
.has_key('SAMBA_SELFTEST'):
2375 logger
.info("Failed to chown %s to bind gid %u",
2376 paths
.binddns_dir
, paths
.bind_gid
)
2379 os
.chmod(bind_dns_keytab_path
, 0o640)
2380 os
.chown(bind_dns_keytab_path
, -1, paths
.bind_gid
)
2382 if not os
.environ
.has_key('SAMBA_SELFTEST'):
2383 logger
.info("Failed to chown %s to bind gid %u",
2384 bind_dns_keytab_path
, paths
.bind_gid
)
2386 result
= ProvisionResult()
2387 result
.server_role
= serverrole
2388 result
.domaindn
= domaindn
2389 result
.paths
= paths
2390 result
.names
= names
2392 result
.samdb
= samdb
2393 result
.idmap
= idmap
2394 result
.domainsid
= str(domainsid
)
2396 if samdb_fill
== FILL_FULL
:
2397 result
.adminpass_generated
= adminpass_generated
2398 result
.adminpass
= adminpass
2400 result
.adminpass_generated
= False
2401 result
.adminpass
= None
2403 result
.backend_result
= backend_result
2406 provision_fake_ypserver(logger
=logger
, samdb
=samdb
,
2407 domaindn
=names
.domaindn
, netbiosname
=names
.netbiosname
,
2408 nisdomain
=names
.domain
.lower(), maxuid
=maxuid
, maxgid
=maxgid
)
2413 def provision_become_dc(smbconf
=None, targetdir
=None,
2414 realm
=None, rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
2415 serverdn
=None, domain
=None, hostname
=None, domainsid
=None,
2416 adminpass
=None, krbtgtpass
=None, domainguid
=None, policyguid
=None,
2417 policyguid_dc
=None, invocationid
=None, machinepass
=None, dnspass
=None,
2418 dns_backend
=None, root
=None, nobody
=None, users
=None,
2419 backup
=None, serverrole
=None, ldap_backend
=None,
2420 ldap_backend_type
=None, sitename
=None, debuglevel
=1, use_ntvfs
=False):
2422 logger
= logging
.getLogger("provision")
2423 samba
.set_debug_level(debuglevel
)
2425 res
= provision(logger
, system_session(),
2426 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
2427 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
2428 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
2429 hostname
=hostname
, hostip
=None, domainsid
=domainsid
,
2430 machinepass
=machinepass
,
2431 serverrole
="active directory domain controller",
2432 sitename
=sitename
, dns_backend
=dns_backend
, dnspass
=dnspass
,
2433 use_ntvfs
=use_ntvfs
)
2434 res
.lp
.set("debuglevel", str(debuglevel
))
2438 def create_krb5_conf(path
, dnsdomain
, hostname
, realm
):
2439 """Write out a file containing a valid krb5.conf file
2441 :param path: Path of the new krb5.conf file.
2442 :param dnsdomain: DNS Domain name
2443 :param hostname: Local hostname
2444 :param realm: Realm name
2446 setup_file(setup_path("krb5.conf"), path
, {
2447 "DNSDOMAIN": dnsdomain
,
2448 "HOSTNAME": hostname
,
2453 class ProvisioningError(Exception):
2454 """A generic provision error."""
2456 def __init__(self
, value
):
2460 return "ProvisioningError: " + self
.value
2463 class InvalidNetbiosName(Exception):
2464 """A specified name was not a valid NetBIOS name."""
2466 def __init__(self
, name
):
2467 super(InvalidNetbiosName
, self
).__init
__(
2468 "The name '%r' is not a valid NetBIOS name" % name
)
2471 class MissingShareError(ProvisioningError
):
2473 def __init__(self
, name
, smbconf
):
2474 super(MissingShareError
, self
).__init
__(
2475 "Existing smb.conf does not have a [%s] share, but you are "
2476 "configuring a DC. Please remove %s or add the share manually." %