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
.provision
.descriptor
import (
81 get_config_descriptor
,
82 get_config_partitions_descriptor
,
85 from samba
.provision
.common
import (
90 from samba
.provision
.sambadns
import (
92 create_dns_update_list
97 from samba
.schema
import Schema
98 from samba
.samdb
import SamDB
99 from samba
.dbchecker
import dbcheck
102 DEFAULT_POLICY_GUID
= "31B2F340-016D-11D2-945F-00C04FB984F9"
103 DEFAULT_DC_POLICY_GUID
= "6AC1786C-016F-11D2-945F-00C04fB984F9"
104 DEFAULTSITE
= "Default-First-Site-Name"
105 LAST_PROVISION_USN_ATTRIBUTE
= "lastProvisionUSN"
108 class ProvisionPaths(object):
111 self
.shareconf
= None
122 self
.dns_keytab
= None
125 self
.private_dir
= None
126 self
.state_dir
= None
129 class ProvisionNames(object):
136 self
.ldapmanagerdn
= None
137 self
.dnsdomain
= None
139 self
.netbiosname
= None
146 def find_provision_key_parameters(samdb
, secretsdb
, idmapdb
, paths
, smbconf
,
148 """Get key provision parameters (realm, domain, ...) from a given provision
150 :param samdb: An LDB object connected to the sam.ldb file
151 :param secretsdb: An LDB object connected to the secrets.ldb file
152 :param idmapdb: An LDB object connected to the idmap.ldb file
153 :param paths: A list of path to provision object
154 :param smbconf: Path to the smb.conf file
155 :param lp: A LoadParm object
156 :return: A list of key provision parameters
158 names
= ProvisionNames()
159 names
.adminpass
= None
161 # NT domain, kerberos realm, root dn, domain dn, domain dns name
162 names
.domain
= string
.upper(lp
.get("workgroup"))
163 names
.realm
= lp
.get("realm")
164 names
.dnsdomain
= names
.realm
.lower()
165 basedn
= samba
.dn_from_dns_name(names
.dnsdomain
)
166 names
.realm
= string
.upper(names
.realm
)
168 # Get the netbiosname first (could be obtained from smb.conf in theory)
169 res
= secretsdb
.search(expression
="(flatname=%s)" %
170 names
.domain
,base
="CN=Primary Domains",
171 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
172 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
174 names
.smbconf
= smbconf
176 # That's a bit simplistic but it's ok as long as we have only 3
178 current
= samdb
.search(expression
="(objectClass=*)",
179 base
="", scope
=ldb
.SCOPE_BASE
,
180 attrs
=["defaultNamingContext", "schemaNamingContext",
181 "configurationNamingContext","rootDomainNamingContext"])
183 names
.configdn
= current
[0]["configurationNamingContext"]
184 configdn
= str(names
.configdn
)
185 names
.schemadn
= current
[0]["schemaNamingContext"]
186 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
,
187 current
[0]["defaultNamingContext"][0]))):
188 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
189 "is not the same ..." % (paths
.samdb
,
190 str(current
[0]["defaultNamingContext"][0]),
191 paths
.smbconf
, basedn
)))
193 names
.domaindn
=current
[0]["defaultNamingContext"]
194 names
.rootdn
=current
[0]["rootDomainNamingContext"]
196 res3
= samdb
.search(expression
="(objectClass=site)",
197 base
="CN=Sites," + configdn
, scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn"])
198 names
.sitename
= str(res3
[0]["cn"])
200 # dns hostname and server dn
201 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
202 base
="OU=Domain Controllers,%s" % basedn
,
203 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
204 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("." + names
.dnsdomain
, "")
206 server_res
= samdb
.search(expression
="serverReference=%s" % res4
[0].dn
,
207 attrs
=[], base
=configdn
)
208 names
.serverdn
= server_res
[0].dn
210 # invocation id/objectguid
211 res5
= samdb
.search(expression
="(objectClass=*)",
212 base
="CN=NTDS Settings,%s" % str(names
.serverdn
),
213 scope
=ldb
.SCOPE_BASE
,
214 attrs
=["invocationID", "objectGUID"])
215 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
216 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
219 res6
= samdb
.search(expression
="(objectClass=*)", base
=basedn
,
220 scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID",
221 "objectSid","msDS-Behavior-Version" ])
222 names
.domainguid
= str(ndr_unpack(misc
.GUID
, res6
[0]["objectGUID"][0]))
223 names
.domainsid
= ndr_unpack( security
.dom_sid
, res6
[0]["objectSid"][0])
224 if res6
[0].get("msDS-Behavior-Version") is None or \
225 int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
226 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
228 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
231 res7
= samdb
.search(expression
="(displayName=Default Domain Policy)",
232 base
="CN=Policies,CN=System," + basedn
,
233 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
234 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
236 res8
= samdb
.search(expression
="(displayName=Default Domain Controllers"
238 base
="CN=Policies,CN=System," + basedn
,
239 scope
=ldb
.SCOPE_ONELEVEL
,
240 attrs
=["cn","displayName"])
242 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
244 names
.policyid_dc
= None
246 res9
= idmapdb
.search(expression
="(cn=%s-%s)" %
247 (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
),
248 attrs
=["xidNumber", "type"])
250 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
))
251 if res9
[0]["type"][0] == "ID_TYPE_BOTH":
252 names
.root_gid
= res9
[0]["xidNumber"][0]
254 names
.root_gid
= pwd
.getpwuid(int(res9
[0]["xidNumber"][0])).pw_gid
258 def update_provision_usn(samdb
, low
, high
, id, replace
=False):
259 """Update the field provisionUSN in sam.ldb
261 This field is used to track range of USN modified by provision and
263 This value is used afterward by next provision to figure out if
264 the field have been modified since last provision.
266 :param samdb: An LDB object connect to sam.ldb
267 :param low: The lowest USN modified by this upgrade
268 :param high: The highest USN modified by this upgrade
269 :param id: The invocation id of the samba's dc
270 :param replace: A boolean indicating if the range should replace any
271 existing one or appended (default)
276 entry
= samdb
.search(base
="@PROVISION",
277 scope
=ldb
.SCOPE_BASE
,
278 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "dn"])
279 for e
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
280 if not re
.search(';', e
):
281 e
= "%s;%s" % (e
, id)
284 tab
.append("%s-%s;%s" % (low
, high
, id))
285 delta
= ldb
.Message()
286 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
287 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
288 ldb
.FLAG_MOD_REPLACE
, LAST_PROVISION_USN_ATTRIBUTE
)
289 entry
= samdb
.search(expression
='provisionnerID=*',
290 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
291 attrs
=["provisionnerID"])
292 if len(entry
) == 0 or len(entry
[0]) == 0:
293 delta
["provisionnerID"] = ldb
.MessageElement(id, ldb
.FLAG_MOD_ADD
, "provisionnerID")
297 def set_provision_usn(samdb
, low
, high
, id):
298 """Set the field provisionUSN in sam.ldb
299 This field is used to track range of USN modified by provision and
301 This value is used afterward by next provision to figure out if
302 the field have been modified since last provision.
304 :param samdb: An LDB object connect to sam.ldb
305 :param low: The lowest USN modified by this upgrade
306 :param high: The highest USN modified by this upgrade
307 :param id: The invocationId of the provision"""
310 tab
.append("%s-%s;%s" % (low
, high
, id))
312 delta
= ldb
.Message()
313 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
314 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
315 ldb
.FLAG_MOD_ADD
, LAST_PROVISION_USN_ATTRIBUTE
)
319 def get_max_usn(samdb
,basedn
):
320 """ This function return the biggest USN present in the provision
322 :param samdb: A LDB object pointing to the sam.ldb
323 :param basedn: A string containing the base DN of the provision
325 :return: The biggest USN in the provision"""
327 res
= samdb
.search(expression
="objectClass=*",base
=basedn
,
328 scope
=ldb
.SCOPE_SUBTREE
,attrs
=["uSNChanged"],
329 controls
=["search_options:1:2",
330 "server_sort:1:1:uSNChanged",
331 "paged_results:1:1"])
332 return res
[0]["uSNChanged"]
335 def get_last_provision_usn(sam
):
336 """Get USNs ranges modified by a provision or an upgradeprovision
338 :param sam: An LDB object pointing to the sam.ldb
339 :return: a dictionary which keys are invocation id and values are an array
340 of integer representing the different ranges
343 entry
= sam
.search(expression
="%s=*" % LAST_PROVISION_USN_ATTRIBUTE
,
344 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
345 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "provisionnerID"])
346 except ldb
.LdbError
, (ecode
, emsg
):
347 if ecode
== ldb
.ERR_NO_SUCH_OBJECT
:
354 if entry
[0].get("provisionnerID"):
355 for e
in entry
[0]["provisionnerID"]:
357 for r
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
358 tab1
= str(r
).split(';')
363 if (len(myids
) > 0 and id not in myids
):
365 tab2
= p
.split(tab1
[0])
366 if range.get(id) is None:
368 range[id].append(tab2
[0])
369 range[id].append(tab2
[1])
375 class ProvisionResult(object):
376 """Result of a provision.
378 :ivar server_role: The server role
379 :ivar paths: ProvisionPaths instance
380 :ivar domaindn: The domain dn, as string
384 self
.server_role
= None
391 self
.domainsid
= None
392 self
.adminpass_generated
= None
393 self
.adminpass
= None
394 self
.backend_result
= None
396 def report_logger(self
, logger
):
397 """Report this provision result to a logger."""
399 "Once the above files are installed, your Samba4 server will "
401 if self
.adminpass_generated
:
402 logger
.info("Admin password: %s", self
.adminpass
)
403 logger
.info("Server Role: %s", self
.server_role
)
404 logger
.info("Hostname: %s", self
.names
.hostname
)
405 logger
.info("NetBIOS Domain: %s", self
.names
.domain
)
406 logger
.info("DNS Domain: %s", self
.names
.dnsdomain
)
407 logger
.info("DOMAIN SID: %s", self
.domainsid
)
409 if self
.backend_result
:
410 self
.backend_result
.report_logger(logger
)
413 def check_install(lp
, session_info
, credentials
):
414 """Check whether the current install seems ok.
416 :param lp: Loadparm context
417 :param session_info: Session information
418 :param credentials: Credentials
420 if lp
.get("realm") == "":
421 raise Exception("Realm empty")
422 samdb
= Ldb(lp
.samdb_url(), session_info
=session_info
,
423 credentials
=credentials
, lp
=lp
)
424 if len(samdb
.search("(cn=Administrator)")) != 1:
425 raise ProvisioningError("No administrator account found")
428 def findnss(nssfn
, names
):
429 """Find a user or group from a list of possibilities.
431 :param nssfn: NSS Function to try (should raise KeyError if not found)
432 :param names: Names to check.
433 :return: Value return by first names list.
440 raise KeyError("Unable to find user/group in %r" % names
)
443 findnss_uid
= lambda names
: findnss(pwd
.getpwnam
, names
)[2]
444 findnss_gid
= lambda names
: findnss(grp
.getgrnam
, names
)[2]
447 def provision_paths_from_lp(lp
, dnsdomain
):
448 """Set the default paths for provisioning.
450 :param lp: Loadparm context.
451 :param dnsdomain: DNS Domain name
453 paths
= ProvisionPaths()
454 paths
.private_dir
= lp
.get("private dir")
455 paths
.state_dir
= lp
.get("state directory")
457 # This is stored without path prefix for the "privateKeytab" attribute in
458 # "secrets_dns.ldif".
459 paths
.dns_keytab
= "dns.keytab"
460 paths
.keytab
= "secrets.keytab"
462 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
463 paths
.samdb
= os
.path
.join(paths
.private_dir
, "sam.ldb")
464 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, "idmap.ldb")
465 paths
.secrets
= os
.path
.join(paths
.private_dir
, "secrets.ldb")
466 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
467 paths
.dns
= os
.path
.join(paths
.private_dir
, "dns", dnsdomain
+ ".zone")
468 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
469 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
470 paths
.namedconf
= os
.path
.join(paths
.private_dir
, "named.conf")
471 paths
.namedconf_update
= os
.path
.join(paths
.private_dir
, "named.conf.update")
472 paths
.namedtxt
= os
.path
.join(paths
.private_dir
, "named.txt")
473 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
474 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
475 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
476 paths
.hklm
= "hklm.ldb"
477 paths
.hkcr
= "hkcr.ldb"
478 paths
.hkcu
= "hkcu.ldb"
479 paths
.hku
= "hku.ldb"
480 paths
.hkpd
= "hkpd.ldb"
481 paths
.hkpt
= "hkpt.ldb"
482 paths
.sysvol
= lp
.get("path", "sysvol")
483 paths
.netlogon
= lp
.get("path", "netlogon")
484 paths
.smbconf
= lp
.configfile
488 def determine_netbios_name(hostname
):
489 """Determine a netbios name from a hostname."""
490 # remove forbidden chars and force the length to be <16
491 netbiosname
= "".join([x
for x
in hostname
if is_valid_netbios_char(x
)])
492 return netbiosname
[:MAX_NETBIOS_NAME_LEN
].upper()
495 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
496 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
497 schemadn
=None, serverdn
=None, sitename
=None):
498 """Guess configuration settings to use."""
501 hostname
= socket
.gethostname().split(".")[0]
503 netbiosname
= lp
.get("netbios name")
504 if netbiosname
is None:
505 netbiosname
= determine_netbios_name(hostname
)
506 netbiosname
= netbiosname
.upper()
507 if not valid_netbios_name(netbiosname
):
508 raise InvalidNetbiosName(netbiosname
)
510 if dnsdomain
is None:
511 dnsdomain
= lp
.get("realm")
512 if dnsdomain
is None or dnsdomain
== "":
513 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp
.configfile
)
515 dnsdomain
= dnsdomain
.lower()
517 if serverrole
is None:
518 serverrole
= lp
.get("server role")
519 if serverrole
is None:
520 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
522 serverrole
= serverrole
.lower()
524 realm
= dnsdomain
.upper()
526 if lp
.get("realm") == "":
527 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
529 if lp
.get("realm").upper() != realm
:
530 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(), realm
, lp
.configfile
))
532 if lp
.get("server role").lower() != serverrole
:
533 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
))
535 if serverrole
== "active directory domain controller":
537 # This will, for better or worse, default to 'WORKGROUP'
538 domain
= lp
.get("workgroup")
539 domain
= domain
.upper()
541 if lp
.get("workgroup").upper() != domain
:
542 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
))
545 domaindn
= samba
.dn_from_dns_name(dnsdomain
)
547 if domain
== netbiosname
:
548 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain
, netbiosname
))
552 domaindn
= "DC=" + netbiosname
554 if not valid_netbios_name(domain
):
555 raise InvalidNetbiosName(domain
)
557 if hostname
.upper() == realm
:
558 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
559 if netbiosname
.upper() == realm
:
560 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm
, netbiosname
))
562 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
568 configdn
= "CN=Configuration," + rootdn
570 schemadn
= "CN=Schema," + configdn
573 sitename
= DEFAULTSITE
575 names
= ProvisionNames()
576 names
.rootdn
= rootdn
577 names
.domaindn
= domaindn
578 names
.configdn
= configdn
579 names
.schemadn
= schemadn
580 names
.ldapmanagerdn
= "CN=Manager," + rootdn
581 names
.dnsdomain
= dnsdomain
582 names
.domain
= domain
584 names
.netbiosname
= netbiosname
585 names
.hostname
= hostname
586 names
.sitename
= sitename
587 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
588 netbiosname
, sitename
, configdn
)
593 def make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
594 serverrole
=None, eadb
=False, use_ntvfs
=False, lp
=None,
596 """Create a new smb.conf file based on a couple of basic settings.
598 assert smbconf
is not None
601 hostname
= socket
.gethostname().split(".")[0]
603 netbiosname
= determine_netbios_name(hostname
)
605 if serverrole
is None:
606 serverrole
= "standalone server"
608 assert domain
is not None
609 domain
= domain
.upper()
611 assert realm
is not None
612 realm
= realm
.upper()
615 "netbios name": netbiosname
,
618 "server role": serverrole
,
622 lp
= samba
.param
.LoadParm()
623 #Load non-existent file
624 if os
.path
.exists(smbconf
):
627 if global_param
is not None:
628 for ent
in global_param
:
629 if global_param
[ent
] is not None:
630 global_settings
[ent
] = " ".join(global_param
[ent
])
632 if targetdir
is not None:
633 global_settings
["private dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "private"))
634 global_settings
["lock dir"] = os
.path
.abspath(targetdir
)
635 global_settings
["state directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "state"))
636 global_settings
["cache directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "cache"))
638 lp
.set("lock dir", os
.path
.abspath(targetdir
))
639 lp
.set("state directory", global_settings
["state directory"])
640 lp
.set("cache directory", global_settings
["cache directory"])
643 if use_ntvfs
and not lp
.get("posix:eadb"):
644 if targetdir
is not None:
645 privdir
= os
.path
.join(targetdir
, "private")
647 privdir
= lp
.get("private dir")
648 lp
.set("posix:eadb", os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
649 elif not use_ntvfs
and not lp
.get("xattr_tdb:file"):
650 if targetdir
is not None:
651 statedir
= os
.path
.join(targetdir
, "state")
653 statedir
= lp
.get("state directory")
654 lp
.set("xattr_tdb:file", os
.path
.abspath(os
.path
.join(statedir
, "xattr.tdb")))
657 if serverrole
== "active directory domain controller":
658 shares
["sysvol"] = os
.path
.join(lp
.get("state directory"), "sysvol")
659 shares
["netlogon"] = os
.path
.join(shares
["sysvol"], realm
.lower(),
662 global_settings
["passdb backend"] = "samba_dsdb"
664 f
= open(smbconf
, 'w')
666 f
.write("[globals]\n")
667 for key
, val
in global_settings
.iteritems():
668 f
.write("\t%s = %s\n" % (key
, val
))
671 for name
, path
in shares
.iteritems():
672 f
.write("[%s]\n" % name
)
673 f
.write("\tpath = %s\n" % path
)
674 f
.write("\tread only = no\n")
678 # reload the smb.conf
681 # and dump it without any values that are the default
682 # this ensures that any smb.conf parameters that were set
683 # on the provision/join command line are set in the resulting smb.conf
684 f
= open(smbconf
, mode
='w')
691 def setup_name_mappings(idmap
, sid
, root_uid
, nobody_uid
,
692 users_gid
, root_gid
):
693 """setup reasonable name mappings for sam names to unix names.
695 :param samdb: SamDB object.
696 :param idmap: IDmap db object.
697 :param sid: The domain sid.
698 :param domaindn: The domain DN.
699 :param root_uid: uid of the UNIX root user.
700 :param nobody_uid: uid of the UNIX nobody user.
701 :param users_gid: gid of the UNIX users group.
702 :param root_gid: gid of the UNIX root group.
704 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
706 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
707 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
710 def setup_samdb_partitions(samdb_path
, logger
, lp
, session_info
,
711 provision_backend
, names
, schema
, serverrole
,
713 """Setup the partitions for the SAM database.
715 Alternatively, provision() may call this, and then populate the database.
717 :note: This will wipe the Sam Database!
719 :note: This function always removes the local SAM LDB file. The erase
720 parameter controls whether to erase the existing data, which
721 may not be stored locally but in LDAP.
724 assert session_info
is not None
726 # We use options=["modules:"] to stop the modules loading - we
727 # just want to wipe and re-initialise the database, not start it up
730 os
.unlink(samdb_path
)
734 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
735 lp
=lp
, options
=["modules:"])
737 ldap_backend_line
= "# No LDAP backend"
738 if provision_backend
.type != "ldb":
739 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldap_uri
741 samdb
.transaction_start()
743 logger
.info("Setting up sam.ldb partitions and settings")
744 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
745 "LDAP_BACKEND_LINE": ldap_backend_line
749 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
750 "BACKEND_TYPE": provision_backend
.type,
751 "SERVER_ROLE": serverrole
754 logger
.info("Setting up sam.ldb rootDSE")
755 setup_samdb_rootdse(samdb
, names
)
757 samdb
.transaction_cancel()
760 samdb
.transaction_commit()
763 def secretsdb_self_join(secretsdb
, domain
,
764 netbiosname
, machinepass
, domainsid
=None,
765 realm
=None, dnsdomain
=None,
767 key_version_number
=1,
768 secure_channel_type
=SEC_CHAN_WKSTA
):
769 """Add domain join-specific bits to a secrets database.
771 :param secretsdb: Ldb Handle to the secrets database
772 :param machinepass: Machine password
774 attrs
= ["whenChanged",
781 if realm
is not None:
782 if dnsdomain
is None:
783 dnsdomain
= realm
.lower()
784 dnsname
= '%s.%s' % (netbiosname
.lower(), dnsdomain
.lower())
787 shortname
= netbiosname
.lower()
789 # We don't need to set msg["flatname"] here, because rdn_name will handle
790 # it, and it causes problems for modifies anyway
791 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
792 msg
["secureChannelType"] = [str(secure_channel_type
)]
793 msg
["objectClass"] = ["top", "primaryDomain"]
794 if dnsname
is not None:
795 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
796 msg
["realm"] = [realm
]
797 msg
["saltPrincipal"] = ["host/%s@%s" % (dnsname
, realm
.upper())]
798 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
799 msg
["privateKeytab"] = ["secrets.keytab"]
801 msg
["secret"] = [machinepass
]
802 msg
["samAccountName"] = ["%s$" % netbiosname
]
803 msg
["secureChannelType"] = [str(secure_channel_type
)]
804 if domainsid
is not None:
805 msg
["objectSid"] = [ndr_pack(domainsid
)]
807 # This complex expression tries to ensure that we don't have more
808 # than one record for this SID, realm or netbios domain at a time,
809 # but we don't delete the old record that we are about to modify,
810 # because that would delete the keytab and previous password.
811 res
= secretsdb
.search(base
="cn=Primary Domains", attrs
=attrs
,
812 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain
, realm
, str(domainsid
), str(msg
.dn
))),
813 scope
=ldb
.SCOPE_ONELEVEL
)
816 secretsdb
.delete(del_msg
.dn
)
818 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
821 msg
["priorSecret"] = [res
[0]["secret"][0]]
822 msg
["priorWhenChanged"] = [res
[0]["whenChanged"][0]]
825 msg
["privateKeytab"] = [res
[0]["privateKeytab"][0]]
830 msg
["krb5Keytab"] = [res
[0]["krb5Keytab"][0]]
836 msg
[el
].set_flags(ldb
.FLAG_MOD_REPLACE
)
837 secretsdb
.modify(msg
)
838 secretsdb
.rename(res
[0].dn
, msg
.dn
)
840 spn
= [ 'HOST/%s' % shortname
]
841 if secure_channel_type
== SEC_CHAN_BDC
and dnsname
is not None:
842 # we are a domain controller then we add servicePrincipalName
843 # entries for the keytab code to update.
844 spn
.extend([ 'HOST/%s' % dnsname
])
845 msg
["servicePrincipalName"] = spn
850 def setup_secretsdb(paths
, session_info
, backend_credentials
, lp
):
851 """Setup the secrets database.
853 :note: This function does not handle exceptions and transaction on purpose,
854 it's up to the caller to do this job.
856 :param path: Path to the secrets database.
857 :param session_info: Session info.
858 :param credentials: Credentials
859 :param lp: Loadparm context
860 :return: LDB handle for the created secrets database
862 if os
.path
.exists(paths
.secrets
):
863 os
.unlink(paths
.secrets
)
865 keytab_path
= os
.path
.join(paths
.private_dir
, paths
.keytab
)
866 if os
.path
.exists(keytab_path
):
867 os
.unlink(keytab_path
)
869 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
870 if os
.path
.exists(dns_keytab_path
):
871 os
.unlink(dns_keytab_path
)
875 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
877 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
878 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
879 secrets_ldb
.transaction_start()
881 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
883 if (backend_credentials
is not None and
884 backend_credentials
.authentication_requested()):
885 if backend_credentials
.get_bind_dn() is not None:
886 setup_add_ldif(secrets_ldb
,
887 setup_path("secrets_simple_ldap.ldif"), {
888 "LDAPMANAGERDN": backend_credentials
.get_bind_dn(),
889 "LDAPMANAGERPASS_B64": b64encode(backend_credentials
.get_password())
892 setup_add_ldif(secrets_ldb
,
893 setup_path("secrets_sasl_ldap.ldif"), {
894 "LDAPADMINUSER": backend_credentials
.get_username(),
895 "LDAPADMINREALM": backend_credentials
.get_realm(),
896 "LDAPADMINPASS_B64": b64encode(backend_credentials
.get_password())
899 secrets_ldb
.transaction_cancel()
904 def setup_privileges(path
, session_info
, lp
):
905 """Setup the privileges database.
907 :param path: Path to the privileges database.
908 :param session_info: Session info.
909 :param credentials: Credentials
910 :param lp: Loadparm context
911 :return: LDB handle for the created secrets database
913 if os
.path
.exists(path
):
915 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
916 privilege_ldb
.erase()
917 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
920 def setup_registry(path
, session_info
, lp
):
921 """Setup the registry.
923 :param path: Path to the registry database
924 :param session_info: Session information
925 :param credentials: Credentials
926 :param lp: Loadparm context
928 reg
= samba
.registry
.Registry()
929 hive
= samba
.registry
.open_ldb(path
, session_info
=session_info
, lp_ctx
=lp
)
930 reg
.mount_hive(hive
, samba
.registry
.HKEY_LOCAL_MACHINE
)
931 provision_reg
= setup_path("provision.reg")
932 assert os
.path
.exists(provision_reg
)
933 reg
.diff_apply(provision_reg
)
936 def setup_idmapdb(path
, session_info
, lp
):
937 """Setup the idmap database.
939 :param path: path to the idmap database
940 :param session_info: Session information
941 :param credentials: Credentials
942 :param lp: Loadparm context
944 if os
.path
.exists(path
):
947 idmap_ldb
= IDmapDB(path
, session_info
=session_info
, lp
=lp
)
949 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
953 def setup_samdb_rootdse(samdb
, names
):
954 """Setup the SamDB rootdse.
956 :param samdb: Sam Database handle
958 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
959 "SCHEMADN": names
.schemadn
,
960 "DOMAINDN": names
.domaindn
,
961 "ROOTDN" : names
.rootdn
,
962 "CONFIGDN": names
.configdn
,
963 "SERVERDN": names
.serverdn
,
967 def setup_self_join(samdb
, admin_session_info
, names
, fill
, machinepass
,
968 dns_backend
, dnspass
, domainsid
, next_rid
, invocationid
,
969 policyguid
, policyguid_dc
,
970 domainControllerFunctionality
, ntdsguid
=None, dc_rid
=None):
971 """Join a host to its own domain."""
972 assert isinstance(invocationid
, str)
973 if ntdsguid
is not None:
974 ntdsguid_line
= "objectGUID: %s\n"%ntdsguid
981 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
982 "CONFIGDN": names
.configdn
,
983 "SCHEMADN": names
.schemadn
,
984 "DOMAINDN": names
.domaindn
,
985 "SERVERDN": names
.serverdn
,
986 "INVOCATIONID": invocationid
,
987 "NETBIOSNAME": names
.netbiosname
,
988 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
989 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
990 "DOMAINSID": str(domainsid
),
991 "DCRID": str(dc_rid
),
992 "SAMBA_VERSION_STRING": version
,
993 "NTDSGUID": ntdsguid_line
,
994 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
995 domainControllerFunctionality
),
996 "RIDALLOCATIONSTART": str(next_rid
+ 100),
997 "RIDALLOCATIONEND": str(next_rid
+ 100 + 499)})
999 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
1000 "POLICYGUID": policyguid
,
1001 "POLICYGUID_DC": policyguid_dc
,
1002 "DNSDOMAIN": names
.dnsdomain
,
1003 "DOMAINDN": names
.domaindn
})
1005 # If we are setting up a subdomain, then this has been replicated in, so we
1006 # don't need to add it
1007 if fill
== FILL_FULL
:
1008 setup_add_ldif(samdb
, setup_path("provision_self_join_config.ldif"), {
1009 "CONFIGDN": names
.configdn
,
1010 "SCHEMADN": names
.schemadn
,
1011 "DOMAINDN": names
.domaindn
,
1012 "SERVERDN": names
.serverdn
,
1013 "INVOCATIONID": invocationid
,
1014 "NETBIOSNAME": names
.netbiosname
,
1015 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1016 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1017 "DOMAINSID": str(domainsid
),
1018 "DCRID": str(dc_rid
),
1019 "SAMBA_VERSION_STRING": version
,
1020 "NTDSGUID": ntdsguid_line
,
1021 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1022 domainControllerFunctionality
)})
1024 # Setup fSMORoleOwner entries to point at the newly created DC entry
1025 setup_modify_ldif(samdb
,
1026 setup_path("provision_self_join_modify_config.ldif"), {
1027 "CONFIGDN": names
.configdn
,
1028 "SCHEMADN": names
.schemadn
,
1029 "DEFAULTSITE": names
.sitename
,
1030 "NETBIOSNAME": names
.netbiosname
,
1031 "SERVERDN": names
.serverdn
,
1034 system_session_info
= system_session()
1035 samdb
.set_session_info(system_session_info
)
1036 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1037 # modify a serverReference under cn=config when we are a subdomain, we must
1038 # be system due to ACLs
1039 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
1040 "DOMAINDN": names
.domaindn
,
1041 "SERVERDN": names
.serverdn
,
1042 "NETBIOSNAME": names
.netbiosname
,
1045 samdb
.set_session_info(admin_session_info
)
1047 if dns_backend
!= "SAMBA_INTERNAL":
1048 # This is Samba4 specific and should be replaced by the correct
1049 # DNS AD-style setup
1050 setup_add_ldif(samdb
, setup_path("provision_dns_add_samba.ldif"), {
1051 "DNSDOMAIN": names
.dnsdomain
,
1052 "DOMAINDN": names
.domaindn
,
1053 "DNSPASS_B64": b64encode(dnspass
.encode('utf-16-le')),
1054 "HOSTNAME" : names
.hostname
,
1055 "DNSNAME" : '%s.%s' % (
1056 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
1060 def getpolicypath(sysvolpath
, dnsdomain
, guid
):
1061 """Return the physical path of policy given its guid.
1063 :param sysvolpath: Path to the sysvol folder
1064 :param dnsdomain: DNS name of the AD domain
1065 :param guid: The GUID of the policy
1066 :return: A string with the complete path to the policy folder
1069 guid
= "{%s}" % guid
1070 policy_path
= os
.path
.join(sysvolpath
, dnsdomain
, "Policies", guid
)
1074 def create_gpo_struct(policy_path
):
1075 if not os
.path
.exists(policy_path
):
1076 os
.makedirs(policy_path
, 0775)
1077 f
= open(os
.path
.join(policy_path
, "GPT.INI"), 'w')
1079 f
.write("[General]\r\nVersion=0")
1082 p
= os
.path
.join(policy_path
, "MACHINE")
1083 if not os
.path
.exists(p
):
1084 os
.makedirs(p
, 0775)
1085 p
= os
.path
.join(policy_path
, "USER")
1086 if not os
.path
.exists(p
):
1087 os
.makedirs(p
, 0775)
1090 def create_default_gpo(sysvolpath
, dnsdomain
, policyguid
, policyguid_dc
):
1091 """Create the default GPO for a domain
1093 :param sysvolpath: Physical path for the sysvol folder
1094 :param dnsdomain: DNS domain name of the AD domain
1095 :param policyguid: GUID of the default domain policy
1096 :param policyguid_dc: GUID of the default domain controler policy
1098 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid
)
1099 create_gpo_struct(policy_path
)
1101 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid_dc
)
1102 create_gpo_struct(policy_path
)
1105 def setup_samdb(path
, session_info
, provision_backend
, lp
, names
,
1106 logger
, fill
, serverrole
, schema
, am_rodc
=False):
1107 """Setup a complete SAM Database.
1109 :note: This will wipe the main SAM database file!
1112 # Also wipes the database
1113 setup_samdb_partitions(path
, logger
=logger
, lp
=lp
,
1114 provision_backend
=provision_backend
, session_info
=session_info
,
1115 names
=names
, serverrole
=serverrole
, schema
=schema
)
1117 # Load the database, but don's load the global schema and don't connect
1119 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
1120 credentials
=provision_backend
.credentials
, lp
=lp
,
1121 global_schema
=False, am_rodc
=am_rodc
)
1123 logger
.info("Pre-loading the Samba 4 and AD schema")
1125 # Load the schema from the one we computed earlier
1126 samdb
.set_schema(schema
, write_indices_and_attributes
=False)
1128 # Set the NTDS settings DN manually - in order to have it already around
1129 # before the provisioned tree exists and we connect
1130 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1132 # And now we can connect to the DB - the schema won't be loaded from the
1136 # But we have to give it one more kick to have it use the schema
1137 # during provision - it needs, now that it is connected, to write
1138 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1139 samdb
.set_schema(schema
, write_indices_and_attributes
=True)
1144 def fill_samdb(samdb
, lp
, names
, logger
, domainsid
, domainguid
, policyguid
,
1145 policyguid_dc
, fill
, adminpass
, krbtgtpass
, machinepass
, dns_backend
,
1146 dnspass
, invocationid
, ntdsguid
, serverrole
, am_rodc
=False,
1147 dom_for_fun_level
=None, schema
=None, next_rid
=None, dc_rid
=None):
1149 if next_rid
is None:
1152 # Provision does not make much sense values larger than 1000000000
1153 # as the upper range of the rIDAvailablePool is 1073741823 and
1154 # we don't want to create a domain that cannot allocate rids.
1155 if next_rid
< 1000 or next_rid
> 1000000000:
1156 error
= "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid
)
1157 error
+= "the valid range is %u-%u. The default is %u." % (
1158 1000, 1000000000, 1000)
1159 raise ProvisioningError(error
)
1161 # ATTENTION: Do NOT change these default values without discussion with the
1162 # team and/or release manager. They have a big impact on the whole program!
1163 domainControllerFunctionality
= DS_DOMAIN_FUNCTION_2008_R2
1165 if dom_for_fun_level
is None:
1166 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2003
1168 if dom_for_fun_level
> domainControllerFunctionality
:
1169 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!")
1171 domainFunctionality
= dom_for_fun_level
1172 forestFunctionality
= dom_for_fun_level
1174 # Set the NTDS settings DN manually - in order to have it already around
1175 # before the provisioned tree exists and we connect
1176 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1178 samdb
.transaction_start()
1180 # Set the domain functionality levels onto the database.
1181 # Various module (the password_hash module in particular) need
1182 # to know what level of AD we are emulating.
1184 # These will be fixed into the database via the database
1185 # modifictions below, but we need them set from the start.
1186 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
1187 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
1188 samdb
.set_opaque_integer("domainControllerFunctionality",
1189 domainControllerFunctionality
)
1191 samdb
.set_domain_sid(str(domainsid
))
1192 samdb
.set_invocation_id(invocationid
)
1194 logger
.info("Adding DomainDN: %s" % names
.domaindn
)
1196 # impersonate domain admin
1197 admin_session_info
= admin_session(lp
, str(domainsid
))
1198 samdb
.set_session_info(admin_session_info
)
1199 if domainguid
is not None:
1200 domainguid_line
= "objectGUID: %s\n-" % domainguid
1202 domainguid_line
= ""
1204 descr
= b64encode(get_domain_descriptor(domainsid
))
1205 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
1206 "DOMAINDN": names
.domaindn
,
1207 "DOMAINSID": str(domainsid
),
1208 "DESCRIPTOR": descr
,
1209 "DOMAINGUID": domainguid_line
1212 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
1213 "DOMAINDN": names
.domaindn
,
1214 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1215 "NEXTRID": str(next_rid
),
1216 "DEFAULTSITE": names
.sitename
,
1217 "CONFIGDN": names
.configdn
,
1218 "POLICYGUID": policyguid
,
1219 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1220 "SAMBA_VERSION_STRING": version
1223 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1224 if fill
== FILL_FULL
:
1225 logger
.info("Adding configuration container")
1226 descr
= b64encode(get_config_descriptor(domainsid
))
1227 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
1228 "CONFIGDN": names
.configdn
,
1229 "DESCRIPTOR": descr
,
1232 # The LDIF here was created when the Schema object was constructed
1233 logger
.info("Setting up sam.ldb schema")
1234 samdb
.add_ldif(schema
.schema_dn_add
, controls
=["relax:0"])
1235 samdb
.modify_ldif(schema
.schema_dn_modify
)
1236 samdb
.write_prefixes_from_schema()
1237 samdb
.add_ldif(schema
.schema_data
, controls
=["relax:0"])
1238 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
1239 {"SCHEMADN": names
.schemadn
})
1241 # Now register this container in the root of the forest
1242 msg
= ldb
.Message(ldb
.Dn(samdb
, names
.domaindn
))
1243 msg
["subRefs"] = ldb
.MessageElement(names
.configdn
, ldb
.FLAG_MOD_ADD
,
1247 samdb
.transaction_cancel()
1250 samdb
.transaction_commit()
1252 samdb
.transaction_start()
1254 samdb
.invocation_id
= invocationid
1256 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1257 if fill
== FILL_FULL
:
1258 logger
.info("Setting up sam.ldb configuration data")
1259 partitions_descr
= b64encode(get_config_partitions_descriptor(domainsid
))
1260 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
1261 "CONFIGDN": names
.configdn
,
1262 "NETBIOSNAME": names
.netbiosname
,
1263 "DEFAULTSITE": names
.sitename
,
1264 "DNSDOMAIN": names
.dnsdomain
,
1265 "DOMAIN": names
.domain
,
1266 "SCHEMADN": names
.schemadn
,
1267 "DOMAINDN": names
.domaindn
,
1268 "SERVERDN": names
.serverdn
,
1269 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
1270 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1271 "PARTITIONS_DESCRIPTOR": partitions_descr
,
1274 logger
.info("Setting up display specifiers")
1275 display_specifiers_ldif
= read_ms_ldif(
1276 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1277 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
,
1278 {"CONFIGDN": names
.configdn
})
1279 check_all_substituted(display_specifiers_ldif
)
1280 samdb
.add_ldif(display_specifiers_ldif
)
1282 logger
.info("Adding users container")
1283 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
1284 "DOMAINDN": names
.domaindn
})
1285 logger
.info("Modifying users container")
1286 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
1287 "DOMAINDN": names
.domaindn
})
1288 logger
.info("Adding computers container")
1289 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
1290 "DOMAINDN": names
.domaindn
})
1291 logger
.info("Modifying computers container")
1292 setup_modify_ldif(samdb
,
1293 setup_path("provision_computers_modify.ldif"), {
1294 "DOMAINDN": names
.domaindn
})
1295 logger
.info("Setting up sam.ldb data")
1296 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1297 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1298 "DOMAINDN": names
.domaindn
,
1299 "NETBIOSNAME": names
.netbiosname
,
1300 "DEFAULTSITE": names
.sitename
,
1301 "CONFIGDN": names
.configdn
,
1302 "SERVERDN": names
.serverdn
,
1303 "RIDAVAILABLESTART": str(next_rid
+ 600),
1304 "POLICYGUID_DC": policyguid_dc
1307 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1308 if fill
== FILL_FULL
:
1309 setup_modify_ldif(samdb
,
1310 setup_path("provision_configuration_references.ldif"), {
1311 "CONFIGDN": names
.configdn
,
1312 "SCHEMADN": names
.schemadn
})
1314 logger
.info("Setting up well known security principals")
1315 setup_add_ldif(samdb
, setup_path("provision_well_known_sec_princ.ldif"), {
1316 "CONFIGDN": names
.configdn
,
1319 if fill
== FILL_FULL
or fill
== FILL_SUBDOMAIN
:
1320 setup_modify_ldif(samdb
,
1321 setup_path("provision_basedn_references.ldif"),
1322 {"DOMAINDN": names
.domaindn
})
1324 logger
.info("Setting up sam.ldb users and groups")
1325 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1326 "DOMAINDN": names
.domaindn
,
1327 "DOMAINSID": str(domainsid
),
1328 "ADMINPASS_B64": b64encode(adminpass
.encode('utf-16-le')),
1329 "KRBTGTPASS_B64": b64encode(krbtgtpass
.encode('utf-16-le'))
1332 logger
.info("Setting up self join")
1333 setup_self_join(samdb
, admin_session_info
, names
=names
, fill
=fill
,
1334 invocationid
=invocationid
,
1335 dns_backend
=dns_backend
,
1337 machinepass
=machinepass
,
1338 domainsid
=domainsid
,
1341 policyguid
=policyguid
,
1342 policyguid_dc
=policyguid_dc
,
1343 domainControllerFunctionality
=domainControllerFunctionality
,
1346 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1347 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1348 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
)
1349 assert isinstance(names
.ntdsguid
, str)
1351 samdb
.transaction_cancel()
1354 samdb
.transaction_commit()
1359 FILL_SUBDOMAIN
= "SUBDOMAIN"
1360 FILL_NT4SYNC
= "NT4SYNC"
1362 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1363 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)"
1366 def set_dir_acl(path
, acl
, lp
, domsid
, use_ntvfs
, passdb
):
1367 setntacl(lp
, path
, acl
, domsid
, use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1368 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1370 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1371 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1373 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1374 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1377 def set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
):
1378 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1381 :param sysvol: Physical path for the sysvol folder
1382 :param dnsdomain: The DNS name of the domain
1383 :param domainsid: The SID of the domain
1384 :param domaindn: The DN of the domain (ie. DC=...)
1385 :param samdb: An LDB object on the SAM db
1386 :param lp: an LP object
1389 # Set ACL for GPO root folder
1390 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1391 setntacl(lp
, root_policy_path
, POLICIES_ACL
, str(domainsid
),
1392 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1394 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1395 attrs
=["cn", "nTSecurityDescriptor"],
1396 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1399 acl
= ndr_unpack(security
.descriptor
,
1400 str(policy
["nTSecurityDescriptor"])).as_sddl()
1401 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1402 set_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1403 str(domainsid
), use_ntvfs
,
1407 def setsysvolacl(samdb
, netlogon
, sysvol
, uid
, gid
, domainsid
, dnsdomain
,
1408 domaindn
, lp
, use_ntvfs
):
1409 """Set the ACL for the sysvol share and the subfolders
1411 :param samdb: An LDB object on the SAM db
1412 :param netlogon: Physical path for the netlogon folder
1413 :param sysvol: Physical path for the sysvol folder
1414 :param uid: The UID of the "Administrator" user
1415 :param gid: The GID of the "Domain adminstrators" group
1416 :param domainsid: The SID of the domain
1417 :param dnsdomain: The DNS name of the domain
1418 :param domaindn: The DN of the domain (ie. DC=...)
1423 # This will ensure that the smbd code we are running when setting ACLs
1424 # is initialised with the smb.conf
1425 s3conf
= s3param
.get_context()
1426 s3conf
.load(lp
.configfile
)
1427 # ensure we are using the right samba_dsdb passdb backend, no matter what
1428 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1429 passdb
.reload_static_pdb()
1431 # ensure that we init the samba_dsdb backend, so the domain sid is
1432 # marked in secrets.tdb
1433 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1435 # now ensure everything matches correctly, to avoid wierd issues
1436 if passdb
.get_global_sam_sid() != domainsid
:
1437 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
))
1439 domain_info
= s4_passdb
.domain_info()
1440 if domain_info
["dom_sid"] != domainsid
:
1441 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
))
1443 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1444 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()))
1449 os
.chown(sysvol
, -1, gid
)
1455 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1456 setntacl(lp
,sysvol
, SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=s4_passdb
)
1457 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1459 if use_ntvfs
and canchown
:
1460 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1461 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=s4_passdb
)
1463 if use_ntvfs
and canchown
:
1464 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1465 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=s4_passdb
)
1467 # Set acls on Policy folder and policies folders
1468 set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
=s4_passdb
)
1470 def acl_type(direct_db_access
):
1471 if direct_db_access
:
1476 def check_dir_acl(path
, acl
, lp
, domainsid
, direct_db_access
):
1477 fsacl
= getntacl(lp
, path
, direct_db_access
=direct_db_access
)
1478 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1479 if fsacl_sddl
!= acl
:
1480 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
))
1482 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1484 fsacl
= getntacl(lp
, os
.path
.join(root
, name
), direct_db_access
=direct_db_access
)
1486 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1487 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1488 if fsacl_sddl
!= acl
:
1489 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
))
1492 fsacl
= getntacl(lp
, os
.path
.join(root
, name
), direct_db_access
=direct_db_access
)
1494 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1495 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1496 if fsacl_sddl
!= acl
:
1497 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
))
1500 def check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1502 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1505 :param sysvol: Physical path for the sysvol folder
1506 :param dnsdomain: The DNS name of the domain
1507 :param domainsid: The SID of the domain
1508 :param domaindn: The DN of the domain (ie. DC=...)
1509 :param samdb: An LDB object on the SAM db
1510 :param lp: an LP object
1513 # Set ACL for GPO root folder
1514 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1515 fsacl
= getntacl(lp
, root_policy_path
, direct_db_access
=direct_db_access
)
1517 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access
), root_policy_path
))
1518 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1519 if fsacl_sddl
!= POLICIES_ACL
:
1520 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
))
1521 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1522 attrs
=["cn", "nTSecurityDescriptor"],
1523 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1526 acl
= ndr_unpack(security
.descriptor
,
1527 str(policy
["nTSecurityDescriptor"])).as_sddl()
1528 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1529 check_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1530 domainsid
, direct_db_access
)
1533 def checksysvolacl(samdb
, netlogon
, sysvol
, domainsid
, dnsdomain
, domaindn
,
1535 """Set the ACL for the sysvol share and the subfolders
1537 :param samdb: An LDB object on the SAM db
1538 :param netlogon: Physical path for the netlogon folder
1539 :param sysvol: Physical path for the sysvol folder
1540 :param uid: The UID of the "Administrator" user
1541 :param gid: The GID of the "Domain adminstrators" group
1542 :param domainsid: The SID of the domain
1543 :param dnsdomain: The DNS name of the domain
1544 :param domaindn: The DN of the domain (ie. DC=...)
1547 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1548 s3conf
= s3param
.get_context()
1549 s3conf
.load(lp
.configfile
)
1550 # ensure we are using the right samba_dsdb passdb backend, no matter what
1551 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1552 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1553 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1555 # now ensure everything matches correctly, to avoid wierd issues
1556 if passdb
.get_global_sam_sid() != domainsid
:
1557 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
))
1559 domain_info
= s4_passdb
.domain_info()
1560 if domain_info
["dom_sid"] != domainsid
:
1561 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
))
1563 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1564 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()))
1566 # Ensure we can read this directly, and via the smbd VFS
1567 for direct_db_access
in [True, False]:
1568 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1569 for dir_path
in [os
.path
.join(sysvol
, dnsdomain
), netlogon
]:
1570 fsacl
= getntacl(lp
, dir_path
, direct_db_access
=direct_db_access
)
1572 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access
), dir_path
))
1573 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1574 if fsacl_sddl
!= SYSVOL_ACL
:
1575 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
))
1577 # Check acls on Policy folder and policies folders
1578 check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1582 def interface_ips_v4(lp
):
1583 """return only IPv4 IPs"""
1584 ips
= samba
.interface_ips(lp
, False)
1587 if i
.find(':') == -1:
1592 def interface_ips_v6(lp
, linklocal
=False):
1593 """return only IPv6 IPs"""
1594 ips
= samba
.interface_ips(lp
, False)
1597 if i
.find(':') != -1 and (linklocal
or i
.find('%') == -1):
1602 def provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
1603 domainsid
, schema
=None,
1604 targetdir
=None, samdb_fill
=FILL_FULL
,
1605 hostip
=None, hostip6
=None,
1606 next_rid
=1000, dc_rid
=None, adminpass
=None, krbtgtpass
=None,
1607 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1608 invocationid
=None, machinepass
=None, ntdsguid
=None,
1609 dns_backend
=None, dnspass
=None,
1610 serverrole
=None, dom_for_fun_level
=None,
1611 am_rodc
=False, lp
=None, use_ntvfs
=False, skip_sysvolacl
=False):
1612 # create/adapt the group policy GUIDs
1613 # Default GUID for default policy are described at
1614 # "How Core Group Policy Works"
1615 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1616 if policyguid
is None:
1617 policyguid
= DEFAULT_POLICY_GUID
1618 policyguid
= policyguid
.upper()
1619 if policyguid_dc
is None:
1620 policyguid_dc
= DEFAULT_DC_POLICY_GUID
1621 policyguid_dc
= policyguid_dc
.upper()
1623 if invocationid
is None:
1624 invocationid
= str(uuid
.uuid4())
1626 if krbtgtpass
is None:
1627 krbtgtpass
= samba
.generate_random_password(128, 255)
1628 if machinepass
is None:
1629 machinepass
= samba
.generate_random_password(128, 255)
1631 dnspass
= samba
.generate_random_password(128, 255)
1633 samdb
= fill_samdb(samdb
, lp
, names
, logger
=logger
,
1634 domainsid
=domainsid
, schema
=schema
, domainguid
=domainguid
,
1635 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1636 fill
=samdb_fill
, adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1637 invocationid
=invocationid
, machinepass
=machinepass
,
1638 dns_backend
=dns_backend
, dnspass
=dnspass
,
1639 ntdsguid
=ntdsguid
, serverrole
=serverrole
,
1640 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
1641 next_rid
=next_rid
, dc_rid
=dc_rid
)
1643 if serverrole
== "active directory domain controller":
1645 # Set up group policies (domain policy and domain controller
1647 create_default_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
,
1649 if not skip_sysvolacl
:
1650 setsysvolacl(samdb
, paths
.netlogon
, paths
.sysvol
, paths
.root_uid
,
1651 paths
.root_gid
, domainsid
, names
.dnsdomain
,
1652 names
.domaindn
, lp
, use_ntvfs
)
1654 logger
.info("Setting acl on sysvol skipped")
1656 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1657 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1658 netbiosname
=names
.netbiosname
, domainsid
=domainsid
,
1659 machinepass
=machinepass
, secure_channel_type
=SEC_CHAN_BDC
)
1661 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1662 # In future, this might be determined from some configuration
1663 kerberos_enctypes
= str(ENC_ALL_TYPES
)
1666 msg
= ldb
.Message(ldb
.Dn(samdb
,
1667 samdb
.searchone("distinguishedName",
1668 expression
="samAccountName=%s$" % names
.netbiosname
,
1669 scope
=ldb
.SCOPE_SUBTREE
)))
1670 msg
["msDS-SupportedEncryptionTypes"] = ldb
.MessageElement(
1671 elements
=kerberos_enctypes
, flags
=ldb
.FLAG_MOD_REPLACE
,
1672 name
="msDS-SupportedEncryptionTypes")
1674 except ldb
.LdbError
, (enum
, estr
):
1675 if enum
!= ldb
.ERR_NO_SUCH_ATTRIBUTE
:
1676 # It might be that this attribute does not exist in this schema
1679 setup_ad_dns(samdb
, secrets_ldb
, domainsid
, names
, paths
, lp
, logger
,
1680 hostip
=hostip
, hostip6
=hostip6
, dns_backend
=dns_backend
,
1681 dnspass
=dnspass
, os_level
=dom_for_fun_level
,
1682 targetdir
=targetdir
, site
=DEFAULTSITE
)
1684 domainguid
= samdb
.searchone(basedn
=samdb
.get_default_basedn(),
1685 attribute
="objectGUID")
1686 assert isinstance(domainguid
, str)
1688 lastProvisionUSNs
= get_last_provision_usn(samdb
)
1689 maxUSN
= get_max_usn(samdb
, str(names
.rootdn
))
1690 if lastProvisionUSNs
is not None:
1691 update_provision_usn(samdb
, 0, maxUSN
, invocationid
, 1)
1693 set_provision_usn(samdb
, 0, maxUSN
, invocationid
)
1695 logger
.info("Setting up sam.ldb rootDSE marking as synchronized")
1696 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"),
1697 { 'NTDSGUID' : names
.ntdsguid
})
1699 # fix any dangling GUIDs from the provision
1700 logger
.info("Fixing provision GUIDs")
1701 chk
= dbcheck(samdb
, samdb_schema
=samdb
, verbose
=False, fix
=True, yes
=True,
1703 samdb
.transaction_start()
1705 # a small number of GUIDs are missing because of ordering issues in the
1707 for schema_obj
in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1708 chk
.check_database(DN
="%s,%s" % (schema_obj
, names
.schemadn
),
1709 scope
=ldb
.SCOPE_BASE
,
1710 attrs
=['defaultObjectCategory'])
1711 chk
.check_database(DN
="CN=IP Security,CN=System,%s" % names
.domaindn
,
1712 scope
=ldb
.SCOPE_ONELEVEL
,
1713 attrs
=['ipsecOwnersReference',
1714 'ipsecFilterReference',
1715 'ipsecISAKMPReference',
1716 'ipsecNegotiationPolicyReference',
1717 'ipsecNFAReference'])
1719 samdb
.transaction_cancel()
1722 samdb
.transaction_commit()
1726 "ROLE_STANDALONE": "standalone server",
1727 "ROLE_DOMAIN_MEMBER": "member server",
1728 "ROLE_DOMAIN_BDC": "active directory domain controller",
1729 "ROLE_DOMAIN_PDC": "active directory domain controller",
1730 "dc": "active directory domain controller",
1731 "member": "member server",
1732 "domain controller": "active directory domain controller",
1733 "active directory domain controller": "active directory domain controller",
1734 "member server": "member server",
1735 "standalone": "standalone server",
1736 "standalone server": "standalone server",
1740 def sanitize_server_role(role
):
1741 """Sanitize a server role name.
1743 :param role: Server role
1744 :raise ValueError: If the role can not be interpreted
1745 :return: Sanitized server role (one of "member server",
1746 "active directory domain controller", "standalone server")
1749 return _ROLES_MAP
[role
]
1751 raise ValueError(role
)
1754 def provision_fake_ypserver(logger
, samdb
, domaindn
, netbiosname
, nisdomain
,
1756 """Create AD entries for the fake ypserver.
1758 This is needed for being able to manipulate posix attrs via ADUC.
1760 samdb
.transaction_start()
1762 logger
.info("Setting up fake yp server settings")
1763 setup_add_ldif(samdb
, setup_path("ypServ30.ldif"), {
1764 "DOMAINDN": domaindn
,
1765 "NETBIOSNAME": netbiosname
,
1766 "NISDOMAIN": nisdomain
,
1769 samdb
.transaction_cancel()
1772 samdb
.transaction_commit()
1775 def provision(logger
, session_info
, credentials
, smbconf
=None,
1776 targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None, rootdn
=None,
1777 domaindn
=None, schemadn
=None, configdn
=None, serverdn
=None,
1778 domain
=None, hostname
=None, hostip
=None, hostip6
=None, domainsid
=None,
1779 next_rid
=1000, dc_rid
=None, adminpass
=None, ldapadminpass
=None,
1780 krbtgtpass
=None, domainguid
=None, policyguid
=None, policyguid_dc
=None,
1781 dns_backend
=None, dns_forwarder
=None, dnspass
=None,
1782 invocationid
=None, machinepass
=None, ntdsguid
=None,
1783 root
=None, nobody
=None, users
=None, backup
=None, aci
=None,
1784 serverrole
=None, dom_for_fun_level
=None, backend_type
=None,
1785 sitename
=None, ol_mmr_urls
=None, ol_olc
=None, slapd_path
="/bin/false",
1786 useeadb
=False, am_rodc
=False, lp
=None, use_ntvfs
=False,
1787 use_rfc2307
=False, maxuid
=None, maxgid
=None, skip_sysvolacl
=True):
1790 :note: caution, this wipes all existing data!
1794 serverrole
= sanitize_server_role(serverrole
)
1796 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole
)
1798 if ldapadminpass
is None:
1799 # Make a new, random password between Samba and it's LDAP server
1800 ldapadminpass
= samba
.generate_random_password(128, 255)
1802 if backend_type
is None:
1803 backend_type
= "ldb"
1805 if domainsid
is None:
1806 domainsid
= security
.random_sid()
1808 domainsid
= security
.dom_sid(domainsid
)
1810 root_uid
= findnss_uid([root
or "root"])
1811 nobody_uid
= findnss_uid([nobody
or "nobody"])
1812 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
1813 root_gid
= pwd
.getpwuid(root_uid
).pw_gid
1816 bind_gid
= findnss_gid(["bind", "named"])
1820 if targetdir
is not None:
1821 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
1822 elif smbconf
is None:
1823 smbconf
= samba
.param
.default_path()
1824 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
1825 os
.makedirs(os
.path
.dirname(smbconf
))
1827 server_services
= []
1830 global_param
["idmap_ldb:use rfc2307"] = ["yes"]
1832 if dns_backend
!= "SAMBA_INTERNAL":
1833 server_services
.append("-dns")
1835 if dns_forwarder
is not None:
1836 global_param
["dns forwarder"] = [dns_forwarder
]
1839 server_services
.append("+smb")
1840 server_services
.append("-s3fs")
1841 global_param
["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1843 if len(server_services
) > 0:
1844 global_param
["server services"] = server_services
1846 # only install a new smb.conf if there isn't one there already
1847 if os
.path
.exists(smbconf
):
1848 # if Samba Team members can't figure out the weird errors
1849 # loading an empty smb.conf gives, then we need to be smarter.
1850 # Pretend it just didn't exist --abartlet
1851 f
= open(smbconf
, 'r')
1853 data
= f
.read().lstrip()
1856 if data
is None or data
== "":
1857 make_smbconf(smbconf
, hostname
, domain
, realm
,
1858 targetdir
, serverrole
=serverrole
,
1859 eadb
=useeadb
, use_ntvfs
=use_ntvfs
,
1860 lp
=lp
, global_param
=global_param
)
1862 make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
1863 serverrole
=serverrole
,
1864 eadb
=useeadb
, use_ntvfs
=use_ntvfs
, lp
=lp
, global_param
=global_param
)
1867 lp
= samba
.param
.LoadParm()
1869 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
1870 dnsdomain
=realm
, serverrole
=serverrole
, domaindn
=domaindn
,
1871 configdn
=configdn
, schemadn
=schemadn
, serverdn
=serverdn
,
1872 sitename
=sitename
, rootdn
=rootdn
)
1873 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
1875 paths
.bind_gid
= bind_gid
1876 paths
.root_uid
= root_uid
;
1877 paths
.root_gid
= root_gid
1880 logger
.info("Looking up IPv4 addresses")
1881 hostips
= interface_ips_v4(lp
)
1882 if len(hostips
) > 0:
1884 if len(hostips
) > 1:
1885 logger
.warning("More than one IPv4 address found. Using %s",
1887 if hostip
== "127.0.0.1":
1890 logger
.warning("No IPv4 address will be assigned")
1893 logger
.info("Looking up IPv6 addresses")
1894 hostips
= interface_ips_v6(lp
, linklocal
=False)
1896 hostip6
= hostips
[0]
1897 if len(hostips
) > 1:
1898 logger
.warning("More than one IPv6 address found. Using %s", hostip6
)
1900 logger
.warning("No IPv6 address will be assigned")
1902 names
.hostip
= hostip
1903 names
.hostip6
= hostip6
1905 if serverrole
is None:
1906 serverrole
= lp
.get("server role")
1908 if not os
.path
.exists(paths
.private_dir
):
1909 os
.mkdir(paths
.private_dir
)
1910 if not os
.path
.exists(os
.path
.join(paths
.private_dir
, "tls")):
1911 os
.mkdir(os
.path
.join(paths
.private_dir
, "tls"))
1912 if not os
.path
.exists(paths
.state_dir
):
1913 os
.mkdir(paths
.state_dir
)
1915 if paths
.sysvol
and not os
.path
.exists(paths
.sysvol
):
1916 os
.makedirs(paths
.sysvol
, 0775)
1918 if not use_ntvfs
and serverrole
== "active directory domain controller":
1919 s3conf
= s3param
.get_context()
1920 s3conf
.load(lp
.configfile
)
1922 if paths
.sysvol
is None:
1923 raise MissingShareError("sysvol", paths
.smbconf
)
1925 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(paths
.sysvol
))
1928 smbd
.set_simple_acl(file.name
, 0755, root_gid
)
1930 if not smbd
.have_posix_acls():
1931 # This clue is only strictly correct for RPM and
1932 # Debian-like Linux systems, but hopefully other users
1933 # will get enough clue from it.
1934 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1936 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1938 smbd
.chown(file.name
, root_uid
, root_gid
)
1940 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1944 ldapi_url
= "ldapi://%s" % urllib
.quote(paths
.s4_ldapi_path
, safe
="")
1946 schema
= Schema(domainsid
, invocationid
=invocationid
,
1947 schemadn
=names
.schemadn
)
1949 if backend_type
== "ldb":
1950 provision_backend
= LDBBackend(backend_type
, paths
=paths
,
1951 lp
=lp
, credentials
=credentials
,
1952 names
=names
, logger
=logger
)
1953 elif backend_type
== "existing":
1954 # If support for this is ever added back, then the URI will need to be
1956 provision_backend
= ExistingBackend(backend_type
, paths
=paths
,
1957 lp
=lp
, credentials
=credentials
,
1958 names
=names
, logger
=logger
,
1959 ldap_backend_forced_uri
=None)
1960 elif backend_type
== "fedora-ds":
1961 provision_backend
= FDSBackend(backend_type
, paths
=paths
,
1962 lp
=lp
, credentials
=credentials
,
1963 names
=names
, logger
=logger
, domainsid
=domainsid
,
1964 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
1965 slapd_path
=slapd_path
,
1967 elif backend_type
== "openldap":
1968 provision_backend
= OpenLDAPBackend(backend_type
, paths
=paths
,
1969 lp
=lp
, credentials
=credentials
,
1970 names
=names
, logger
=logger
, domainsid
=domainsid
,
1971 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
1972 slapd_path
=slapd_path
, ol_mmr_urls
=ol_mmr_urls
)
1974 raise ValueError("Unknown LDAP backend type selected")
1976 provision_backend
.init()
1977 provision_backend
.start()
1979 # only install a new shares config db if there is none
1980 if not os
.path
.exists(paths
.shareconf
):
1981 logger
.info("Setting up share.ldb")
1982 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
, lp
=lp
)
1983 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
1985 logger
.info("Setting up secrets.ldb")
1986 secrets_ldb
= setup_secretsdb(paths
,
1987 session_info
=session_info
,
1988 backend_credentials
=provision_backend
.secrets_credentials
, lp
=lp
)
1991 logger
.info("Setting up the registry")
1992 setup_registry(paths
.hklm
, session_info
, lp
=lp
)
1994 logger
.info("Setting up the privileges database")
1995 setup_privileges(paths
.privilege
, session_info
, lp
=lp
)
1997 logger
.info("Setting up idmap db")
1998 idmap
= setup_idmapdb(paths
.idmapdb
, session_info
=session_info
, lp
=lp
)
2000 setup_name_mappings(idmap
, sid
=str(domainsid
),
2001 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
2002 users_gid
=users_gid
, root_gid
=root_gid
)
2004 logger
.info("Setting up SAM db")
2005 samdb
= setup_samdb(paths
.samdb
, session_info
,
2006 provision_backend
, lp
, names
, logger
=logger
,
2007 serverrole
=serverrole
,
2008 schema
=schema
, fill
=samdb_fill
, am_rodc
=am_rodc
)
2010 if serverrole
== "active directory domain controller":
2011 if paths
.netlogon
is None:
2012 raise MissingShareError("netlogon", paths
.smbconf
)
2014 if paths
.sysvol
is None:
2015 raise MissingShareError("sysvol", paths
.smbconf
)
2017 if not os
.path
.isdir(paths
.netlogon
):
2018 os
.makedirs(paths
.netlogon
, 0755)
2020 if adminpass
is None:
2021 adminpass
= samba
.generate_random_password(12, 32)
2022 adminpass_generated
= True
2024 adminpass_generated
= False
2026 if samdb_fill
== FILL_FULL
:
2027 provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
2028 schema
=schema
, targetdir
=targetdir
, samdb_fill
=samdb_fill
,
2029 hostip
=hostip
, hostip6
=hostip6
, domainsid
=domainsid
,
2030 next_rid
=next_rid
, dc_rid
=dc_rid
, adminpass
=adminpass
,
2031 krbtgtpass
=krbtgtpass
, domainguid
=domainguid
,
2032 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
2033 invocationid
=invocationid
, machinepass
=machinepass
,
2034 ntdsguid
=ntdsguid
, dns_backend
=dns_backend
,
2035 dnspass
=dnspass
, serverrole
=serverrole
,
2036 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
2037 lp
=lp
, use_ntvfs
=use_ntvfs
,
2038 skip_sysvolacl
=skip_sysvolacl
)
2040 create_krb5_conf(paths
.krb5conf
,
2041 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
2043 logger
.info("A Kerberos configuration suitable for Samba 4 has been "
2044 "generated at %s", paths
.krb5conf
)
2046 if serverrole
== "active directory domain controller":
2047 create_dns_update_list(lp
, logger
, paths
)
2049 backend_result
= provision_backend
.post_setup()
2050 provision_backend
.shutdown()
2053 secrets_ldb
.transaction_cancel()
2056 # Now commit the secrets.ldb to disk
2057 secrets_ldb
.transaction_commit()
2059 # the commit creates the dns.keytab, now chown it
2060 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
2061 if os
.path
.isfile(dns_keytab_path
) and paths
.bind_gid
is not None:
2063 os
.chmod(dns_keytab_path
, 0640)
2064 os
.chown(dns_keytab_path
, -1, paths
.bind_gid
)
2066 if not os
.environ
.has_key('SAMBA_SELFTEST'):
2067 logger
.info("Failed to chown %s to bind gid %u",
2068 dns_keytab_path
, paths
.bind_gid
)
2070 result
= ProvisionResult()
2071 result
.server_role
= serverrole
2072 result
.domaindn
= domaindn
2073 result
.paths
= paths
2074 result
.names
= names
2076 result
.samdb
= samdb
2077 result
.idmap
= idmap
2078 result
.domainsid
= str(domainsid
)
2080 if samdb_fill
== FILL_FULL
:
2081 result
.adminpass_generated
= adminpass_generated
2082 result
.adminpass
= adminpass
2084 result
.adminpass_generated
= False
2085 result
.adminpass
= None
2087 result
.backend_result
= backend_result
2090 provision_fake_ypserver(logger
=logger
, samdb
=samdb
,
2091 domaindn
=names
.domaindn
, netbiosname
=names
.netbiosname
,
2092 nisdomain
=names
.domain
.lower(), maxuid
=maxuid
, maxgid
=maxgid
)
2097 def provision_become_dc(smbconf
=None, targetdir
=None,
2098 realm
=None, rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
2099 serverdn
=None, domain
=None, hostname
=None, domainsid
=None,
2100 adminpass
=None, krbtgtpass
=None, domainguid
=None, policyguid
=None,
2101 policyguid_dc
=None, invocationid
=None, machinepass
=None, dnspass
=None,
2102 dns_backend
=None, root
=None, nobody
=None, users
=None,
2103 backup
=None, serverrole
=None, ldap_backend
=None,
2104 ldap_backend_type
=None, sitename
=None, debuglevel
=1, use_ntvfs
=False):
2106 logger
= logging
.getLogger("provision")
2107 samba
.set_debug_level(debuglevel
)
2109 res
= provision(logger
, system_session(), None,
2110 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
2111 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
2112 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
2113 hostname
=hostname
, hostip
=None, domainsid
=domainsid
,
2114 machinepass
=machinepass
,
2115 serverrole
="active directory domain controller",
2116 sitename
=sitename
, dns_backend
=dns_backend
, dnspass
=dnspass
,
2117 use_ntvfs
=use_ntvfs
)
2118 res
.lp
.set("debuglevel", str(debuglevel
))
2122 def create_krb5_conf(path
, dnsdomain
, hostname
, realm
):
2123 """Write out a file containing zone statements suitable for inclusion in a
2124 named.conf file (including GSS-TSIG configuration).
2126 :param path: Path of the new named.conf file.
2127 :param dnsdomain: DNS Domain name
2128 :param hostname: Local hostname
2129 :param realm: Realm name
2131 setup_file(setup_path("krb5.conf"), path
, {
2132 "DNSDOMAIN": dnsdomain
,
2133 "HOSTNAME": hostname
,
2138 class ProvisioningError(Exception):
2139 """A generic provision error."""
2141 def __init__(self
, value
):
2145 return "ProvisioningError: " + self
.value
2148 class InvalidNetbiosName(Exception):
2149 """A specified name was not a valid NetBIOS name."""
2151 def __init__(self
, name
):
2152 super(InvalidNetbiosName
, self
).__init
__(
2153 "The name '%r' is not a valid NetBIOS name" % name
)
2156 class MissingShareError(ProvisioningError
):
2158 def __init__(self
, name
, smbconf
):
2159 super(MissingShareError
, self
).__init
__(
2160 "Existing smb.conf does not have a [%s] share, but you are "
2161 "configuring a DC. Please remove %s or add the share manually." %