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
,
83 get_config_sites_descriptor
,
84 get_domain_descriptor
,
85 get_domain_infrastructure_descriptor
,
86 get_domain_builtin_descriptor
,
87 get_domain_computers_descriptor
,
88 get_domain_users_descriptor
,
89 get_domain_controllers_descriptor
91 from samba
.provision
.common
import (
96 from samba
.provision
.sambadns
import (
98 create_dns_update_list
102 import samba
.registry
103 from samba
.schema
import Schema
104 from samba
.samdb
import SamDB
105 from samba
.dbchecker
import dbcheck
108 DEFAULT_POLICY_GUID
= "31B2F340-016D-11D2-945F-00C04FB984F9"
109 DEFAULT_DC_POLICY_GUID
= "6AC1786C-016F-11D2-945F-00C04fB984F9"
110 DEFAULTSITE
= "Default-First-Site-Name"
111 LAST_PROVISION_USN_ATTRIBUTE
= "lastProvisionUSN"
114 class ProvisionPaths(object):
117 self
.shareconf
= None
128 self
.dns_keytab
= None
131 self
.private_dir
= None
132 self
.state_dir
= None
135 class ProvisionNames(object):
142 self
.ldapmanagerdn
= None
143 self
.dnsdomain
= None
145 self
.netbiosname
= None
152 def find_provision_key_parameters(samdb
, secretsdb
, idmapdb
, paths
, smbconf
,
154 """Get key provision parameters (realm, domain, ...) from a given provision
156 :param samdb: An LDB object connected to the sam.ldb file
157 :param secretsdb: An LDB object connected to the secrets.ldb file
158 :param idmapdb: An LDB object connected to the idmap.ldb file
159 :param paths: A list of path to provision object
160 :param smbconf: Path to the smb.conf file
161 :param lp: A LoadParm object
162 :return: A list of key provision parameters
164 names
= ProvisionNames()
165 names
.adminpass
= None
167 # NT domain, kerberos realm, root dn, domain dn, domain dns name
168 names
.domain
= string
.upper(lp
.get("workgroup"))
169 names
.realm
= lp
.get("realm")
170 names
.dnsdomain
= names
.realm
.lower()
171 basedn
= samba
.dn_from_dns_name(names
.dnsdomain
)
172 names
.realm
= string
.upper(names
.realm
)
174 # Get the netbiosname first (could be obtained from smb.conf in theory)
175 res
= secretsdb
.search(expression
="(flatname=%s)" %
176 names
.domain
,base
="CN=Primary Domains",
177 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
178 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
180 names
.smbconf
= smbconf
182 # That's a bit simplistic but it's ok as long as we have only 3
184 current
= samdb
.search(expression
="(objectClass=*)",
185 base
="", scope
=ldb
.SCOPE_BASE
,
186 attrs
=["defaultNamingContext", "schemaNamingContext",
187 "configurationNamingContext","rootDomainNamingContext"])
189 names
.configdn
= current
[0]["configurationNamingContext"]
190 configdn
= str(names
.configdn
)
191 names
.schemadn
= current
[0]["schemaNamingContext"]
192 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
,
193 current
[0]["defaultNamingContext"][0]))):
194 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
195 "is not the same ..." % (paths
.samdb
,
196 str(current
[0]["defaultNamingContext"][0]),
197 paths
.smbconf
, basedn
)))
199 names
.domaindn
=current
[0]["defaultNamingContext"]
200 names
.rootdn
=current
[0]["rootDomainNamingContext"]
202 res3
= samdb
.search(expression
="(objectClass=site)",
203 base
="CN=Sites," + configdn
, scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn"])
204 names
.sitename
= str(res3
[0]["cn"])
206 # dns hostname and server dn
207 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
208 base
="OU=Domain Controllers,%s" % basedn
,
209 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
210 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("." + names
.dnsdomain
, "")
212 server_res
= samdb
.search(expression
="serverReference=%s" % res4
[0].dn
,
213 attrs
=[], base
=configdn
)
214 names
.serverdn
= server_res
[0].dn
216 # invocation id/objectguid
217 res5
= samdb
.search(expression
="(objectClass=*)",
218 base
="CN=NTDS Settings,%s" % str(names
.serverdn
),
219 scope
=ldb
.SCOPE_BASE
,
220 attrs
=["invocationID", "objectGUID"])
221 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
222 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
225 res6
= samdb
.search(expression
="(objectClass=*)", base
=basedn
,
226 scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID",
227 "objectSid","msDS-Behavior-Version" ])
228 names
.domainguid
= str(ndr_unpack(misc
.GUID
, res6
[0]["objectGUID"][0]))
229 names
.domainsid
= ndr_unpack( security
.dom_sid
, res6
[0]["objectSid"][0])
230 if res6
[0].get("msDS-Behavior-Version") is None or \
231 int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
232 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
234 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
237 res7
= samdb
.search(expression
="(displayName=Default Domain Policy)",
238 base
="CN=Policies,CN=System," + basedn
,
239 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
240 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
242 res8
= samdb
.search(expression
="(displayName=Default Domain Controllers"
244 base
="CN=Policies,CN=System," + basedn
,
245 scope
=ldb
.SCOPE_ONELEVEL
,
246 attrs
=["cn","displayName"])
248 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
250 names
.policyid_dc
= None
252 res9
= idmapdb
.search(expression
="(cn=%s-%s)" %
253 (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
),
254 attrs
=["xidNumber", "type"])
256 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
))
257 if res9
[0]["type"][0] == "ID_TYPE_BOTH":
258 names
.root_gid
= res9
[0]["xidNumber"][0]
260 names
.root_gid
= pwd
.getpwuid(int(res9
[0]["xidNumber"][0])).pw_gid
264 def update_provision_usn(samdb
, low
, high
, id, replace
=False):
265 """Update the field provisionUSN in sam.ldb
267 This field is used to track range of USN modified by provision and
269 This value is used afterward by next provision to figure out if
270 the field have been modified since last provision.
272 :param samdb: An LDB object connect to sam.ldb
273 :param low: The lowest USN modified by this upgrade
274 :param high: The highest USN modified by this upgrade
275 :param id: The invocation id of the samba's dc
276 :param replace: A boolean indicating if the range should replace any
277 existing one or appended (default)
282 entry
= samdb
.search(base
="@PROVISION",
283 scope
=ldb
.SCOPE_BASE
,
284 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "dn"])
285 for e
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
286 if not re
.search(';', e
):
287 e
= "%s;%s" % (e
, id)
290 tab
.append("%s-%s;%s" % (low
, high
, id))
291 delta
= ldb
.Message()
292 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
293 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
294 ldb
.FLAG_MOD_REPLACE
, LAST_PROVISION_USN_ATTRIBUTE
)
295 entry
= samdb
.search(expression
='provisionnerID=*',
296 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
297 attrs
=["provisionnerID"])
298 if len(entry
) == 0 or len(entry
[0]) == 0:
299 delta
["provisionnerID"] = ldb
.MessageElement(id, ldb
.FLAG_MOD_ADD
, "provisionnerID")
303 def set_provision_usn(samdb
, low
, high
, id):
304 """Set the field provisionUSN in sam.ldb
305 This field is used to track range of USN modified by provision and
307 This value is used afterward by next provision to figure out if
308 the field have been modified since last provision.
310 :param samdb: An LDB object connect to sam.ldb
311 :param low: The lowest USN modified by this upgrade
312 :param high: The highest USN modified by this upgrade
313 :param id: The invocationId of the provision"""
316 tab
.append("%s-%s;%s" % (low
, high
, id))
318 delta
= ldb
.Message()
319 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
320 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
321 ldb
.FLAG_MOD_ADD
, LAST_PROVISION_USN_ATTRIBUTE
)
325 def get_max_usn(samdb
,basedn
):
326 """ This function return the biggest USN present in the provision
328 :param samdb: A LDB object pointing to the sam.ldb
329 :param basedn: A string containing the base DN of the provision
331 :return: The biggest USN in the provision"""
333 res
= samdb
.search(expression
="objectClass=*",base
=basedn
,
334 scope
=ldb
.SCOPE_SUBTREE
,attrs
=["uSNChanged"],
335 controls
=["search_options:1:2",
336 "server_sort:1:1:uSNChanged",
337 "paged_results:1:1"])
338 return res
[0]["uSNChanged"]
341 def get_last_provision_usn(sam
):
342 """Get USNs ranges modified by a provision or an upgradeprovision
344 :param sam: An LDB object pointing to the sam.ldb
345 :return: a dictionary which keys are invocation id and values are an array
346 of integer representing the different ranges
349 entry
= sam
.search(expression
="%s=*" % LAST_PROVISION_USN_ATTRIBUTE
,
350 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
351 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "provisionnerID"])
352 except ldb
.LdbError
, (ecode
, emsg
):
353 if ecode
== ldb
.ERR_NO_SUCH_OBJECT
:
360 if entry
[0].get("provisionnerID"):
361 for e
in entry
[0]["provisionnerID"]:
363 for r
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
364 tab1
= str(r
).split(';')
369 if (len(myids
) > 0 and id not in myids
):
371 tab2
= p
.split(tab1
[0])
372 if range.get(id) is None:
374 range[id].append(tab2
[0])
375 range[id].append(tab2
[1])
381 class ProvisionResult(object):
382 """Result of a provision.
384 :ivar server_role: The server role
385 :ivar paths: ProvisionPaths instance
386 :ivar domaindn: The domain dn, as string
390 self
.server_role
= None
397 self
.domainsid
= None
398 self
.adminpass_generated
= None
399 self
.adminpass
= None
400 self
.backend_result
= None
402 def report_logger(self
, logger
):
403 """Report this provision result to a logger."""
405 "Once the above files are installed, your Samba4 server will "
407 if self
.adminpass_generated
:
408 logger
.info("Admin password: %s", self
.adminpass
)
409 logger
.info("Server Role: %s", self
.server_role
)
410 logger
.info("Hostname: %s", self
.names
.hostname
)
411 logger
.info("NetBIOS Domain: %s", self
.names
.domain
)
412 logger
.info("DNS Domain: %s", self
.names
.dnsdomain
)
413 logger
.info("DOMAIN SID: %s", self
.domainsid
)
415 if self
.backend_result
:
416 self
.backend_result
.report_logger(logger
)
419 def check_install(lp
, session_info
, credentials
):
420 """Check whether the current install seems ok.
422 :param lp: Loadparm context
423 :param session_info: Session information
424 :param credentials: Credentials
426 if lp
.get("realm") == "":
427 raise Exception("Realm empty")
428 samdb
= Ldb(lp
.samdb_url(), session_info
=session_info
,
429 credentials
=credentials
, lp
=lp
)
430 if len(samdb
.search("(cn=Administrator)")) != 1:
431 raise ProvisioningError("No administrator account found")
434 def findnss(nssfn
, names
):
435 """Find a user or group from a list of possibilities.
437 :param nssfn: NSS Function to try (should raise KeyError if not found)
438 :param names: Names to check.
439 :return: Value return by first names list.
446 raise KeyError("Unable to find user/group in %r" % names
)
449 findnss_uid
= lambda names
: findnss(pwd
.getpwnam
, names
)[2]
450 findnss_gid
= lambda names
: findnss(grp
.getgrnam
, names
)[2]
453 def provision_paths_from_lp(lp
, dnsdomain
):
454 """Set the default paths for provisioning.
456 :param lp: Loadparm context.
457 :param dnsdomain: DNS Domain name
459 paths
= ProvisionPaths()
460 paths
.private_dir
= lp
.get("private dir")
461 paths
.state_dir
= lp
.get("state directory")
463 # This is stored without path prefix for the "privateKeytab" attribute in
464 # "secrets_dns.ldif".
465 paths
.dns_keytab
= "dns.keytab"
466 paths
.keytab
= "secrets.keytab"
468 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
469 paths
.samdb
= os
.path
.join(paths
.private_dir
, "sam.ldb")
470 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, "idmap.ldb")
471 paths
.secrets
= os
.path
.join(paths
.private_dir
, "secrets.ldb")
472 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
473 paths
.dns
= os
.path
.join(paths
.private_dir
, "dns", dnsdomain
+ ".zone")
474 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
475 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
476 paths
.namedconf
= os
.path
.join(paths
.private_dir
, "named.conf")
477 paths
.namedconf_update
= os
.path
.join(paths
.private_dir
, "named.conf.update")
478 paths
.namedtxt
= os
.path
.join(paths
.private_dir
, "named.txt")
479 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
480 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
481 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
482 paths
.hklm
= "hklm.ldb"
483 paths
.hkcr
= "hkcr.ldb"
484 paths
.hkcu
= "hkcu.ldb"
485 paths
.hku
= "hku.ldb"
486 paths
.hkpd
= "hkpd.ldb"
487 paths
.hkpt
= "hkpt.ldb"
488 paths
.sysvol
= lp
.get("path", "sysvol")
489 paths
.netlogon
= lp
.get("path", "netlogon")
490 paths
.smbconf
= lp
.configfile
494 def determine_netbios_name(hostname
):
495 """Determine a netbios name from a hostname."""
496 # remove forbidden chars and force the length to be <16
497 netbiosname
= "".join([x
for x
in hostname
if is_valid_netbios_char(x
)])
498 return netbiosname
[:MAX_NETBIOS_NAME_LEN
].upper()
501 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
502 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
503 schemadn
=None, serverdn
=None, sitename
=None):
504 """Guess configuration settings to use."""
507 hostname
= socket
.gethostname().split(".")[0]
509 netbiosname
= lp
.get("netbios name")
510 if netbiosname
is None:
511 netbiosname
= determine_netbios_name(hostname
)
512 netbiosname
= netbiosname
.upper()
513 if not valid_netbios_name(netbiosname
):
514 raise InvalidNetbiosName(netbiosname
)
516 if dnsdomain
is None:
517 dnsdomain
= lp
.get("realm")
518 if dnsdomain
is None or dnsdomain
== "":
519 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp
.configfile
)
521 dnsdomain
= dnsdomain
.lower()
523 if serverrole
is None:
524 serverrole
= lp
.get("server role")
525 if serverrole
is None:
526 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
528 serverrole
= serverrole
.lower()
530 realm
= dnsdomain
.upper()
532 if lp
.get("realm") == "":
533 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
535 if lp
.get("realm").upper() != realm
:
536 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
))
538 if lp
.get("server role").lower() != serverrole
:
539 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
))
541 if serverrole
== "active directory domain controller":
543 # This will, for better or worse, default to 'WORKGROUP'
544 domain
= lp
.get("workgroup")
545 domain
= domain
.upper()
547 if lp
.get("workgroup").upper() != domain
:
548 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
))
551 domaindn
= samba
.dn_from_dns_name(dnsdomain
)
553 if domain
== netbiosname
:
554 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain
, netbiosname
))
558 domaindn
= "DC=" + netbiosname
560 if not valid_netbios_name(domain
):
561 raise InvalidNetbiosName(domain
)
563 if hostname
.upper() == realm
:
564 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
565 if netbiosname
.upper() == realm
:
566 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm
, netbiosname
))
568 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
574 configdn
= "CN=Configuration," + rootdn
576 schemadn
= "CN=Schema," + configdn
579 sitename
= DEFAULTSITE
581 names
= ProvisionNames()
582 names
.rootdn
= rootdn
583 names
.domaindn
= domaindn
584 names
.configdn
= configdn
585 names
.schemadn
= schemadn
586 names
.ldapmanagerdn
= "CN=Manager," + rootdn
587 names
.dnsdomain
= dnsdomain
588 names
.domain
= domain
590 names
.netbiosname
= netbiosname
591 names
.hostname
= hostname
592 names
.sitename
= sitename
593 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
594 netbiosname
, sitename
, configdn
)
599 def make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
600 serverrole
=None, eadb
=False, use_ntvfs
=False, lp
=None,
602 """Create a new smb.conf file based on a couple of basic settings.
604 assert smbconf
is not None
607 hostname
= socket
.gethostname().split(".")[0]
609 netbiosname
= determine_netbios_name(hostname
)
611 if serverrole
is None:
612 serverrole
= "standalone server"
614 assert domain
is not None
615 domain
= domain
.upper()
617 assert realm
is not None
618 realm
= realm
.upper()
621 "netbios name": netbiosname
,
624 "server role": serverrole
,
628 lp
= samba
.param
.LoadParm()
629 #Load non-existent file
630 if os
.path
.exists(smbconf
):
633 if global_param
is not None:
634 for ent
in global_param
:
635 if global_param
[ent
] is not None:
636 global_settings
[ent
] = " ".join(global_param
[ent
])
638 if targetdir
is not None:
639 global_settings
["private dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "private"))
640 global_settings
["lock dir"] = os
.path
.abspath(targetdir
)
641 global_settings
["state directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "state"))
642 global_settings
["cache directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "cache"))
644 lp
.set("lock dir", os
.path
.abspath(targetdir
))
645 lp
.set("state directory", global_settings
["state directory"])
646 lp
.set("cache directory", global_settings
["cache directory"])
649 if use_ntvfs
and not lp
.get("posix:eadb"):
650 if targetdir
is not None:
651 privdir
= os
.path
.join(targetdir
, "private")
653 privdir
= lp
.get("private dir")
654 lp
.set("posix:eadb", os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
655 elif not use_ntvfs
and not lp
.get("xattr_tdb:file"):
656 if targetdir
is not None:
657 statedir
= os
.path
.join(targetdir
, "state")
659 statedir
= lp
.get("state directory")
660 lp
.set("xattr_tdb:file", os
.path
.abspath(os
.path
.join(statedir
, "xattr.tdb")))
663 if serverrole
== "active directory domain controller":
664 shares
["sysvol"] = os
.path
.join(lp
.get("state directory"), "sysvol")
665 shares
["netlogon"] = os
.path
.join(shares
["sysvol"], realm
.lower(),
668 global_settings
["passdb backend"] = "samba_dsdb"
670 f
= open(smbconf
, 'w')
672 f
.write("[globals]\n")
673 for key
, val
in global_settings
.iteritems():
674 f
.write("\t%s = %s\n" % (key
, val
))
677 for name
, path
in shares
.iteritems():
678 f
.write("[%s]\n" % name
)
679 f
.write("\tpath = %s\n" % path
)
680 f
.write("\tread only = no\n")
684 # reload the smb.conf
687 # and dump it without any values that are the default
688 # this ensures that any smb.conf parameters that were set
689 # on the provision/join command line are set in the resulting smb.conf
690 f
= open(smbconf
, mode
='w')
697 def setup_name_mappings(idmap
, sid
, root_uid
, nobody_uid
,
698 users_gid
, root_gid
):
699 """setup reasonable name mappings for sam names to unix names.
701 :param samdb: SamDB object.
702 :param idmap: IDmap db object.
703 :param sid: The domain sid.
704 :param domaindn: The domain DN.
705 :param root_uid: uid of the UNIX root user.
706 :param nobody_uid: uid of the UNIX nobody user.
707 :param users_gid: gid of the UNIX users group.
708 :param root_gid: gid of the UNIX root group.
710 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
712 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
713 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
716 def setup_samdb_partitions(samdb_path
, logger
, lp
, session_info
,
717 provision_backend
, names
, schema
, serverrole
,
719 """Setup the partitions for the SAM database.
721 Alternatively, provision() may call this, and then populate the database.
723 :note: This will wipe the Sam Database!
725 :note: This function always removes the local SAM LDB file. The erase
726 parameter controls whether to erase the existing data, which
727 may not be stored locally but in LDAP.
730 assert session_info
is not None
732 # We use options=["modules:"] to stop the modules loading - we
733 # just want to wipe and re-initialise the database, not start it up
736 os
.unlink(samdb_path
)
740 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
741 lp
=lp
, options
=["modules:"])
743 ldap_backend_line
= "# No LDAP backend"
744 if provision_backend
.type != "ldb":
745 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldap_uri
747 samdb
.transaction_start()
749 logger
.info("Setting up sam.ldb partitions and settings")
750 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
751 "LDAP_BACKEND_LINE": ldap_backend_line
755 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
756 "BACKEND_TYPE": provision_backend
.type,
757 "SERVER_ROLE": serverrole
760 logger
.info("Setting up sam.ldb rootDSE")
761 setup_samdb_rootdse(samdb
, names
)
763 samdb
.transaction_cancel()
766 samdb
.transaction_commit()
769 def secretsdb_self_join(secretsdb
, domain
,
770 netbiosname
, machinepass
, domainsid
=None,
771 realm
=None, dnsdomain
=None,
773 key_version_number
=1,
774 secure_channel_type
=SEC_CHAN_WKSTA
):
775 """Add domain join-specific bits to a secrets database.
777 :param secretsdb: Ldb Handle to the secrets database
778 :param machinepass: Machine password
780 attrs
= ["whenChanged",
787 if realm
is not None:
788 if dnsdomain
is None:
789 dnsdomain
= realm
.lower()
790 dnsname
= '%s.%s' % (netbiosname
.lower(), dnsdomain
.lower())
793 shortname
= netbiosname
.lower()
795 # We don't need to set msg["flatname"] here, because rdn_name will handle
796 # it, and it causes problems for modifies anyway
797 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
798 msg
["secureChannelType"] = [str(secure_channel_type
)]
799 msg
["objectClass"] = ["top", "primaryDomain"]
800 if dnsname
is not None:
801 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
802 msg
["realm"] = [realm
]
803 msg
["saltPrincipal"] = ["host/%s@%s" % (dnsname
, realm
.upper())]
804 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
805 msg
["privateKeytab"] = ["secrets.keytab"]
807 msg
["secret"] = [machinepass
]
808 msg
["samAccountName"] = ["%s$" % netbiosname
]
809 msg
["secureChannelType"] = [str(secure_channel_type
)]
810 if domainsid
is not None:
811 msg
["objectSid"] = [ndr_pack(domainsid
)]
813 # This complex expression tries to ensure that we don't have more
814 # than one record for this SID, realm or netbios domain at a time,
815 # but we don't delete the old record that we are about to modify,
816 # because that would delete the keytab and previous password.
817 res
= secretsdb
.search(base
="cn=Primary Domains", attrs
=attrs
,
818 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain
, realm
, str(domainsid
), str(msg
.dn
))),
819 scope
=ldb
.SCOPE_ONELEVEL
)
822 secretsdb
.delete(del_msg
.dn
)
824 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
827 msg
["priorSecret"] = [res
[0]["secret"][0]]
828 msg
["priorWhenChanged"] = [res
[0]["whenChanged"][0]]
831 msg
["privateKeytab"] = [res
[0]["privateKeytab"][0]]
836 msg
["krb5Keytab"] = [res
[0]["krb5Keytab"][0]]
842 msg
[el
].set_flags(ldb
.FLAG_MOD_REPLACE
)
843 secretsdb
.modify(msg
)
844 secretsdb
.rename(res
[0].dn
, msg
.dn
)
846 spn
= [ 'HOST/%s' % shortname
]
847 if secure_channel_type
== SEC_CHAN_BDC
and dnsname
is not None:
848 # we are a domain controller then we add servicePrincipalName
849 # entries for the keytab code to update.
850 spn
.extend([ 'HOST/%s' % dnsname
])
851 msg
["servicePrincipalName"] = spn
856 def setup_secretsdb(paths
, session_info
, backend_credentials
, lp
):
857 """Setup the secrets database.
859 :note: This function does not handle exceptions and transaction on purpose,
860 it's up to the caller to do this job.
862 :param path: Path to the secrets database.
863 :param session_info: Session info.
864 :param credentials: Credentials
865 :param lp: Loadparm context
866 :return: LDB handle for the created secrets database
868 if os
.path
.exists(paths
.secrets
):
869 os
.unlink(paths
.secrets
)
871 keytab_path
= os
.path
.join(paths
.private_dir
, paths
.keytab
)
872 if os
.path
.exists(keytab_path
):
873 os
.unlink(keytab_path
)
875 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
876 if os
.path
.exists(dns_keytab_path
):
877 os
.unlink(dns_keytab_path
)
881 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
883 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
884 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
885 secrets_ldb
.transaction_start()
887 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
889 if (backend_credentials
is not None and
890 backend_credentials
.authentication_requested()):
891 if backend_credentials
.get_bind_dn() is not None:
892 setup_add_ldif(secrets_ldb
,
893 setup_path("secrets_simple_ldap.ldif"), {
894 "LDAPMANAGERDN": backend_credentials
.get_bind_dn(),
895 "LDAPMANAGERPASS_B64": b64encode(backend_credentials
.get_password())
898 setup_add_ldif(secrets_ldb
,
899 setup_path("secrets_sasl_ldap.ldif"), {
900 "LDAPADMINUSER": backend_credentials
.get_username(),
901 "LDAPADMINREALM": backend_credentials
.get_realm(),
902 "LDAPADMINPASS_B64": b64encode(backend_credentials
.get_password())
905 secrets_ldb
.transaction_cancel()
910 def setup_privileges(path
, session_info
, lp
):
911 """Setup the privileges database.
913 :param path: Path to the privileges database.
914 :param session_info: Session info.
915 :param credentials: Credentials
916 :param lp: Loadparm context
917 :return: LDB handle for the created secrets database
919 if os
.path
.exists(path
):
921 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
922 privilege_ldb
.erase()
923 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
926 def setup_registry(path
, session_info
, lp
):
927 """Setup the registry.
929 :param path: Path to the registry database
930 :param session_info: Session information
931 :param credentials: Credentials
932 :param lp: Loadparm context
934 reg
= samba
.registry
.Registry()
935 hive
= samba
.registry
.open_ldb(path
, session_info
=session_info
, lp_ctx
=lp
)
936 reg
.mount_hive(hive
, samba
.registry
.HKEY_LOCAL_MACHINE
)
937 provision_reg
= setup_path("provision.reg")
938 assert os
.path
.exists(provision_reg
)
939 reg
.diff_apply(provision_reg
)
942 def setup_idmapdb(path
, session_info
, lp
):
943 """Setup the idmap database.
945 :param path: path to the idmap database
946 :param session_info: Session information
947 :param credentials: Credentials
948 :param lp: Loadparm context
950 if os
.path
.exists(path
):
953 idmap_ldb
= IDmapDB(path
, session_info
=session_info
, lp
=lp
)
955 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
959 def setup_samdb_rootdse(samdb
, names
):
960 """Setup the SamDB rootdse.
962 :param samdb: Sam Database handle
964 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
965 "SCHEMADN": names
.schemadn
,
966 "DOMAINDN": names
.domaindn
,
967 "ROOTDN" : names
.rootdn
,
968 "CONFIGDN": names
.configdn
,
969 "SERVERDN": names
.serverdn
,
973 def setup_self_join(samdb
, admin_session_info
, names
, fill
, machinepass
,
974 dns_backend
, dnspass
, domainsid
, next_rid
, invocationid
,
975 policyguid
, policyguid_dc
,
976 domainControllerFunctionality
, ntdsguid
=None, dc_rid
=None):
977 """Join a host to its own domain."""
978 assert isinstance(invocationid
, str)
979 if ntdsguid
is not None:
980 ntdsguid_line
= "objectGUID: %s\n"%ntdsguid
987 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
988 "CONFIGDN": names
.configdn
,
989 "SCHEMADN": names
.schemadn
,
990 "DOMAINDN": names
.domaindn
,
991 "SERVERDN": names
.serverdn
,
992 "INVOCATIONID": invocationid
,
993 "NETBIOSNAME": names
.netbiosname
,
994 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
995 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
996 "DOMAINSID": str(domainsid
),
997 "DCRID": str(dc_rid
),
998 "SAMBA_VERSION_STRING": version
,
999 "NTDSGUID": ntdsguid_line
,
1000 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1001 domainControllerFunctionality
),
1002 "RIDALLOCATIONSTART": str(next_rid
+ 100),
1003 "RIDALLOCATIONEND": str(next_rid
+ 100 + 499)})
1005 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
1006 "POLICYGUID": policyguid
,
1007 "POLICYGUID_DC": policyguid_dc
,
1008 "DNSDOMAIN": names
.dnsdomain
,
1009 "DOMAINDN": names
.domaindn
})
1011 # If we are setting up a subdomain, then this has been replicated in, so we
1012 # don't need to add it
1013 if fill
== FILL_FULL
:
1014 setup_add_ldif(samdb
, setup_path("provision_self_join_config.ldif"), {
1015 "CONFIGDN": names
.configdn
,
1016 "SCHEMADN": names
.schemadn
,
1017 "DOMAINDN": names
.domaindn
,
1018 "SERVERDN": names
.serverdn
,
1019 "INVOCATIONID": invocationid
,
1020 "NETBIOSNAME": names
.netbiosname
,
1021 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1022 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1023 "DOMAINSID": str(domainsid
),
1024 "DCRID": str(dc_rid
),
1025 "SAMBA_VERSION_STRING": version
,
1026 "NTDSGUID": ntdsguid_line
,
1027 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1028 domainControllerFunctionality
)})
1030 # Setup fSMORoleOwner entries to point at the newly created DC entry
1031 setup_modify_ldif(samdb
,
1032 setup_path("provision_self_join_modify_config.ldif"), {
1033 "CONFIGDN": names
.configdn
,
1034 "SCHEMADN": names
.schemadn
,
1035 "DEFAULTSITE": names
.sitename
,
1036 "NETBIOSNAME": names
.netbiosname
,
1037 "SERVERDN": names
.serverdn
,
1040 system_session_info
= system_session()
1041 samdb
.set_session_info(system_session_info
)
1042 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1043 # modify a serverReference under cn=config when we are a subdomain, we must
1044 # be system due to ACLs
1045 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
1046 "DOMAINDN": names
.domaindn
,
1047 "SERVERDN": names
.serverdn
,
1048 "NETBIOSNAME": names
.netbiosname
,
1051 samdb
.set_session_info(admin_session_info
)
1053 if dns_backend
!= "SAMBA_INTERNAL":
1054 # This is Samba4 specific and should be replaced by the correct
1055 # DNS AD-style setup
1056 setup_add_ldif(samdb
, setup_path("provision_dns_add_samba.ldif"), {
1057 "DNSDOMAIN": names
.dnsdomain
,
1058 "DOMAINDN": names
.domaindn
,
1059 "DNSPASS_B64": b64encode(dnspass
.encode('utf-16-le')),
1060 "HOSTNAME" : names
.hostname
,
1061 "DNSNAME" : '%s.%s' % (
1062 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
1066 def getpolicypath(sysvolpath
, dnsdomain
, guid
):
1067 """Return the physical path of policy given its guid.
1069 :param sysvolpath: Path to the sysvol folder
1070 :param dnsdomain: DNS name of the AD domain
1071 :param guid: The GUID of the policy
1072 :return: A string with the complete path to the policy folder
1075 guid
= "{%s}" % guid
1076 policy_path
= os
.path
.join(sysvolpath
, dnsdomain
, "Policies", guid
)
1080 def create_gpo_struct(policy_path
):
1081 if not os
.path
.exists(policy_path
):
1082 os
.makedirs(policy_path
, 0775)
1083 f
= open(os
.path
.join(policy_path
, "GPT.INI"), 'w')
1085 f
.write("[General]\r\nVersion=0")
1088 p
= os
.path
.join(policy_path
, "MACHINE")
1089 if not os
.path
.exists(p
):
1090 os
.makedirs(p
, 0775)
1091 p
= os
.path
.join(policy_path
, "USER")
1092 if not os
.path
.exists(p
):
1093 os
.makedirs(p
, 0775)
1096 def create_default_gpo(sysvolpath
, dnsdomain
, policyguid
, policyguid_dc
):
1097 """Create the default GPO for a domain
1099 :param sysvolpath: Physical path for the sysvol folder
1100 :param dnsdomain: DNS domain name of the AD domain
1101 :param policyguid: GUID of the default domain policy
1102 :param policyguid_dc: GUID of the default domain controler policy
1104 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid
)
1105 create_gpo_struct(policy_path
)
1107 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid_dc
)
1108 create_gpo_struct(policy_path
)
1111 def setup_samdb(path
, session_info
, provision_backend
, lp
, names
,
1112 logger
, fill
, serverrole
, schema
, am_rodc
=False):
1113 """Setup a complete SAM Database.
1115 :note: This will wipe the main SAM database file!
1118 # Also wipes the database
1119 setup_samdb_partitions(path
, logger
=logger
, lp
=lp
,
1120 provision_backend
=provision_backend
, session_info
=session_info
,
1121 names
=names
, serverrole
=serverrole
, schema
=schema
)
1123 # Load the database, but don's load the global schema and don't connect
1125 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
1126 credentials
=provision_backend
.credentials
, lp
=lp
,
1127 global_schema
=False, am_rodc
=am_rodc
)
1129 logger
.info("Pre-loading the Samba 4 and AD schema")
1131 # Load the schema from the one we computed earlier
1132 samdb
.set_schema(schema
, write_indices_and_attributes
=False)
1134 # Set the NTDS settings DN manually - in order to have it already around
1135 # before the provisioned tree exists and we connect
1136 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1138 # And now we can connect to the DB - the schema won't be loaded from the
1142 # But we have to give it one more kick to have it use the schema
1143 # during provision - it needs, now that it is connected, to write
1144 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1145 samdb
.set_schema(schema
, write_indices_and_attributes
=True)
1150 def fill_samdb(samdb
, lp
, names
, logger
, domainsid
, domainguid
, policyguid
,
1151 policyguid_dc
, fill
, adminpass
, krbtgtpass
, machinepass
, dns_backend
,
1152 dnspass
, invocationid
, ntdsguid
, serverrole
, am_rodc
=False,
1153 dom_for_fun_level
=None, schema
=None, next_rid
=None, dc_rid
=None):
1155 if next_rid
is None:
1158 # Provision does not make much sense values larger than 1000000000
1159 # as the upper range of the rIDAvailablePool is 1073741823 and
1160 # we don't want to create a domain that cannot allocate rids.
1161 if next_rid
< 1000 or next_rid
> 1000000000:
1162 error
= "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid
)
1163 error
+= "the valid range is %u-%u. The default is %u." % (
1164 1000, 1000000000, 1000)
1165 raise ProvisioningError(error
)
1167 # ATTENTION: Do NOT change these default values without discussion with the
1168 # team and/or release manager. They have a big impact on the whole program!
1169 domainControllerFunctionality
= DS_DOMAIN_FUNCTION_2008_R2
1171 if dom_for_fun_level
is None:
1172 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2003
1174 if dom_for_fun_level
> domainControllerFunctionality
:
1175 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!")
1177 domainFunctionality
= dom_for_fun_level
1178 forestFunctionality
= dom_for_fun_level
1180 # Set the NTDS settings DN manually - in order to have it already around
1181 # before the provisioned tree exists and we connect
1182 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1184 samdb
.transaction_start()
1186 # Set the domain functionality levels onto the database.
1187 # Various module (the password_hash module in particular) need
1188 # to know what level of AD we are emulating.
1190 # These will be fixed into the database via the database
1191 # modifictions below, but we need them set from the start.
1192 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
1193 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
1194 samdb
.set_opaque_integer("domainControllerFunctionality",
1195 domainControllerFunctionality
)
1197 samdb
.set_domain_sid(str(domainsid
))
1198 samdb
.set_invocation_id(invocationid
)
1200 logger
.info("Adding DomainDN: %s" % names
.domaindn
)
1202 # impersonate domain admin
1203 admin_session_info
= admin_session(lp
, str(domainsid
))
1204 samdb
.set_session_info(admin_session_info
)
1205 if domainguid
is not None:
1206 domainguid_line
= "objectGUID: %s\n-" % domainguid
1208 domainguid_line
= ""
1210 descr
= b64encode(get_domain_descriptor(domainsid
))
1211 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
1212 "DOMAINDN": names
.domaindn
,
1213 "DOMAINSID": str(domainsid
),
1214 "DESCRIPTOR": descr
,
1215 "DOMAINGUID": domainguid_line
1218 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
1219 "DOMAINDN": names
.domaindn
,
1220 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1221 "NEXTRID": str(next_rid
),
1222 "DEFAULTSITE": names
.sitename
,
1223 "CONFIGDN": names
.configdn
,
1224 "POLICYGUID": policyguid
,
1225 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1226 "SAMBA_VERSION_STRING": version
1229 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1230 if fill
== FILL_FULL
:
1231 logger
.info("Adding configuration container")
1232 descr
= b64encode(get_config_descriptor(domainsid
))
1233 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
1234 "CONFIGDN": names
.configdn
,
1235 "DESCRIPTOR": descr
,
1238 # The LDIF here was created when the Schema object was constructed
1239 logger
.info("Setting up sam.ldb schema")
1240 samdb
.add_ldif(schema
.schema_dn_add
, controls
=["relax:0"])
1241 samdb
.modify_ldif(schema
.schema_dn_modify
)
1242 samdb
.write_prefixes_from_schema()
1243 samdb
.add_ldif(schema
.schema_data
, controls
=["relax:0"])
1244 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
1245 {"SCHEMADN": names
.schemadn
})
1247 # Now register this container in the root of the forest
1248 msg
= ldb
.Message(ldb
.Dn(samdb
, names
.domaindn
))
1249 msg
["subRefs"] = ldb
.MessageElement(names
.configdn
, ldb
.FLAG_MOD_ADD
,
1253 samdb
.transaction_cancel()
1256 samdb
.transaction_commit()
1258 samdb
.transaction_start()
1260 samdb
.invocation_id
= invocationid
1262 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1263 if fill
== FILL_FULL
:
1264 logger
.info("Setting up sam.ldb configuration data")
1265 partitions_descr
= b64encode(get_config_partitions_descriptor(domainsid
))
1266 sites_descr
= b64encode(get_config_sites_descriptor(domainsid
))
1267 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
1268 "CONFIGDN": names
.configdn
,
1269 "NETBIOSNAME": names
.netbiosname
,
1270 "DEFAULTSITE": names
.sitename
,
1271 "DNSDOMAIN": names
.dnsdomain
,
1272 "DOMAIN": names
.domain
,
1273 "SCHEMADN": names
.schemadn
,
1274 "DOMAINDN": names
.domaindn
,
1275 "SERVERDN": names
.serverdn
,
1276 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
1277 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1278 "PARTITIONS_DESCRIPTOR": partitions_descr
,
1279 "SITES_DESCRIPTOR": sites_descr
,
1282 logger
.info("Setting up display specifiers")
1283 display_specifiers_ldif
= read_ms_ldif(
1284 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1285 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
,
1286 {"CONFIGDN": names
.configdn
})
1287 check_all_substituted(display_specifiers_ldif
)
1288 samdb
.add_ldif(display_specifiers_ldif
)
1290 logger
.info("Adding users container")
1291 users_desc
= b64encode(get_domain_users_descriptor(domainsid
))
1292 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
1293 "DOMAINDN": names
.domaindn
,
1294 "USERS_DESCRIPTOR": users_desc
1296 logger
.info("Modifying users container")
1297 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
1298 "DOMAINDN": names
.domaindn
})
1299 logger
.info("Adding computers container")
1300 computers_desc
= b64encode(get_domain_computers_descriptor(domainsid
))
1301 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
1302 "DOMAINDN": names
.domaindn
,
1303 "COMPUTERS_DESCRIPTOR": computers_desc
1305 logger
.info("Modifying computers container")
1306 setup_modify_ldif(samdb
,
1307 setup_path("provision_computers_modify.ldif"), {
1308 "DOMAINDN": names
.domaindn
})
1309 logger
.info("Setting up sam.ldb data")
1310 infrastructure_desc
= b64encode(get_domain_infrastructure_descriptor(domainsid
))
1311 builtin_desc
= b64encode(get_domain_builtin_descriptor(domainsid
))
1312 controllers_desc
= b64encode(get_domain_controllers_descriptor(domainsid
))
1313 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1314 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1315 "DOMAINDN": names
.domaindn
,
1316 "NETBIOSNAME": names
.netbiosname
,
1317 "DEFAULTSITE": names
.sitename
,
1318 "CONFIGDN": names
.configdn
,
1319 "SERVERDN": names
.serverdn
,
1320 "RIDAVAILABLESTART": str(next_rid
+ 600),
1321 "POLICYGUID_DC": policyguid_dc
,
1322 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc
,
1323 "BUILTIN_DESCRIPTOR": builtin_desc
,
1324 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc
,
1327 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1328 if fill
== FILL_FULL
:
1329 setup_modify_ldif(samdb
,
1330 setup_path("provision_configuration_references.ldif"), {
1331 "CONFIGDN": names
.configdn
,
1332 "SCHEMADN": names
.schemadn
})
1334 logger
.info("Setting up well known security principals")
1335 setup_add_ldif(samdb
, setup_path("provision_well_known_sec_princ.ldif"), {
1336 "CONFIGDN": names
.configdn
,
1339 if fill
== FILL_FULL
or fill
== FILL_SUBDOMAIN
:
1340 setup_modify_ldif(samdb
,
1341 setup_path("provision_basedn_references.ldif"),
1342 {"DOMAINDN": names
.domaindn
})
1344 logger
.info("Setting up sam.ldb users and groups")
1345 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1346 "DOMAINDN": names
.domaindn
,
1347 "DOMAINSID": str(domainsid
),
1348 "ADMINPASS_B64": b64encode(adminpass
.encode('utf-16-le')),
1349 "KRBTGTPASS_B64": b64encode(krbtgtpass
.encode('utf-16-le'))
1352 logger
.info("Setting up self join")
1353 setup_self_join(samdb
, admin_session_info
, names
=names
, fill
=fill
,
1354 invocationid
=invocationid
,
1355 dns_backend
=dns_backend
,
1357 machinepass
=machinepass
,
1358 domainsid
=domainsid
,
1361 policyguid
=policyguid
,
1362 policyguid_dc
=policyguid_dc
,
1363 domainControllerFunctionality
=domainControllerFunctionality
,
1366 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1367 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1368 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
)
1369 assert isinstance(names
.ntdsguid
, str)
1371 samdb
.transaction_cancel()
1374 samdb
.transaction_commit()
1379 FILL_SUBDOMAIN
= "SUBDOMAIN"
1380 FILL_NT4SYNC
= "NT4SYNC"
1382 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1383 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)"
1384 SYSVOL_SERVICE
="sysvol"
1386 def set_dir_acl(path
, acl
, lp
, domsid
, use_ntvfs
, passdb
, service
=SYSVOL_SERVICE
):
1387 setntacl(lp
, path
, acl
, domsid
, use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1388 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1390 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1391 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1393 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1394 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1397 def set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
):
1398 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1401 :param sysvol: Physical path for the sysvol folder
1402 :param dnsdomain: The DNS name of the domain
1403 :param domainsid: The SID of the domain
1404 :param domaindn: The DN of the domain (ie. DC=...)
1405 :param samdb: An LDB object on the SAM db
1406 :param lp: an LP object
1409 # Set ACL for GPO root folder
1410 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1411 setntacl(lp
, root_policy_path
, POLICIES_ACL
, str(domainsid
),
1412 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=SYSVOL_SERVICE
)
1414 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1415 attrs
=["cn", "nTSecurityDescriptor"],
1416 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1419 acl
= ndr_unpack(security
.descriptor
,
1420 str(policy
["nTSecurityDescriptor"])).as_sddl()
1421 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1422 set_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1423 str(domainsid
), use_ntvfs
,
1427 def setsysvolacl(samdb
, netlogon
, sysvol
, uid
, gid
, domainsid
, dnsdomain
,
1428 domaindn
, lp
, use_ntvfs
):
1429 """Set the ACL for the sysvol share and the subfolders
1431 :param samdb: An LDB object on the SAM db
1432 :param netlogon: Physical path for the netlogon folder
1433 :param sysvol: Physical path for the sysvol folder
1434 :param uid: The UID of the "Administrator" user
1435 :param gid: The GID of the "Domain adminstrators" group
1436 :param domainsid: The SID of the domain
1437 :param dnsdomain: The DNS name of the domain
1438 :param domaindn: The DN of the domain (ie. DC=...)
1443 # This will ensure that the smbd code we are running when setting ACLs
1444 # is initialised with the smb.conf
1445 s3conf
= s3param
.get_context()
1446 s3conf
.load(lp
.configfile
)
1447 # ensure we are using the right samba_dsdb passdb backend, no matter what
1448 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1449 passdb
.reload_static_pdb()
1451 # ensure that we init the samba_dsdb backend, so the domain sid is
1452 # marked in secrets.tdb
1453 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1455 # now ensure everything matches correctly, to avoid wierd issues
1456 if passdb
.get_global_sam_sid() != domainsid
:
1457 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
))
1459 domain_info
= s4_passdb
.domain_info()
1460 if domain_info
["dom_sid"] != domainsid
:
1461 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
))
1463 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1464 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()))
1469 os
.chown(sysvol
, -1, gid
)
1475 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1476 setntacl(lp
,sysvol
, SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
,
1477 skip_invalid_chown
=True, passdb
=s4_passdb
,
1478 service
=SYSVOL_SERVICE
)
1479 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1481 if use_ntvfs
and canchown
:
1482 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1483 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
),
1484 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True,
1485 passdb
=s4_passdb
, service
=SYSVOL_SERVICE
)
1487 if use_ntvfs
and canchown
:
1488 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1489 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
),
1490 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True,
1491 passdb
=s4_passdb
, service
=SYSVOL_SERVICE
)
1493 # Set acls on Policy folder and policies folders
1494 set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
=s4_passdb
)
1496 def acl_type(direct_db_access
):
1497 if direct_db_access
:
1502 def check_dir_acl(path
, acl
, lp
, domainsid
, direct_db_access
):
1503 fsacl
= getntacl(lp
, path
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1504 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1505 if fsacl_sddl
!= acl
:
1506 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
))
1508 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1510 fsacl
= getntacl(lp
, os
.path
.join(root
, name
),
1511 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1513 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1514 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1515 if fsacl_sddl
!= acl
:
1516 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
))
1519 fsacl
= getntacl(lp
, os
.path
.join(root
, name
),
1520 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1522 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1523 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1524 if fsacl_sddl
!= acl
:
1525 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
))
1528 def check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1530 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1533 :param sysvol: Physical path for the sysvol folder
1534 :param dnsdomain: The DNS name of the domain
1535 :param domainsid: The SID of the domain
1536 :param domaindn: The DN of the domain (ie. DC=...)
1537 :param samdb: An LDB object on the SAM db
1538 :param lp: an LP object
1541 # Set ACL for GPO root folder
1542 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1543 fsacl
= getntacl(lp
, root_policy_path
,
1544 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1546 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access
), root_policy_path
))
1547 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1548 if fsacl_sddl
!= POLICIES_ACL
:
1549 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
))
1550 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1551 attrs
=["cn", "nTSecurityDescriptor"],
1552 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1555 acl
= ndr_unpack(security
.descriptor
,
1556 str(policy
["nTSecurityDescriptor"])).as_sddl()
1557 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1558 check_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1559 domainsid
, direct_db_access
)
1562 def checksysvolacl(samdb
, netlogon
, sysvol
, domainsid
, dnsdomain
, domaindn
,
1564 """Set the ACL for the sysvol share and the subfolders
1566 :param samdb: An LDB object on the SAM db
1567 :param netlogon: Physical path for the netlogon folder
1568 :param sysvol: Physical path for the sysvol folder
1569 :param uid: The UID of the "Administrator" user
1570 :param gid: The GID of the "Domain adminstrators" group
1571 :param domainsid: The SID of the domain
1572 :param dnsdomain: The DNS name of the domain
1573 :param domaindn: The DN of the domain (ie. DC=...)
1576 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1577 s3conf
= s3param
.get_context()
1578 s3conf
.load(lp
.configfile
)
1579 # ensure we are using the right samba_dsdb passdb backend, no matter what
1580 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1581 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1582 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1584 # now ensure everything matches correctly, to avoid wierd issues
1585 if passdb
.get_global_sam_sid() != domainsid
:
1586 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
))
1588 domain_info
= s4_passdb
.domain_info()
1589 if domain_info
["dom_sid"] != domainsid
:
1590 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
))
1592 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1593 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()))
1595 # Ensure we can read this directly, and via the smbd VFS
1596 for direct_db_access
in [True, False]:
1597 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1598 for dir_path
in [os
.path
.join(sysvol
, dnsdomain
), netlogon
]:
1599 fsacl
= getntacl(lp
, dir_path
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1601 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access
), dir_path
))
1602 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1603 if fsacl_sddl
!= SYSVOL_ACL
:
1604 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
))
1606 # Check acls on Policy folder and policies folders
1607 check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1611 def interface_ips_v4(lp
):
1612 """return only IPv4 IPs"""
1613 ips
= samba
.interface_ips(lp
, False)
1616 if i
.find(':') == -1:
1621 def interface_ips_v6(lp
, linklocal
=False):
1622 """return only IPv6 IPs"""
1623 ips
= samba
.interface_ips(lp
, False)
1626 if i
.find(':') != -1 and (linklocal
or i
.find('%') == -1):
1631 def provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
1632 domainsid
, schema
=None,
1633 targetdir
=None, samdb_fill
=FILL_FULL
,
1634 hostip
=None, hostip6
=None,
1635 next_rid
=1000, dc_rid
=None, adminpass
=None, krbtgtpass
=None,
1636 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1637 invocationid
=None, machinepass
=None, ntdsguid
=None,
1638 dns_backend
=None, dnspass
=None,
1639 serverrole
=None, dom_for_fun_level
=None,
1640 am_rodc
=False, lp
=None, use_ntvfs
=False, skip_sysvolacl
=False):
1641 # create/adapt the group policy GUIDs
1642 # Default GUID for default policy are described at
1643 # "How Core Group Policy Works"
1644 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1645 if policyguid
is None:
1646 policyguid
= DEFAULT_POLICY_GUID
1647 policyguid
= policyguid
.upper()
1648 if policyguid_dc
is None:
1649 policyguid_dc
= DEFAULT_DC_POLICY_GUID
1650 policyguid_dc
= policyguid_dc
.upper()
1652 if invocationid
is None:
1653 invocationid
= str(uuid
.uuid4())
1655 if krbtgtpass
is None:
1656 krbtgtpass
= samba
.generate_random_password(128, 255)
1657 if machinepass
is None:
1658 machinepass
= samba
.generate_random_password(128, 255)
1660 dnspass
= samba
.generate_random_password(128, 255)
1662 samdb
= fill_samdb(samdb
, lp
, names
, logger
=logger
,
1663 domainsid
=domainsid
, schema
=schema
, domainguid
=domainguid
,
1664 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1665 fill
=samdb_fill
, adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1666 invocationid
=invocationid
, machinepass
=machinepass
,
1667 dns_backend
=dns_backend
, dnspass
=dnspass
,
1668 ntdsguid
=ntdsguid
, serverrole
=serverrole
,
1669 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
1670 next_rid
=next_rid
, dc_rid
=dc_rid
)
1672 if serverrole
== "active directory domain controller":
1674 # Set up group policies (domain policy and domain controller
1676 create_default_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
,
1678 if not skip_sysvolacl
:
1679 setsysvolacl(samdb
, paths
.netlogon
, paths
.sysvol
, paths
.root_uid
,
1680 paths
.root_gid
, domainsid
, names
.dnsdomain
,
1681 names
.domaindn
, lp
, use_ntvfs
)
1683 logger
.info("Setting acl on sysvol skipped")
1685 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1686 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1687 netbiosname
=names
.netbiosname
, domainsid
=domainsid
,
1688 machinepass
=machinepass
, secure_channel_type
=SEC_CHAN_BDC
)
1690 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1691 # In future, this might be determined from some configuration
1692 kerberos_enctypes
= str(ENC_ALL_TYPES
)
1695 msg
= ldb
.Message(ldb
.Dn(samdb
,
1696 samdb
.searchone("distinguishedName",
1697 expression
="samAccountName=%s$" % names
.netbiosname
,
1698 scope
=ldb
.SCOPE_SUBTREE
)))
1699 msg
["msDS-SupportedEncryptionTypes"] = ldb
.MessageElement(
1700 elements
=kerberos_enctypes
, flags
=ldb
.FLAG_MOD_REPLACE
,
1701 name
="msDS-SupportedEncryptionTypes")
1703 except ldb
.LdbError
, (enum
, estr
):
1704 if enum
!= ldb
.ERR_NO_SUCH_ATTRIBUTE
:
1705 # It might be that this attribute does not exist in this schema
1708 setup_ad_dns(samdb
, secrets_ldb
, domainsid
, names
, paths
, lp
, logger
,
1709 hostip
=hostip
, hostip6
=hostip6
, dns_backend
=dns_backend
,
1710 dnspass
=dnspass
, os_level
=dom_for_fun_level
,
1711 targetdir
=targetdir
, site
=DEFAULTSITE
)
1713 domainguid
= samdb
.searchone(basedn
=samdb
.get_default_basedn(),
1714 attribute
="objectGUID")
1715 assert isinstance(domainguid
, str)
1717 lastProvisionUSNs
= get_last_provision_usn(samdb
)
1718 maxUSN
= get_max_usn(samdb
, str(names
.rootdn
))
1719 if lastProvisionUSNs
is not None:
1720 update_provision_usn(samdb
, 0, maxUSN
, invocationid
, 1)
1722 set_provision_usn(samdb
, 0, maxUSN
, invocationid
)
1724 logger
.info("Setting up sam.ldb rootDSE marking as synchronized")
1725 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"),
1726 { 'NTDSGUID' : names
.ntdsguid
})
1728 # fix any dangling GUIDs from the provision
1729 logger
.info("Fixing provision GUIDs")
1730 chk
= dbcheck(samdb
, samdb_schema
=samdb
, verbose
=False, fix
=True, yes
=True,
1732 samdb
.transaction_start()
1734 # a small number of GUIDs are missing because of ordering issues in the
1736 for schema_obj
in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1737 chk
.check_database(DN
="%s,%s" % (schema_obj
, names
.schemadn
),
1738 scope
=ldb
.SCOPE_BASE
,
1739 attrs
=['defaultObjectCategory'])
1740 chk
.check_database(DN
="CN=IP Security,CN=System,%s" % names
.domaindn
,
1741 scope
=ldb
.SCOPE_ONELEVEL
,
1742 attrs
=['ipsecOwnersReference',
1743 'ipsecFilterReference',
1744 'ipsecISAKMPReference',
1745 'ipsecNegotiationPolicyReference',
1746 'ipsecNFAReference'])
1748 samdb
.transaction_cancel()
1751 samdb
.transaction_commit()
1755 "ROLE_STANDALONE": "standalone server",
1756 "ROLE_DOMAIN_MEMBER": "member server",
1757 "ROLE_DOMAIN_BDC": "active directory domain controller",
1758 "ROLE_DOMAIN_PDC": "active directory domain controller",
1759 "dc": "active directory domain controller",
1760 "member": "member server",
1761 "domain controller": "active directory domain controller",
1762 "active directory domain controller": "active directory domain controller",
1763 "member server": "member server",
1764 "standalone": "standalone server",
1765 "standalone server": "standalone server",
1769 def sanitize_server_role(role
):
1770 """Sanitize a server role name.
1772 :param role: Server role
1773 :raise ValueError: If the role can not be interpreted
1774 :return: Sanitized server role (one of "member server",
1775 "active directory domain controller", "standalone server")
1778 return _ROLES_MAP
[role
]
1780 raise ValueError(role
)
1783 def provision_fake_ypserver(logger
, samdb
, domaindn
, netbiosname
, nisdomain
,
1785 """Create AD entries for the fake ypserver.
1787 This is needed for being able to manipulate posix attrs via ADUC.
1789 samdb
.transaction_start()
1791 logger
.info("Setting up fake yp server settings")
1792 setup_add_ldif(samdb
, setup_path("ypServ30.ldif"), {
1793 "DOMAINDN": domaindn
,
1794 "NETBIOSNAME": netbiosname
,
1795 "NISDOMAIN": nisdomain
,
1798 samdb
.transaction_cancel()
1801 samdb
.transaction_commit()
1804 def provision(logger
, session_info
, credentials
, smbconf
=None,
1805 targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None, rootdn
=None,
1806 domaindn
=None, schemadn
=None, configdn
=None, serverdn
=None,
1807 domain
=None, hostname
=None, hostip
=None, hostip6
=None, domainsid
=None,
1808 next_rid
=1000, dc_rid
=None, adminpass
=None, ldapadminpass
=None,
1809 krbtgtpass
=None, domainguid
=None, policyguid
=None, policyguid_dc
=None,
1810 dns_backend
=None, dns_forwarder
=None, dnspass
=None,
1811 invocationid
=None, machinepass
=None, ntdsguid
=None,
1812 root
=None, nobody
=None, users
=None, backup
=None, aci
=None,
1813 serverrole
=None, dom_for_fun_level
=None, backend_type
=None,
1814 sitename
=None, ol_mmr_urls
=None, ol_olc
=None, slapd_path
="/bin/false",
1815 useeadb
=False, am_rodc
=False, lp
=None, use_ntvfs
=False,
1816 use_rfc2307
=False, maxuid
=None, maxgid
=None, skip_sysvolacl
=True):
1819 :note: caution, this wipes all existing data!
1823 serverrole
= sanitize_server_role(serverrole
)
1825 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole
)
1827 if ldapadminpass
is None:
1828 # Make a new, random password between Samba and it's LDAP server
1829 ldapadminpass
= samba
.generate_random_password(128, 255)
1831 if backend_type
is None:
1832 backend_type
= "ldb"
1834 if domainsid
is None:
1835 domainsid
= security
.random_sid()
1837 domainsid
= security
.dom_sid(domainsid
)
1839 root_uid
= findnss_uid([root
or "root"])
1840 nobody_uid
= findnss_uid([nobody
or "nobody"])
1841 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
1842 root_gid
= pwd
.getpwuid(root_uid
).pw_gid
1845 bind_gid
= findnss_gid(["bind", "named"])
1849 if targetdir
is not None:
1850 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
1851 elif smbconf
is None:
1852 smbconf
= samba
.param
.default_path()
1853 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
1854 os
.makedirs(os
.path
.dirname(smbconf
))
1856 server_services
= []
1859 global_param
["idmap_ldb:use rfc2307"] = ["yes"]
1861 if dns_backend
!= "SAMBA_INTERNAL":
1862 server_services
.append("-dns")
1864 if dns_forwarder
is not None:
1865 global_param
["dns forwarder"] = [dns_forwarder
]
1868 server_services
.append("+smb")
1869 server_services
.append("-s3fs")
1870 global_param
["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1872 if len(server_services
) > 0:
1873 global_param
["server services"] = server_services
1875 # only install a new smb.conf if there isn't one there already
1876 if os
.path
.exists(smbconf
):
1877 # if Samba Team members can't figure out the weird errors
1878 # loading an empty smb.conf gives, then we need to be smarter.
1879 # Pretend it just didn't exist --abartlet
1880 f
= open(smbconf
, 'r')
1882 data
= f
.read().lstrip()
1885 if data
is None or data
== "":
1886 make_smbconf(smbconf
, hostname
, domain
, realm
,
1887 targetdir
, serverrole
=serverrole
,
1888 eadb
=useeadb
, use_ntvfs
=use_ntvfs
,
1889 lp
=lp
, global_param
=global_param
)
1891 make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
1892 serverrole
=serverrole
,
1893 eadb
=useeadb
, use_ntvfs
=use_ntvfs
, lp
=lp
, global_param
=global_param
)
1896 lp
= samba
.param
.LoadParm()
1898 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
1899 dnsdomain
=realm
, serverrole
=serverrole
, domaindn
=domaindn
,
1900 configdn
=configdn
, schemadn
=schemadn
, serverdn
=serverdn
,
1901 sitename
=sitename
, rootdn
=rootdn
)
1902 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
1904 paths
.bind_gid
= bind_gid
1905 paths
.root_uid
= root_uid
;
1906 paths
.root_gid
= root_gid
1909 logger
.info("Looking up IPv4 addresses")
1910 hostips
= interface_ips_v4(lp
)
1911 if len(hostips
) > 0:
1913 if len(hostips
) > 1:
1914 logger
.warning("More than one IPv4 address found. Using %s",
1916 if hostip
== "127.0.0.1":
1919 logger
.warning("No IPv4 address will be assigned")
1922 logger
.info("Looking up IPv6 addresses")
1923 hostips
= interface_ips_v6(lp
, linklocal
=False)
1925 hostip6
= hostips
[0]
1926 if len(hostips
) > 1:
1927 logger
.warning("More than one IPv6 address found. Using %s", hostip6
)
1929 logger
.warning("No IPv6 address will be assigned")
1931 names
.hostip
= hostip
1932 names
.hostip6
= hostip6
1934 if serverrole
is None:
1935 serverrole
= lp
.get("server role")
1937 if not os
.path
.exists(paths
.private_dir
):
1938 os
.mkdir(paths
.private_dir
)
1939 if not os
.path
.exists(os
.path
.join(paths
.private_dir
, "tls")):
1940 os
.mkdir(os
.path
.join(paths
.private_dir
, "tls"))
1941 if not os
.path
.exists(paths
.state_dir
):
1942 os
.mkdir(paths
.state_dir
)
1944 if paths
.sysvol
and not os
.path
.exists(paths
.sysvol
):
1945 os
.makedirs(paths
.sysvol
, 0775)
1947 if not use_ntvfs
and serverrole
== "active directory domain controller":
1948 s3conf
= s3param
.get_context()
1949 s3conf
.load(lp
.configfile
)
1951 if paths
.sysvol
is None:
1952 raise MissingShareError("sysvol", paths
.smbconf
)
1954 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(paths
.sysvol
))
1957 smbd
.set_simple_acl(file.name
, 0755, root_gid
)
1959 if not smbd
.have_posix_acls():
1960 # This clue is only strictly correct for RPM and
1961 # Debian-like Linux systems, but hopefully other users
1962 # will get enough clue from it.
1963 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.")
1965 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1967 smbd
.chown(file.name
, root_uid
, root_gid
)
1969 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1973 ldapi_url
= "ldapi://%s" % urllib
.quote(paths
.s4_ldapi_path
, safe
="")
1975 schema
= Schema(domainsid
, invocationid
=invocationid
,
1976 schemadn
=names
.schemadn
)
1978 if backend_type
== "ldb":
1979 provision_backend
= LDBBackend(backend_type
, paths
=paths
,
1980 lp
=lp
, credentials
=credentials
,
1981 names
=names
, logger
=logger
)
1982 elif backend_type
== "existing":
1983 # If support for this is ever added back, then the URI will need to be
1985 provision_backend
= ExistingBackend(backend_type
, paths
=paths
,
1986 lp
=lp
, credentials
=credentials
,
1987 names
=names
, logger
=logger
,
1988 ldap_backend_forced_uri
=None)
1989 elif backend_type
== "fedora-ds":
1990 provision_backend
= FDSBackend(backend_type
, paths
=paths
,
1991 lp
=lp
, credentials
=credentials
,
1992 names
=names
, logger
=logger
, domainsid
=domainsid
,
1993 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
1994 slapd_path
=slapd_path
,
1996 elif backend_type
== "openldap":
1997 provision_backend
= OpenLDAPBackend(backend_type
, paths
=paths
,
1998 lp
=lp
, credentials
=credentials
,
1999 names
=names
, logger
=logger
, domainsid
=domainsid
,
2000 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
2001 slapd_path
=slapd_path
, ol_mmr_urls
=ol_mmr_urls
)
2003 raise ValueError("Unknown LDAP backend type selected")
2005 provision_backend
.init()
2006 provision_backend
.start()
2008 # only install a new shares config db if there is none
2009 if not os
.path
.exists(paths
.shareconf
):
2010 logger
.info("Setting up share.ldb")
2011 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
, lp
=lp
)
2012 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
2014 logger
.info("Setting up secrets.ldb")
2015 secrets_ldb
= setup_secretsdb(paths
,
2016 session_info
=session_info
,
2017 backend_credentials
=provision_backend
.secrets_credentials
, lp
=lp
)
2020 logger
.info("Setting up the registry")
2021 setup_registry(paths
.hklm
, session_info
, lp
=lp
)
2023 logger
.info("Setting up the privileges database")
2024 setup_privileges(paths
.privilege
, session_info
, lp
=lp
)
2026 logger
.info("Setting up idmap db")
2027 idmap
= setup_idmapdb(paths
.idmapdb
, session_info
=session_info
, lp
=lp
)
2029 setup_name_mappings(idmap
, sid
=str(domainsid
),
2030 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
2031 users_gid
=users_gid
, root_gid
=root_gid
)
2033 logger
.info("Setting up SAM db")
2034 samdb
= setup_samdb(paths
.samdb
, session_info
,
2035 provision_backend
, lp
, names
, logger
=logger
,
2036 serverrole
=serverrole
,
2037 schema
=schema
, fill
=samdb_fill
, am_rodc
=am_rodc
)
2039 if serverrole
== "active directory domain controller":
2040 if paths
.netlogon
is None:
2041 raise MissingShareError("netlogon", paths
.smbconf
)
2043 if paths
.sysvol
is None:
2044 raise MissingShareError("sysvol", paths
.smbconf
)
2046 if not os
.path
.isdir(paths
.netlogon
):
2047 os
.makedirs(paths
.netlogon
, 0755)
2049 if adminpass
is None:
2050 adminpass
= samba
.generate_random_password(12, 32)
2051 adminpass_generated
= True
2053 adminpass_generated
= False
2055 if samdb_fill
== FILL_FULL
:
2056 provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
2057 schema
=schema
, targetdir
=targetdir
, samdb_fill
=samdb_fill
,
2058 hostip
=hostip
, hostip6
=hostip6
, domainsid
=domainsid
,
2059 next_rid
=next_rid
, dc_rid
=dc_rid
, adminpass
=adminpass
,
2060 krbtgtpass
=krbtgtpass
, domainguid
=domainguid
,
2061 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
2062 invocationid
=invocationid
, machinepass
=machinepass
,
2063 ntdsguid
=ntdsguid
, dns_backend
=dns_backend
,
2064 dnspass
=dnspass
, serverrole
=serverrole
,
2065 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
2066 lp
=lp
, use_ntvfs
=use_ntvfs
,
2067 skip_sysvolacl
=skip_sysvolacl
)
2069 create_krb5_conf(paths
.krb5conf
,
2070 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
2072 logger
.info("A Kerberos configuration suitable for Samba 4 has been "
2073 "generated at %s", paths
.krb5conf
)
2075 if serverrole
== "active directory domain controller":
2076 create_dns_update_list(lp
, logger
, paths
)
2078 backend_result
= provision_backend
.post_setup()
2079 provision_backend
.shutdown()
2082 secrets_ldb
.transaction_cancel()
2085 # Now commit the secrets.ldb to disk
2086 secrets_ldb
.transaction_commit()
2088 # the commit creates the dns.keytab, now chown it
2089 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
2090 if os
.path
.isfile(dns_keytab_path
) and paths
.bind_gid
is not None:
2092 os
.chmod(dns_keytab_path
, 0640)
2093 os
.chown(dns_keytab_path
, -1, paths
.bind_gid
)
2095 if not os
.environ
.has_key('SAMBA_SELFTEST'):
2096 logger
.info("Failed to chown %s to bind gid %u",
2097 dns_keytab_path
, paths
.bind_gid
)
2099 result
= ProvisionResult()
2100 result
.server_role
= serverrole
2101 result
.domaindn
= domaindn
2102 result
.paths
= paths
2103 result
.names
= names
2105 result
.samdb
= samdb
2106 result
.idmap
= idmap
2107 result
.domainsid
= str(domainsid
)
2109 if samdb_fill
== FILL_FULL
:
2110 result
.adminpass_generated
= adminpass_generated
2111 result
.adminpass
= adminpass
2113 result
.adminpass_generated
= False
2114 result
.adminpass
= None
2116 result
.backend_result
= backend_result
2119 provision_fake_ypserver(logger
=logger
, samdb
=samdb
,
2120 domaindn
=names
.domaindn
, netbiosname
=names
.netbiosname
,
2121 nisdomain
=names
.domain
.lower(), maxuid
=maxuid
, maxgid
=maxgid
)
2126 def provision_become_dc(smbconf
=None, targetdir
=None,
2127 realm
=None, rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
2128 serverdn
=None, domain
=None, hostname
=None, domainsid
=None,
2129 adminpass
=None, krbtgtpass
=None, domainguid
=None, policyguid
=None,
2130 policyguid_dc
=None, invocationid
=None, machinepass
=None, dnspass
=None,
2131 dns_backend
=None, root
=None, nobody
=None, users
=None,
2132 backup
=None, serverrole
=None, ldap_backend
=None,
2133 ldap_backend_type
=None, sitename
=None, debuglevel
=1, use_ntvfs
=False):
2135 logger
= logging
.getLogger("provision")
2136 samba
.set_debug_level(debuglevel
)
2138 res
= provision(logger
, system_session(), None,
2139 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
2140 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
2141 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
2142 hostname
=hostname
, hostip
=None, domainsid
=domainsid
,
2143 machinepass
=machinepass
,
2144 serverrole
="active directory domain controller",
2145 sitename
=sitename
, dns_backend
=dns_backend
, dnspass
=dnspass
,
2146 use_ntvfs
=use_ntvfs
)
2147 res
.lp
.set("debuglevel", str(debuglevel
))
2151 def create_krb5_conf(path
, dnsdomain
, hostname
, realm
):
2152 """Write out a file containing zone statements suitable for inclusion in a
2153 named.conf file (including GSS-TSIG configuration).
2155 :param path: Path of the new named.conf file.
2156 :param dnsdomain: DNS Domain name
2157 :param hostname: Local hostname
2158 :param realm: Realm name
2160 setup_file(setup_path("krb5.conf"), path
, {
2161 "DNSDOMAIN": dnsdomain
,
2162 "HOSTNAME": hostname
,
2167 class ProvisioningError(Exception):
2168 """A generic provision error."""
2170 def __init__(self
, value
):
2174 return "ProvisioningError: " + self
.value
2177 class InvalidNetbiosName(Exception):
2178 """A specified name was not a valid NetBIOS name."""
2180 def __init__(self
, name
):
2181 super(InvalidNetbiosName
, self
).__init
__(
2182 "The name '%r' is not a valid NetBIOS name" % name
)
2185 class MissingShareError(ProvisioningError
):
2187 def __init__(self
, name
, smbconf
):
2188 super(MissingShareError
, self
).__init
__(
2189 "Existing smb.conf does not have a [%s] share, but you are "
2190 "configuring a DC. Please remove %s or add the share manually." %