1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba4 server
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 __docformat__
= "restructuredText"
29 from base64
import b64encode
44 from samba
.auth
import system_session
, admin_session
46 from samba
.samba3
import smbd
, passdb
47 from samba
.samba3
import param
as s3param
48 from samba
.dsdb
import DS_DOMAIN_FUNCTION_2000
52 check_all_substituted
,
53 is_valid_netbios_char
,
59 from samba
.dcerpc
import security
, misc
60 from samba
.dcerpc
.misc
import (
64 from samba
.dsdb
import (
65 DS_DOMAIN_FUNCTION_2003
,
66 DS_DOMAIN_FUNCTION_2008_R2
,
69 from samba
.idmap
import IDmapDB
70 from samba
.ms_display_specifiers
import read_ms_ldif
71 from samba
.ntacls
import setntacl
, getntacl
, dsacl2fsacl
72 from samba
.ndr
import ndr_pack
, ndr_unpack
73 from samba
.provision
.backend
import (
79 from samba
.descriptor
import (
81 get_config_descriptor
,
82 get_config_partitions_descriptor
,
83 get_config_sites_descriptor
,
84 get_config_ntds_quotas_descriptor
,
85 get_config_delete_protected1_descriptor
,
86 get_config_delete_protected1wd_descriptor
,
87 get_config_delete_protected2_descriptor
,
88 get_domain_descriptor
,
89 get_domain_infrastructure_descriptor
,
90 get_domain_builtin_descriptor
,
91 get_domain_computers_descriptor
,
92 get_domain_users_descriptor
,
93 get_domain_controllers_descriptor
,
94 get_domain_delete_protected1_descriptor
,
95 get_domain_delete_protected2_descriptor
,
96 get_dns_partition_descriptor
,
97 get_dns_forest_microsoft_dns_descriptor
,
98 get_dns_domain_microsoft_dns_descriptor
,
100 from samba
.provision
.common
import (
109 from samba
.provision
.sambadns
import (
112 create_dns_update_list
116 import samba
.registry
117 from samba
.schema
import Schema
118 from samba
.samdb
import SamDB
119 from samba
.dbchecker
import dbcheck
122 DEFAULT_POLICY_GUID
= "31B2F340-016D-11D2-945F-00C04FB984F9"
123 DEFAULT_DC_POLICY_GUID
= "6AC1786C-016F-11D2-945F-00C04fB984F9"
124 DEFAULTSITE
= "Default-First-Site-Name"
125 LAST_PROVISION_USN_ATTRIBUTE
= "lastProvisionUSN"
128 class ProvisionPaths(object):
131 self
.shareconf
= None
142 self
.dns_keytab
= None
145 self
.private_dir
= None
146 self
.state_dir
= None
149 class ProvisionNames(object):
157 self
.dnsforestdn
= None
158 self
.dnsdomaindn
= None
159 self
.ldapmanagerdn
= None
160 self
.dnsdomain
= None
162 self
.netbiosname
= None
167 self
.domainsid
= None
168 self
.forestsid
= None
169 self
.domainguid
= None
173 def find_provision_key_parameters(samdb
, secretsdb
, idmapdb
, paths
, smbconf
,
175 """Get key provision parameters (realm, domain, ...) from a given provision
177 :param samdb: An LDB object connected to the sam.ldb file
178 :param secretsdb: An LDB object connected to the secrets.ldb file
179 :param idmapdb: An LDB object connected to the idmap.ldb file
180 :param paths: A list of path to provision object
181 :param smbconf: Path to the smb.conf file
182 :param lp: A LoadParm object
183 :return: A list of key provision parameters
185 names
= ProvisionNames()
186 names
.adminpass
= None
188 # NT domain, kerberos realm, root dn, domain dn, domain dns name
189 names
.domain
= string
.upper(lp
.get("workgroup"))
190 names
.realm
= lp
.get("realm")
191 names
.dnsdomain
= names
.realm
.lower()
192 basedn
= samba
.dn_from_dns_name(names
.dnsdomain
)
193 names
.realm
= string
.upper(names
.realm
)
195 # Get the netbiosname first (could be obtained from smb.conf in theory)
196 res
= secretsdb
.search(expression
="(flatname=%s)" %
197 names
.domain
,base
="CN=Primary Domains",
198 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
199 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
201 names
.smbconf
= smbconf
203 # That's a bit simplistic but it's ok as long as we have only 3
205 current
= samdb
.search(expression
="(objectClass=*)",
206 base
="", scope
=ldb
.SCOPE_BASE
,
207 attrs
=["defaultNamingContext", "schemaNamingContext",
208 "configurationNamingContext","rootDomainNamingContext",
211 names
.configdn
= current
[0]["configurationNamingContext"][0]
212 names
.schemadn
= current
[0]["schemaNamingContext"][0]
213 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
,
214 current
[0]["defaultNamingContext"][0]))):
215 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
216 "is not the same ..." % (paths
.samdb
,
217 str(current
[0]["defaultNamingContext"][0]),
218 paths
.smbconf
, basedn
)))
220 names
.domaindn
=current
[0]["defaultNamingContext"][0]
221 names
.rootdn
=current
[0]["rootDomainNamingContext"][0]
222 names
.ncs
=current
[0]["namingContexts"]
223 names
.dnsforestdn
= None
224 names
.dnsdomaindn
= None
226 for i
in range(0, len(names
.ncs
)):
229 dnsforestdn
= "DC=ForestDnsZones,%s" % (str(names
.rootdn
))
230 if nc
== dnsforestdn
:
231 names
.dnsforestdn
= dnsforestdn
234 dnsdomaindn
= "DC=DomainDnsZones,%s" % (str(names
.domaindn
))
235 if nc
== dnsdomaindn
:
236 names
.dnsdomaindn
= dnsdomaindn
240 res3
= samdb
.search(expression
="(objectClass=site)",
241 base
="CN=Sites," + names
.configdn
, scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn"])
242 names
.sitename
= str(res3
[0]["cn"])
244 # dns hostname and server dn
245 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
246 base
="OU=Domain Controllers,%s" % basedn
,
247 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
248 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("." + names
.dnsdomain
, "")
250 server_res
= samdb
.search(expression
="serverReference=%s" % res4
[0].dn
,
251 attrs
=[], base
=names
.configdn
)
252 names
.serverdn
= str(server_res
[0].dn
)
254 # invocation id/objectguid
255 res5
= samdb
.search(expression
="(objectClass=*)",
256 base
="CN=NTDS Settings,%s" % str(names
.serverdn
),
257 scope
=ldb
.SCOPE_BASE
,
258 attrs
=["invocationID", "objectGUID"])
259 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
260 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
263 res6
= samdb
.search(expression
="(objectClass=*)", base
=basedn
,
264 scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID",
265 "objectSid","msDS-Behavior-Version" ])
266 names
.domainguid
= str(ndr_unpack(misc
.GUID
, res6
[0]["objectGUID"][0]))
267 names
.domainsid
= ndr_unpack( security
.dom_sid
, res6
[0]["objectSid"][0])
268 names
.forestsid
= ndr_unpack( security
.dom_sid
, res6
[0]["objectSid"][0])
269 if res6
[0].get("msDS-Behavior-Version") is None or \
270 int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
271 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
273 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
276 res7
= samdb
.search(expression
="(displayName=Default Domain Policy)",
277 base
="CN=Policies,CN=System," + basedn
,
278 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
279 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
281 res8
= samdb
.search(expression
="(displayName=Default Domain Controllers"
283 base
="CN=Policies,CN=System," + basedn
,
284 scope
=ldb
.SCOPE_ONELEVEL
,
285 attrs
=["cn","displayName"])
287 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
289 names
.policyid_dc
= None
291 res9
= idmapdb
.search(expression
="(cn=%s-%s)" %
292 (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
),
293 attrs
=["xidNumber", "type"])
295 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
))
296 if res9
[0]["type"][0] == "ID_TYPE_BOTH":
297 names
.root_gid
= res9
[0]["xidNumber"][0]
299 names
.root_gid
= pwd
.getpwuid(int(res9
[0]["xidNumber"][0])).pw_gid
301 res10
= samdb
.search(expression
="(samaccountname=dns)",
302 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["dn"],
303 controls
=["search_options:1:2"])
305 has_legacy_dns_account
= True
307 has_legacy_dns_account
= False
309 res11
= samdb
.search(expression
="(samaccountname=dns-%s)" % names
.netbiosname
,
310 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["dn"],
311 controls
=["search_options:1:2"])
313 has_dns_account
= True
315 has_dns_account
= False
317 if names
.dnsdomaindn
is not None:
319 names
.dns_backend
= 'BIND9_DLZ'
321 names
.dns_backend
= 'SAMBA_INTERNAL'
322 elif has_dns_account
or has_legacy_dns_account
:
323 names
.dns_backend
= 'BIND9_FLATFILE'
325 names
.dns_backend
= 'NONE'
327 dns_admins_sid
= get_dnsadmins_sid(samdb
, names
.domaindn
)
328 names
.name_map
['DnsAdmins'] = str(dns_admins_sid
)
333 def update_provision_usn(samdb
, low
, high
, id, replace
=False):
334 """Update the field provisionUSN in sam.ldb
336 This field is used to track range of USN modified by provision and
338 This value is used afterward by next provision to figure out if
339 the field have been modified since last provision.
341 :param samdb: An LDB object connect to sam.ldb
342 :param low: The lowest USN modified by this upgrade
343 :param high: The highest USN modified by this upgrade
344 :param id: The invocation id of the samba's dc
345 :param replace: A boolean indicating if the range should replace any
346 existing one or appended (default)
351 entry
= samdb
.search(base
="@PROVISION",
352 scope
=ldb
.SCOPE_BASE
,
353 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "dn"])
354 for e
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
355 if not re
.search(';', e
):
356 e
= "%s;%s" % (e
, id)
359 tab
.append("%s-%s;%s" % (low
, high
, id))
360 delta
= ldb
.Message()
361 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
362 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
363 ldb
.FLAG_MOD_REPLACE
, LAST_PROVISION_USN_ATTRIBUTE
)
364 entry
= samdb
.search(expression
='provisionnerID=*',
365 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
366 attrs
=["provisionnerID"])
367 if len(entry
) == 0 or len(entry
[0]) == 0:
368 delta
["provisionnerID"] = ldb
.MessageElement(id, ldb
.FLAG_MOD_ADD
, "provisionnerID")
372 def set_provision_usn(samdb
, low
, high
, id):
373 """Set the field provisionUSN in sam.ldb
374 This field is used to track range of USN modified by provision and
376 This value is used afterward by next provision to figure out if
377 the field have been modified since last provision.
379 :param samdb: An LDB object connect to sam.ldb
380 :param low: The lowest USN modified by this upgrade
381 :param high: The highest USN modified by this upgrade
382 :param id: The invocationId of the provision"""
385 tab
.append("%s-%s;%s" % (low
, high
, id))
387 delta
= ldb
.Message()
388 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
389 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
390 ldb
.FLAG_MOD_ADD
, LAST_PROVISION_USN_ATTRIBUTE
)
394 def get_max_usn(samdb
,basedn
):
395 """ This function return the biggest USN present in the provision
397 :param samdb: A LDB object pointing to the sam.ldb
398 :param basedn: A string containing the base DN of the provision
400 :return: The biggest USN in the provision"""
402 res
= samdb
.search(expression
="objectClass=*",base
=basedn
,
403 scope
=ldb
.SCOPE_SUBTREE
,attrs
=["uSNChanged"],
404 controls
=["search_options:1:2",
405 "server_sort:1:1:uSNChanged",
406 "paged_results:1:1"])
407 return res
[0]["uSNChanged"]
410 def get_last_provision_usn(sam
):
411 """Get USNs ranges modified by a provision or an upgradeprovision
413 :param sam: An LDB object pointing to the sam.ldb
414 :return: a dictionary which keys are invocation id and values are an array
415 of integer representing the different ranges
418 entry
= sam
.search(expression
="%s=*" % LAST_PROVISION_USN_ATTRIBUTE
,
419 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
420 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "provisionnerID"])
421 except ldb
.LdbError
, (ecode
, emsg
):
422 if ecode
== ldb
.ERR_NO_SUCH_OBJECT
:
429 if entry
[0].get("provisionnerID"):
430 for e
in entry
[0]["provisionnerID"]:
432 for r
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
433 tab1
= str(r
).split(';')
438 if (len(myids
) > 0 and id not in myids
):
440 tab2
= p
.split(tab1
[0])
441 if range.get(id) is None:
443 range[id].append(tab2
[0])
444 range[id].append(tab2
[1])
450 class ProvisionResult(object):
451 """Result of a provision.
453 :ivar server_role: The server role
454 :ivar paths: ProvisionPaths instance
455 :ivar domaindn: The domain dn, as string
459 self
.server_role
= None
466 self
.domainsid
= None
467 self
.adminpass_generated
= None
468 self
.adminpass
= None
469 self
.backend_result
= None
471 def report_logger(self
, logger
):
472 """Report this provision result to a logger."""
474 "Once the above files are installed, your Samba4 server will "
476 if self
.adminpass_generated
:
477 logger
.info("Admin password: %s", self
.adminpass
)
478 logger
.info("Server Role: %s", self
.server_role
)
479 logger
.info("Hostname: %s", self
.names
.hostname
)
480 logger
.info("NetBIOS Domain: %s", self
.names
.domain
)
481 logger
.info("DNS Domain: %s", self
.names
.dnsdomain
)
482 logger
.info("DOMAIN SID: %s", self
.domainsid
)
484 if self
.backend_result
:
485 self
.backend_result
.report_logger(logger
)
488 def check_install(lp
, session_info
, credentials
):
489 """Check whether the current install seems ok.
491 :param lp: Loadparm context
492 :param session_info: Session information
493 :param credentials: Credentials
495 if lp
.get("realm") == "":
496 raise Exception("Realm empty")
497 samdb
= Ldb(lp
.samdb_url(), session_info
=session_info
,
498 credentials
=credentials
, lp
=lp
)
499 if len(samdb
.search("(cn=Administrator)")) != 1:
500 raise ProvisioningError("No administrator account found")
503 def findnss(nssfn
, names
):
504 """Find a user or group from a list of possibilities.
506 :param nssfn: NSS Function to try (should raise KeyError if not found)
507 :param names: Names to check.
508 :return: Value return by first names list.
515 raise KeyError("Unable to find user/group in %r" % names
)
518 findnss_uid
= lambda names
: findnss(pwd
.getpwnam
, names
)[2]
519 findnss_gid
= lambda names
: findnss(grp
.getgrnam
, names
)[2]
522 def provision_paths_from_lp(lp
, dnsdomain
):
523 """Set the default paths for provisioning.
525 :param lp: Loadparm context.
526 :param dnsdomain: DNS Domain name
528 paths
= ProvisionPaths()
529 paths
.private_dir
= lp
.get("private dir")
530 paths
.state_dir
= lp
.get("state directory")
532 # This is stored without path prefix for the "privateKeytab" attribute in
533 # "secrets_dns.ldif".
534 paths
.dns_keytab
= "dns.keytab"
535 paths
.keytab
= "secrets.keytab"
537 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
538 paths
.samdb
= os
.path
.join(paths
.private_dir
, "sam.ldb")
539 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, "idmap.ldb")
540 paths
.secrets
= os
.path
.join(paths
.private_dir
, "secrets.ldb")
541 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
542 paths
.dns
= os
.path
.join(paths
.private_dir
, "dns", dnsdomain
+ ".zone")
543 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
544 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
545 paths
.namedconf
= os
.path
.join(paths
.private_dir
, "named.conf")
546 paths
.namedconf_update
= os
.path
.join(paths
.private_dir
, "named.conf.update")
547 paths
.namedtxt
= os
.path
.join(paths
.private_dir
, "named.txt")
548 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
549 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
550 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
551 paths
.hklm
= "hklm.ldb"
552 paths
.hkcr
= "hkcr.ldb"
553 paths
.hkcu
= "hkcu.ldb"
554 paths
.hku
= "hku.ldb"
555 paths
.hkpd
= "hkpd.ldb"
556 paths
.hkpt
= "hkpt.ldb"
557 paths
.sysvol
= lp
.get("path", "sysvol")
558 paths
.netlogon
= lp
.get("path", "netlogon")
559 paths
.smbconf
= lp
.configfile
563 def determine_netbios_name(hostname
):
564 """Determine a netbios name from a hostname."""
565 # remove forbidden chars and force the length to be <16
566 netbiosname
= "".join([x
for x
in hostname
if is_valid_netbios_char(x
)])
567 return netbiosname
[:MAX_NETBIOS_NAME_LEN
].upper()
570 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
571 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
572 schemadn
=None, serverdn
=None, sitename
=None,
573 domain_names_forced
=False):
574 """Guess configuration settings to use."""
577 hostname
= socket
.gethostname().split(".")[0]
579 netbiosname
= lp
.get("netbios name")
580 if netbiosname
is None:
581 netbiosname
= determine_netbios_name(hostname
)
582 netbiosname
= netbiosname
.upper()
583 if not valid_netbios_name(netbiosname
):
584 raise InvalidNetbiosName(netbiosname
)
586 if dnsdomain
is None:
587 dnsdomain
= lp
.get("realm")
588 if dnsdomain
is None or dnsdomain
== "":
589 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp
.configfile
)
591 dnsdomain
= dnsdomain
.lower()
593 if serverrole
is None:
594 serverrole
= lp
.get("server role")
595 if serverrole
is None:
596 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
598 serverrole
= serverrole
.lower()
600 realm
= dnsdomain
.upper()
602 if lp
.get("realm") == "":
603 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
605 if lp
.get("realm").upper() != realm
:
606 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
))
608 if lp
.get("server role").lower() != serverrole
:
609 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
))
611 if serverrole
== "active directory domain controller":
613 # This will, for better or worse, default to 'WORKGROUP'
614 domain
= lp
.get("workgroup")
615 domain
= domain
.upper()
617 if lp
.get("workgroup").upper() != domain
:
618 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
))
621 domaindn
= samba
.dn_from_dns_name(dnsdomain
)
623 if domain
== netbiosname
:
624 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain
, netbiosname
))
628 domaindn
= "DC=" + netbiosname
630 if not valid_netbios_name(domain
):
631 raise InvalidNetbiosName(domain
)
633 if hostname
.upper() == realm
:
634 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
635 if netbiosname
.upper() == realm
:
636 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm
, netbiosname
))
637 if domain
== realm
and not domain_names_forced
:
638 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
644 configdn
= "CN=Configuration," + rootdn
646 schemadn
= "CN=Schema," + configdn
649 sitename
= DEFAULTSITE
651 names
= ProvisionNames()
652 names
.rootdn
= rootdn
653 names
.domaindn
= domaindn
654 names
.configdn
= configdn
655 names
.schemadn
= schemadn
656 names
.ldapmanagerdn
= "CN=Manager," + rootdn
657 names
.dnsdomain
= dnsdomain
658 names
.domain
= domain
660 names
.netbiosname
= netbiosname
661 names
.hostname
= hostname
662 names
.sitename
= sitename
663 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
664 netbiosname
, sitename
, configdn
)
669 def make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
670 serverrole
=None, eadb
=False, use_ntvfs
=False, lp
=None,
672 """Create a new smb.conf file based on a couple of basic settings.
674 assert smbconf
is not None
677 hostname
= socket
.gethostname().split(".")[0]
679 netbiosname
= determine_netbios_name(hostname
)
681 if serverrole
is None:
682 serverrole
= "standalone server"
684 assert domain
is not None
685 domain
= domain
.upper()
687 assert realm
is not None
688 realm
= realm
.upper()
691 "netbios name": netbiosname
,
694 "server role": serverrole
,
698 lp
= samba
.param
.LoadParm()
699 #Load non-existent file
700 if os
.path
.exists(smbconf
):
703 if global_param
is not None:
704 for ent
in global_param
:
705 if global_param
[ent
] is not None:
706 global_settings
[ent
] = " ".join(global_param
[ent
])
708 if targetdir
is not None:
709 global_settings
["private dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "private"))
710 global_settings
["lock dir"] = os
.path
.abspath(targetdir
)
711 global_settings
["state directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "state"))
712 global_settings
["cache directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "cache"))
714 lp
.set("lock dir", os
.path
.abspath(targetdir
))
715 lp
.set("state directory", global_settings
["state directory"])
716 lp
.set("cache directory", global_settings
["cache directory"])
719 if use_ntvfs
and not lp
.get("posix:eadb"):
720 if targetdir
is not None:
721 privdir
= os
.path
.join(targetdir
, "private")
723 privdir
= lp
.get("private dir")
724 lp
.set("posix:eadb", os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
725 elif not use_ntvfs
and not lp
.get("xattr_tdb:file"):
726 if targetdir
is not None:
727 statedir
= os
.path
.join(targetdir
, "state")
729 statedir
= lp
.get("state directory")
730 lp
.set("xattr_tdb:file", os
.path
.abspath(os
.path
.join(statedir
, "xattr.tdb")))
733 if serverrole
== "active directory domain controller":
734 shares
["sysvol"] = os
.path
.join(lp
.get("state directory"), "sysvol")
735 shares
["netlogon"] = os
.path
.join(shares
["sysvol"], realm
.lower(),
738 global_settings
["passdb backend"] = "samba_dsdb"
740 f
= open(smbconf
, 'w')
742 f
.write("[globals]\n")
743 for key
, val
in global_settings
.iteritems():
744 f
.write("\t%s = %s\n" % (key
, val
))
747 for name
, path
in shares
.iteritems():
748 f
.write("[%s]\n" % name
)
749 f
.write("\tpath = %s\n" % path
)
750 f
.write("\tread only = no\n")
754 # reload the smb.conf
757 # and dump it without any values that are the default
758 # this ensures that any smb.conf parameters that were set
759 # on the provision/join command line are set in the resulting smb.conf
760 f
= open(smbconf
, mode
='w')
767 def setup_name_mappings(idmap
, sid
, root_uid
, nobody_uid
,
768 users_gid
, root_gid
):
769 """setup reasonable name mappings for sam names to unix names.
771 :param samdb: SamDB object.
772 :param idmap: IDmap db object.
773 :param sid: The domain sid.
774 :param domaindn: The domain DN.
775 :param root_uid: uid of the UNIX root user.
776 :param nobody_uid: uid of the UNIX nobody user.
777 :param users_gid: gid of the UNIX users group.
778 :param root_gid: gid of the UNIX root group.
780 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
782 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
783 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
786 def setup_samdb_partitions(samdb_path
, logger
, lp
, session_info
,
787 provision_backend
, names
, schema
, serverrole
,
789 """Setup the partitions for the SAM database.
791 Alternatively, provision() may call this, and then populate the database.
793 :note: This will wipe the Sam Database!
795 :note: This function always removes the local SAM LDB file. The erase
796 parameter controls whether to erase the existing data, which
797 may not be stored locally but in LDAP.
800 assert session_info
is not None
802 # We use options=["modules:"] to stop the modules loading - we
803 # just want to wipe and re-initialise the database, not start it up
806 os
.unlink(samdb_path
)
810 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
811 lp
=lp
, options
=["modules:"])
813 ldap_backend_line
= "# No LDAP backend"
814 if provision_backend
.type != "ldb":
815 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldap_uri
817 samdb
.transaction_start()
819 logger
.info("Setting up sam.ldb partitions and settings")
820 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
821 "LDAP_BACKEND_LINE": ldap_backend_line
825 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
826 "BACKEND_TYPE": provision_backend
.type,
827 "SERVER_ROLE": serverrole
830 logger
.info("Setting up sam.ldb rootDSE")
831 setup_samdb_rootdse(samdb
, names
)
833 samdb
.transaction_cancel()
836 samdb
.transaction_commit()
839 def secretsdb_self_join(secretsdb
, domain
,
840 netbiosname
, machinepass
, domainsid
=None,
841 realm
=None, dnsdomain
=None,
843 key_version_number
=1,
844 secure_channel_type
=SEC_CHAN_WKSTA
):
845 """Add domain join-specific bits to a secrets database.
847 :param secretsdb: Ldb Handle to the secrets database
848 :param machinepass: Machine password
850 attrs
= ["whenChanged",
857 if realm
is not None:
858 if dnsdomain
is None:
859 dnsdomain
= realm
.lower()
860 dnsname
= '%s.%s' % (netbiosname
.lower(), dnsdomain
.lower())
863 shortname
= netbiosname
.lower()
865 # We don't need to set msg["flatname"] here, because rdn_name will handle
866 # it, and it causes problems for modifies anyway
867 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
868 msg
["secureChannelType"] = [str(secure_channel_type
)]
869 msg
["objectClass"] = ["top", "primaryDomain"]
870 if dnsname
is not None:
871 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
872 msg
["realm"] = [realm
]
873 msg
["saltPrincipal"] = ["host/%s@%s" % (dnsname
, realm
.upper())]
874 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
875 msg
["privateKeytab"] = ["secrets.keytab"]
877 msg
["secret"] = [machinepass
]
878 msg
["samAccountName"] = ["%s$" % netbiosname
]
879 msg
["secureChannelType"] = [str(secure_channel_type
)]
880 if domainsid
is not None:
881 msg
["objectSid"] = [ndr_pack(domainsid
)]
883 # This complex expression tries to ensure that we don't have more
884 # than one record for this SID, realm or netbios domain at a time,
885 # but we don't delete the old record that we are about to modify,
886 # because that would delete the keytab and previous password.
887 res
= secretsdb
.search(base
="cn=Primary Domains", attrs
=attrs
,
888 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain
, realm
, str(domainsid
), str(msg
.dn
))),
889 scope
=ldb
.SCOPE_ONELEVEL
)
892 secretsdb
.delete(del_msg
.dn
)
894 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
897 msg
["priorSecret"] = [res
[0]["secret"][0]]
898 msg
["priorWhenChanged"] = [res
[0]["whenChanged"][0]]
901 msg
["privateKeytab"] = [res
[0]["privateKeytab"][0]]
906 msg
["krb5Keytab"] = [res
[0]["krb5Keytab"][0]]
912 msg
[el
].set_flags(ldb
.FLAG_MOD_REPLACE
)
913 secretsdb
.modify(msg
)
914 secretsdb
.rename(res
[0].dn
, msg
.dn
)
916 spn
= [ 'HOST/%s' % shortname
]
917 if secure_channel_type
== SEC_CHAN_BDC
and dnsname
is not None:
918 # we are a domain controller then we add servicePrincipalName
919 # entries for the keytab code to update.
920 spn
.extend([ 'HOST/%s' % dnsname
])
921 msg
["servicePrincipalName"] = spn
926 def setup_secretsdb(paths
, session_info
, backend_credentials
, lp
):
927 """Setup the secrets database.
929 :note: This function does not handle exceptions and transaction on purpose,
930 it's up to the caller to do this job.
932 :param path: Path to the secrets database.
933 :param session_info: Session info.
934 :param credentials: Credentials
935 :param lp: Loadparm context
936 :return: LDB handle for the created secrets database
938 if os
.path
.exists(paths
.secrets
):
939 os
.unlink(paths
.secrets
)
941 keytab_path
= os
.path
.join(paths
.private_dir
, paths
.keytab
)
942 if os
.path
.exists(keytab_path
):
943 os
.unlink(keytab_path
)
945 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
946 if os
.path
.exists(dns_keytab_path
):
947 os
.unlink(dns_keytab_path
)
951 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
953 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
954 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
955 secrets_ldb
.transaction_start()
957 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
959 if (backend_credentials
is not None and
960 backend_credentials
.authentication_requested()):
961 if backend_credentials
.get_bind_dn() is not None:
962 setup_add_ldif(secrets_ldb
,
963 setup_path("secrets_simple_ldap.ldif"), {
964 "LDAPMANAGERDN": backend_credentials
.get_bind_dn(),
965 "LDAPMANAGERPASS_B64": b64encode(backend_credentials
.get_password())
968 setup_add_ldif(secrets_ldb
,
969 setup_path("secrets_sasl_ldap.ldif"), {
970 "LDAPADMINUSER": backend_credentials
.get_username(),
971 "LDAPADMINREALM": backend_credentials
.get_realm(),
972 "LDAPADMINPASS_B64": b64encode(backend_credentials
.get_password())
975 secrets_ldb
.transaction_cancel()
980 def setup_privileges(path
, session_info
, lp
):
981 """Setup the privileges database.
983 :param path: Path to the privileges database.
984 :param session_info: Session info.
985 :param credentials: Credentials
986 :param lp: Loadparm context
987 :return: LDB handle for the created secrets database
989 if os
.path
.exists(path
):
991 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
992 privilege_ldb
.erase()
993 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
996 def setup_registry(path
, session_info
, lp
):
997 """Setup the registry.
999 :param path: Path to the registry database
1000 :param session_info: Session information
1001 :param credentials: Credentials
1002 :param lp: Loadparm context
1004 reg
= samba
.registry
.Registry()
1005 hive
= samba
.registry
.open_ldb(path
, session_info
=session_info
, lp_ctx
=lp
)
1006 reg
.mount_hive(hive
, samba
.registry
.HKEY_LOCAL_MACHINE
)
1007 provision_reg
= setup_path("provision.reg")
1008 assert os
.path
.exists(provision_reg
)
1009 reg
.diff_apply(provision_reg
)
1012 def setup_idmapdb(path
, session_info
, lp
):
1013 """Setup the idmap database.
1015 :param path: path to the idmap database
1016 :param session_info: Session information
1017 :param credentials: Credentials
1018 :param lp: Loadparm context
1020 if os
.path
.exists(path
):
1023 idmap_ldb
= IDmapDB(path
, session_info
=session_info
, lp
=lp
)
1025 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
1029 def setup_samdb_rootdse(samdb
, names
):
1030 """Setup the SamDB rootdse.
1032 :param samdb: Sam Database handle
1034 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
1035 "SCHEMADN": names
.schemadn
,
1036 "DOMAINDN": names
.domaindn
,
1037 "ROOTDN" : names
.rootdn
,
1038 "CONFIGDN": names
.configdn
,
1039 "SERVERDN": names
.serverdn
,
1043 def setup_self_join(samdb
, admin_session_info
, names
, fill
, machinepass
,
1044 dns_backend
, dnspass
, domainsid
, next_rid
, invocationid
,
1045 policyguid
, policyguid_dc
,
1046 domainControllerFunctionality
, ntdsguid
=None, dc_rid
=None):
1047 """Join a host to its own domain."""
1048 assert isinstance(invocationid
, str)
1049 if ntdsguid
is not None:
1050 ntdsguid_line
= "objectGUID: %s\n"%ntdsguid
1057 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
1058 "CONFIGDN": names
.configdn
,
1059 "SCHEMADN": names
.schemadn
,
1060 "DOMAINDN": names
.domaindn
,
1061 "SERVERDN": names
.serverdn
,
1062 "INVOCATIONID": invocationid
,
1063 "NETBIOSNAME": names
.netbiosname
,
1064 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1065 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1066 "DOMAINSID": str(domainsid
),
1067 "DCRID": str(dc_rid
),
1068 "SAMBA_VERSION_STRING": version
,
1069 "NTDSGUID": ntdsguid_line
,
1070 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1071 domainControllerFunctionality
),
1072 "RIDALLOCATIONSTART": str(next_rid
+ 100),
1073 "RIDALLOCATIONEND": str(next_rid
+ 100 + 499)})
1075 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
1076 "POLICYGUID": policyguid
,
1077 "POLICYGUID_DC": policyguid_dc
,
1078 "DNSDOMAIN": names
.dnsdomain
,
1079 "DOMAINDN": names
.domaindn
})
1081 # If we are setting up a subdomain, then this has been replicated in, so we
1082 # don't need to add it
1083 if fill
== FILL_FULL
:
1084 setup_add_ldif(samdb
, setup_path("provision_self_join_config.ldif"), {
1085 "CONFIGDN": names
.configdn
,
1086 "SCHEMADN": names
.schemadn
,
1087 "DOMAINDN": names
.domaindn
,
1088 "SERVERDN": names
.serverdn
,
1089 "INVOCATIONID": invocationid
,
1090 "NETBIOSNAME": names
.netbiosname
,
1091 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1092 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1093 "DOMAINSID": str(domainsid
),
1094 "DCRID": str(dc_rid
),
1095 "SAMBA_VERSION_STRING": version
,
1096 "NTDSGUID": ntdsguid_line
,
1097 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1098 domainControllerFunctionality
)})
1100 # Setup fSMORoleOwner entries to point at the newly created DC entry
1101 setup_modify_ldif(samdb
,
1102 setup_path("provision_self_join_modify_config.ldif"), {
1103 "CONFIGDN": names
.configdn
,
1104 "SCHEMADN": names
.schemadn
,
1105 "DEFAULTSITE": names
.sitename
,
1106 "NETBIOSNAME": names
.netbiosname
,
1107 "SERVERDN": names
.serverdn
,
1110 system_session_info
= system_session()
1111 samdb
.set_session_info(system_session_info
)
1112 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1113 # modify a serverReference under cn=config when we are a subdomain, we must
1114 # be system due to ACLs
1115 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
1116 "DOMAINDN": names
.domaindn
,
1117 "SERVERDN": names
.serverdn
,
1118 "NETBIOSNAME": names
.netbiosname
,
1121 samdb
.set_session_info(admin_session_info
)
1123 if dns_backend
!= "SAMBA_INTERNAL":
1124 # This is Samba4 specific and should be replaced by the correct
1125 # DNS AD-style setup
1126 setup_add_ldif(samdb
, setup_path("provision_dns_add_samba.ldif"), {
1127 "DNSDOMAIN": names
.dnsdomain
,
1128 "DOMAINDN": names
.domaindn
,
1129 "DNSPASS_B64": b64encode(dnspass
.encode('utf-16-le')),
1130 "HOSTNAME" : names
.hostname
,
1131 "DNSNAME" : '%s.%s' % (
1132 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
1136 def getpolicypath(sysvolpath
, dnsdomain
, guid
):
1137 """Return the physical path of policy given its guid.
1139 :param sysvolpath: Path to the sysvol folder
1140 :param dnsdomain: DNS name of the AD domain
1141 :param guid: The GUID of the policy
1142 :return: A string with the complete path to the policy folder
1145 guid
= "{%s}" % guid
1146 policy_path
= os
.path
.join(sysvolpath
, dnsdomain
, "Policies", guid
)
1150 def create_gpo_struct(policy_path
):
1151 if not os
.path
.exists(policy_path
):
1152 os
.makedirs(policy_path
, 0775)
1153 f
= open(os
.path
.join(policy_path
, "GPT.INI"), 'w')
1155 f
.write("[General]\r\nVersion=0")
1158 p
= os
.path
.join(policy_path
, "MACHINE")
1159 if not os
.path
.exists(p
):
1160 os
.makedirs(p
, 0775)
1161 p
= os
.path
.join(policy_path
, "USER")
1162 if not os
.path
.exists(p
):
1163 os
.makedirs(p
, 0775)
1166 def create_default_gpo(sysvolpath
, dnsdomain
, policyguid
, policyguid_dc
):
1167 """Create the default GPO for a domain
1169 :param sysvolpath: Physical path for the sysvol folder
1170 :param dnsdomain: DNS domain name of the AD domain
1171 :param policyguid: GUID of the default domain policy
1172 :param policyguid_dc: GUID of the default domain controler policy
1174 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid
)
1175 create_gpo_struct(policy_path
)
1177 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid_dc
)
1178 create_gpo_struct(policy_path
)
1181 def setup_samdb(path
, session_info
, provision_backend
, lp
, names
,
1182 logger
, fill
, serverrole
, schema
, am_rodc
=False):
1183 """Setup a complete SAM Database.
1185 :note: This will wipe the main SAM database file!
1188 # Also wipes the database
1189 setup_samdb_partitions(path
, logger
=logger
, lp
=lp
,
1190 provision_backend
=provision_backend
, session_info
=session_info
,
1191 names
=names
, serverrole
=serverrole
, schema
=schema
)
1193 # Load the database, but don's load the global schema and don't connect
1195 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
1196 credentials
=provision_backend
.credentials
, lp
=lp
,
1197 global_schema
=False, am_rodc
=am_rodc
)
1199 logger
.info("Pre-loading the Samba 4 and AD schema")
1201 # Load the schema from the one we computed earlier
1202 samdb
.set_schema(schema
, write_indices_and_attributes
=False)
1204 # Set the NTDS settings DN manually - in order to have it already around
1205 # before the provisioned tree exists and we connect
1206 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1208 # And now we can connect to the DB - the schema won't be loaded from the
1212 except ldb
.LdbError
, (num
, string_error
):
1213 if (num
== ldb
.ERR_INSUFFICIENT_ACCESS_RIGHTS
):
1214 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path
)
1218 # But we have to give it one more kick to have it use the schema
1219 # during provision - it needs, now that it is connected, to write
1220 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1221 samdb
.set_schema(schema
, write_indices_and_attributes
=True)
1226 def fill_samdb(samdb
, lp
, names
, logger
, policyguid
,
1227 policyguid_dc
, fill
, adminpass
, krbtgtpass
, machinepass
, dns_backend
,
1228 dnspass
, invocationid
, ntdsguid
, serverrole
, am_rodc
=False,
1229 dom_for_fun_level
=None, schema
=None, next_rid
=None, dc_rid
=None):
1231 if next_rid
is None:
1234 # Provision does not make much sense values larger than 1000000000
1235 # as the upper range of the rIDAvailablePool is 1073741823 and
1236 # we don't want to create a domain that cannot allocate rids.
1237 if next_rid
< 1000 or next_rid
> 1000000000:
1238 error
= "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid
)
1239 error
+= "the valid range is %u-%u. The default is %u." % (
1240 1000, 1000000000, 1000)
1241 raise ProvisioningError(error
)
1243 # ATTENTION: Do NOT change these default values without discussion with the
1244 # team and/or release manager. They have a big impact on the whole program!
1245 domainControllerFunctionality
= DS_DOMAIN_FUNCTION_2008_R2
1247 if dom_for_fun_level
is None:
1248 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2008_R2
1250 if dom_for_fun_level
> domainControllerFunctionality
:
1251 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!")
1253 domainFunctionality
= dom_for_fun_level
1254 forestFunctionality
= dom_for_fun_level
1256 # Set the NTDS settings DN manually - in order to have it already around
1257 # before the provisioned tree exists and we connect
1258 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1260 samdb
.transaction_start()
1262 # Set the domain functionality levels onto the database.
1263 # Various module (the password_hash module in particular) need
1264 # to know what level of AD we are emulating.
1266 # These will be fixed into the database via the database
1267 # modifictions below, but we need them set from the start.
1268 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
1269 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
1270 samdb
.set_opaque_integer("domainControllerFunctionality",
1271 domainControllerFunctionality
)
1273 samdb
.set_domain_sid(str(names
.domainsid
))
1274 samdb
.set_invocation_id(invocationid
)
1276 logger
.info("Adding DomainDN: %s" % names
.domaindn
)
1278 # impersonate domain admin
1279 admin_session_info
= admin_session(lp
, str(names
.domainsid
))
1280 samdb
.set_session_info(admin_session_info
)
1281 if names
.domainguid
is not None:
1282 domainguid_line
= "objectGUID: %s\n-" % names
.domainguid
1284 domainguid_line
= ""
1286 descr
= b64encode(get_domain_descriptor(names
.domainsid
))
1287 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
1288 "DOMAINDN": names
.domaindn
,
1289 "DOMAINSID": str(names
.domainsid
),
1290 "DESCRIPTOR": descr
,
1291 "DOMAINGUID": domainguid_line
1294 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
1295 "DOMAINDN": names
.domaindn
,
1296 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1297 "NEXTRID": str(next_rid
),
1298 "DEFAULTSITE": names
.sitename
,
1299 "CONFIGDN": names
.configdn
,
1300 "POLICYGUID": policyguid
,
1301 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1302 "SAMBA_VERSION_STRING": version
1305 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1306 if fill
== FILL_FULL
:
1307 logger
.info("Adding configuration container")
1308 descr
= b64encode(get_config_descriptor(names
.domainsid
))
1309 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
1310 "CONFIGDN": names
.configdn
,
1311 "DESCRIPTOR": descr
,
1314 # The LDIF here was created when the Schema object was constructed
1315 logger
.info("Setting up sam.ldb schema")
1316 samdb
.add_ldif(schema
.schema_dn_add
, controls
=["relax:0"])
1317 samdb
.modify_ldif(schema
.schema_dn_modify
)
1318 samdb
.write_prefixes_from_schema()
1319 samdb
.add_ldif(schema
.schema_data
, controls
=["relax:0"])
1320 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
1321 {"SCHEMADN": names
.schemadn
})
1323 # Now register this container in the root of the forest
1324 msg
= ldb
.Message(ldb
.Dn(samdb
, names
.domaindn
))
1325 msg
["subRefs"] = ldb
.MessageElement(names
.configdn
, ldb
.FLAG_MOD_ADD
,
1329 samdb
.transaction_cancel()
1332 samdb
.transaction_commit()
1334 samdb
.transaction_start()
1336 samdb
.invocation_id
= invocationid
1338 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1339 if fill
== FILL_FULL
:
1340 logger
.info("Setting up sam.ldb configuration data")
1342 partitions_descr
= b64encode(get_config_partitions_descriptor(names
.domainsid
))
1343 sites_descr
= b64encode(get_config_sites_descriptor(names
.domainsid
))
1344 ntdsquotas_descr
= b64encode(get_config_ntds_quotas_descriptor(names
.domainsid
))
1345 protected1_descr
= b64encode(get_config_delete_protected1_descriptor(names
.domainsid
))
1346 protected1wd_descr
= b64encode(get_config_delete_protected1wd_descriptor(names
.domainsid
))
1347 protected2_descr
= b64encode(get_config_delete_protected2_descriptor(names
.domainsid
))
1349 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
1350 "CONFIGDN": names
.configdn
,
1351 "NETBIOSNAME": names
.netbiosname
,
1352 "DEFAULTSITE": names
.sitename
,
1353 "DNSDOMAIN": names
.dnsdomain
,
1354 "DOMAIN": names
.domain
,
1355 "SCHEMADN": names
.schemadn
,
1356 "DOMAINDN": names
.domaindn
,
1357 "SERVERDN": names
.serverdn
,
1358 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
1359 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1360 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr
,
1361 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr
,
1362 "SERVICES_DESCRIPTOR": protected1_descr
,
1363 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr
,
1364 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr
,
1365 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr
,
1366 "PARTITIONS_DESCRIPTOR": partitions_descr
,
1367 "SITES_DESCRIPTOR": sites_descr
,
1370 logger
.info("Setting up display specifiers")
1371 display_specifiers_ldif
= read_ms_ldif(
1372 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1373 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
,
1374 {"CONFIGDN": names
.configdn
})
1375 check_all_substituted(display_specifiers_ldif
)
1376 samdb
.add_ldif(display_specifiers_ldif
)
1378 logger
.info("Modifying display specifiers")
1379 setup_modify_ldif(samdb
,
1380 setup_path("provision_configuration_modify.ldif"), {
1381 "CONFIGDN": names
.configdn
,
1382 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1385 logger
.info("Adding users container")
1386 users_desc
= b64encode(get_domain_users_descriptor(names
.domainsid
))
1387 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
1388 "DOMAINDN": names
.domaindn
,
1389 "USERS_DESCRIPTOR": users_desc
1391 logger
.info("Modifying users container")
1392 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
1393 "DOMAINDN": names
.domaindn
})
1394 logger
.info("Adding computers container")
1395 computers_desc
= b64encode(get_domain_computers_descriptor(names
.domainsid
))
1396 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
1397 "DOMAINDN": names
.domaindn
,
1398 "COMPUTERS_DESCRIPTOR": computers_desc
1400 logger
.info("Modifying computers container")
1401 setup_modify_ldif(samdb
,
1402 setup_path("provision_computers_modify.ldif"), {
1403 "DOMAINDN": names
.domaindn
})
1404 logger
.info("Setting up sam.ldb data")
1405 infrastructure_desc
= b64encode(get_domain_infrastructure_descriptor(names
.domainsid
))
1406 lostandfound_desc
= b64encode(get_domain_delete_protected2_descriptor(names
.domainsid
))
1407 system_desc
= b64encode(get_domain_delete_protected1_descriptor(names
.domainsid
))
1408 builtin_desc
= b64encode(get_domain_builtin_descriptor(names
.domainsid
))
1409 controllers_desc
= b64encode(get_domain_controllers_descriptor(names
.domainsid
))
1410 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1411 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1412 "DOMAINDN": names
.domaindn
,
1413 "NETBIOSNAME": names
.netbiosname
,
1414 "DEFAULTSITE": names
.sitename
,
1415 "CONFIGDN": names
.configdn
,
1416 "SERVERDN": names
.serverdn
,
1417 "RIDAVAILABLESTART": str(next_rid
+ 600),
1418 "POLICYGUID_DC": policyguid_dc
,
1419 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc
,
1420 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc
,
1421 "SYSTEM_DESCRIPTOR": system_desc
,
1422 "BUILTIN_DESCRIPTOR": builtin_desc
,
1423 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc
,
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 setup_modify_ldif(samdb
,
1429 setup_path("provision_configuration_references.ldif"), {
1430 "CONFIGDN": names
.configdn
,
1431 "SCHEMADN": names
.schemadn
})
1433 logger
.info("Setting up well known security principals")
1434 protected1wd_descr
= b64encode(get_config_delete_protected1wd_descriptor(names
.domainsid
))
1435 setup_add_ldif(samdb
, setup_path("provision_well_known_sec_princ.ldif"), {
1436 "CONFIGDN": names
.configdn
,
1437 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr
,
1440 if fill
== FILL_FULL
or fill
== FILL_SUBDOMAIN
:
1441 setup_modify_ldif(samdb
,
1442 setup_path("provision_basedn_references.ldif"),
1443 {"DOMAINDN": names
.domaindn
})
1445 logger
.info("Setting up sam.ldb users and groups")
1446 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1447 "DOMAINDN": names
.domaindn
,
1448 "DOMAINSID": str(names
.domainsid
),
1449 "ADMINPASS_B64": b64encode(adminpass
.encode('utf-16-le')),
1450 "KRBTGTPASS_B64": b64encode(krbtgtpass
.encode('utf-16-le'))
1453 logger
.info("Setting up self join")
1454 setup_self_join(samdb
, admin_session_info
, names
=names
, fill
=fill
,
1455 invocationid
=invocationid
,
1456 dns_backend
=dns_backend
,
1458 machinepass
=machinepass
,
1459 domainsid
=names
.domainsid
,
1462 policyguid
=policyguid
,
1463 policyguid_dc
=policyguid_dc
,
1464 domainControllerFunctionality
=domainControllerFunctionality
,
1467 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1468 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1469 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
)
1470 assert isinstance(names
.ntdsguid
, str)
1472 samdb
.transaction_cancel()
1475 samdb
.transaction_commit()
1479 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1480 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)"
1481 SYSVOL_SERVICE
="sysvol"
1483 def set_dir_acl(path
, acl
, lp
, domsid
, use_ntvfs
, passdb
, service
=SYSVOL_SERVICE
):
1484 setntacl(lp
, path
, acl
, domsid
, use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1485 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1487 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1488 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1490 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1491 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1494 def set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
):
1495 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1498 :param sysvol: Physical path for the sysvol folder
1499 :param dnsdomain: The DNS name of the domain
1500 :param domainsid: The SID of the domain
1501 :param domaindn: The DN of the domain (ie. DC=...)
1502 :param samdb: An LDB object on the SAM db
1503 :param lp: an LP object
1506 # Set ACL for GPO root folder
1507 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1508 setntacl(lp
, root_policy_path
, POLICIES_ACL
, str(domainsid
),
1509 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=SYSVOL_SERVICE
)
1511 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1512 attrs
=["cn", "nTSecurityDescriptor"],
1513 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1516 acl
= ndr_unpack(security
.descriptor
,
1517 str(policy
["nTSecurityDescriptor"])).as_sddl()
1518 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1519 set_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1520 str(domainsid
), use_ntvfs
,
1524 def setsysvolacl(samdb
, netlogon
, sysvol
, uid
, gid
, domainsid
, dnsdomain
,
1525 domaindn
, lp
, use_ntvfs
):
1526 """Set the ACL for the sysvol share and the subfolders
1528 :param samdb: An LDB object on the SAM db
1529 :param netlogon: Physical path for the netlogon folder
1530 :param sysvol: Physical path for the sysvol folder
1531 :param uid: The UID of the "Administrator" user
1532 :param gid: The GID of the "Domain adminstrators" group
1533 :param domainsid: The SID of the domain
1534 :param dnsdomain: The DNS name of the domain
1535 :param domaindn: The DN of the domain (ie. DC=...)
1540 s3conf
= s3param
.get_context()
1541 s3conf
.load(lp
.configfile
)
1543 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(sysvol
))
1546 smbd
.set_simple_acl(file.name
, 0755, gid
)
1548 if not smbd
.have_posix_acls():
1549 # This clue is only strictly correct for RPM and
1550 # Debian-like Linux systems, but hopefully other users
1551 # will get enough clue from it.
1552 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1553 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1555 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1556 "Try the mounting the filesystem with the 'acl' option.")
1558 smbd
.chown(file.name
, uid
, gid
)
1560 raise ProvisioningError("Unable to chown a file on your filesystem. "
1561 "You may not be running provision as root.")
1565 # This will ensure that the smbd code we are running when setting ACLs
1566 # is initialised with the smb.conf
1567 s3conf
= s3param
.get_context()
1568 s3conf
.load(lp
.configfile
)
1569 # ensure we are using the right samba_dsdb passdb backend, no matter what
1570 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1571 passdb
.reload_static_pdb()
1573 # ensure that we init the samba_dsdb backend, so the domain sid is
1574 # marked in secrets.tdb
1575 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1577 # now ensure everything matches correctly, to avoid wierd issues
1578 if passdb
.get_global_sam_sid() != domainsid
:
1579 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
))
1581 domain_info
= s4_passdb
.domain_info()
1582 if domain_info
["dom_sid"] != domainsid
:
1583 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
))
1585 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1586 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()))
1591 os
.chown(sysvol
, -1, gid
)
1597 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1598 setntacl(lp
,sysvol
, SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
,
1599 skip_invalid_chown
=True, passdb
=s4_passdb
,
1600 service
=SYSVOL_SERVICE
)
1601 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1603 if use_ntvfs
and canchown
:
1604 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1605 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
),
1606 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True,
1607 passdb
=s4_passdb
, service
=SYSVOL_SERVICE
)
1609 if use_ntvfs
and canchown
:
1610 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1611 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
),
1612 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True,
1613 passdb
=s4_passdb
, service
=SYSVOL_SERVICE
)
1615 # Set acls on Policy folder and policies folders
1616 set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
=s4_passdb
)
1618 def acl_type(direct_db_access
):
1619 if direct_db_access
:
1624 def check_dir_acl(path
, acl
, lp
, domainsid
, direct_db_access
):
1625 fsacl
= getntacl(lp
, path
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1626 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1627 if fsacl_sddl
!= acl
:
1628 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
))
1630 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1632 fsacl
= getntacl(lp
, os
.path
.join(root
, name
),
1633 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1635 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1636 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1637 if fsacl_sddl
!= acl
:
1638 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
))
1641 fsacl
= getntacl(lp
, os
.path
.join(root
, name
),
1642 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1644 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1645 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1646 if fsacl_sddl
!= acl
:
1647 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
))
1650 def check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1652 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1655 :param sysvol: Physical path for the sysvol folder
1656 :param dnsdomain: The DNS name of the domain
1657 :param domainsid: The SID of the domain
1658 :param domaindn: The DN of the domain (ie. DC=...)
1659 :param samdb: An LDB object on the SAM db
1660 :param lp: an LP object
1663 # Set ACL for GPO root folder
1664 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1665 fsacl
= getntacl(lp
, root_policy_path
,
1666 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1668 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access
), root_policy_path
))
1669 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1670 if fsacl_sddl
!= POLICIES_ACL
:
1671 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
))
1672 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1673 attrs
=["cn", "nTSecurityDescriptor"],
1674 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1677 acl
= ndr_unpack(security
.descriptor
,
1678 str(policy
["nTSecurityDescriptor"])).as_sddl()
1679 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1680 check_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1681 domainsid
, direct_db_access
)
1684 def checksysvolacl(samdb
, netlogon
, sysvol
, domainsid
, dnsdomain
, domaindn
,
1686 """Set the ACL for the sysvol share and the subfolders
1688 :param samdb: An LDB object on the SAM db
1689 :param netlogon: Physical path for the netlogon folder
1690 :param sysvol: Physical path for the sysvol folder
1691 :param uid: The UID of the "Administrator" user
1692 :param gid: The GID of the "Domain adminstrators" group
1693 :param domainsid: The SID of the domain
1694 :param dnsdomain: The DNS name of the domain
1695 :param domaindn: The DN of the domain (ie. DC=...)
1698 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1699 s3conf
= s3param
.get_context()
1700 s3conf
.load(lp
.configfile
)
1701 # ensure we are using the right samba_dsdb passdb backend, no matter what
1702 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1703 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1704 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1706 # now ensure everything matches correctly, to avoid wierd 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()))
1717 # Ensure we can read this directly, and via the smbd VFS
1718 for direct_db_access
in [True, False]:
1719 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1720 for dir_path
in [os
.path
.join(sysvol
, dnsdomain
), netlogon
]:
1721 fsacl
= getntacl(lp
, dir_path
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1723 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access
), dir_path
))
1724 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1725 if fsacl_sddl
!= SYSVOL_ACL
:
1726 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
))
1728 # Check acls on Policy folder and policies folders
1729 check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1733 def interface_ips_v4(lp
):
1734 """return only IPv4 IPs"""
1735 ips
= samba
.interface_ips(lp
, False)
1738 if i
.find(':') == -1:
1743 def interface_ips_v6(lp
):
1744 """return only IPv6 IPs"""
1745 ips
= samba
.interface_ips(lp
, False)
1748 if i
.find(':') != -1:
1753 def provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
1755 targetdir
=None, samdb_fill
=FILL_FULL
,
1756 hostip
=None, hostip6
=None,
1757 next_rid
=1000, dc_rid
=None, adminpass
=None, krbtgtpass
=None,
1758 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1759 invocationid
=None, machinepass
=None, ntdsguid
=None,
1760 dns_backend
=None, dnspass
=None,
1761 serverrole
=None, dom_for_fun_level
=None,
1762 am_rodc
=False, lp
=None, use_ntvfs
=False, skip_sysvolacl
=False):
1763 # create/adapt the group policy GUIDs
1764 # Default GUID for default policy are described at
1765 # "How Core Group Policy Works"
1766 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1767 if policyguid
is None:
1768 policyguid
= DEFAULT_POLICY_GUID
1769 policyguid
= policyguid
.upper()
1770 if policyguid_dc
is None:
1771 policyguid_dc
= DEFAULT_DC_POLICY_GUID
1772 policyguid_dc
= policyguid_dc
.upper()
1774 if invocationid
is None:
1775 invocationid
= str(uuid
.uuid4())
1777 if krbtgtpass
is None:
1778 krbtgtpass
= samba
.generate_random_password(128, 255)
1779 if machinepass
is None:
1780 machinepass
= samba
.generate_random_password(128, 255)
1782 dnspass
= samba
.generate_random_password(128, 255)
1784 samdb
= fill_samdb(samdb
, lp
, names
, logger
=logger
,
1786 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1787 fill
=samdb_fill
, adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1788 invocationid
=invocationid
, machinepass
=machinepass
,
1789 dns_backend
=dns_backend
, dnspass
=dnspass
,
1790 ntdsguid
=ntdsguid
, serverrole
=serverrole
,
1791 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
1792 next_rid
=next_rid
, dc_rid
=dc_rid
)
1794 if serverrole
== "active directory domain controller":
1796 # Set up group policies (domain policy and domain controller
1798 create_default_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
,
1800 if not skip_sysvolacl
:
1801 setsysvolacl(samdb
, paths
.netlogon
, paths
.sysvol
, paths
.root_uid
,
1802 paths
.root_gid
, names
.domainsid
, names
.dnsdomain
,
1803 names
.domaindn
, lp
, use_ntvfs
)
1805 logger
.info("Setting acl on sysvol skipped")
1807 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1808 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1809 netbiosname
=names
.netbiosname
, domainsid
=names
.domainsid
,
1810 machinepass
=machinepass
, secure_channel_type
=SEC_CHAN_BDC
)
1812 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1813 # In future, this might be determined from some configuration
1814 kerberos_enctypes
= str(ENC_ALL_TYPES
)
1817 msg
= ldb
.Message(ldb
.Dn(samdb
,
1818 samdb
.searchone("distinguishedName",
1819 expression
="samAccountName=%s$" % names
.netbiosname
,
1820 scope
=ldb
.SCOPE_SUBTREE
)))
1821 msg
["msDS-SupportedEncryptionTypes"] = ldb
.MessageElement(
1822 elements
=kerberos_enctypes
, flags
=ldb
.FLAG_MOD_REPLACE
,
1823 name
="msDS-SupportedEncryptionTypes")
1825 except ldb
.LdbError
, (enum
, estr
):
1826 if enum
!= ldb
.ERR_NO_SUCH_ATTRIBUTE
:
1827 # It might be that this attribute does not exist in this schema
1830 setup_ad_dns(samdb
, secrets_ldb
, names
, paths
, lp
, logger
,
1831 hostip
=hostip
, hostip6
=hostip6
, dns_backend
=dns_backend
,
1832 dnspass
=dnspass
, os_level
=dom_for_fun_level
,
1833 targetdir
=targetdir
, fill_level
=samdb_fill
)
1835 domainguid
= samdb
.searchone(basedn
=samdb
.get_default_basedn(),
1836 attribute
="objectGUID")
1837 assert isinstance(domainguid
, str)
1839 lastProvisionUSNs
= get_last_provision_usn(samdb
)
1840 maxUSN
= get_max_usn(samdb
, str(names
.rootdn
))
1841 if lastProvisionUSNs
is not None:
1842 update_provision_usn(samdb
, 0, maxUSN
, invocationid
, 1)
1844 set_provision_usn(samdb
, 0, maxUSN
, invocationid
)
1846 logger
.info("Setting up sam.ldb rootDSE marking as synchronized")
1847 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"),
1848 { 'NTDSGUID' : names
.ntdsguid
})
1850 # fix any dangling GUIDs from the provision
1851 logger
.info("Fixing provision GUIDs")
1852 chk
= dbcheck(samdb
, samdb_schema
=samdb
, verbose
=False, fix
=True, yes
=True,
1854 samdb
.transaction_start()
1856 # a small number of GUIDs are missing because of ordering issues in the
1858 for schema_obj
in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1859 chk
.check_database(DN
="%s,%s" % (schema_obj
, names
.schemadn
),
1860 scope
=ldb
.SCOPE_BASE
,
1861 attrs
=['defaultObjectCategory'])
1862 chk
.check_database(DN
="CN=IP Security,CN=System,%s" % names
.domaindn
,
1863 scope
=ldb
.SCOPE_ONELEVEL
,
1864 attrs
=['ipsecOwnersReference',
1865 'ipsecFilterReference',
1866 'ipsecISAKMPReference',
1867 'ipsecNegotiationPolicyReference',
1868 'ipsecNFAReference'])
1870 samdb
.transaction_cancel()
1873 samdb
.transaction_commit()
1877 "ROLE_STANDALONE": "standalone server",
1878 "ROLE_DOMAIN_MEMBER": "member server",
1879 "ROLE_DOMAIN_BDC": "active directory domain controller",
1880 "ROLE_DOMAIN_PDC": "active directory domain controller",
1881 "dc": "active directory domain controller",
1882 "member": "member server",
1883 "domain controller": "active directory domain controller",
1884 "active directory domain controller": "active directory domain controller",
1885 "member server": "member server",
1886 "standalone": "standalone server",
1887 "standalone server": "standalone server",
1891 def sanitize_server_role(role
):
1892 """Sanitize a server role name.
1894 :param role: Server role
1895 :raise ValueError: If the role can not be interpreted
1896 :return: Sanitized server role (one of "member server",
1897 "active directory domain controller", "standalone server")
1900 return _ROLES_MAP
[role
]
1902 raise ValueError(role
)
1905 def provision_fake_ypserver(logger
, samdb
, domaindn
, netbiosname
, nisdomain
,
1907 """Create AD entries for the fake ypserver.
1909 This is needed for being able to manipulate posix attrs via ADUC.
1911 samdb
.transaction_start()
1913 logger
.info("Setting up fake yp server settings")
1914 setup_add_ldif(samdb
, setup_path("ypServ30.ldif"), {
1915 "DOMAINDN": domaindn
,
1916 "NETBIOSNAME": netbiosname
,
1917 "NISDOMAIN": nisdomain
,
1920 samdb
.transaction_cancel()
1923 samdb
.transaction_commit()
1926 def provision(logger
, session_info
, smbconf
=None,
1927 targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None, rootdn
=None,
1928 domaindn
=None, schemadn
=None, configdn
=None, serverdn
=None,
1929 domain
=None, hostname
=None, hostip
=None, hostip6
=None, domainsid
=None,
1930 next_rid
=1000, dc_rid
=None, adminpass
=None, ldapadminpass
=None,
1931 krbtgtpass
=None, domainguid
=None, policyguid
=None, policyguid_dc
=None,
1932 dns_backend
=None, dns_forwarder
=None, dnspass
=None,
1933 invocationid
=None, machinepass
=None, ntdsguid
=None,
1934 root
=None, nobody
=None, users
=None, backup
=None, aci
=None,
1935 serverrole
=None, dom_for_fun_level
=None, backend_type
=None,
1936 sitename
=None, ol_mmr_urls
=None, ol_olc
=None, slapd_path
=None,
1937 useeadb
=False, am_rodc
=False, lp
=None, use_ntvfs
=False,
1938 use_rfc2307
=False, maxuid
=None, maxgid
=None, skip_sysvolacl
=True,
1939 ldap_backend_forced_uri
=None, nosync
=False, ldap_dryrun_mode
=False, ldap_backend_extra_port
=None):
1942 :note: caution, this wipes all existing data!
1946 serverrole
= sanitize_server_role(serverrole
)
1948 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole
)
1950 if ldapadminpass
is None:
1951 # Make a new, random password between Samba and it's LDAP server
1952 ldapadminpass
= samba
.generate_random_password(128, 255)
1954 if backend_type
is None:
1955 backend_type
= "ldb"
1957 if domainsid
is None:
1958 domainsid
= security
.random_sid()
1960 root_uid
= findnss_uid([root
or "root"])
1961 nobody_uid
= findnss_uid([nobody
or "nobody"])
1962 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
1963 root_gid
= pwd
.getpwuid(root_uid
).pw_gid
1966 bind_gid
= findnss_gid(["bind", "named"])
1970 if targetdir
is not None:
1971 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
1972 elif smbconf
is None:
1973 smbconf
= samba
.param
.default_path()
1974 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
1975 os
.makedirs(os
.path
.dirname(smbconf
))
1977 server_services
= []
1980 global_param
["idmap_ldb:use rfc2307"] = ["yes"]
1982 if dns_backend
!= "SAMBA_INTERNAL":
1983 server_services
.append("-dns")
1985 if dns_forwarder
is not None:
1986 global_param
["dns forwarder"] = [dns_forwarder
]
1989 server_services
.append("+smb")
1990 server_services
.append("-s3fs")
1991 global_param
["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1993 if len(server_services
) > 0:
1994 global_param
["server services"] = server_services
1996 # only install a new smb.conf if there isn't one there already
1997 if os
.path
.exists(smbconf
):
1998 # if Samba Team members can't figure out the weird errors
1999 # loading an empty smb.conf gives, then we need to be smarter.
2000 # Pretend it just didn't exist --abartlet
2001 f
= open(smbconf
, 'r')
2003 data
= f
.read().lstrip()
2006 if data
is None or data
== "":
2007 make_smbconf(smbconf
, hostname
, domain
, realm
,
2008 targetdir
, serverrole
=serverrole
,
2009 eadb
=useeadb
, use_ntvfs
=use_ntvfs
,
2010 lp
=lp
, global_param
=global_param
)
2012 make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
2013 serverrole
=serverrole
,
2014 eadb
=useeadb
, use_ntvfs
=use_ntvfs
, lp
=lp
, global_param
=global_param
)
2017 lp
= samba
.param
.LoadParm()
2019 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
2020 dnsdomain
=realm
, serverrole
=serverrole
, domaindn
=domaindn
,
2021 configdn
=configdn
, schemadn
=schemadn
, serverdn
=serverdn
,
2022 sitename
=sitename
, rootdn
=rootdn
, domain_names_forced
=(samdb_fill
== FILL_DRS
))
2023 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
2025 paths
.bind_gid
= bind_gid
2026 paths
.root_uid
= root_uid
;
2027 paths
.root_gid
= root_gid
2030 logger
.info("Looking up IPv4 addresses")
2031 hostips
= interface_ips_v4(lp
)
2032 if len(hostips
) > 0:
2034 if len(hostips
) > 1:
2035 logger
.warning("More than one IPv4 address found. Using %s",
2037 if hostip
== "127.0.0.1":
2040 logger
.warning("No IPv4 address will be assigned")
2043 logger
.info("Looking up IPv6 addresses")
2044 hostips
= interface_ips_v6(lp
)
2046 hostip6
= hostips
[0]
2047 if len(hostips
) > 1:
2048 logger
.warning("More than one IPv6 address found. Using %s", hostip6
)
2050 logger
.warning("No IPv6 address will be assigned")
2052 names
.hostip
= hostip
2053 names
.hostip6
= hostip6
2054 names
.domainguid
= domainguid
2055 names
.domainsid
= domainsid
2056 names
.forestsid
= domainsid
2058 if serverrole
is None:
2059 serverrole
= lp
.get("server role")
2061 if not os
.path
.exists(paths
.private_dir
):
2062 os
.mkdir(paths
.private_dir
)
2063 if not os
.path
.exists(os
.path
.join(paths
.private_dir
, "tls")):
2064 os
.makedirs(os
.path
.join(paths
.private_dir
, "tls"), 0700)
2065 if not os
.path
.exists(paths
.state_dir
):
2066 os
.mkdir(paths
.state_dir
)
2068 if paths
.sysvol
and not os
.path
.exists(paths
.sysvol
):
2069 os
.makedirs(paths
.sysvol
, 0775)
2071 ldapi_url
= "ldapi://%s" % urllib
.quote(paths
.s4_ldapi_path
, safe
="")
2073 schema
= Schema(domainsid
, invocationid
=invocationid
,
2074 schemadn
=names
.schemadn
)
2076 if backend_type
== "ldb":
2077 provision_backend
= LDBBackend(backend_type
, paths
=paths
,
2079 names
=names
, logger
=logger
)
2080 elif backend_type
== "existing":
2081 # If support for this is ever added back, then the URI will need to be
2083 provision_backend
= ExistingBackend(backend_type
, paths
=paths
,
2085 names
=names
, logger
=logger
,
2086 ldap_backend_forced_uri
=ldap_backend_forced_uri
)
2087 elif backend_type
== "fedora-ds":
2088 provision_backend
= FDSBackend(backend_type
, paths
=paths
,
2090 names
=names
, logger
=logger
, domainsid
=domainsid
,
2091 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
2092 slapd_path
=slapd_path
,
2094 elif backend_type
== "openldap":
2095 provision_backend
= OpenLDAPBackend(backend_type
, paths
=paths
,
2097 names
=names
, logger
=logger
, domainsid
=domainsid
,
2098 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
2099 slapd_path
=slapd_path
, ol_mmr_urls
=ol_mmr_urls
,
2100 ldap_backend_extra_port
=ldap_backend_extra_port
,
2101 ldap_dryrun_mode
=ldap_dryrun_mode
, nosync
=nosync
,
2102 ldap_backend_forced_uri
=ldap_backend_forced_uri
)
2104 raise ValueError("Unknown LDAP backend type selected")
2106 provision_backend
.init()
2107 provision_backend
.start()
2109 # only install a new shares config db if there is none
2110 if not os
.path
.exists(paths
.shareconf
):
2111 logger
.info("Setting up share.ldb")
2112 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
, lp
=lp
)
2113 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
2115 logger
.info("Setting up secrets.ldb")
2116 secrets_ldb
= setup_secretsdb(paths
,
2117 session_info
=session_info
,
2118 backend_credentials
=provision_backend
.credentials
, lp
=lp
)
2121 logger
.info("Setting up the registry")
2122 setup_registry(paths
.hklm
, session_info
, lp
=lp
)
2124 logger
.info("Setting up the privileges database")
2125 setup_privileges(paths
.privilege
, session_info
, lp
=lp
)
2127 logger
.info("Setting up idmap db")
2128 idmap
= setup_idmapdb(paths
.idmapdb
, session_info
=session_info
, lp
=lp
)
2130 setup_name_mappings(idmap
, sid
=str(domainsid
),
2131 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
2132 users_gid
=users_gid
, root_gid
=root_gid
)
2134 logger
.info("Setting up SAM db")
2135 samdb
= setup_samdb(paths
.samdb
, session_info
,
2136 provision_backend
, lp
, names
, logger
=logger
,
2137 serverrole
=serverrole
,
2138 schema
=schema
, fill
=samdb_fill
, am_rodc
=am_rodc
)
2140 if serverrole
== "active directory domain controller":
2141 if paths
.netlogon
is None:
2142 raise MissingShareError("netlogon", paths
.smbconf
)
2144 if paths
.sysvol
is None:
2145 raise MissingShareError("sysvol", paths
.smbconf
)
2147 if not os
.path
.isdir(paths
.netlogon
):
2148 os
.makedirs(paths
.netlogon
, 0755)
2150 if adminpass
is None:
2151 adminpass
= samba
.generate_random_password(12, 32)
2152 adminpass_generated
= True
2154 adminpass
= unicode(adminpass
, 'utf-8')
2155 adminpass_generated
= False
2157 if samdb_fill
== FILL_FULL
:
2158 provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
2159 schema
=schema
, targetdir
=targetdir
, samdb_fill
=samdb_fill
,
2160 hostip
=hostip
, hostip6
=hostip6
,
2161 next_rid
=next_rid
, dc_rid
=dc_rid
, adminpass
=adminpass
,
2162 krbtgtpass
=krbtgtpass
,
2163 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
2164 invocationid
=invocationid
, machinepass
=machinepass
,
2165 ntdsguid
=ntdsguid
, dns_backend
=dns_backend
,
2166 dnspass
=dnspass
, serverrole
=serverrole
,
2167 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
2168 lp
=lp
, use_ntvfs
=use_ntvfs
,
2169 skip_sysvolacl
=skip_sysvolacl
)
2171 create_krb5_conf(paths
.krb5conf
,
2172 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
2174 logger
.info("A Kerberos configuration suitable for Samba 4 has been "
2175 "generated at %s", paths
.krb5conf
)
2177 if serverrole
== "active directory domain controller":
2178 create_dns_update_list(lp
, logger
, paths
)
2180 backend_result
= provision_backend
.post_setup()
2181 provision_backend
.shutdown()
2184 secrets_ldb
.transaction_cancel()
2187 # Now commit the secrets.ldb to disk
2188 secrets_ldb
.transaction_commit()
2190 # the commit creates the dns.keytab, now chown it
2191 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
2192 if os
.path
.isfile(dns_keytab_path
) and paths
.bind_gid
is not None:
2194 os
.chmod(dns_keytab_path
, 0640)
2195 os
.chown(dns_keytab_path
, -1, paths
.bind_gid
)
2197 if not os
.environ
.has_key('SAMBA_SELFTEST'):
2198 logger
.info("Failed to chown %s to bind gid %u",
2199 dns_keytab_path
, paths
.bind_gid
)
2201 result
= ProvisionResult()
2202 result
.server_role
= serverrole
2203 result
.domaindn
= domaindn
2204 result
.paths
= paths
2205 result
.names
= names
2207 result
.samdb
= samdb
2208 result
.idmap
= idmap
2209 result
.domainsid
= str(domainsid
)
2211 if samdb_fill
== FILL_FULL
:
2212 result
.adminpass_generated
= adminpass_generated
2213 result
.adminpass
= adminpass
2215 result
.adminpass_generated
= False
2216 result
.adminpass
= None
2218 result
.backend_result
= backend_result
2221 provision_fake_ypserver(logger
=logger
, samdb
=samdb
,
2222 domaindn
=names
.domaindn
, netbiosname
=names
.netbiosname
,
2223 nisdomain
=names
.domain
.lower(), maxuid
=maxuid
, maxgid
=maxgid
)
2228 def provision_become_dc(smbconf
=None, targetdir
=None,
2229 realm
=None, rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
2230 serverdn
=None, domain
=None, hostname
=None, domainsid
=None,
2231 adminpass
=None, krbtgtpass
=None, domainguid
=None, policyguid
=None,
2232 policyguid_dc
=None, invocationid
=None, machinepass
=None, dnspass
=None,
2233 dns_backend
=None, root
=None, nobody
=None, users
=None,
2234 backup
=None, serverrole
=None, ldap_backend
=None,
2235 ldap_backend_type
=None, sitename
=None, debuglevel
=1, use_ntvfs
=False):
2237 logger
= logging
.getLogger("provision")
2238 samba
.set_debug_level(debuglevel
)
2240 res
= provision(logger
, system_session(),
2241 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
2242 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
2243 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
2244 hostname
=hostname
, hostip
=None, domainsid
=domainsid
,
2245 machinepass
=machinepass
,
2246 serverrole
="active directory domain controller",
2247 sitename
=sitename
, dns_backend
=dns_backend
, dnspass
=dnspass
,
2248 use_ntvfs
=use_ntvfs
)
2249 res
.lp
.set("debuglevel", str(debuglevel
))
2253 def create_krb5_conf(path
, dnsdomain
, hostname
, realm
):
2254 """Write out a file containing a valid krb5.conf file
2256 :param path: Path of the new krb5.conf file.
2257 :param dnsdomain: DNS Domain name
2258 :param hostname: Local hostname
2259 :param realm: Realm name
2261 setup_file(setup_path("krb5.conf"), path
, {
2262 "DNSDOMAIN": dnsdomain
,
2263 "HOSTNAME": hostname
,
2268 class ProvisioningError(Exception):
2269 """A generic provision error."""
2271 def __init__(self
, value
):
2275 return "ProvisioningError: " + self
.value
2278 class InvalidNetbiosName(Exception):
2279 """A specified name was not a valid NetBIOS name."""
2281 def __init__(self
, name
):
2282 super(InvalidNetbiosName
, self
).__init
__(
2283 "The name '%r' is not a valid NetBIOS name" % name
)
2286 class MissingShareError(ProvisioningError
):
2288 def __init__(self
, name
, smbconf
):
2289 super(MissingShareError
, self
).__init
__(
2290 "Existing smb.conf does not have a [%s] share, but you are "
2291 "configuring a DC. Please remove %s or add the share manually." %