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
,
86 from samba
.provision
.common
import (
91 from samba
.provision
.sambadns
import (
93 create_dns_update_list
98 from samba
.schema
import Schema
99 from samba
.samdb
import SamDB
100 from samba
.dbchecker
import dbcheck
103 DEFAULT_POLICY_GUID
= "31B2F340-016D-11D2-945F-00C04FB984F9"
104 DEFAULT_DC_POLICY_GUID
= "6AC1786C-016F-11D2-945F-00C04fB984F9"
105 DEFAULTSITE
= "Default-First-Site-Name"
106 LAST_PROVISION_USN_ATTRIBUTE
= "lastProvisionUSN"
109 class ProvisionPaths(object):
112 self
.shareconf
= None
123 self
.dns_keytab
= None
126 self
.private_dir
= None
127 self
.state_dir
= None
130 class ProvisionNames(object):
137 self
.ldapmanagerdn
= None
138 self
.dnsdomain
= None
140 self
.netbiosname
= None
147 def find_provision_key_parameters(samdb
, secretsdb
, idmapdb
, paths
, smbconf
,
149 """Get key provision parameters (realm, domain, ...) from a given provision
151 :param samdb: An LDB object connected to the sam.ldb file
152 :param secretsdb: An LDB object connected to the secrets.ldb file
153 :param idmapdb: An LDB object connected to the idmap.ldb file
154 :param paths: A list of path to provision object
155 :param smbconf: Path to the smb.conf file
156 :param lp: A LoadParm object
157 :return: A list of key provision parameters
159 names
= ProvisionNames()
160 names
.adminpass
= None
162 # NT domain, kerberos realm, root dn, domain dn, domain dns name
163 names
.domain
= string
.upper(lp
.get("workgroup"))
164 names
.realm
= lp
.get("realm")
165 names
.dnsdomain
= names
.realm
.lower()
166 basedn
= samba
.dn_from_dns_name(names
.dnsdomain
)
167 names
.realm
= string
.upper(names
.realm
)
169 # Get the netbiosname first (could be obtained from smb.conf in theory)
170 res
= secretsdb
.search(expression
="(flatname=%s)" %
171 names
.domain
,base
="CN=Primary Domains",
172 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
173 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
175 names
.smbconf
= smbconf
177 # That's a bit simplistic but it's ok as long as we have only 3
179 current
= samdb
.search(expression
="(objectClass=*)",
180 base
="", scope
=ldb
.SCOPE_BASE
,
181 attrs
=["defaultNamingContext", "schemaNamingContext",
182 "configurationNamingContext","rootDomainNamingContext"])
184 names
.configdn
= current
[0]["configurationNamingContext"]
185 configdn
= str(names
.configdn
)
186 names
.schemadn
= current
[0]["schemaNamingContext"]
187 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
,
188 current
[0]["defaultNamingContext"][0]))):
189 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
190 "is not the same ..." % (paths
.samdb
,
191 str(current
[0]["defaultNamingContext"][0]),
192 paths
.smbconf
, basedn
)))
194 names
.domaindn
=current
[0]["defaultNamingContext"]
195 names
.rootdn
=current
[0]["rootDomainNamingContext"]
197 res3
= samdb
.search(expression
="(objectClass=site)",
198 base
="CN=Sites," + configdn
, scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn"])
199 names
.sitename
= str(res3
[0]["cn"])
201 # dns hostname and server dn
202 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
203 base
="OU=Domain Controllers,%s" % basedn
,
204 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
205 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("." + names
.dnsdomain
, "")
207 server_res
= samdb
.search(expression
="serverReference=%s" % res4
[0].dn
,
208 attrs
=[], base
=configdn
)
209 names
.serverdn
= server_res
[0].dn
211 # invocation id/objectguid
212 res5
= samdb
.search(expression
="(objectClass=*)",
213 base
="CN=NTDS Settings,%s" % str(names
.serverdn
),
214 scope
=ldb
.SCOPE_BASE
,
215 attrs
=["invocationID", "objectGUID"])
216 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
217 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
220 res6
= samdb
.search(expression
="(objectClass=*)", base
=basedn
,
221 scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID",
222 "objectSid","msDS-Behavior-Version" ])
223 names
.domainguid
= str(ndr_unpack(misc
.GUID
, res6
[0]["objectGUID"][0]))
224 names
.domainsid
= ndr_unpack( security
.dom_sid
, res6
[0]["objectSid"][0])
225 if res6
[0].get("msDS-Behavior-Version") is None or \
226 int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
227 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
229 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
232 res7
= samdb
.search(expression
="(displayName=Default Domain Policy)",
233 base
="CN=Policies,CN=System," + basedn
,
234 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
235 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
237 res8
= samdb
.search(expression
="(displayName=Default Domain Controllers"
239 base
="CN=Policies,CN=System," + basedn
,
240 scope
=ldb
.SCOPE_ONELEVEL
,
241 attrs
=["cn","displayName"])
243 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
245 names
.policyid_dc
= None
247 res9
= idmapdb
.search(expression
="(cn=%s-%s)" %
248 (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
),
249 attrs
=["xidNumber", "type"])
251 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
))
252 if res9
[0]["type"][0] == "ID_TYPE_BOTH":
253 names
.root_gid
= res9
[0]["xidNumber"][0]
255 names
.root_gid
= pwd
.getpwuid(int(res9
[0]["xidNumber"][0])).pw_gid
259 def update_provision_usn(samdb
, low
, high
, id, replace
=False):
260 """Update the field provisionUSN in sam.ldb
262 This field is used to track range of USN modified by provision and
264 This value is used afterward by next provision to figure out if
265 the field have been modified since last provision.
267 :param samdb: An LDB object connect to sam.ldb
268 :param low: The lowest USN modified by this upgrade
269 :param high: The highest USN modified by this upgrade
270 :param id: The invocation id of the samba's dc
271 :param replace: A boolean indicating if the range should replace any
272 existing one or appended (default)
277 entry
= samdb
.search(base
="@PROVISION",
278 scope
=ldb
.SCOPE_BASE
,
279 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "dn"])
280 for e
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
281 if not re
.search(';', e
):
282 e
= "%s;%s" % (e
, id)
285 tab
.append("%s-%s;%s" % (low
, high
, id))
286 delta
= ldb
.Message()
287 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
288 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
289 ldb
.FLAG_MOD_REPLACE
, LAST_PROVISION_USN_ATTRIBUTE
)
290 entry
= samdb
.search(expression
='provisionnerID=*',
291 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
292 attrs
=["provisionnerID"])
293 if len(entry
) == 0 or len(entry
[0]) == 0:
294 delta
["provisionnerID"] = ldb
.MessageElement(id, ldb
.FLAG_MOD_ADD
, "provisionnerID")
298 def set_provision_usn(samdb
, low
, high
, id):
299 """Set the field provisionUSN in sam.ldb
300 This field is used to track range of USN modified by provision and
302 This value is used afterward by next provision to figure out if
303 the field have been modified since last provision.
305 :param samdb: An LDB object connect to sam.ldb
306 :param low: The lowest USN modified by this upgrade
307 :param high: The highest USN modified by this upgrade
308 :param id: The invocationId of the provision"""
311 tab
.append("%s-%s;%s" % (low
, high
, id))
313 delta
= ldb
.Message()
314 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
315 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
316 ldb
.FLAG_MOD_ADD
, LAST_PROVISION_USN_ATTRIBUTE
)
320 def get_max_usn(samdb
,basedn
):
321 """ This function return the biggest USN present in the provision
323 :param samdb: A LDB object pointing to the sam.ldb
324 :param basedn: A string containing the base DN of the provision
326 :return: The biggest USN in the provision"""
328 res
= samdb
.search(expression
="objectClass=*",base
=basedn
,
329 scope
=ldb
.SCOPE_SUBTREE
,attrs
=["uSNChanged"],
330 controls
=["search_options:1:2",
331 "server_sort:1:1:uSNChanged",
332 "paged_results:1:1"])
333 return res
[0]["uSNChanged"]
336 def get_last_provision_usn(sam
):
337 """Get USNs ranges modified by a provision or an upgradeprovision
339 :param sam: An LDB object pointing to the sam.ldb
340 :return: a dictionary which keys are invocation id and values are an array
341 of integer representing the different ranges
344 entry
= sam
.search(expression
="%s=*" % LAST_PROVISION_USN_ATTRIBUTE
,
345 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
346 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "provisionnerID"])
347 except ldb
.LdbError
, (ecode
, emsg
):
348 if ecode
== ldb
.ERR_NO_SUCH_OBJECT
:
355 if entry
[0].get("provisionnerID"):
356 for e
in entry
[0]["provisionnerID"]:
358 for r
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
359 tab1
= str(r
).split(';')
364 if (len(myids
) > 0 and id not in myids
):
366 tab2
= p
.split(tab1
[0])
367 if range.get(id) is None:
369 range[id].append(tab2
[0])
370 range[id].append(tab2
[1])
376 class ProvisionResult(object):
377 """Result of a provision.
379 :ivar server_role: The server role
380 :ivar paths: ProvisionPaths instance
381 :ivar domaindn: The domain dn, as string
385 self
.server_role
= None
392 self
.domainsid
= None
393 self
.adminpass_generated
= None
394 self
.adminpass
= None
395 self
.backend_result
= None
397 def report_logger(self
, logger
):
398 """Report this provision result to a logger."""
400 "Once the above files are installed, your Samba4 server will "
402 if self
.adminpass_generated
:
403 logger
.info("Admin password: %s", self
.adminpass
)
404 logger
.info("Server Role: %s", self
.server_role
)
405 logger
.info("Hostname: %s", self
.names
.hostname
)
406 logger
.info("NetBIOS Domain: %s", self
.names
.domain
)
407 logger
.info("DNS Domain: %s", self
.names
.dnsdomain
)
408 logger
.info("DOMAIN SID: %s", self
.domainsid
)
410 if self
.backend_result
:
411 self
.backend_result
.report_logger(logger
)
414 def check_install(lp
, session_info
, credentials
):
415 """Check whether the current install seems ok.
417 :param lp: Loadparm context
418 :param session_info: Session information
419 :param credentials: Credentials
421 if lp
.get("realm") == "":
422 raise Exception("Realm empty")
423 samdb
= Ldb(lp
.samdb_url(), session_info
=session_info
,
424 credentials
=credentials
, lp
=lp
)
425 if len(samdb
.search("(cn=Administrator)")) != 1:
426 raise ProvisioningError("No administrator account found")
429 def findnss(nssfn
, names
):
430 """Find a user or group from a list of possibilities.
432 :param nssfn: NSS Function to try (should raise KeyError if not found)
433 :param names: Names to check.
434 :return: Value return by first names list.
441 raise KeyError("Unable to find user/group in %r" % names
)
444 findnss_uid
= lambda names
: findnss(pwd
.getpwnam
, names
)[2]
445 findnss_gid
= lambda names
: findnss(grp
.getgrnam
, names
)[2]
448 def provision_paths_from_lp(lp
, dnsdomain
):
449 """Set the default paths for provisioning.
451 :param lp: Loadparm context.
452 :param dnsdomain: DNS Domain name
454 paths
= ProvisionPaths()
455 paths
.private_dir
= lp
.get("private dir")
456 paths
.state_dir
= lp
.get("state directory")
458 # This is stored without path prefix for the "privateKeytab" attribute in
459 # "secrets_dns.ldif".
460 paths
.dns_keytab
= "dns.keytab"
461 paths
.keytab
= "secrets.keytab"
463 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
464 paths
.samdb
= os
.path
.join(paths
.private_dir
, "sam.ldb")
465 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, "idmap.ldb")
466 paths
.secrets
= os
.path
.join(paths
.private_dir
, "secrets.ldb")
467 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
468 paths
.dns
= os
.path
.join(paths
.private_dir
, "dns", dnsdomain
+ ".zone")
469 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
470 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
471 paths
.namedconf
= os
.path
.join(paths
.private_dir
, "named.conf")
472 paths
.namedconf_update
= os
.path
.join(paths
.private_dir
, "named.conf.update")
473 paths
.namedtxt
= os
.path
.join(paths
.private_dir
, "named.txt")
474 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
475 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
476 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
477 paths
.hklm
= "hklm.ldb"
478 paths
.hkcr
= "hkcr.ldb"
479 paths
.hkcu
= "hkcu.ldb"
480 paths
.hku
= "hku.ldb"
481 paths
.hkpd
= "hkpd.ldb"
482 paths
.hkpt
= "hkpt.ldb"
483 paths
.sysvol
= lp
.get("path", "sysvol")
484 paths
.netlogon
= lp
.get("path", "netlogon")
485 paths
.smbconf
= lp
.configfile
489 def determine_netbios_name(hostname
):
490 """Determine a netbios name from a hostname."""
491 # remove forbidden chars and force the length to be <16
492 netbiosname
= "".join([x
for x
in hostname
if is_valid_netbios_char(x
)])
493 return netbiosname
[:MAX_NETBIOS_NAME_LEN
].upper()
496 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
497 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
498 schemadn
=None, serverdn
=None, sitename
=None):
499 """Guess configuration settings to use."""
502 hostname
= socket
.gethostname().split(".")[0]
504 netbiosname
= lp
.get("netbios name")
505 if netbiosname
is None:
506 netbiosname
= determine_netbios_name(hostname
)
507 netbiosname
= netbiosname
.upper()
508 if not valid_netbios_name(netbiosname
):
509 raise InvalidNetbiosName(netbiosname
)
511 if dnsdomain
is None:
512 dnsdomain
= lp
.get("realm")
513 if dnsdomain
is None or dnsdomain
== "":
514 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp
.configfile
)
516 dnsdomain
= dnsdomain
.lower()
518 if serverrole
is None:
519 serverrole
= lp
.get("server role")
520 if serverrole
is None:
521 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
523 serverrole
= serverrole
.lower()
525 realm
= dnsdomain
.upper()
527 if lp
.get("realm") == "":
528 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
530 if lp
.get("realm").upper() != realm
:
531 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
))
533 if lp
.get("server role").lower() != serverrole
:
534 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
))
536 if serverrole
== "active directory domain controller":
538 # This will, for better or worse, default to 'WORKGROUP'
539 domain
= lp
.get("workgroup")
540 domain
= domain
.upper()
542 if lp
.get("workgroup").upper() != domain
:
543 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
))
546 domaindn
= samba
.dn_from_dns_name(dnsdomain
)
548 if domain
== netbiosname
:
549 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain
, netbiosname
))
553 domaindn
= "DC=" + netbiosname
555 if not valid_netbios_name(domain
):
556 raise InvalidNetbiosName(domain
)
558 if hostname
.upper() == realm
:
559 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
560 if netbiosname
.upper() == realm
:
561 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm
, netbiosname
))
563 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
569 configdn
= "CN=Configuration," + rootdn
571 schemadn
= "CN=Schema," + configdn
574 sitename
= DEFAULTSITE
576 names
= ProvisionNames()
577 names
.rootdn
= rootdn
578 names
.domaindn
= domaindn
579 names
.configdn
= configdn
580 names
.schemadn
= schemadn
581 names
.ldapmanagerdn
= "CN=Manager," + rootdn
582 names
.dnsdomain
= dnsdomain
583 names
.domain
= domain
585 names
.netbiosname
= netbiosname
586 names
.hostname
= hostname
587 names
.sitename
= sitename
588 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
589 netbiosname
, sitename
, configdn
)
594 def make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
595 serverrole
=None, eadb
=False, use_ntvfs
=False, lp
=None,
597 """Create a new smb.conf file based on a couple of basic settings.
599 assert smbconf
is not None
602 hostname
= socket
.gethostname().split(".")[0]
604 netbiosname
= determine_netbios_name(hostname
)
606 if serverrole
is None:
607 serverrole
= "standalone server"
609 assert domain
is not None
610 domain
= domain
.upper()
612 assert realm
is not None
613 realm
= realm
.upper()
616 "netbios name": netbiosname
,
619 "server role": serverrole
,
623 lp
= samba
.param
.LoadParm()
624 #Load non-existent file
625 if os
.path
.exists(smbconf
):
628 if global_param
is not None:
629 for ent
in global_param
:
630 if global_param
[ent
] is not None:
631 global_settings
[ent
] = " ".join(global_param
[ent
])
633 if targetdir
is not None:
634 global_settings
["private dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "private"))
635 global_settings
["lock dir"] = os
.path
.abspath(targetdir
)
636 global_settings
["state directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "state"))
637 global_settings
["cache directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "cache"))
639 lp
.set("lock dir", os
.path
.abspath(targetdir
))
640 lp
.set("state directory", global_settings
["state directory"])
641 lp
.set("cache directory", global_settings
["cache directory"])
644 if use_ntvfs
and not lp
.get("posix:eadb"):
645 if targetdir
is not None:
646 privdir
= os
.path
.join(targetdir
, "private")
648 privdir
= lp
.get("private dir")
649 lp
.set("posix:eadb", os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
650 elif not use_ntvfs
and not lp
.get("xattr_tdb:file"):
651 if targetdir
is not None:
652 statedir
= os
.path
.join(targetdir
, "state")
654 statedir
= lp
.get("state directory")
655 lp
.set("xattr_tdb:file", os
.path
.abspath(os
.path
.join(statedir
, "xattr.tdb")))
658 if serverrole
== "active directory domain controller":
659 shares
["sysvol"] = os
.path
.join(lp
.get("state directory"), "sysvol")
660 shares
["netlogon"] = os
.path
.join(shares
["sysvol"], realm
.lower(),
663 global_settings
["passdb backend"] = "samba_dsdb"
665 f
= open(smbconf
, 'w')
667 f
.write("[globals]\n")
668 for key
, val
in global_settings
.iteritems():
669 f
.write("\t%s = %s\n" % (key
, val
))
672 for name
, path
in shares
.iteritems():
673 f
.write("[%s]\n" % name
)
674 f
.write("\tpath = %s\n" % path
)
675 f
.write("\tread only = no\n")
679 # reload the smb.conf
682 # and dump it without any values that are the default
683 # this ensures that any smb.conf parameters that were set
684 # on the provision/join command line are set in the resulting smb.conf
685 f
= open(smbconf
, mode
='w')
692 def setup_name_mappings(idmap
, sid
, root_uid
, nobody_uid
,
693 users_gid
, root_gid
):
694 """setup reasonable name mappings for sam names to unix names.
696 :param samdb: SamDB object.
697 :param idmap: IDmap db object.
698 :param sid: The domain sid.
699 :param domaindn: The domain DN.
700 :param root_uid: uid of the UNIX root user.
701 :param nobody_uid: uid of the UNIX nobody user.
702 :param users_gid: gid of the UNIX users group.
703 :param root_gid: gid of the UNIX root group.
705 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
707 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
708 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
711 def setup_samdb_partitions(samdb_path
, logger
, lp
, session_info
,
712 provision_backend
, names
, schema
, serverrole
,
714 """Setup the partitions for the SAM database.
716 Alternatively, provision() may call this, and then populate the database.
718 :note: This will wipe the Sam Database!
720 :note: This function always removes the local SAM LDB file. The erase
721 parameter controls whether to erase the existing data, which
722 may not be stored locally but in LDAP.
725 assert session_info
is not None
727 # We use options=["modules:"] to stop the modules loading - we
728 # just want to wipe and re-initialise the database, not start it up
731 os
.unlink(samdb_path
)
735 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
736 lp
=lp
, options
=["modules:"])
738 ldap_backend_line
= "# No LDAP backend"
739 if provision_backend
.type != "ldb":
740 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldap_uri
742 samdb
.transaction_start()
744 logger
.info("Setting up sam.ldb partitions and settings")
745 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
746 "LDAP_BACKEND_LINE": ldap_backend_line
750 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
751 "BACKEND_TYPE": provision_backend
.type,
752 "SERVER_ROLE": serverrole
755 logger
.info("Setting up sam.ldb rootDSE")
756 setup_samdb_rootdse(samdb
, names
)
758 samdb
.transaction_cancel()
761 samdb
.transaction_commit()
764 def secretsdb_self_join(secretsdb
, domain
,
765 netbiosname
, machinepass
, domainsid
=None,
766 realm
=None, dnsdomain
=None,
768 key_version_number
=1,
769 secure_channel_type
=SEC_CHAN_WKSTA
):
770 """Add domain join-specific bits to a secrets database.
772 :param secretsdb: Ldb Handle to the secrets database
773 :param machinepass: Machine password
775 attrs
= ["whenChanged",
782 if realm
is not None:
783 if dnsdomain
is None:
784 dnsdomain
= realm
.lower()
785 dnsname
= '%s.%s' % (netbiosname
.lower(), dnsdomain
.lower())
788 shortname
= netbiosname
.lower()
790 # We don't need to set msg["flatname"] here, because rdn_name will handle
791 # it, and it causes problems for modifies anyway
792 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
793 msg
["secureChannelType"] = [str(secure_channel_type
)]
794 msg
["objectClass"] = ["top", "primaryDomain"]
795 if dnsname
is not None:
796 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
797 msg
["realm"] = [realm
]
798 msg
["saltPrincipal"] = ["host/%s@%s" % (dnsname
, realm
.upper())]
799 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
800 msg
["privateKeytab"] = ["secrets.keytab"]
802 msg
["secret"] = [machinepass
]
803 msg
["samAccountName"] = ["%s$" % netbiosname
]
804 msg
["secureChannelType"] = [str(secure_channel_type
)]
805 if domainsid
is not None:
806 msg
["objectSid"] = [ndr_pack(domainsid
)]
808 # This complex expression tries to ensure that we don't have more
809 # than one record for this SID, realm or netbios domain at a time,
810 # but we don't delete the old record that we are about to modify,
811 # because that would delete the keytab and previous password.
812 res
= secretsdb
.search(base
="cn=Primary Domains", attrs
=attrs
,
813 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain
, realm
, str(domainsid
), str(msg
.dn
))),
814 scope
=ldb
.SCOPE_ONELEVEL
)
817 secretsdb
.delete(del_msg
.dn
)
819 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
822 msg
["priorSecret"] = [res
[0]["secret"][0]]
823 msg
["priorWhenChanged"] = [res
[0]["whenChanged"][0]]
826 msg
["privateKeytab"] = [res
[0]["privateKeytab"][0]]
831 msg
["krb5Keytab"] = [res
[0]["krb5Keytab"][0]]
837 msg
[el
].set_flags(ldb
.FLAG_MOD_REPLACE
)
838 secretsdb
.modify(msg
)
839 secretsdb
.rename(res
[0].dn
, msg
.dn
)
841 spn
= [ 'HOST/%s' % shortname
]
842 if secure_channel_type
== SEC_CHAN_BDC
and dnsname
is not None:
843 # we are a domain controller then we add servicePrincipalName
844 # entries for the keytab code to update.
845 spn
.extend([ 'HOST/%s' % dnsname
])
846 msg
["servicePrincipalName"] = spn
851 def setup_secretsdb(paths
, session_info
, backend_credentials
, lp
):
852 """Setup the secrets database.
854 :note: This function does not handle exceptions and transaction on purpose,
855 it's up to the caller to do this job.
857 :param path: Path to the secrets database.
858 :param session_info: Session info.
859 :param credentials: Credentials
860 :param lp: Loadparm context
861 :return: LDB handle for the created secrets database
863 if os
.path
.exists(paths
.secrets
):
864 os
.unlink(paths
.secrets
)
866 keytab_path
= os
.path
.join(paths
.private_dir
, paths
.keytab
)
867 if os
.path
.exists(keytab_path
):
868 os
.unlink(keytab_path
)
870 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
871 if os
.path
.exists(dns_keytab_path
):
872 os
.unlink(dns_keytab_path
)
876 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
878 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
879 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
880 secrets_ldb
.transaction_start()
882 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
884 if (backend_credentials
is not None and
885 backend_credentials
.authentication_requested()):
886 if backend_credentials
.get_bind_dn() is not None:
887 setup_add_ldif(secrets_ldb
,
888 setup_path("secrets_simple_ldap.ldif"), {
889 "LDAPMANAGERDN": backend_credentials
.get_bind_dn(),
890 "LDAPMANAGERPASS_B64": b64encode(backend_credentials
.get_password())
893 setup_add_ldif(secrets_ldb
,
894 setup_path("secrets_sasl_ldap.ldif"), {
895 "LDAPADMINUSER": backend_credentials
.get_username(),
896 "LDAPADMINREALM": backend_credentials
.get_realm(),
897 "LDAPADMINPASS_B64": b64encode(backend_credentials
.get_password())
900 secrets_ldb
.transaction_cancel()
905 def setup_privileges(path
, session_info
, lp
):
906 """Setup the privileges database.
908 :param path: Path to the privileges database.
909 :param session_info: Session info.
910 :param credentials: Credentials
911 :param lp: Loadparm context
912 :return: LDB handle for the created secrets database
914 if os
.path
.exists(path
):
916 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
917 privilege_ldb
.erase()
918 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
921 def setup_registry(path
, session_info
, lp
):
922 """Setup the registry.
924 :param path: Path to the registry database
925 :param session_info: Session information
926 :param credentials: Credentials
927 :param lp: Loadparm context
929 reg
= samba
.registry
.Registry()
930 hive
= samba
.registry
.open_ldb(path
, session_info
=session_info
, lp_ctx
=lp
)
931 reg
.mount_hive(hive
, samba
.registry
.HKEY_LOCAL_MACHINE
)
932 provision_reg
= setup_path("provision.reg")
933 assert os
.path
.exists(provision_reg
)
934 reg
.diff_apply(provision_reg
)
937 def setup_idmapdb(path
, session_info
, lp
):
938 """Setup the idmap database.
940 :param path: path to the idmap database
941 :param session_info: Session information
942 :param credentials: Credentials
943 :param lp: Loadparm context
945 if os
.path
.exists(path
):
948 idmap_ldb
= IDmapDB(path
, session_info
=session_info
, lp
=lp
)
950 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
954 def setup_samdb_rootdse(samdb
, names
):
955 """Setup the SamDB rootdse.
957 :param samdb: Sam Database handle
959 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
960 "SCHEMADN": names
.schemadn
,
961 "DOMAINDN": names
.domaindn
,
962 "ROOTDN" : names
.rootdn
,
963 "CONFIGDN": names
.configdn
,
964 "SERVERDN": names
.serverdn
,
968 def setup_self_join(samdb
, admin_session_info
, names
, fill
, machinepass
,
969 dns_backend
, dnspass
, domainsid
, next_rid
, invocationid
,
970 policyguid
, policyguid_dc
,
971 domainControllerFunctionality
, ntdsguid
=None, dc_rid
=None):
972 """Join a host to its own domain."""
973 assert isinstance(invocationid
, str)
974 if ntdsguid
is not None:
975 ntdsguid_line
= "objectGUID: %s\n"%ntdsguid
982 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
983 "CONFIGDN": names
.configdn
,
984 "SCHEMADN": names
.schemadn
,
985 "DOMAINDN": names
.domaindn
,
986 "SERVERDN": names
.serverdn
,
987 "INVOCATIONID": invocationid
,
988 "NETBIOSNAME": names
.netbiosname
,
989 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
990 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
991 "DOMAINSID": str(domainsid
),
992 "DCRID": str(dc_rid
),
993 "SAMBA_VERSION_STRING": version
,
994 "NTDSGUID": ntdsguid_line
,
995 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
996 domainControllerFunctionality
),
997 "RIDALLOCATIONSTART": str(next_rid
+ 100),
998 "RIDALLOCATIONEND": str(next_rid
+ 100 + 499)})
1000 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
1001 "POLICYGUID": policyguid
,
1002 "POLICYGUID_DC": policyguid_dc
,
1003 "DNSDOMAIN": names
.dnsdomain
,
1004 "DOMAINDN": names
.domaindn
})
1006 # If we are setting up a subdomain, then this has been replicated in, so we
1007 # don't need to add it
1008 if fill
== FILL_FULL
:
1009 setup_add_ldif(samdb
, setup_path("provision_self_join_config.ldif"), {
1010 "CONFIGDN": names
.configdn
,
1011 "SCHEMADN": names
.schemadn
,
1012 "DOMAINDN": names
.domaindn
,
1013 "SERVERDN": names
.serverdn
,
1014 "INVOCATIONID": invocationid
,
1015 "NETBIOSNAME": names
.netbiosname
,
1016 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1017 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1018 "DOMAINSID": str(domainsid
),
1019 "DCRID": str(dc_rid
),
1020 "SAMBA_VERSION_STRING": version
,
1021 "NTDSGUID": ntdsguid_line
,
1022 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1023 domainControllerFunctionality
)})
1025 # Setup fSMORoleOwner entries to point at the newly created DC entry
1026 setup_modify_ldif(samdb
,
1027 setup_path("provision_self_join_modify_config.ldif"), {
1028 "CONFIGDN": names
.configdn
,
1029 "SCHEMADN": names
.schemadn
,
1030 "DEFAULTSITE": names
.sitename
,
1031 "NETBIOSNAME": names
.netbiosname
,
1032 "SERVERDN": names
.serverdn
,
1035 system_session_info
= system_session()
1036 samdb
.set_session_info(system_session_info
)
1037 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1038 # modify a serverReference under cn=config when we are a subdomain, we must
1039 # be system due to ACLs
1040 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
1041 "DOMAINDN": names
.domaindn
,
1042 "SERVERDN": names
.serverdn
,
1043 "NETBIOSNAME": names
.netbiosname
,
1046 samdb
.set_session_info(admin_session_info
)
1048 if dns_backend
!= "SAMBA_INTERNAL":
1049 # This is Samba4 specific and should be replaced by the correct
1050 # DNS AD-style setup
1051 setup_add_ldif(samdb
, setup_path("provision_dns_add_samba.ldif"), {
1052 "DNSDOMAIN": names
.dnsdomain
,
1053 "DOMAINDN": names
.domaindn
,
1054 "DNSPASS_B64": b64encode(dnspass
.encode('utf-16-le')),
1055 "HOSTNAME" : names
.hostname
,
1056 "DNSNAME" : '%s.%s' % (
1057 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
1061 def getpolicypath(sysvolpath
, dnsdomain
, guid
):
1062 """Return the physical path of policy given its guid.
1064 :param sysvolpath: Path to the sysvol folder
1065 :param dnsdomain: DNS name of the AD domain
1066 :param guid: The GUID of the policy
1067 :return: A string with the complete path to the policy folder
1070 guid
= "{%s}" % guid
1071 policy_path
= os
.path
.join(sysvolpath
, dnsdomain
, "Policies", guid
)
1075 def create_gpo_struct(policy_path
):
1076 if not os
.path
.exists(policy_path
):
1077 os
.makedirs(policy_path
, 0775)
1078 f
= open(os
.path
.join(policy_path
, "GPT.INI"), 'w')
1080 f
.write("[General]\r\nVersion=0")
1083 p
= os
.path
.join(policy_path
, "MACHINE")
1084 if not os
.path
.exists(p
):
1085 os
.makedirs(p
, 0775)
1086 p
= os
.path
.join(policy_path
, "USER")
1087 if not os
.path
.exists(p
):
1088 os
.makedirs(p
, 0775)
1091 def create_default_gpo(sysvolpath
, dnsdomain
, policyguid
, policyguid_dc
):
1092 """Create the default GPO for a domain
1094 :param sysvolpath: Physical path for the sysvol folder
1095 :param dnsdomain: DNS domain name of the AD domain
1096 :param policyguid: GUID of the default domain policy
1097 :param policyguid_dc: GUID of the default domain controler policy
1099 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid
)
1100 create_gpo_struct(policy_path
)
1102 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid_dc
)
1103 create_gpo_struct(policy_path
)
1106 def setup_samdb(path
, session_info
, provision_backend
, lp
, names
,
1107 logger
, fill
, serverrole
, schema
, am_rodc
=False):
1108 """Setup a complete SAM Database.
1110 :note: This will wipe the main SAM database file!
1113 # Also wipes the database
1114 setup_samdb_partitions(path
, logger
=logger
, lp
=lp
,
1115 provision_backend
=provision_backend
, session_info
=session_info
,
1116 names
=names
, serverrole
=serverrole
, schema
=schema
)
1118 # Load the database, but don's load the global schema and don't connect
1120 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
1121 credentials
=provision_backend
.credentials
, lp
=lp
,
1122 global_schema
=False, am_rodc
=am_rodc
)
1124 logger
.info("Pre-loading the Samba 4 and AD schema")
1126 # Load the schema from the one we computed earlier
1127 samdb
.set_schema(schema
, write_indices_and_attributes
=False)
1129 # Set the NTDS settings DN manually - in order to have it already around
1130 # before the provisioned tree exists and we connect
1131 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1133 # And now we can connect to the DB - the schema won't be loaded from the
1137 # But we have to give it one more kick to have it use the schema
1138 # during provision - it needs, now that it is connected, to write
1139 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1140 samdb
.set_schema(schema
, write_indices_and_attributes
=True)
1145 def fill_samdb(samdb
, lp
, names
, logger
, domainsid
, domainguid
, policyguid
,
1146 policyguid_dc
, fill
, adminpass
, krbtgtpass
, machinepass
, dns_backend
,
1147 dnspass
, invocationid
, ntdsguid
, serverrole
, am_rodc
=False,
1148 dom_for_fun_level
=None, schema
=None, next_rid
=None, dc_rid
=None):
1150 if next_rid
is None:
1153 # Provision does not make much sense values larger than 1000000000
1154 # as the upper range of the rIDAvailablePool is 1073741823 and
1155 # we don't want to create a domain that cannot allocate rids.
1156 if next_rid
< 1000 or next_rid
> 1000000000:
1157 error
= "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid
)
1158 error
+= "the valid range is %u-%u. The default is %u." % (
1159 1000, 1000000000, 1000)
1160 raise ProvisioningError(error
)
1162 # ATTENTION: Do NOT change these default values without discussion with the
1163 # team and/or release manager. They have a big impact on the whole program!
1164 domainControllerFunctionality
= DS_DOMAIN_FUNCTION_2008_R2
1166 if dom_for_fun_level
is None:
1167 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2003
1169 if dom_for_fun_level
> domainControllerFunctionality
:
1170 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!")
1172 domainFunctionality
= dom_for_fun_level
1173 forestFunctionality
= dom_for_fun_level
1175 # Set the NTDS settings DN manually - in order to have it already around
1176 # before the provisioned tree exists and we connect
1177 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1179 samdb
.transaction_start()
1181 # Set the domain functionality levels onto the database.
1182 # Various module (the password_hash module in particular) need
1183 # to know what level of AD we are emulating.
1185 # These will be fixed into the database via the database
1186 # modifictions below, but we need them set from the start.
1187 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
1188 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
1189 samdb
.set_opaque_integer("domainControllerFunctionality",
1190 domainControllerFunctionality
)
1192 samdb
.set_domain_sid(str(domainsid
))
1193 samdb
.set_invocation_id(invocationid
)
1195 logger
.info("Adding DomainDN: %s" % names
.domaindn
)
1197 # impersonate domain admin
1198 admin_session_info
= admin_session(lp
, str(domainsid
))
1199 samdb
.set_session_info(admin_session_info
)
1200 if domainguid
is not None:
1201 domainguid_line
= "objectGUID: %s\n-" % domainguid
1203 domainguid_line
= ""
1205 descr
= b64encode(get_domain_descriptor(domainsid
))
1206 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
1207 "DOMAINDN": names
.domaindn
,
1208 "DOMAINSID": str(domainsid
),
1209 "DESCRIPTOR": descr
,
1210 "DOMAINGUID": domainguid_line
1213 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
1214 "DOMAINDN": names
.domaindn
,
1215 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1216 "NEXTRID": str(next_rid
),
1217 "DEFAULTSITE": names
.sitename
,
1218 "CONFIGDN": names
.configdn
,
1219 "POLICYGUID": policyguid
,
1220 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1221 "SAMBA_VERSION_STRING": version
1224 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1225 if fill
== FILL_FULL
:
1226 logger
.info("Adding configuration container")
1227 descr
= b64encode(get_config_descriptor(domainsid
))
1228 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
1229 "CONFIGDN": names
.configdn
,
1230 "DESCRIPTOR": descr
,
1233 # The LDIF here was created when the Schema object was constructed
1234 logger
.info("Setting up sam.ldb schema")
1235 samdb
.add_ldif(schema
.schema_dn_add
, controls
=["relax:0"])
1236 samdb
.modify_ldif(schema
.schema_dn_modify
)
1237 samdb
.write_prefixes_from_schema()
1238 samdb
.add_ldif(schema
.schema_data
, controls
=["relax:0"])
1239 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
1240 {"SCHEMADN": names
.schemadn
})
1242 # Now register this container in the root of the forest
1243 msg
= ldb
.Message(ldb
.Dn(samdb
, names
.domaindn
))
1244 msg
["subRefs"] = ldb
.MessageElement(names
.configdn
, ldb
.FLAG_MOD_ADD
,
1248 samdb
.transaction_cancel()
1251 samdb
.transaction_commit()
1253 samdb
.transaction_start()
1255 samdb
.invocation_id
= invocationid
1257 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1258 if fill
== FILL_FULL
:
1259 logger
.info("Setting up sam.ldb configuration data")
1260 partitions_descr
= b64encode(get_config_partitions_descriptor(domainsid
))
1261 sites_descr
= b64encode(get_config_sites_descriptor(domainsid
))
1262 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
1263 "CONFIGDN": names
.configdn
,
1264 "NETBIOSNAME": names
.netbiosname
,
1265 "DEFAULTSITE": names
.sitename
,
1266 "DNSDOMAIN": names
.dnsdomain
,
1267 "DOMAIN": names
.domain
,
1268 "SCHEMADN": names
.schemadn
,
1269 "DOMAINDN": names
.domaindn
,
1270 "SERVERDN": names
.serverdn
,
1271 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
1272 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1273 "PARTITIONS_DESCRIPTOR": partitions_descr
,
1274 "SITES_DESCRIPTOR": sites_descr
,
1277 logger
.info("Setting up display specifiers")
1278 display_specifiers_ldif
= read_ms_ldif(
1279 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1280 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
,
1281 {"CONFIGDN": names
.configdn
})
1282 check_all_substituted(display_specifiers_ldif
)
1283 samdb
.add_ldif(display_specifiers_ldif
)
1285 logger
.info("Adding users container")
1286 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
1287 "DOMAINDN": names
.domaindn
})
1288 logger
.info("Modifying users container")
1289 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
1290 "DOMAINDN": names
.domaindn
})
1291 logger
.info("Adding computers container")
1292 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
1293 "DOMAINDN": names
.domaindn
})
1294 logger
.info("Modifying computers container")
1295 setup_modify_ldif(samdb
,
1296 setup_path("provision_computers_modify.ldif"), {
1297 "DOMAINDN": names
.domaindn
})
1298 logger
.info("Setting up sam.ldb data")
1299 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1300 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1301 "DOMAINDN": names
.domaindn
,
1302 "NETBIOSNAME": names
.netbiosname
,
1303 "DEFAULTSITE": names
.sitename
,
1304 "CONFIGDN": names
.configdn
,
1305 "SERVERDN": names
.serverdn
,
1306 "RIDAVAILABLESTART": str(next_rid
+ 600),
1307 "POLICYGUID_DC": policyguid_dc
1310 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1311 if fill
== FILL_FULL
:
1312 setup_modify_ldif(samdb
,
1313 setup_path("provision_configuration_references.ldif"), {
1314 "CONFIGDN": names
.configdn
,
1315 "SCHEMADN": names
.schemadn
})
1317 logger
.info("Setting up well known security principals")
1318 setup_add_ldif(samdb
, setup_path("provision_well_known_sec_princ.ldif"), {
1319 "CONFIGDN": names
.configdn
,
1322 if fill
== FILL_FULL
or fill
== FILL_SUBDOMAIN
:
1323 setup_modify_ldif(samdb
,
1324 setup_path("provision_basedn_references.ldif"),
1325 {"DOMAINDN": names
.domaindn
})
1327 logger
.info("Setting up sam.ldb users and groups")
1328 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1329 "DOMAINDN": names
.domaindn
,
1330 "DOMAINSID": str(domainsid
),
1331 "ADMINPASS_B64": b64encode(adminpass
.encode('utf-16-le')),
1332 "KRBTGTPASS_B64": b64encode(krbtgtpass
.encode('utf-16-le'))
1335 logger
.info("Setting up self join")
1336 setup_self_join(samdb
, admin_session_info
, names
=names
, fill
=fill
,
1337 invocationid
=invocationid
,
1338 dns_backend
=dns_backend
,
1340 machinepass
=machinepass
,
1341 domainsid
=domainsid
,
1344 policyguid
=policyguid
,
1345 policyguid_dc
=policyguid_dc
,
1346 domainControllerFunctionality
=domainControllerFunctionality
,
1349 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1350 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1351 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
)
1352 assert isinstance(names
.ntdsguid
, str)
1354 samdb
.transaction_cancel()
1357 samdb
.transaction_commit()
1362 FILL_SUBDOMAIN
= "SUBDOMAIN"
1363 FILL_NT4SYNC
= "NT4SYNC"
1365 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1366 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)"
1369 def set_dir_acl(path
, acl
, lp
, domsid
, use_ntvfs
, passdb
):
1370 setntacl(lp
, path
, acl
, domsid
, use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1371 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1373 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1374 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1376 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1377 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1380 def set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
):
1381 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1384 :param sysvol: Physical path for the sysvol folder
1385 :param dnsdomain: The DNS name of the domain
1386 :param domainsid: The SID of the domain
1387 :param domaindn: The DN of the domain (ie. DC=...)
1388 :param samdb: An LDB object on the SAM db
1389 :param lp: an LP object
1392 # Set ACL for GPO root folder
1393 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1394 setntacl(lp
, root_policy_path
, POLICIES_ACL
, str(domainsid
),
1395 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1397 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1398 attrs
=["cn", "nTSecurityDescriptor"],
1399 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1402 acl
= ndr_unpack(security
.descriptor
,
1403 str(policy
["nTSecurityDescriptor"])).as_sddl()
1404 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1405 set_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1406 str(domainsid
), use_ntvfs
,
1410 def setsysvolacl(samdb
, netlogon
, sysvol
, uid
, gid
, domainsid
, dnsdomain
,
1411 domaindn
, lp
, use_ntvfs
):
1412 """Set the ACL for the sysvol share and the subfolders
1414 :param samdb: An LDB object on the SAM db
1415 :param netlogon: Physical path for the netlogon folder
1416 :param sysvol: Physical path for the sysvol folder
1417 :param uid: The UID of the "Administrator" user
1418 :param gid: The GID of the "Domain adminstrators" group
1419 :param domainsid: The SID of the domain
1420 :param dnsdomain: The DNS name of the domain
1421 :param domaindn: The DN of the domain (ie. DC=...)
1426 # This will ensure that the smbd code we are running when setting ACLs
1427 # is initialised with the smb.conf
1428 s3conf
= s3param
.get_context()
1429 s3conf
.load(lp
.configfile
)
1430 # ensure we are using the right samba_dsdb passdb backend, no matter what
1431 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1432 passdb
.reload_static_pdb()
1434 # ensure that we init the samba_dsdb backend, so the domain sid is
1435 # marked in secrets.tdb
1436 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1438 # now ensure everything matches correctly, to avoid wierd issues
1439 if passdb
.get_global_sam_sid() != domainsid
:
1440 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
))
1442 domain_info
= s4_passdb
.domain_info()
1443 if domain_info
["dom_sid"] != domainsid
:
1444 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
))
1446 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1447 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()))
1452 os
.chown(sysvol
, -1, gid
)
1458 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1459 setntacl(lp
,sysvol
, SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=s4_passdb
)
1460 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1462 if use_ntvfs
and canchown
:
1463 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1464 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=s4_passdb
)
1466 if use_ntvfs
and canchown
:
1467 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1468 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=s4_passdb
)
1470 # Set acls on Policy folder and policies folders
1471 set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
=s4_passdb
)
1473 def acl_type(direct_db_access
):
1474 if direct_db_access
:
1479 def check_dir_acl(path
, acl
, lp
, domainsid
, direct_db_access
):
1480 fsacl
= getntacl(lp
, path
, direct_db_access
=direct_db_access
)
1481 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1482 if fsacl_sddl
!= acl
:
1483 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
))
1485 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1487 fsacl
= getntacl(lp
, os
.path
.join(root
, name
), direct_db_access
=direct_db_access
)
1489 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1490 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1491 if fsacl_sddl
!= acl
:
1492 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
))
1495 fsacl
= getntacl(lp
, os
.path
.join(root
, name
), direct_db_access
=direct_db_access
)
1497 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1498 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1499 if fsacl_sddl
!= acl
:
1500 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
))
1503 def check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1505 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1508 :param sysvol: Physical path for the sysvol folder
1509 :param dnsdomain: The DNS name of the domain
1510 :param domainsid: The SID of the domain
1511 :param domaindn: The DN of the domain (ie. DC=...)
1512 :param samdb: An LDB object on the SAM db
1513 :param lp: an LP object
1516 # Set ACL for GPO root folder
1517 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1518 fsacl
= getntacl(lp
, root_policy_path
, direct_db_access
=direct_db_access
)
1520 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access
), root_policy_path
))
1521 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1522 if fsacl_sddl
!= POLICIES_ACL
:
1523 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
))
1524 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1525 attrs
=["cn", "nTSecurityDescriptor"],
1526 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1529 acl
= ndr_unpack(security
.descriptor
,
1530 str(policy
["nTSecurityDescriptor"])).as_sddl()
1531 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1532 check_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1533 domainsid
, direct_db_access
)
1536 def checksysvolacl(samdb
, netlogon
, sysvol
, domainsid
, dnsdomain
, domaindn
,
1538 """Set the ACL for the sysvol share and the subfolders
1540 :param samdb: An LDB object on the SAM db
1541 :param netlogon: Physical path for the netlogon folder
1542 :param sysvol: Physical path for the sysvol folder
1543 :param uid: The UID of the "Administrator" user
1544 :param gid: The GID of the "Domain adminstrators" group
1545 :param domainsid: The SID of the domain
1546 :param dnsdomain: The DNS name of the domain
1547 :param domaindn: The DN of the domain (ie. DC=...)
1550 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1551 s3conf
= s3param
.get_context()
1552 s3conf
.load(lp
.configfile
)
1553 # ensure we are using the right samba_dsdb passdb backend, no matter what
1554 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1555 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1556 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1558 # now ensure everything matches correctly, to avoid wierd issues
1559 if passdb
.get_global_sam_sid() != domainsid
:
1560 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
))
1562 domain_info
= s4_passdb
.domain_info()
1563 if domain_info
["dom_sid"] != domainsid
:
1564 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
))
1566 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1567 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()))
1569 # Ensure we can read this directly, and via the smbd VFS
1570 for direct_db_access
in [True, False]:
1571 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1572 for dir_path
in [os
.path
.join(sysvol
, dnsdomain
), netlogon
]:
1573 fsacl
= getntacl(lp
, dir_path
, direct_db_access
=direct_db_access
)
1575 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access
), dir_path
))
1576 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1577 if fsacl_sddl
!= SYSVOL_ACL
:
1578 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
))
1580 # Check acls on Policy folder and policies folders
1581 check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1585 def interface_ips_v4(lp
):
1586 """return only IPv4 IPs"""
1587 ips
= samba
.interface_ips(lp
, False)
1590 if i
.find(':') == -1:
1595 def interface_ips_v6(lp
, linklocal
=False):
1596 """return only IPv6 IPs"""
1597 ips
= samba
.interface_ips(lp
, False)
1600 if i
.find(':') != -1 and (linklocal
or i
.find('%') == -1):
1605 def provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
1606 domainsid
, schema
=None,
1607 targetdir
=None, samdb_fill
=FILL_FULL
,
1608 hostip
=None, hostip6
=None,
1609 next_rid
=1000, dc_rid
=None, adminpass
=None, krbtgtpass
=None,
1610 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1611 invocationid
=None, machinepass
=None, ntdsguid
=None,
1612 dns_backend
=None, dnspass
=None,
1613 serverrole
=None, dom_for_fun_level
=None,
1614 am_rodc
=False, lp
=None, use_ntvfs
=False, skip_sysvolacl
=False):
1615 # create/adapt the group policy GUIDs
1616 # Default GUID for default policy are described at
1617 # "How Core Group Policy Works"
1618 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1619 if policyguid
is None:
1620 policyguid
= DEFAULT_POLICY_GUID
1621 policyguid
= policyguid
.upper()
1622 if policyguid_dc
is None:
1623 policyguid_dc
= DEFAULT_DC_POLICY_GUID
1624 policyguid_dc
= policyguid_dc
.upper()
1626 if invocationid
is None:
1627 invocationid
= str(uuid
.uuid4())
1629 if krbtgtpass
is None:
1630 krbtgtpass
= samba
.generate_random_password(128, 255)
1631 if machinepass
is None:
1632 machinepass
= samba
.generate_random_password(128, 255)
1634 dnspass
= samba
.generate_random_password(128, 255)
1636 samdb
= fill_samdb(samdb
, lp
, names
, logger
=logger
,
1637 domainsid
=domainsid
, schema
=schema
, domainguid
=domainguid
,
1638 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1639 fill
=samdb_fill
, adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1640 invocationid
=invocationid
, machinepass
=machinepass
,
1641 dns_backend
=dns_backend
, dnspass
=dnspass
,
1642 ntdsguid
=ntdsguid
, serverrole
=serverrole
,
1643 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
1644 next_rid
=next_rid
, dc_rid
=dc_rid
)
1646 if serverrole
== "active directory domain controller":
1648 # Set up group policies (domain policy and domain controller
1650 create_default_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
,
1652 if not skip_sysvolacl
:
1653 setsysvolacl(samdb
, paths
.netlogon
, paths
.sysvol
, paths
.root_uid
,
1654 paths
.root_gid
, domainsid
, names
.dnsdomain
,
1655 names
.domaindn
, lp
, use_ntvfs
)
1657 logger
.info("Setting acl on sysvol skipped")
1659 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1660 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1661 netbiosname
=names
.netbiosname
, domainsid
=domainsid
,
1662 machinepass
=machinepass
, secure_channel_type
=SEC_CHAN_BDC
)
1664 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1665 # In future, this might be determined from some configuration
1666 kerberos_enctypes
= str(ENC_ALL_TYPES
)
1669 msg
= ldb
.Message(ldb
.Dn(samdb
,
1670 samdb
.searchone("distinguishedName",
1671 expression
="samAccountName=%s$" % names
.netbiosname
,
1672 scope
=ldb
.SCOPE_SUBTREE
)))
1673 msg
["msDS-SupportedEncryptionTypes"] = ldb
.MessageElement(
1674 elements
=kerberos_enctypes
, flags
=ldb
.FLAG_MOD_REPLACE
,
1675 name
="msDS-SupportedEncryptionTypes")
1677 except ldb
.LdbError
, (enum
, estr
):
1678 if enum
!= ldb
.ERR_NO_SUCH_ATTRIBUTE
:
1679 # It might be that this attribute does not exist in this schema
1682 setup_ad_dns(samdb
, secrets_ldb
, domainsid
, names
, paths
, lp
, logger
,
1683 hostip
=hostip
, hostip6
=hostip6
, dns_backend
=dns_backend
,
1684 dnspass
=dnspass
, os_level
=dom_for_fun_level
,
1685 targetdir
=targetdir
, site
=DEFAULTSITE
)
1687 domainguid
= samdb
.searchone(basedn
=samdb
.get_default_basedn(),
1688 attribute
="objectGUID")
1689 assert isinstance(domainguid
, str)
1691 lastProvisionUSNs
= get_last_provision_usn(samdb
)
1692 maxUSN
= get_max_usn(samdb
, str(names
.rootdn
))
1693 if lastProvisionUSNs
is not None:
1694 update_provision_usn(samdb
, 0, maxUSN
, invocationid
, 1)
1696 set_provision_usn(samdb
, 0, maxUSN
, invocationid
)
1698 logger
.info("Setting up sam.ldb rootDSE marking as synchronized")
1699 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"),
1700 { 'NTDSGUID' : names
.ntdsguid
})
1702 # fix any dangling GUIDs from the provision
1703 logger
.info("Fixing provision GUIDs")
1704 chk
= dbcheck(samdb
, samdb_schema
=samdb
, verbose
=False, fix
=True, yes
=True,
1706 samdb
.transaction_start()
1708 # a small number of GUIDs are missing because of ordering issues in the
1710 for schema_obj
in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1711 chk
.check_database(DN
="%s,%s" % (schema_obj
, names
.schemadn
),
1712 scope
=ldb
.SCOPE_BASE
,
1713 attrs
=['defaultObjectCategory'])
1714 chk
.check_database(DN
="CN=IP Security,CN=System,%s" % names
.domaindn
,
1715 scope
=ldb
.SCOPE_ONELEVEL
,
1716 attrs
=['ipsecOwnersReference',
1717 'ipsecFilterReference',
1718 'ipsecISAKMPReference',
1719 'ipsecNegotiationPolicyReference',
1720 'ipsecNFAReference'])
1722 samdb
.transaction_cancel()
1725 samdb
.transaction_commit()
1729 "ROLE_STANDALONE": "standalone server",
1730 "ROLE_DOMAIN_MEMBER": "member server",
1731 "ROLE_DOMAIN_BDC": "active directory domain controller",
1732 "ROLE_DOMAIN_PDC": "active directory domain controller",
1733 "dc": "active directory domain controller",
1734 "member": "member server",
1735 "domain controller": "active directory domain controller",
1736 "active directory domain controller": "active directory domain controller",
1737 "member server": "member server",
1738 "standalone": "standalone server",
1739 "standalone server": "standalone server",
1743 def sanitize_server_role(role
):
1744 """Sanitize a server role name.
1746 :param role: Server role
1747 :raise ValueError: If the role can not be interpreted
1748 :return: Sanitized server role (one of "member server",
1749 "active directory domain controller", "standalone server")
1752 return _ROLES_MAP
[role
]
1754 raise ValueError(role
)
1757 def provision_fake_ypserver(logger
, samdb
, domaindn
, netbiosname
, nisdomain
,
1759 """Create AD entries for the fake ypserver.
1761 This is needed for being able to manipulate posix attrs via ADUC.
1763 samdb
.transaction_start()
1765 logger
.info("Setting up fake yp server settings")
1766 setup_add_ldif(samdb
, setup_path("ypServ30.ldif"), {
1767 "DOMAINDN": domaindn
,
1768 "NETBIOSNAME": netbiosname
,
1769 "NISDOMAIN": nisdomain
,
1772 samdb
.transaction_cancel()
1775 samdb
.transaction_commit()
1778 def provision(logger
, session_info
, credentials
, smbconf
=None,
1779 targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None, rootdn
=None,
1780 domaindn
=None, schemadn
=None, configdn
=None, serverdn
=None,
1781 domain
=None, hostname
=None, hostip
=None, hostip6
=None, domainsid
=None,
1782 next_rid
=1000, dc_rid
=None, adminpass
=None, ldapadminpass
=None,
1783 krbtgtpass
=None, domainguid
=None, policyguid
=None, policyguid_dc
=None,
1784 dns_backend
=None, dns_forwarder
=None, dnspass
=None,
1785 invocationid
=None, machinepass
=None, ntdsguid
=None,
1786 root
=None, nobody
=None, users
=None, backup
=None, aci
=None,
1787 serverrole
=None, dom_for_fun_level
=None, backend_type
=None,
1788 sitename
=None, ol_mmr_urls
=None, ol_olc
=None, slapd_path
="/bin/false",
1789 useeadb
=False, am_rodc
=False, lp
=None, use_ntvfs
=False,
1790 use_rfc2307
=False, maxuid
=None, maxgid
=None, skip_sysvolacl
=True):
1793 :note: caution, this wipes all existing data!
1797 serverrole
= sanitize_server_role(serverrole
)
1799 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole
)
1801 if ldapadminpass
is None:
1802 # Make a new, random password between Samba and it's LDAP server
1803 ldapadminpass
= samba
.generate_random_password(128, 255)
1805 if backend_type
is None:
1806 backend_type
= "ldb"
1808 if domainsid
is None:
1809 domainsid
= security
.random_sid()
1811 domainsid
= security
.dom_sid(domainsid
)
1813 root_uid
= findnss_uid([root
or "root"])
1814 nobody_uid
= findnss_uid([nobody
or "nobody"])
1815 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
1816 root_gid
= pwd
.getpwuid(root_uid
).pw_gid
1819 bind_gid
= findnss_gid(["bind", "named"])
1823 if targetdir
is not None:
1824 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
1825 elif smbconf
is None:
1826 smbconf
= samba
.param
.default_path()
1827 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
1828 os
.makedirs(os
.path
.dirname(smbconf
))
1830 server_services
= []
1833 global_param
["idmap_ldb:use rfc2307"] = ["yes"]
1835 if dns_backend
!= "SAMBA_INTERNAL":
1836 server_services
.append("-dns")
1838 if dns_forwarder
is not None:
1839 global_param
["dns forwarder"] = [dns_forwarder
]
1842 server_services
.append("+smb")
1843 server_services
.append("-s3fs")
1844 global_param
["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1846 if len(server_services
) > 0:
1847 global_param
["server services"] = server_services
1849 # only install a new smb.conf if there isn't one there already
1850 if os
.path
.exists(smbconf
):
1851 # if Samba Team members can't figure out the weird errors
1852 # loading an empty smb.conf gives, then we need to be smarter.
1853 # Pretend it just didn't exist --abartlet
1854 f
= open(smbconf
, 'r')
1856 data
= f
.read().lstrip()
1859 if data
is None or data
== "":
1860 make_smbconf(smbconf
, hostname
, domain
, realm
,
1861 targetdir
, serverrole
=serverrole
,
1862 eadb
=useeadb
, use_ntvfs
=use_ntvfs
,
1863 lp
=lp
, global_param
=global_param
)
1865 make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
1866 serverrole
=serverrole
,
1867 eadb
=useeadb
, use_ntvfs
=use_ntvfs
, lp
=lp
, global_param
=global_param
)
1870 lp
= samba
.param
.LoadParm()
1872 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
1873 dnsdomain
=realm
, serverrole
=serverrole
, domaindn
=domaindn
,
1874 configdn
=configdn
, schemadn
=schemadn
, serverdn
=serverdn
,
1875 sitename
=sitename
, rootdn
=rootdn
)
1876 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
1878 paths
.bind_gid
= bind_gid
1879 paths
.root_uid
= root_uid
;
1880 paths
.root_gid
= root_gid
1883 logger
.info("Looking up IPv4 addresses")
1884 hostips
= interface_ips_v4(lp
)
1885 if len(hostips
) > 0:
1887 if len(hostips
) > 1:
1888 logger
.warning("More than one IPv4 address found. Using %s",
1890 if hostip
== "127.0.0.1":
1893 logger
.warning("No IPv4 address will be assigned")
1896 logger
.info("Looking up IPv6 addresses")
1897 hostips
= interface_ips_v6(lp
, linklocal
=False)
1899 hostip6
= hostips
[0]
1900 if len(hostips
) > 1:
1901 logger
.warning("More than one IPv6 address found. Using %s", hostip6
)
1903 logger
.warning("No IPv6 address will be assigned")
1905 names
.hostip
= hostip
1906 names
.hostip6
= hostip6
1908 if serverrole
is None:
1909 serverrole
= lp
.get("server role")
1911 if not os
.path
.exists(paths
.private_dir
):
1912 os
.mkdir(paths
.private_dir
)
1913 if not os
.path
.exists(os
.path
.join(paths
.private_dir
, "tls")):
1914 os
.mkdir(os
.path
.join(paths
.private_dir
, "tls"))
1915 if not os
.path
.exists(paths
.state_dir
):
1916 os
.mkdir(paths
.state_dir
)
1918 if paths
.sysvol
and not os
.path
.exists(paths
.sysvol
):
1919 os
.makedirs(paths
.sysvol
, 0775)
1921 if not use_ntvfs
and serverrole
== "active directory domain controller":
1922 s3conf
= s3param
.get_context()
1923 s3conf
.load(lp
.configfile
)
1925 if paths
.sysvol
is None:
1926 raise MissingShareError("sysvol", paths
.smbconf
)
1928 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(paths
.sysvol
))
1931 smbd
.set_simple_acl(file.name
, 0755, root_gid
)
1933 if not smbd
.have_posix_acls():
1934 # This clue is only strictly correct for RPM and
1935 # Debian-like Linux systems, but hopefully other users
1936 # will get enough clue from it.
1937 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.")
1939 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1941 smbd
.chown(file.name
, root_uid
, root_gid
)
1943 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1947 ldapi_url
= "ldapi://%s" % urllib
.quote(paths
.s4_ldapi_path
, safe
="")
1949 schema
= Schema(domainsid
, invocationid
=invocationid
,
1950 schemadn
=names
.schemadn
)
1952 if backend_type
== "ldb":
1953 provision_backend
= LDBBackend(backend_type
, paths
=paths
,
1954 lp
=lp
, credentials
=credentials
,
1955 names
=names
, logger
=logger
)
1956 elif backend_type
== "existing":
1957 # If support for this is ever added back, then the URI will need to be
1959 provision_backend
= ExistingBackend(backend_type
, paths
=paths
,
1960 lp
=lp
, credentials
=credentials
,
1961 names
=names
, logger
=logger
,
1962 ldap_backend_forced_uri
=None)
1963 elif backend_type
== "fedora-ds":
1964 provision_backend
= FDSBackend(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
,
1970 elif backend_type
== "openldap":
1971 provision_backend
= OpenLDAPBackend(backend_type
, paths
=paths
,
1972 lp
=lp
, credentials
=credentials
,
1973 names
=names
, logger
=logger
, domainsid
=domainsid
,
1974 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
1975 slapd_path
=slapd_path
, ol_mmr_urls
=ol_mmr_urls
)
1977 raise ValueError("Unknown LDAP backend type selected")
1979 provision_backend
.init()
1980 provision_backend
.start()
1982 # only install a new shares config db if there is none
1983 if not os
.path
.exists(paths
.shareconf
):
1984 logger
.info("Setting up share.ldb")
1985 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
, lp
=lp
)
1986 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
1988 logger
.info("Setting up secrets.ldb")
1989 secrets_ldb
= setup_secretsdb(paths
,
1990 session_info
=session_info
,
1991 backend_credentials
=provision_backend
.secrets_credentials
, lp
=lp
)
1994 logger
.info("Setting up the registry")
1995 setup_registry(paths
.hklm
, session_info
, lp
=lp
)
1997 logger
.info("Setting up the privileges database")
1998 setup_privileges(paths
.privilege
, session_info
, lp
=lp
)
2000 logger
.info("Setting up idmap db")
2001 idmap
= setup_idmapdb(paths
.idmapdb
, session_info
=session_info
, lp
=lp
)
2003 setup_name_mappings(idmap
, sid
=str(domainsid
),
2004 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
2005 users_gid
=users_gid
, root_gid
=root_gid
)
2007 logger
.info("Setting up SAM db")
2008 samdb
= setup_samdb(paths
.samdb
, session_info
,
2009 provision_backend
, lp
, names
, logger
=logger
,
2010 serverrole
=serverrole
,
2011 schema
=schema
, fill
=samdb_fill
, am_rodc
=am_rodc
)
2013 if serverrole
== "active directory domain controller":
2014 if paths
.netlogon
is None:
2015 raise MissingShareError("netlogon", paths
.smbconf
)
2017 if paths
.sysvol
is None:
2018 raise MissingShareError("sysvol", paths
.smbconf
)
2020 if not os
.path
.isdir(paths
.netlogon
):
2021 os
.makedirs(paths
.netlogon
, 0755)
2023 if adminpass
is None:
2024 adminpass
= samba
.generate_random_password(12, 32)
2025 adminpass_generated
= True
2027 adminpass_generated
= False
2029 if samdb_fill
== FILL_FULL
:
2030 provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
2031 schema
=schema
, targetdir
=targetdir
, samdb_fill
=samdb_fill
,
2032 hostip
=hostip
, hostip6
=hostip6
, domainsid
=domainsid
,
2033 next_rid
=next_rid
, dc_rid
=dc_rid
, adminpass
=adminpass
,
2034 krbtgtpass
=krbtgtpass
, domainguid
=domainguid
,
2035 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
2036 invocationid
=invocationid
, machinepass
=machinepass
,
2037 ntdsguid
=ntdsguid
, dns_backend
=dns_backend
,
2038 dnspass
=dnspass
, serverrole
=serverrole
,
2039 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
2040 lp
=lp
, use_ntvfs
=use_ntvfs
,
2041 skip_sysvolacl
=skip_sysvolacl
)
2043 create_krb5_conf(paths
.krb5conf
,
2044 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
2046 logger
.info("A Kerberos configuration suitable for Samba 4 has been "
2047 "generated at %s", paths
.krb5conf
)
2049 if serverrole
== "active directory domain controller":
2050 create_dns_update_list(lp
, logger
, paths
)
2052 backend_result
= provision_backend
.post_setup()
2053 provision_backend
.shutdown()
2056 secrets_ldb
.transaction_cancel()
2059 # Now commit the secrets.ldb to disk
2060 secrets_ldb
.transaction_commit()
2062 # the commit creates the dns.keytab, now chown it
2063 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
2064 if os
.path
.isfile(dns_keytab_path
) and paths
.bind_gid
is not None:
2066 os
.chmod(dns_keytab_path
, 0640)
2067 os
.chown(dns_keytab_path
, -1, paths
.bind_gid
)
2069 if not os
.environ
.has_key('SAMBA_SELFTEST'):
2070 logger
.info("Failed to chown %s to bind gid %u",
2071 dns_keytab_path
, paths
.bind_gid
)
2073 result
= ProvisionResult()
2074 result
.server_role
= serverrole
2075 result
.domaindn
= domaindn
2076 result
.paths
= paths
2077 result
.names
= names
2079 result
.samdb
= samdb
2080 result
.idmap
= idmap
2081 result
.domainsid
= str(domainsid
)
2083 if samdb_fill
== FILL_FULL
:
2084 result
.adminpass_generated
= adminpass_generated
2085 result
.adminpass
= adminpass
2087 result
.adminpass_generated
= False
2088 result
.adminpass
= None
2090 result
.backend_result
= backend_result
2093 provision_fake_ypserver(logger
=logger
, samdb
=samdb
,
2094 domaindn
=names
.domaindn
, netbiosname
=names
.netbiosname
,
2095 nisdomain
=names
.domain
.lower(), maxuid
=maxuid
, maxgid
=maxgid
)
2100 def provision_become_dc(smbconf
=None, targetdir
=None,
2101 realm
=None, rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
2102 serverdn
=None, domain
=None, hostname
=None, domainsid
=None,
2103 adminpass
=None, krbtgtpass
=None, domainguid
=None, policyguid
=None,
2104 policyguid_dc
=None, invocationid
=None, machinepass
=None, dnspass
=None,
2105 dns_backend
=None, root
=None, nobody
=None, users
=None,
2106 backup
=None, serverrole
=None, ldap_backend
=None,
2107 ldap_backend_type
=None, sitename
=None, debuglevel
=1, use_ntvfs
=False):
2109 logger
= logging
.getLogger("provision")
2110 samba
.set_debug_level(debuglevel
)
2112 res
= provision(logger
, system_session(), None,
2113 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
2114 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
2115 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
2116 hostname
=hostname
, hostip
=None, domainsid
=domainsid
,
2117 machinepass
=machinepass
,
2118 serverrole
="active directory domain controller",
2119 sitename
=sitename
, dns_backend
=dns_backend
, dnspass
=dnspass
,
2120 use_ntvfs
=use_ntvfs
)
2121 res
.lp
.set("debuglevel", str(debuglevel
))
2125 def create_krb5_conf(path
, dnsdomain
, hostname
, realm
):
2126 """Write out a file containing zone statements suitable for inclusion in a
2127 named.conf file (including GSS-TSIG configuration).
2129 :param path: Path of the new named.conf file.
2130 :param dnsdomain: DNS Domain name
2131 :param hostname: Local hostname
2132 :param realm: Realm name
2134 setup_file(setup_path("krb5.conf"), path
, {
2135 "DNSDOMAIN": dnsdomain
,
2136 "HOSTNAME": hostname
,
2141 class ProvisioningError(Exception):
2142 """A generic provision error."""
2144 def __init__(self
, value
):
2148 return "ProvisioningError: " + self
.value
2151 class InvalidNetbiosName(Exception):
2152 """A specified name was not a valid NetBIOS name."""
2154 def __init__(self
, name
):
2155 super(InvalidNetbiosName
, self
).__init
__(
2156 "The name '%r' is not a valid NetBIOS name" % name
)
2159 class MissingShareError(ProvisioningError
):
2161 def __init__(self
, name
, smbconf
):
2162 super(MissingShareError
, self
).__init
__(
2163 "Existing smb.conf does not have a [%s] share, but you are "
2164 "configuring a DC. Please remove %s or add the share manually." %