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 (
80 get_config_descriptor
,
83 from samba
.provision
.common
import (
88 from samba
.provision
.sambadns
import (
90 create_dns_update_list
95 from samba
.schema
import Schema
96 from samba
.samdb
import SamDB
97 from samba
.dbchecker
import dbcheck
100 DEFAULT_POLICY_GUID
= "31B2F340-016D-11D2-945F-00C04FB984F9"
101 DEFAULT_DC_POLICY_GUID
= "6AC1786C-016F-11D2-945F-00C04fB984F9"
102 DEFAULTSITE
= "Default-First-Site-Name"
103 LAST_PROVISION_USN_ATTRIBUTE
= "lastProvisionUSN"
106 class ProvisionPaths(object):
109 self
.shareconf
= None
120 self
.dns_keytab
= None
123 self
.private_dir
= None
124 self
.state_dir
= None
127 class ProvisionNames(object):
134 self
.ldapmanagerdn
= None
135 self
.dnsdomain
= None
137 self
.netbiosname
= None
144 def find_provision_key_parameters(samdb
, secretsdb
, idmapdb
, paths
, smbconf
,
146 """Get key provision parameters (realm, domain, ...) from a given provision
148 :param samdb: An LDB object connected to the sam.ldb file
149 :param secretsdb: An LDB object connected to the secrets.ldb file
150 :param idmapdb: An LDB object connected to the idmap.ldb file
151 :param paths: A list of path to provision object
152 :param smbconf: Path to the smb.conf file
153 :param lp: A LoadParm object
154 :return: A list of key provision parameters
156 names
= ProvisionNames()
157 names
.adminpass
= None
159 # NT domain, kerberos realm, root dn, domain dn, domain dns name
160 names
.domain
= string
.upper(lp
.get("workgroup"))
161 names
.realm
= lp
.get("realm")
162 names
.dnsdomain
= names
.realm
.lower()
163 basedn
= samba
.dn_from_dns_name(names
.dnsdomain
)
164 names
.realm
= string
.upper(names
.realm
)
166 # Get the netbiosname first (could be obtained from smb.conf in theory)
167 res
= secretsdb
.search(expression
="(flatname=%s)" %
168 names
.domain
,base
="CN=Primary Domains",
169 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
170 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
172 names
.smbconf
= smbconf
174 # That's a bit simplistic but it's ok as long as we have only 3
176 current
= samdb
.search(expression
="(objectClass=*)",
177 base
="", scope
=ldb
.SCOPE_BASE
,
178 attrs
=["defaultNamingContext", "schemaNamingContext",
179 "configurationNamingContext","rootDomainNamingContext"])
181 names
.configdn
= current
[0]["configurationNamingContext"]
182 configdn
= str(names
.configdn
)
183 names
.schemadn
= current
[0]["schemaNamingContext"]
184 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
,
185 current
[0]["defaultNamingContext"][0]))):
186 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
187 "is not the same ..." % (paths
.samdb
,
188 str(current
[0]["defaultNamingContext"][0]),
189 paths
.smbconf
, basedn
)))
191 names
.domaindn
=current
[0]["defaultNamingContext"]
192 names
.rootdn
=current
[0]["rootDomainNamingContext"]
194 res3
= samdb
.search(expression
="(objectClass=site)",
195 base
="CN=Sites," + configdn
, scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn"])
196 names
.sitename
= str(res3
[0]["cn"])
198 # dns hostname and server dn
199 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
200 base
="OU=Domain Controllers,%s" % basedn
,
201 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
202 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("." + names
.dnsdomain
, "")
204 server_res
= samdb
.search(expression
="serverReference=%s" % res4
[0].dn
,
205 attrs
=[], base
=configdn
)
206 names
.serverdn
= server_res
[0].dn
208 # invocation id/objectguid
209 res5
= samdb
.search(expression
="(objectClass=*)",
210 base
="CN=NTDS Settings,%s" % str(names
.serverdn
),
211 scope
=ldb
.SCOPE_BASE
,
212 attrs
=["invocationID", "objectGUID"])
213 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
214 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
217 res6
= samdb
.search(expression
="(objectClass=*)", base
=basedn
,
218 scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID",
219 "objectSid","msDS-Behavior-Version" ])
220 names
.domainguid
= str(ndr_unpack(misc
.GUID
, res6
[0]["objectGUID"][0]))
221 names
.domainsid
= ndr_unpack( security
.dom_sid
, res6
[0]["objectSid"][0])
222 if res6
[0].get("msDS-Behavior-Version") is None or \
223 int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
224 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
226 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
229 res7
= samdb
.search(expression
="(displayName=Default Domain Policy)",
230 base
="CN=Policies,CN=System," + basedn
,
231 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
232 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
234 res8
= samdb
.search(expression
="(displayName=Default Domain Controllers"
236 base
="CN=Policies,CN=System," + basedn
,
237 scope
=ldb
.SCOPE_ONELEVEL
,
238 attrs
=["cn","displayName"])
240 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
242 names
.policyid_dc
= None
244 res9
= idmapdb
.search(expression
="(cn=%s-%s)" %
245 (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
),
246 attrs
=["xidNumber", "type"])
248 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
))
249 if res9
[0]["type"][0] == "ID_TYPE_BOTH":
250 names
.root_gid
= res9
[0]["xidNumber"][0]
252 names
.root_gid
= pwd
.getpwuid(int(res9
[0]["xidNumber"][0])).pw_gid
256 def update_provision_usn(samdb
, low
, high
, id, replace
=False):
257 """Update the field provisionUSN in sam.ldb
259 This field is used to track range of USN modified by provision and
261 This value is used afterward by next provision to figure out if
262 the field have been modified since last provision.
264 :param samdb: An LDB object connect to sam.ldb
265 :param low: The lowest USN modified by this upgrade
266 :param high: The highest USN modified by this upgrade
267 :param id: The invocation id of the samba's dc
268 :param replace: A boolean indicating if the range should replace any
269 existing one or appended (default)
274 entry
= samdb
.search(base
="@PROVISION",
275 scope
=ldb
.SCOPE_BASE
,
276 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "dn"])
277 for e
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
278 if not re
.search(';', e
):
279 e
= "%s;%s" % (e
, id)
282 tab
.append("%s-%s;%s" % (low
, high
, id))
283 delta
= ldb
.Message()
284 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
285 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
286 ldb
.FLAG_MOD_REPLACE
, LAST_PROVISION_USN_ATTRIBUTE
)
287 entry
= samdb
.search(expression
='provisionnerID=*',
288 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
289 attrs
=["provisionnerID"])
290 if len(entry
) == 0 or len(entry
[0]) == 0:
291 delta
["provisionnerID"] = ldb
.MessageElement(id, ldb
.FLAG_MOD_ADD
, "provisionnerID")
295 def set_provision_usn(samdb
, low
, high
, id):
296 """Set the field provisionUSN in sam.ldb
297 This field is used to track range of USN modified by provision and
299 This value is used afterward by next provision to figure out if
300 the field have been modified since last provision.
302 :param samdb: An LDB object connect to sam.ldb
303 :param low: The lowest USN modified by this upgrade
304 :param high: The highest USN modified by this upgrade
305 :param id: The invocationId of the provision"""
308 tab
.append("%s-%s;%s" % (low
, high
, id))
310 delta
= ldb
.Message()
311 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
312 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
313 ldb
.FLAG_MOD_ADD
, LAST_PROVISION_USN_ATTRIBUTE
)
317 def get_max_usn(samdb
,basedn
):
318 """ This function return the biggest USN present in the provision
320 :param samdb: A LDB object pointing to the sam.ldb
321 :param basedn: A string containing the base DN of the provision
323 :return: The biggest USN in the provision"""
325 res
= samdb
.search(expression
="objectClass=*",base
=basedn
,
326 scope
=ldb
.SCOPE_SUBTREE
,attrs
=["uSNChanged"],
327 controls
=["search_options:1:2",
328 "server_sort:1:1:uSNChanged",
329 "paged_results:1:1"])
330 return res
[0]["uSNChanged"]
333 def get_last_provision_usn(sam
):
334 """Get USNs ranges modified by a provision or an upgradeprovision
336 :param sam: An LDB object pointing to the sam.ldb
337 :return: a dictionary which keys are invocation id and values are an array
338 of integer representing the different ranges
341 entry
= sam
.search(expression
="%s=*" % LAST_PROVISION_USN_ATTRIBUTE
,
342 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
343 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "provisionnerID"])
344 except ldb
.LdbError
, (ecode
, emsg
):
345 if ecode
== ldb
.ERR_NO_SUCH_OBJECT
:
352 if entry
[0].get("provisionnerID"):
353 for e
in entry
[0]["provisionnerID"]:
355 for r
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
356 tab1
= str(r
).split(';')
361 if (len(myids
) > 0 and id not in myids
):
363 tab2
= p
.split(tab1
[0])
364 if range.get(id) is None:
366 range[id].append(tab2
[0])
367 range[id].append(tab2
[1])
373 class ProvisionResult(object):
374 """Result of a provision.
376 :ivar server_role: The server role
377 :ivar paths: ProvisionPaths instance
378 :ivar domaindn: The domain dn, as string
382 self
.server_role
= None
389 self
.domainsid
= None
390 self
.adminpass_generated
= None
391 self
.adminpass
= None
392 self
.backend_result
= None
394 def report_logger(self
, logger
):
395 """Report this provision result to a logger."""
397 "Once the above files are installed, your Samba4 server will "
399 if self
.adminpass_generated
:
400 logger
.info("Admin password: %s", self
.adminpass
)
401 logger
.info("Server Role: %s", self
.server_role
)
402 logger
.info("Hostname: %s", self
.names
.hostname
)
403 logger
.info("NetBIOS Domain: %s", self
.names
.domain
)
404 logger
.info("DNS Domain: %s", self
.names
.dnsdomain
)
405 logger
.info("DOMAIN SID: %s", self
.domainsid
)
407 if self
.backend_result
:
408 self
.backend_result
.report_logger(logger
)
411 def check_install(lp
, session_info
, credentials
):
412 """Check whether the current install seems ok.
414 :param lp: Loadparm context
415 :param session_info: Session information
416 :param credentials: Credentials
418 if lp
.get("realm") == "":
419 raise Exception("Realm empty")
420 samdb
= Ldb(lp
.samdb_url(), session_info
=session_info
,
421 credentials
=credentials
, lp
=lp
)
422 if len(samdb
.search("(cn=Administrator)")) != 1:
423 raise ProvisioningError("No administrator account found")
426 def findnss(nssfn
, names
):
427 """Find a user or group from a list of possibilities.
429 :param nssfn: NSS Function to try (should raise KeyError if not found)
430 :param names: Names to check.
431 :return: Value return by first names list.
438 raise KeyError("Unable to find user/group in %r" % names
)
441 findnss_uid
= lambda names
: findnss(pwd
.getpwnam
, names
)[2]
442 findnss_gid
= lambda names
: findnss(grp
.getgrnam
, names
)[2]
445 def provision_paths_from_lp(lp
, dnsdomain
):
446 """Set the default paths for provisioning.
448 :param lp: Loadparm context.
449 :param dnsdomain: DNS Domain name
451 paths
= ProvisionPaths()
452 paths
.private_dir
= lp
.get("private dir")
453 paths
.state_dir
= lp
.get("state directory")
455 # This is stored without path prefix for the "privateKeytab" attribute in
456 # "secrets_dns.ldif".
457 paths
.dns_keytab
= "dns.keytab"
458 paths
.keytab
= "secrets.keytab"
460 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
461 paths
.samdb
= os
.path
.join(paths
.private_dir
, "sam.ldb")
462 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, "idmap.ldb")
463 paths
.secrets
= os
.path
.join(paths
.private_dir
, "secrets.ldb")
464 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
465 paths
.dns
= os
.path
.join(paths
.private_dir
, "dns", dnsdomain
+ ".zone")
466 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
467 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
468 paths
.namedconf
= os
.path
.join(paths
.private_dir
, "named.conf")
469 paths
.namedconf_update
= os
.path
.join(paths
.private_dir
, "named.conf.update")
470 paths
.namedtxt
= os
.path
.join(paths
.private_dir
, "named.txt")
471 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
472 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
473 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
474 paths
.hklm
= "hklm.ldb"
475 paths
.hkcr
= "hkcr.ldb"
476 paths
.hkcu
= "hkcu.ldb"
477 paths
.hku
= "hku.ldb"
478 paths
.hkpd
= "hkpd.ldb"
479 paths
.hkpt
= "hkpt.ldb"
480 paths
.sysvol
= lp
.get("path", "sysvol")
481 paths
.netlogon
= lp
.get("path", "netlogon")
482 paths
.smbconf
= lp
.configfile
486 def determine_netbios_name(hostname
):
487 """Determine a netbios name from a hostname."""
488 # remove forbidden chars and force the length to be <16
489 netbiosname
= "".join([x
for x
in hostname
if is_valid_netbios_char(x
)])
490 return netbiosname
[:MAX_NETBIOS_NAME_LEN
].upper()
493 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
494 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
495 schemadn
=None, serverdn
=None, sitename
=None):
496 """Guess configuration settings to use."""
499 hostname
= socket
.gethostname().split(".")[0]
501 netbiosname
= lp
.get("netbios name")
502 if netbiosname
is None:
503 netbiosname
= determine_netbios_name(hostname
)
504 netbiosname
= netbiosname
.upper()
505 if not valid_netbios_name(netbiosname
):
506 raise InvalidNetbiosName(netbiosname
)
508 if dnsdomain
is None:
509 dnsdomain
= lp
.get("realm")
510 if dnsdomain
is None or dnsdomain
== "":
511 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp
.configfile
)
513 dnsdomain
= dnsdomain
.lower()
515 if serverrole
is None:
516 serverrole
= lp
.get("server role")
517 if serverrole
is None:
518 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
520 serverrole
= serverrole
.lower()
522 realm
= dnsdomain
.upper()
524 if lp
.get("realm") == "":
525 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
527 if lp
.get("realm").upper() != realm
:
528 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
))
530 if lp
.get("server role").lower() != serverrole
:
531 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
))
533 if serverrole
== "active directory domain controller":
535 # This will, for better or worse, default to 'WORKGROUP'
536 domain
= lp
.get("workgroup")
537 domain
= domain
.upper()
539 if lp
.get("workgroup").upper() != domain
:
540 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
))
543 domaindn
= samba
.dn_from_dns_name(dnsdomain
)
545 if domain
== netbiosname
:
546 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain
, netbiosname
))
550 domaindn
= "DC=" + netbiosname
552 if not valid_netbios_name(domain
):
553 raise InvalidNetbiosName(domain
)
555 if hostname
.upper() == realm
:
556 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
557 if netbiosname
.upper() == realm
:
558 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm
, netbiosname
))
560 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
566 configdn
= "CN=Configuration," + rootdn
568 schemadn
= "CN=Schema," + configdn
571 sitename
= DEFAULTSITE
573 names
= ProvisionNames()
574 names
.rootdn
= rootdn
575 names
.domaindn
= domaindn
576 names
.configdn
= configdn
577 names
.schemadn
= schemadn
578 names
.ldapmanagerdn
= "CN=Manager," + rootdn
579 names
.dnsdomain
= dnsdomain
580 names
.domain
= domain
582 names
.netbiosname
= netbiosname
583 names
.hostname
= hostname
584 names
.sitename
= sitename
585 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
586 netbiosname
, sitename
, configdn
)
591 def make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
592 serverrole
=None, eadb
=False, use_ntvfs
=False, lp
=None,
594 """Create a new smb.conf file based on a couple of basic settings.
596 assert smbconf
is not None
599 hostname
= socket
.gethostname().split(".")[0]
601 netbiosname
= determine_netbios_name(hostname
)
603 if serverrole
is None:
604 serverrole
= "standalone server"
606 assert domain
is not None
607 domain
= domain
.upper()
609 assert realm
is not None
610 realm
= realm
.upper()
613 "netbios name": netbiosname
,
616 "server role": serverrole
,
620 lp
= samba
.param
.LoadParm()
621 #Load non-existent file
622 if os
.path
.exists(smbconf
):
625 if global_param
is not None:
626 for ent
in global_param
:
627 if global_param
[ent
] is not None:
628 global_settings
[ent
] = " ".join(global_param
[ent
])
630 if targetdir
is not None:
631 global_settings
["private dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "private"))
632 global_settings
["lock dir"] = os
.path
.abspath(targetdir
)
633 global_settings
["state directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "state"))
634 global_settings
["cache directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "cache"))
636 lp
.set("lock dir", os
.path
.abspath(targetdir
))
637 lp
.set("state directory", global_settings
["state directory"])
638 lp
.set("cache directory", global_settings
["cache directory"])
641 if use_ntvfs
and not lp
.get("posix:eadb"):
642 if targetdir
is not None:
643 privdir
= os
.path
.join(targetdir
, "private")
645 privdir
= lp
.get("private dir")
646 lp
.set("posix:eadb", os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
647 elif not use_ntvfs
and not lp
.get("xattr_tdb:file"):
648 if targetdir
is not None:
649 statedir
= os
.path
.join(targetdir
, "state")
651 statedir
= lp
.get("state directory")
652 lp
.set("xattr_tdb:file", os
.path
.abspath(os
.path
.join(statedir
, "xattr.tdb")))
655 if serverrole
== "active directory domain controller":
656 shares
["sysvol"] = os
.path
.join(lp
.get("state directory"), "sysvol")
657 shares
["netlogon"] = os
.path
.join(shares
["sysvol"], realm
.lower(),
660 global_settings
["passdb backend"] = "samba_dsdb"
662 f
= open(smbconf
, 'w')
664 f
.write("[globals]\n")
665 for key
, val
in global_settings
.iteritems():
666 f
.write("\t%s = %s\n" % (key
, val
))
669 for name
, path
in shares
.iteritems():
670 f
.write("[%s]\n" % name
)
671 f
.write("\tpath = %s\n" % path
)
672 f
.write("\tread only = no\n")
676 # reload the smb.conf
679 # and dump it without any values that are the default
680 # this ensures that any smb.conf parameters that were set
681 # on the provision/join command line are set in the resulting smb.conf
682 f
= open(smbconf
, mode
='w')
689 def setup_name_mappings(idmap
, sid
, root_uid
, nobody_uid
,
690 users_gid
, root_gid
):
691 """setup reasonable name mappings for sam names to unix names.
693 :param samdb: SamDB object.
694 :param idmap: IDmap db object.
695 :param sid: The domain sid.
696 :param domaindn: The domain DN.
697 :param root_uid: uid of the UNIX root user.
698 :param nobody_uid: uid of the UNIX nobody user.
699 :param users_gid: gid of the UNIX users group.
700 :param root_gid: gid of the UNIX root group.
702 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
704 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
705 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
708 def setup_samdb_partitions(samdb_path
, logger
, lp
, session_info
,
709 provision_backend
, names
, schema
, serverrole
,
711 """Setup the partitions for the SAM database.
713 Alternatively, provision() may call this, and then populate the database.
715 :note: This will wipe the Sam Database!
717 :note: This function always removes the local SAM LDB file. The erase
718 parameter controls whether to erase the existing data, which
719 may not be stored locally but in LDAP.
722 assert session_info
is not None
724 # We use options=["modules:"] to stop the modules loading - we
725 # just want to wipe and re-initialise the database, not start it up
728 os
.unlink(samdb_path
)
732 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
733 lp
=lp
, options
=["modules:"])
735 ldap_backend_line
= "# No LDAP backend"
736 if provision_backend
.type != "ldb":
737 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldap_uri
739 samdb
.transaction_start()
741 logger
.info("Setting up sam.ldb partitions and settings")
742 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
743 "LDAP_BACKEND_LINE": ldap_backend_line
747 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
748 "BACKEND_TYPE": provision_backend
.type,
749 "SERVER_ROLE": serverrole
752 logger
.info("Setting up sam.ldb rootDSE")
753 setup_samdb_rootdse(samdb
, names
)
755 samdb
.transaction_cancel()
758 samdb
.transaction_commit()
761 def secretsdb_self_join(secretsdb
, domain
,
762 netbiosname
, machinepass
, domainsid
=None,
763 realm
=None, dnsdomain
=None,
765 key_version_number
=1,
766 secure_channel_type
=SEC_CHAN_WKSTA
):
767 """Add domain join-specific bits to a secrets database.
769 :param secretsdb: Ldb Handle to the secrets database
770 :param machinepass: Machine password
772 attrs
= ["whenChanged",
779 if realm
is not None:
780 if dnsdomain
is None:
781 dnsdomain
= realm
.lower()
782 dnsname
= '%s.%s' % (netbiosname
.lower(), dnsdomain
.lower())
785 shortname
= netbiosname
.lower()
787 # We don't need to set msg["flatname"] here, because rdn_name will handle
788 # it, and it causes problems for modifies anyway
789 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
790 msg
["secureChannelType"] = [str(secure_channel_type
)]
791 msg
["objectClass"] = ["top", "primaryDomain"]
792 if dnsname
is not None:
793 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
794 msg
["realm"] = [realm
]
795 msg
["saltPrincipal"] = ["host/%s@%s" % (dnsname
, realm
.upper())]
796 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
797 msg
["privateKeytab"] = ["secrets.keytab"]
799 msg
["secret"] = [machinepass
]
800 msg
["samAccountName"] = ["%s$" % netbiosname
]
801 msg
["secureChannelType"] = [str(secure_channel_type
)]
802 if domainsid
is not None:
803 msg
["objectSid"] = [ndr_pack(domainsid
)]
805 # This complex expression tries to ensure that we don't have more
806 # than one record for this SID, realm or netbios domain at a time,
807 # but we don't delete the old record that we are about to modify,
808 # because that would delete the keytab and previous password.
809 res
= secretsdb
.search(base
="cn=Primary Domains", attrs
=attrs
,
810 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain
, realm
, str(domainsid
), str(msg
.dn
))),
811 scope
=ldb
.SCOPE_ONELEVEL
)
814 secretsdb
.delete(del_msg
.dn
)
816 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
819 msg
["priorSecret"] = [res
[0]["secret"][0]]
820 msg
["priorWhenChanged"] = [res
[0]["whenChanged"][0]]
823 msg
["privateKeytab"] = [res
[0]["privateKeytab"][0]]
828 msg
["krb5Keytab"] = [res
[0]["krb5Keytab"][0]]
834 msg
[el
].set_flags(ldb
.FLAG_MOD_REPLACE
)
835 secretsdb
.modify(msg
)
836 secretsdb
.rename(res
[0].dn
, msg
.dn
)
838 spn
= [ 'HOST/%s' % shortname
]
839 if secure_channel_type
== SEC_CHAN_BDC
and dnsname
is not None:
840 # we are a domain controller then we add servicePrincipalName
841 # entries for the keytab code to update.
842 spn
.extend([ 'HOST/%s' % dnsname
])
843 msg
["servicePrincipalName"] = spn
848 def setup_secretsdb(paths
, session_info
, backend_credentials
, lp
):
849 """Setup the secrets database.
851 :note: This function does not handle exceptions and transaction on purpose,
852 it's up to the caller to do this job.
854 :param path: Path to the secrets database.
855 :param session_info: Session info.
856 :param credentials: Credentials
857 :param lp: Loadparm context
858 :return: LDB handle for the created secrets database
860 if os
.path
.exists(paths
.secrets
):
861 os
.unlink(paths
.secrets
)
863 keytab_path
= os
.path
.join(paths
.private_dir
, paths
.keytab
)
864 if os
.path
.exists(keytab_path
):
865 os
.unlink(keytab_path
)
867 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
868 if os
.path
.exists(dns_keytab_path
):
869 os
.unlink(dns_keytab_path
)
873 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
875 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
876 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
877 secrets_ldb
.transaction_start()
879 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
881 if (backend_credentials
is not None and
882 backend_credentials
.authentication_requested()):
883 if backend_credentials
.get_bind_dn() is not None:
884 setup_add_ldif(secrets_ldb
,
885 setup_path("secrets_simple_ldap.ldif"), {
886 "LDAPMANAGERDN": backend_credentials
.get_bind_dn(),
887 "LDAPMANAGERPASS_B64": b64encode(backend_credentials
.get_password())
890 setup_add_ldif(secrets_ldb
,
891 setup_path("secrets_sasl_ldap.ldif"), {
892 "LDAPADMINUSER": backend_credentials
.get_username(),
893 "LDAPADMINREALM": backend_credentials
.get_realm(),
894 "LDAPADMINPASS_B64": b64encode(backend_credentials
.get_password())
897 secrets_ldb
.transaction_cancel()
902 def setup_privileges(path
, session_info
, lp
):
903 """Setup the privileges database.
905 :param path: Path to the privileges database.
906 :param session_info: Session info.
907 :param credentials: Credentials
908 :param lp: Loadparm context
909 :return: LDB handle for the created secrets database
911 if os
.path
.exists(path
):
913 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
914 privilege_ldb
.erase()
915 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
918 def setup_registry(path
, session_info
, lp
):
919 """Setup the registry.
921 :param path: Path to the registry database
922 :param session_info: Session information
923 :param credentials: Credentials
924 :param lp: Loadparm context
926 reg
= samba
.registry
.Registry()
927 hive
= samba
.registry
.open_ldb(path
, session_info
=session_info
, lp_ctx
=lp
)
928 reg
.mount_hive(hive
, samba
.registry
.HKEY_LOCAL_MACHINE
)
929 provision_reg
= setup_path("provision.reg")
930 assert os
.path
.exists(provision_reg
)
931 reg
.diff_apply(provision_reg
)
934 def setup_idmapdb(path
, session_info
, lp
):
935 """Setup the idmap database.
937 :param path: path to the idmap database
938 :param session_info: Session information
939 :param credentials: Credentials
940 :param lp: Loadparm context
942 if os
.path
.exists(path
):
945 idmap_ldb
= IDmapDB(path
, session_info
=session_info
, lp
=lp
)
947 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
951 def setup_samdb_rootdse(samdb
, names
):
952 """Setup the SamDB rootdse.
954 :param samdb: Sam Database handle
956 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
957 "SCHEMADN": names
.schemadn
,
958 "DOMAINDN": names
.domaindn
,
959 "ROOTDN" : names
.rootdn
,
960 "CONFIGDN": names
.configdn
,
961 "SERVERDN": names
.serverdn
,
965 def setup_self_join(samdb
, admin_session_info
, names
, fill
, machinepass
,
966 dns_backend
, dnspass
, domainsid
, next_rid
, invocationid
,
967 policyguid
, policyguid_dc
,
968 domainControllerFunctionality
, ntdsguid
=None, dc_rid
=None):
969 """Join a host to its own domain."""
970 assert isinstance(invocationid
, str)
971 if ntdsguid
is not None:
972 ntdsguid_line
= "objectGUID: %s\n"%ntdsguid
979 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
980 "CONFIGDN": names
.configdn
,
981 "SCHEMADN": names
.schemadn
,
982 "DOMAINDN": names
.domaindn
,
983 "SERVERDN": names
.serverdn
,
984 "INVOCATIONID": invocationid
,
985 "NETBIOSNAME": names
.netbiosname
,
986 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
987 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
988 "DOMAINSID": str(domainsid
),
989 "DCRID": str(dc_rid
),
990 "SAMBA_VERSION_STRING": version
,
991 "NTDSGUID": ntdsguid_line
,
992 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
993 domainControllerFunctionality
),
994 "RIDALLOCATIONSTART": str(next_rid
+ 100),
995 "RIDALLOCATIONEND": str(next_rid
+ 100 + 499)})
997 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
998 "POLICYGUID": policyguid
,
999 "POLICYGUID_DC": policyguid_dc
,
1000 "DNSDOMAIN": names
.dnsdomain
,
1001 "DOMAINDN": names
.domaindn
})
1003 # If we are setting up a subdomain, then this has been replicated in, so we
1004 # don't need to add it
1005 if fill
== FILL_FULL
:
1006 setup_add_ldif(samdb
, setup_path("provision_self_join_config.ldif"), {
1007 "CONFIGDN": names
.configdn
,
1008 "SCHEMADN": names
.schemadn
,
1009 "DOMAINDN": names
.domaindn
,
1010 "SERVERDN": names
.serverdn
,
1011 "INVOCATIONID": invocationid
,
1012 "NETBIOSNAME": names
.netbiosname
,
1013 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1014 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1015 "DOMAINSID": str(domainsid
),
1016 "DCRID": str(dc_rid
),
1017 "SAMBA_VERSION_STRING": version
,
1018 "NTDSGUID": ntdsguid_line
,
1019 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1020 domainControllerFunctionality
)})
1022 # Setup fSMORoleOwner entries to point at the newly created DC entry
1023 setup_modify_ldif(samdb
,
1024 setup_path("provision_self_join_modify_config.ldif"), {
1025 "CONFIGDN": names
.configdn
,
1026 "SCHEMADN": names
.schemadn
,
1027 "DEFAULTSITE": names
.sitename
,
1028 "NETBIOSNAME": names
.netbiosname
,
1029 "SERVERDN": names
.serverdn
,
1032 system_session_info
= system_session()
1033 samdb
.set_session_info(system_session_info
)
1034 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1035 # modify a serverReference under cn=config when we are a subdomain, we must
1036 # be system due to ACLs
1037 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
1038 "DOMAINDN": names
.domaindn
,
1039 "SERVERDN": names
.serverdn
,
1040 "NETBIOSNAME": names
.netbiosname
,
1043 samdb
.set_session_info(admin_session_info
)
1045 if dns_backend
!= "SAMBA_INTERNAL":
1046 # This is Samba4 specific and should be replaced by the correct
1047 # DNS AD-style setup
1048 setup_add_ldif(samdb
, setup_path("provision_dns_add_samba.ldif"), {
1049 "DNSDOMAIN": names
.dnsdomain
,
1050 "DOMAINDN": names
.domaindn
,
1051 "DNSPASS_B64": b64encode(dnspass
.encode('utf-16-le')),
1052 "HOSTNAME" : names
.hostname
,
1053 "DNSNAME" : '%s.%s' % (
1054 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
1058 def getpolicypath(sysvolpath
, dnsdomain
, guid
):
1059 """Return the physical path of policy given its guid.
1061 :param sysvolpath: Path to the sysvol folder
1062 :param dnsdomain: DNS name of the AD domain
1063 :param guid: The GUID of the policy
1064 :return: A string with the complete path to the policy folder
1067 guid
= "{%s}" % guid
1068 policy_path
= os
.path
.join(sysvolpath
, dnsdomain
, "Policies", guid
)
1072 def create_gpo_struct(policy_path
):
1073 if not os
.path
.exists(policy_path
):
1074 os
.makedirs(policy_path
, 0775)
1075 f
= open(os
.path
.join(policy_path
, "GPT.INI"), 'w')
1077 f
.write("[General]\r\nVersion=0")
1080 p
= os
.path
.join(policy_path
, "MACHINE")
1081 if not os
.path
.exists(p
):
1082 os
.makedirs(p
, 0775)
1083 p
= os
.path
.join(policy_path
, "USER")
1084 if not os
.path
.exists(p
):
1085 os
.makedirs(p
, 0775)
1088 def create_default_gpo(sysvolpath
, dnsdomain
, policyguid
, policyguid_dc
):
1089 """Create the default GPO for a domain
1091 :param sysvolpath: Physical path for the sysvol folder
1092 :param dnsdomain: DNS domain name of the AD domain
1093 :param policyguid: GUID of the default domain policy
1094 :param policyguid_dc: GUID of the default domain controler policy
1096 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid
)
1097 create_gpo_struct(policy_path
)
1099 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid_dc
)
1100 create_gpo_struct(policy_path
)
1103 def setup_samdb(path
, session_info
, provision_backend
, lp
, names
,
1104 logger
, fill
, serverrole
, schema
, am_rodc
=False):
1105 """Setup a complete SAM Database.
1107 :note: This will wipe the main SAM database file!
1110 # Also wipes the database
1111 setup_samdb_partitions(path
, logger
=logger
, lp
=lp
,
1112 provision_backend
=provision_backend
, session_info
=session_info
,
1113 names
=names
, serverrole
=serverrole
, schema
=schema
)
1115 # Load the database, but don's load the global schema and don't connect
1117 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
1118 credentials
=provision_backend
.credentials
, lp
=lp
,
1119 global_schema
=False, am_rodc
=am_rodc
)
1121 logger
.info("Pre-loading the Samba 4 and AD schema")
1123 # Load the schema from the one we computed earlier
1124 samdb
.set_schema(schema
, write_indices_and_attributes
=False)
1126 # Set the NTDS settings DN manually - in order to have it already around
1127 # before the provisioned tree exists and we connect
1128 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1130 # And now we can connect to the DB - the schema won't be loaded from the
1134 # But we have to give it one more kick to have it use the schema
1135 # during provision - it needs, now that it is connected, to write
1136 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1137 samdb
.set_schema(schema
, write_indices_and_attributes
=True)
1142 def fill_samdb(samdb
, lp
, names
, logger
, domainsid
, domainguid
, policyguid
,
1143 policyguid_dc
, fill
, adminpass
, krbtgtpass
, machinepass
, dns_backend
,
1144 dnspass
, invocationid
, ntdsguid
, serverrole
, am_rodc
=False,
1145 dom_for_fun_level
=None, schema
=None, next_rid
=None, dc_rid
=None):
1147 if next_rid
is None:
1150 # Provision does not make much sense values larger than 1000000000
1151 # as the upper range of the rIDAvailablePool is 1073741823 and
1152 # we don't want to create a domain that cannot allocate rids.
1153 if next_rid
< 1000 or next_rid
> 1000000000:
1154 error
= "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid
)
1155 error
+= "the valid range is %u-%u. The default is %u." % (
1156 1000, 1000000000, 1000)
1157 raise ProvisioningError(error
)
1159 # ATTENTION: Do NOT change these default values without discussion with the
1160 # team and/or release manager. They have a big impact on the whole program!
1161 domainControllerFunctionality
= DS_DOMAIN_FUNCTION_2008_R2
1163 if dom_for_fun_level
is None:
1164 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2003
1166 if dom_for_fun_level
> domainControllerFunctionality
:
1167 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!")
1169 domainFunctionality
= dom_for_fun_level
1170 forestFunctionality
= dom_for_fun_level
1172 # Set the NTDS settings DN manually - in order to have it already around
1173 # before the provisioned tree exists and we connect
1174 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1176 samdb
.transaction_start()
1178 # Set the domain functionality levels onto the database.
1179 # Various module (the password_hash module in particular) need
1180 # to know what level of AD we are emulating.
1182 # These will be fixed into the database via the database
1183 # modifictions below, but we need them set from the start.
1184 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
1185 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
1186 samdb
.set_opaque_integer("domainControllerFunctionality",
1187 domainControllerFunctionality
)
1189 samdb
.set_domain_sid(str(domainsid
))
1190 samdb
.set_invocation_id(invocationid
)
1192 logger
.info("Adding DomainDN: %s" % names
.domaindn
)
1194 # impersonate domain admin
1195 admin_session_info
= admin_session(lp
, str(domainsid
))
1196 samdb
.set_session_info(admin_session_info
)
1197 if domainguid
is not None:
1198 domainguid_line
= "objectGUID: %s\n-" % domainguid
1200 domainguid_line
= ""
1202 descr
= b64encode(get_domain_descriptor(domainsid
))
1203 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
1204 "DOMAINDN": names
.domaindn
,
1205 "DOMAINSID": str(domainsid
),
1206 "DESCRIPTOR": descr
,
1207 "DOMAINGUID": domainguid_line
1210 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
1211 "DOMAINDN": names
.domaindn
,
1212 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1213 "NEXTRID": str(next_rid
),
1214 "DEFAULTSITE": names
.sitename
,
1215 "CONFIGDN": names
.configdn
,
1216 "POLICYGUID": policyguid
,
1217 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1218 "SAMBA_VERSION_STRING": version
1221 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1222 if fill
== FILL_FULL
:
1223 logger
.info("Adding configuration container")
1224 descr
= b64encode(get_config_descriptor(domainsid
))
1225 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
1226 "CONFIGDN": names
.configdn
,
1227 "DESCRIPTOR": descr
,
1230 # The LDIF here was created when the Schema object was constructed
1231 logger
.info("Setting up sam.ldb schema")
1232 samdb
.add_ldif(schema
.schema_dn_add
, controls
=["relax:0"])
1233 samdb
.modify_ldif(schema
.schema_dn_modify
)
1234 samdb
.write_prefixes_from_schema()
1235 samdb
.add_ldif(schema
.schema_data
, controls
=["relax:0"])
1236 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
1237 {"SCHEMADN": names
.schemadn
})
1239 # Now register this container in the root of the forest
1240 msg
= ldb
.Message(ldb
.Dn(samdb
, names
.domaindn
))
1241 msg
["subRefs"] = ldb
.MessageElement(names
.configdn
, ldb
.FLAG_MOD_ADD
,
1245 samdb
.transaction_cancel()
1248 samdb
.transaction_commit()
1250 samdb
.transaction_start()
1252 samdb
.invocation_id
= invocationid
1254 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1255 if fill
== FILL_FULL
:
1256 logger
.info("Setting up sam.ldb configuration data")
1257 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
1258 "CONFIGDN": names
.configdn
,
1259 "NETBIOSNAME": names
.netbiosname
,
1260 "DEFAULTSITE": names
.sitename
,
1261 "DNSDOMAIN": names
.dnsdomain
,
1262 "DOMAIN": names
.domain
,
1263 "SCHEMADN": names
.schemadn
,
1264 "DOMAINDN": names
.domaindn
,
1265 "SERVERDN": names
.serverdn
,
1266 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
1267 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1270 logger
.info("Setting up display specifiers")
1271 display_specifiers_ldif
= read_ms_ldif(
1272 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1273 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
,
1274 {"CONFIGDN": names
.configdn
})
1275 check_all_substituted(display_specifiers_ldif
)
1276 samdb
.add_ldif(display_specifiers_ldif
)
1278 logger
.info("Adding users container")
1279 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
1280 "DOMAINDN": names
.domaindn
})
1281 logger
.info("Modifying users container")
1282 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
1283 "DOMAINDN": names
.domaindn
})
1284 logger
.info("Adding computers container")
1285 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
1286 "DOMAINDN": names
.domaindn
})
1287 logger
.info("Modifying computers container")
1288 setup_modify_ldif(samdb
,
1289 setup_path("provision_computers_modify.ldif"), {
1290 "DOMAINDN": names
.domaindn
})
1291 logger
.info("Setting up sam.ldb data")
1292 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1293 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1294 "DOMAINDN": names
.domaindn
,
1295 "NETBIOSNAME": names
.netbiosname
,
1296 "DEFAULTSITE": names
.sitename
,
1297 "CONFIGDN": names
.configdn
,
1298 "SERVERDN": names
.serverdn
,
1299 "RIDAVAILABLESTART": str(next_rid
+ 600),
1300 "POLICYGUID_DC": policyguid_dc
1303 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1304 if fill
== FILL_FULL
:
1305 setup_modify_ldif(samdb
,
1306 setup_path("provision_configuration_references.ldif"), {
1307 "CONFIGDN": names
.configdn
,
1308 "SCHEMADN": names
.schemadn
})
1310 logger
.info("Setting up well known security principals")
1311 setup_add_ldif(samdb
, setup_path("provision_well_known_sec_princ.ldif"), {
1312 "CONFIGDN": names
.configdn
,
1315 if fill
== FILL_FULL
or fill
== FILL_SUBDOMAIN
:
1316 setup_modify_ldif(samdb
,
1317 setup_path("provision_basedn_references.ldif"),
1318 {"DOMAINDN": names
.domaindn
})
1320 logger
.info("Setting up sam.ldb users and groups")
1321 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1322 "DOMAINDN": names
.domaindn
,
1323 "DOMAINSID": str(domainsid
),
1324 "ADMINPASS_B64": b64encode(adminpass
.encode('utf-16-le')),
1325 "KRBTGTPASS_B64": b64encode(krbtgtpass
.encode('utf-16-le'))
1328 logger
.info("Setting up self join")
1329 setup_self_join(samdb
, admin_session_info
, names
=names
, fill
=fill
,
1330 invocationid
=invocationid
,
1331 dns_backend
=dns_backend
,
1333 machinepass
=machinepass
,
1334 domainsid
=domainsid
,
1337 policyguid
=policyguid
,
1338 policyguid_dc
=policyguid_dc
,
1339 domainControllerFunctionality
=domainControllerFunctionality
,
1342 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1343 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1344 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
)
1345 assert isinstance(names
.ntdsguid
, str)
1347 samdb
.transaction_cancel()
1350 samdb
.transaction_commit()
1355 FILL_SUBDOMAIN
= "SUBDOMAIN"
1356 FILL_NT4SYNC
= "NT4SYNC"
1358 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1359 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)"
1362 def set_dir_acl(path
, acl
, lp
, domsid
, use_ntvfs
, passdb
):
1363 setntacl(lp
, path
, acl
, domsid
, use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1364 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1366 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1367 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1369 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1370 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1373 def set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
):
1374 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1377 :param sysvol: Physical path for the sysvol folder
1378 :param dnsdomain: The DNS name of the domain
1379 :param domainsid: The SID of the domain
1380 :param domaindn: The DN of the domain (ie. DC=...)
1381 :param samdb: An LDB object on the SAM db
1382 :param lp: an LP object
1385 # Set ACL for GPO root folder
1386 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1387 setntacl(lp
, root_policy_path
, POLICIES_ACL
, str(domainsid
),
1388 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1390 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1391 attrs
=["cn", "nTSecurityDescriptor"],
1392 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1395 acl
= ndr_unpack(security
.descriptor
,
1396 str(policy
["nTSecurityDescriptor"])).as_sddl()
1397 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1398 set_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1399 str(domainsid
), use_ntvfs
,
1403 def setsysvolacl(samdb
, netlogon
, sysvol
, uid
, gid
, domainsid
, dnsdomain
,
1404 domaindn
, lp
, use_ntvfs
):
1405 """Set the ACL for the sysvol share and the subfolders
1407 :param samdb: An LDB object on the SAM db
1408 :param netlogon: Physical path for the netlogon folder
1409 :param sysvol: Physical path for the sysvol folder
1410 :param uid: The UID of the "Administrator" user
1411 :param gid: The GID of the "Domain adminstrators" group
1412 :param domainsid: The SID of the domain
1413 :param dnsdomain: The DNS name of the domain
1414 :param domaindn: The DN of the domain (ie. DC=...)
1419 # This will ensure that the smbd code we are running when setting ACLs
1420 # is initialised with the smb.conf
1421 s3conf
= s3param
.get_context()
1422 s3conf
.load(lp
.configfile
)
1423 # ensure we are using the right samba_dsdb passdb backend, no matter what
1424 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1425 passdb
.reload_static_pdb()
1427 # ensure that we init the samba_dsdb backend, so the domain sid is
1428 # marked in secrets.tdb
1429 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1431 # now ensure everything matches correctly, to avoid wierd issues
1432 if passdb
.get_global_sam_sid() != domainsid
:
1433 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
))
1435 domain_info
= s4_passdb
.domain_info()
1436 if domain_info
["dom_sid"] != domainsid
:
1437 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
))
1439 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1440 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()))
1445 os
.chown(sysvol
, -1, gid
)
1451 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1452 setntacl(lp
,sysvol
, SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=s4_passdb
)
1453 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1455 if use_ntvfs
and canchown
:
1456 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1457 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=s4_passdb
)
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 # Set acls on Policy folder and policies folders
1464 set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
=s4_passdb
)
1466 def acl_type(direct_db_access
):
1467 if direct_db_access
:
1472 def check_dir_acl(path
, acl
, lp
, domainsid
, direct_db_access
):
1473 fsacl
= getntacl(lp
, path
, direct_db_access
=direct_db_access
)
1474 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1475 if fsacl_sddl
!= acl
:
1476 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
))
1478 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1480 fsacl
= getntacl(lp
, os
.path
.join(root
, name
), direct_db_access
=direct_db_access
)
1482 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1483 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1484 if fsacl_sddl
!= acl
:
1485 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
))
1488 fsacl
= getntacl(lp
, os
.path
.join(root
, name
), direct_db_access
=direct_db_access
)
1490 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1491 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1492 if fsacl_sddl
!= acl
:
1493 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
))
1496 def check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1498 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1501 :param sysvol: Physical path for the sysvol folder
1502 :param dnsdomain: The DNS name of the domain
1503 :param domainsid: The SID of the domain
1504 :param domaindn: The DN of the domain (ie. DC=...)
1505 :param samdb: An LDB object on the SAM db
1506 :param lp: an LP object
1509 # Set ACL for GPO root folder
1510 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1511 fsacl
= getntacl(lp
, root_policy_path
, direct_db_access
=direct_db_access
)
1513 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access
), root_policy_path
))
1514 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1515 if fsacl_sddl
!= POLICIES_ACL
:
1516 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
))
1517 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1518 attrs
=["cn", "nTSecurityDescriptor"],
1519 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1522 acl
= ndr_unpack(security
.descriptor
,
1523 str(policy
["nTSecurityDescriptor"])).as_sddl()
1524 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1525 check_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1526 domainsid
, direct_db_access
)
1529 def checksysvolacl(samdb
, netlogon
, sysvol
, domainsid
, dnsdomain
, domaindn
,
1531 """Set the ACL for the sysvol share and the subfolders
1533 :param samdb: An LDB object on the SAM db
1534 :param netlogon: Physical path for the netlogon folder
1535 :param sysvol: Physical path for the sysvol folder
1536 :param uid: The UID of the "Administrator" user
1537 :param gid: The GID of the "Domain adminstrators" group
1538 :param domainsid: The SID of the domain
1539 :param dnsdomain: The DNS name of the domain
1540 :param domaindn: The DN of the domain (ie. DC=...)
1543 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1544 s3conf
= s3param
.get_context()
1545 s3conf
.load(lp
.configfile
)
1546 # ensure we are using the right samba_dsdb passdb backend, no matter what
1547 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1548 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1549 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1551 # now ensure everything matches correctly, to avoid wierd issues
1552 if passdb
.get_global_sam_sid() != domainsid
:
1553 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
))
1555 domain_info
= s4_passdb
.domain_info()
1556 if domain_info
["dom_sid"] != domainsid
:
1557 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
))
1559 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1560 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()))
1562 # Ensure we can read this directly, and via the smbd VFS
1563 for direct_db_access
in [True, False]:
1564 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1565 for dir_path
in [os
.path
.join(sysvol
, dnsdomain
), netlogon
]:
1566 fsacl
= getntacl(lp
, dir_path
, direct_db_access
=direct_db_access
)
1568 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access
), dir_path
))
1569 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1570 if fsacl_sddl
!= SYSVOL_ACL
:
1571 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
))
1573 # Check acls on Policy folder and policies folders
1574 check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1578 def interface_ips_v4(lp
):
1579 """return only IPv4 IPs"""
1580 ips
= samba
.interface_ips(lp
, False)
1583 if i
.find(':') == -1:
1588 def interface_ips_v6(lp
, linklocal
=False):
1589 """return only IPv6 IPs"""
1590 ips
= samba
.interface_ips(lp
, False)
1593 if i
.find(':') != -1 and (linklocal
or i
.find('%') == -1):
1598 def provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
1599 domainsid
, schema
=None,
1600 targetdir
=None, samdb_fill
=FILL_FULL
,
1601 hostip
=None, hostip6
=None,
1602 next_rid
=1000, dc_rid
=None, adminpass
=None, krbtgtpass
=None,
1603 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1604 invocationid
=None, machinepass
=None, ntdsguid
=None,
1605 dns_backend
=None, dnspass
=None,
1606 serverrole
=None, dom_for_fun_level
=None,
1607 am_rodc
=False, lp
=None, use_ntvfs
=False, skip_sysvolacl
=False):
1608 # create/adapt the group policy GUIDs
1609 # Default GUID for default policy are described at
1610 # "How Core Group Policy Works"
1611 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1612 if policyguid
is None:
1613 policyguid
= DEFAULT_POLICY_GUID
1614 policyguid
= policyguid
.upper()
1615 if policyguid_dc
is None:
1616 policyguid_dc
= DEFAULT_DC_POLICY_GUID
1617 policyguid_dc
= policyguid_dc
.upper()
1619 if invocationid
is None:
1620 invocationid
= str(uuid
.uuid4())
1622 if krbtgtpass
is None:
1623 krbtgtpass
= samba
.generate_random_password(128, 255)
1624 if machinepass
is None:
1625 machinepass
= samba
.generate_random_password(128, 255)
1627 dnspass
= samba
.generate_random_password(128, 255)
1629 samdb
= fill_samdb(samdb
, lp
, names
, logger
=logger
,
1630 domainsid
=domainsid
, schema
=schema
, domainguid
=domainguid
,
1631 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1632 fill
=samdb_fill
, adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1633 invocationid
=invocationid
, machinepass
=machinepass
,
1634 dns_backend
=dns_backend
, dnspass
=dnspass
,
1635 ntdsguid
=ntdsguid
, serverrole
=serverrole
,
1636 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
1637 next_rid
=next_rid
, dc_rid
=dc_rid
)
1639 if serverrole
== "active directory domain controller":
1641 # Set up group policies (domain policy and domain controller
1643 create_default_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
,
1645 if not skip_sysvolacl
:
1646 setsysvolacl(samdb
, paths
.netlogon
, paths
.sysvol
, paths
.root_uid
,
1647 paths
.root_gid
, domainsid
, names
.dnsdomain
,
1648 names
.domaindn
, lp
, use_ntvfs
)
1650 logger
.info("Setting acl on sysvol skipped")
1652 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1653 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1654 netbiosname
=names
.netbiosname
, domainsid
=domainsid
,
1655 machinepass
=machinepass
, secure_channel_type
=SEC_CHAN_BDC
)
1657 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1658 # In future, this might be determined from some configuration
1659 kerberos_enctypes
= str(ENC_ALL_TYPES
)
1662 msg
= ldb
.Message(ldb
.Dn(samdb
,
1663 samdb
.searchone("distinguishedName",
1664 expression
="samAccountName=%s$" % names
.netbiosname
,
1665 scope
=ldb
.SCOPE_SUBTREE
)))
1666 msg
["msDS-SupportedEncryptionTypes"] = ldb
.MessageElement(
1667 elements
=kerberos_enctypes
, flags
=ldb
.FLAG_MOD_REPLACE
,
1668 name
="msDS-SupportedEncryptionTypes")
1670 except ldb
.LdbError
, (enum
, estr
):
1671 if enum
!= ldb
.ERR_NO_SUCH_ATTRIBUTE
:
1672 # It might be that this attribute does not exist in this schema
1675 setup_ad_dns(samdb
, secrets_ldb
, domainsid
, names
, paths
, lp
, logger
,
1676 hostip
=hostip
, hostip6
=hostip6
, dns_backend
=dns_backend
,
1677 dnspass
=dnspass
, os_level
=dom_for_fun_level
,
1678 targetdir
=targetdir
, site
=DEFAULTSITE
)
1680 domainguid
= samdb
.searchone(basedn
=samdb
.get_default_basedn(),
1681 attribute
="objectGUID")
1682 assert isinstance(domainguid
, str)
1684 lastProvisionUSNs
= get_last_provision_usn(samdb
)
1685 maxUSN
= get_max_usn(samdb
, str(names
.rootdn
))
1686 if lastProvisionUSNs
is not None:
1687 update_provision_usn(samdb
, 0, maxUSN
, invocationid
, 1)
1689 set_provision_usn(samdb
, 0, maxUSN
, invocationid
)
1691 logger
.info("Setting up sam.ldb rootDSE marking as synchronized")
1692 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"),
1693 { 'NTDSGUID' : names
.ntdsguid
})
1695 # fix any dangling GUIDs from the provision
1696 logger
.info("Fixing provision GUIDs")
1697 chk
= dbcheck(samdb
, samdb_schema
=samdb
, verbose
=False, fix
=True, yes
=True,
1699 samdb
.transaction_start()
1701 # a small number of GUIDs are missing because of ordering issues in the
1703 for schema_obj
in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1704 chk
.check_database(DN
="%s,%s" % (schema_obj
, names
.schemadn
),
1705 scope
=ldb
.SCOPE_BASE
,
1706 attrs
=['defaultObjectCategory'])
1707 chk
.check_database(DN
="CN=IP Security,CN=System,%s" % names
.domaindn
,
1708 scope
=ldb
.SCOPE_ONELEVEL
,
1709 attrs
=['ipsecOwnersReference',
1710 'ipsecFilterReference',
1711 'ipsecISAKMPReference',
1712 'ipsecNegotiationPolicyReference',
1713 'ipsecNFAReference'])
1715 samdb
.transaction_cancel()
1718 samdb
.transaction_commit()
1722 "ROLE_STANDALONE": "standalone server",
1723 "ROLE_DOMAIN_MEMBER": "member server",
1724 "ROLE_DOMAIN_BDC": "active directory domain controller",
1725 "ROLE_DOMAIN_PDC": "active directory domain controller",
1726 "dc": "active directory domain controller",
1727 "member": "member server",
1728 "domain controller": "active directory domain controller",
1729 "active directory domain controller": "active directory domain controller",
1730 "member server": "member server",
1731 "standalone": "standalone server",
1732 "standalone server": "standalone server",
1736 def sanitize_server_role(role
):
1737 """Sanitize a server role name.
1739 :param role: Server role
1740 :raise ValueError: If the role can not be interpreted
1741 :return: Sanitized server role (one of "member server",
1742 "active directory domain controller", "standalone server")
1745 return _ROLES_MAP
[role
]
1747 raise ValueError(role
)
1750 def provision_fake_ypserver(logger
, samdb
, domaindn
, netbiosname
, nisdomain
,
1752 """Create AD entries for the fake ypserver.
1754 This is needed for being able to manipulate posix attrs via ADUC.
1756 samdb
.transaction_start()
1758 logger
.info("Setting up fake yp server settings")
1759 setup_add_ldif(samdb
, setup_path("ypServ30.ldif"), {
1760 "DOMAINDN": domaindn
,
1761 "NETBIOSNAME": netbiosname
,
1762 "NISDOMAIN": nisdomain
,
1765 samdb
.transaction_cancel()
1768 samdb
.transaction_commit()
1771 def provision(logger
, session_info
, credentials
, smbconf
=None,
1772 targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None, rootdn
=None,
1773 domaindn
=None, schemadn
=None, configdn
=None, serverdn
=None,
1774 domain
=None, hostname
=None, hostip
=None, hostip6
=None, domainsid
=None,
1775 next_rid
=1000, dc_rid
=None, adminpass
=None, ldapadminpass
=None,
1776 krbtgtpass
=None, domainguid
=None, policyguid
=None, policyguid_dc
=None,
1777 dns_backend
=None, dns_forwarder
=None, dnspass
=None,
1778 invocationid
=None, machinepass
=None, ntdsguid
=None,
1779 root
=None, nobody
=None, users
=None, backup
=None, aci
=None,
1780 serverrole
=None, dom_for_fun_level
=None, backend_type
=None,
1781 sitename
=None, ol_mmr_urls
=None, ol_olc
=None, slapd_path
="/bin/false",
1782 useeadb
=False, am_rodc
=False, lp
=None, use_ntvfs
=False,
1783 use_rfc2307
=False, maxuid
=None, maxgid
=None, skip_sysvolacl
=True):
1786 :note: caution, this wipes all existing data!
1790 serverrole
= sanitize_server_role(serverrole
)
1792 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole
)
1794 if ldapadminpass
is None:
1795 # Make a new, random password between Samba and it's LDAP server
1796 ldapadminpass
= samba
.generate_random_password(128, 255)
1798 if backend_type
is None:
1799 backend_type
= "ldb"
1801 if domainsid
is None:
1802 domainsid
= security
.random_sid()
1804 domainsid
= security
.dom_sid(domainsid
)
1806 root_uid
= findnss_uid([root
or "root"])
1807 nobody_uid
= findnss_uid([nobody
or "nobody"])
1808 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
1809 root_gid
= pwd
.getpwuid(root_uid
).pw_gid
1812 bind_gid
= findnss_gid(["bind", "named"])
1816 if targetdir
is not None:
1817 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
1818 elif smbconf
is None:
1819 smbconf
= samba
.param
.default_path()
1820 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
1821 os
.makedirs(os
.path
.dirname(smbconf
))
1823 server_services
= []
1826 global_param
["idmap_ldb:use rfc2307"] = ["yes"]
1828 if dns_backend
!= "SAMBA_INTERNAL":
1829 server_services
.append("-dns")
1831 if dns_forwarder
is not None:
1832 global_param
["dns forwarder"] = [dns_forwarder
]
1835 server_services
.append("+smb")
1836 server_services
.append("-s3fs")
1837 global_param
["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1839 if len(server_services
) > 0:
1840 global_param
["server services"] = server_services
1842 # only install a new smb.conf if there isn't one there already
1843 if os
.path
.exists(smbconf
):
1844 # if Samba Team members can't figure out the weird errors
1845 # loading an empty smb.conf gives, then we need to be smarter.
1846 # Pretend it just didn't exist --abartlet
1847 f
= open(smbconf
, 'r')
1849 data
= f
.read().lstrip()
1852 if data
is None or data
== "":
1853 make_smbconf(smbconf
, hostname
, domain
, realm
,
1854 targetdir
, serverrole
=serverrole
,
1855 eadb
=useeadb
, use_ntvfs
=use_ntvfs
,
1856 lp
=lp
, global_param
=global_param
)
1858 make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
1859 serverrole
=serverrole
,
1860 eadb
=useeadb
, use_ntvfs
=use_ntvfs
, lp
=lp
, global_param
=global_param
)
1863 lp
= samba
.param
.LoadParm()
1865 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
1866 dnsdomain
=realm
, serverrole
=serverrole
, domaindn
=domaindn
,
1867 configdn
=configdn
, schemadn
=schemadn
, serverdn
=serverdn
,
1868 sitename
=sitename
, rootdn
=rootdn
)
1869 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
1871 paths
.bind_gid
= bind_gid
1872 paths
.root_uid
= root_uid
;
1873 paths
.root_gid
= root_gid
1876 logger
.info("Looking up IPv4 addresses")
1877 hostips
= interface_ips_v4(lp
)
1878 if len(hostips
) > 0:
1880 if len(hostips
) > 1:
1881 logger
.warning("More than one IPv4 address found. Using %s",
1883 if hostip
== "127.0.0.1":
1886 logger
.warning("No IPv4 address will be assigned")
1889 logger
.info("Looking up IPv6 addresses")
1890 hostips
= interface_ips_v6(lp
, linklocal
=False)
1892 hostip6
= hostips
[0]
1893 if len(hostips
) > 1:
1894 logger
.warning("More than one IPv6 address found. Using %s", hostip6
)
1896 logger
.warning("No IPv6 address will be assigned")
1898 names
.hostip
= hostip
1899 names
.hostip6
= hostip6
1901 if serverrole
is None:
1902 serverrole
= lp
.get("server role")
1904 if not os
.path
.exists(paths
.private_dir
):
1905 os
.mkdir(paths
.private_dir
)
1906 if not os
.path
.exists(os
.path
.join(paths
.private_dir
, "tls")):
1907 os
.mkdir(os
.path
.join(paths
.private_dir
, "tls"))
1908 if not os
.path
.exists(paths
.state_dir
):
1909 os
.mkdir(paths
.state_dir
)
1911 if paths
.sysvol
and not os
.path
.exists(paths
.sysvol
):
1912 os
.makedirs(paths
.sysvol
, 0775)
1914 if not use_ntvfs
and serverrole
== "active directory domain controller":
1915 s3conf
= s3param
.get_context()
1916 s3conf
.load(lp
.configfile
)
1918 if paths
.sysvol
is None:
1919 raise MissingShareError("sysvol", paths
.smbconf
)
1921 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(paths
.sysvol
))
1924 smbd
.set_simple_acl(file.name
, 0755, root_gid
)
1926 if not smbd
.have_posix_acls():
1927 # This clue is only strictly correct for RPM and
1928 # Debian-like Linux systems, but hopefully other users
1929 # will get enough clue from it.
1930 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.")
1932 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1934 smbd
.chown(file.name
, root_uid
, root_gid
)
1936 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1940 ldapi_url
= "ldapi://%s" % urllib
.quote(paths
.s4_ldapi_path
, safe
="")
1942 schema
= Schema(domainsid
, invocationid
=invocationid
,
1943 schemadn
=names
.schemadn
)
1945 if backend_type
== "ldb":
1946 provision_backend
= LDBBackend(backend_type
, paths
=paths
,
1947 lp
=lp
, credentials
=credentials
,
1948 names
=names
, logger
=logger
)
1949 elif backend_type
== "existing":
1950 # If support for this is ever added back, then the URI will need to be
1952 provision_backend
= ExistingBackend(backend_type
, paths
=paths
,
1953 lp
=lp
, credentials
=credentials
,
1954 names
=names
, logger
=logger
,
1955 ldap_backend_forced_uri
=None)
1956 elif backend_type
== "fedora-ds":
1957 provision_backend
= FDSBackend(backend_type
, paths
=paths
,
1958 lp
=lp
, credentials
=credentials
,
1959 names
=names
, logger
=logger
, domainsid
=domainsid
,
1960 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
1961 slapd_path
=slapd_path
,
1963 elif backend_type
== "openldap":
1964 provision_backend
= OpenLDAPBackend(backend_type
, paths
=paths
,
1965 lp
=lp
, credentials
=credentials
,
1966 names
=names
, logger
=logger
, domainsid
=domainsid
,
1967 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
1968 slapd_path
=slapd_path
, ol_mmr_urls
=ol_mmr_urls
)
1970 raise ValueError("Unknown LDAP backend type selected")
1972 provision_backend
.init()
1973 provision_backend
.start()
1975 # only install a new shares config db if there is none
1976 if not os
.path
.exists(paths
.shareconf
):
1977 logger
.info("Setting up share.ldb")
1978 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
, lp
=lp
)
1979 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
1981 logger
.info("Setting up secrets.ldb")
1982 secrets_ldb
= setup_secretsdb(paths
,
1983 session_info
=session_info
,
1984 backend_credentials
=provision_backend
.secrets_credentials
, lp
=lp
)
1987 logger
.info("Setting up the registry")
1988 setup_registry(paths
.hklm
, session_info
, lp
=lp
)
1990 logger
.info("Setting up the privileges database")
1991 setup_privileges(paths
.privilege
, session_info
, lp
=lp
)
1993 logger
.info("Setting up idmap db")
1994 idmap
= setup_idmapdb(paths
.idmapdb
, session_info
=session_info
, lp
=lp
)
1996 setup_name_mappings(idmap
, sid
=str(domainsid
),
1997 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
1998 users_gid
=users_gid
, root_gid
=root_gid
)
2000 logger
.info("Setting up SAM db")
2001 samdb
= setup_samdb(paths
.samdb
, session_info
,
2002 provision_backend
, lp
, names
, logger
=logger
,
2003 serverrole
=serverrole
,
2004 schema
=schema
, fill
=samdb_fill
, am_rodc
=am_rodc
)
2006 if serverrole
== "active directory domain controller":
2007 if paths
.netlogon
is None:
2008 raise MissingShareError("netlogon", paths
.smbconf
)
2010 if paths
.sysvol
is None:
2011 raise MissingShareError("sysvol", paths
.smbconf
)
2013 if not os
.path
.isdir(paths
.netlogon
):
2014 os
.makedirs(paths
.netlogon
, 0755)
2016 if adminpass
is None:
2017 adminpass
= samba
.generate_random_password(12, 32)
2018 adminpass_generated
= True
2020 adminpass_generated
= False
2022 if samdb_fill
== FILL_FULL
:
2023 provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
2024 schema
=schema
, targetdir
=targetdir
, samdb_fill
=samdb_fill
,
2025 hostip
=hostip
, hostip6
=hostip6
, domainsid
=domainsid
,
2026 next_rid
=next_rid
, dc_rid
=dc_rid
, adminpass
=adminpass
,
2027 krbtgtpass
=krbtgtpass
, domainguid
=domainguid
,
2028 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
2029 invocationid
=invocationid
, machinepass
=machinepass
,
2030 ntdsguid
=ntdsguid
, dns_backend
=dns_backend
,
2031 dnspass
=dnspass
, serverrole
=serverrole
,
2032 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
2033 lp
=lp
, use_ntvfs
=use_ntvfs
,
2034 skip_sysvolacl
=skip_sysvolacl
)
2036 create_krb5_conf(paths
.krb5conf
,
2037 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
2039 logger
.info("A Kerberos configuration suitable for Samba 4 has been "
2040 "generated at %s", paths
.krb5conf
)
2042 if serverrole
== "active directory domain controller":
2043 create_dns_update_list(lp
, logger
, paths
)
2045 backend_result
= provision_backend
.post_setup()
2046 provision_backend
.shutdown()
2049 secrets_ldb
.transaction_cancel()
2052 # Now commit the secrets.ldb to disk
2053 secrets_ldb
.transaction_commit()
2055 # the commit creates the dns.keytab, now chown it
2056 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
2057 if os
.path
.isfile(dns_keytab_path
) and paths
.bind_gid
is not None:
2059 os
.chmod(dns_keytab_path
, 0640)
2060 os
.chown(dns_keytab_path
, -1, paths
.bind_gid
)
2062 if not os
.environ
.has_key('SAMBA_SELFTEST'):
2063 logger
.info("Failed to chown %s to bind gid %u",
2064 dns_keytab_path
, paths
.bind_gid
)
2066 result
= ProvisionResult()
2067 result
.server_role
= serverrole
2068 result
.domaindn
= domaindn
2069 result
.paths
= paths
2070 result
.names
= names
2072 result
.samdb
= samdb
2073 result
.idmap
= idmap
2074 result
.domainsid
= str(domainsid
)
2076 if samdb_fill
== FILL_FULL
:
2077 result
.adminpass_generated
= adminpass_generated
2078 result
.adminpass
= adminpass
2080 result
.adminpass_generated
= False
2081 result
.adminpass
= None
2083 result
.backend_result
= backend_result
2086 provision_fake_ypserver(logger
=logger
, samdb
=samdb
,
2087 domaindn
=names
.domaindn
, netbiosname
=names
.netbiosname
,
2088 nisdomain
=names
.domain
.lower(), maxuid
=maxuid
, maxgid
=maxgid
)
2093 def provision_become_dc(smbconf
=None, targetdir
=None,
2094 realm
=None, rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
2095 serverdn
=None, domain
=None, hostname
=None, domainsid
=None,
2096 adminpass
=None, krbtgtpass
=None, domainguid
=None, policyguid
=None,
2097 policyguid_dc
=None, invocationid
=None, machinepass
=None, dnspass
=None,
2098 dns_backend
=None, root
=None, nobody
=None, users
=None,
2099 backup
=None, serverrole
=None, ldap_backend
=None,
2100 ldap_backend_type
=None, sitename
=None, debuglevel
=1, use_ntvfs
=False):
2102 logger
= logging
.getLogger("provision")
2103 samba
.set_debug_level(debuglevel
)
2105 res
= provision(logger
, system_session(), None,
2106 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
2107 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
2108 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
2109 hostname
=hostname
, hostip
=None, domainsid
=domainsid
,
2110 machinepass
=machinepass
,
2111 serverrole
="active directory domain controller",
2112 sitename
=sitename
, dns_backend
=dns_backend
, dnspass
=dnspass
,
2113 use_ntvfs
=use_ntvfs
)
2114 res
.lp
.set("debuglevel", str(debuglevel
))
2118 def create_krb5_conf(path
, dnsdomain
, hostname
, realm
):
2119 """Write out a file containing zone statements suitable for inclusion in a
2120 named.conf file (including GSS-TSIG configuration).
2122 :param path: Path of the new named.conf file.
2123 :param dnsdomain: DNS Domain name
2124 :param hostname: Local hostname
2125 :param realm: Realm name
2127 setup_file(setup_path("krb5.conf"), path
, {
2128 "DNSDOMAIN": dnsdomain
,
2129 "HOSTNAME": hostname
,
2134 class ProvisioningError(Exception):
2135 """A generic provision error."""
2137 def __init__(self
, value
):
2141 return "ProvisioningError: " + self
.value
2144 class InvalidNetbiosName(Exception):
2145 """A specified name was not a valid NetBIOS name."""
2147 def __init__(self
, name
):
2148 super(InvalidNetbiosName
, self
).__init
__(
2149 "The name '%r' is not a valid NetBIOS name" % name
)
2152 class MissingShareError(ProvisioningError
):
2154 def __init__(self
, name
, smbconf
):
2155 super(MissingShareError
, self
).__init
__(
2156 "Existing smb.conf does not have a [%s] share, but you are "
2157 "configuring a DC. Please remove %s or add the share manually." %