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
,
84 from samba
.provision
.common
import (
89 from samba
.provision
.sambadns
import (
91 create_dns_update_list
96 from samba
.schema
import Schema
97 from samba
.samdb
import SamDB
98 from samba
.dbchecker
import dbcheck
101 DEFAULT_POLICY_GUID
= "31B2F340-016D-11D2-945F-00C04FB984F9"
102 DEFAULT_DC_POLICY_GUID
= "6AC1786C-016F-11D2-945F-00C04fB984F9"
103 DEFAULTSITE
= "Default-First-Site-Name"
104 LAST_PROVISION_USN_ATTRIBUTE
= "lastProvisionUSN"
107 class ProvisionPaths(object):
110 self
.shareconf
= None
121 self
.dns_keytab
= None
124 self
.private_dir
= None
125 self
.state_dir
= None
128 class ProvisionNames(object):
135 self
.ldapmanagerdn
= None
136 self
.dnsdomain
= None
138 self
.netbiosname
= None
145 def find_provision_key_parameters(samdb
, secretsdb
, idmapdb
, paths
, smbconf
,
147 """Get key provision parameters (realm, domain, ...) from a given provision
149 :param samdb: An LDB object connected to the sam.ldb file
150 :param secretsdb: An LDB object connected to the secrets.ldb file
151 :param idmapdb: An LDB object connected to the idmap.ldb file
152 :param paths: A list of path to provision object
153 :param smbconf: Path to the smb.conf file
154 :param lp: A LoadParm object
155 :return: A list of key provision parameters
157 names
= ProvisionNames()
158 names
.adminpass
= None
160 # NT domain, kerberos realm, root dn, domain dn, domain dns name
161 names
.domain
= string
.upper(lp
.get("workgroup"))
162 names
.realm
= lp
.get("realm")
163 names
.dnsdomain
= names
.realm
.lower()
164 basedn
= samba
.dn_from_dns_name(names
.dnsdomain
)
165 names
.realm
= string
.upper(names
.realm
)
167 # Get the netbiosname first (could be obtained from smb.conf in theory)
168 res
= secretsdb
.search(expression
="(flatname=%s)" %
169 names
.domain
,base
="CN=Primary Domains",
170 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
171 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
173 names
.smbconf
= smbconf
175 # That's a bit simplistic but it's ok as long as we have only 3
177 current
= samdb
.search(expression
="(objectClass=*)",
178 base
="", scope
=ldb
.SCOPE_BASE
,
179 attrs
=["defaultNamingContext", "schemaNamingContext",
180 "configurationNamingContext","rootDomainNamingContext"])
182 names
.configdn
= current
[0]["configurationNamingContext"]
183 configdn
= str(names
.configdn
)
184 names
.schemadn
= current
[0]["schemaNamingContext"]
185 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
,
186 current
[0]["defaultNamingContext"][0]))):
187 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
188 "is not the same ..." % (paths
.samdb
,
189 str(current
[0]["defaultNamingContext"][0]),
190 paths
.smbconf
, basedn
)))
192 names
.domaindn
=current
[0]["defaultNamingContext"]
193 names
.rootdn
=current
[0]["rootDomainNamingContext"]
195 res3
= samdb
.search(expression
="(objectClass=site)",
196 base
="CN=Sites," + configdn
, scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn"])
197 names
.sitename
= str(res3
[0]["cn"])
199 # dns hostname and server dn
200 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
201 base
="OU=Domain Controllers,%s" % basedn
,
202 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
203 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("." + names
.dnsdomain
, "")
205 server_res
= samdb
.search(expression
="serverReference=%s" % res4
[0].dn
,
206 attrs
=[], base
=configdn
)
207 names
.serverdn
= server_res
[0].dn
209 # invocation id/objectguid
210 res5
= samdb
.search(expression
="(objectClass=*)",
211 base
="CN=NTDS Settings,%s" % str(names
.serverdn
),
212 scope
=ldb
.SCOPE_BASE
,
213 attrs
=["invocationID", "objectGUID"])
214 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
215 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
218 res6
= samdb
.search(expression
="(objectClass=*)", base
=basedn
,
219 scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID",
220 "objectSid","msDS-Behavior-Version" ])
221 names
.domainguid
= str(ndr_unpack(misc
.GUID
, res6
[0]["objectGUID"][0]))
222 names
.domainsid
= ndr_unpack( security
.dom_sid
, res6
[0]["objectSid"][0])
223 if res6
[0].get("msDS-Behavior-Version") is None or \
224 int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
225 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
227 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
230 res7
= samdb
.search(expression
="(displayName=Default Domain Policy)",
231 base
="CN=Policies,CN=System," + basedn
,
232 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
233 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
235 res8
= samdb
.search(expression
="(displayName=Default Domain Controllers"
237 base
="CN=Policies,CN=System," + basedn
,
238 scope
=ldb
.SCOPE_ONELEVEL
,
239 attrs
=["cn","displayName"])
241 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
243 names
.policyid_dc
= None
245 res9
= idmapdb
.search(expression
="(cn=%s-%s)" %
246 (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
),
247 attrs
=["xidNumber", "type"])
249 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
))
250 if res9
[0]["type"][0] == "ID_TYPE_BOTH":
251 names
.root_gid
= res9
[0]["xidNumber"][0]
253 names
.root_gid
= pwd
.getpwuid(int(res9
[0]["xidNumber"][0])).pw_gid
257 def update_provision_usn(samdb
, low
, high
, id, replace
=False):
258 """Update the field provisionUSN in sam.ldb
260 This field is used to track range of USN modified by provision and
262 This value is used afterward by next provision to figure out if
263 the field have been modified since last provision.
265 :param samdb: An LDB object connect to sam.ldb
266 :param low: The lowest USN modified by this upgrade
267 :param high: The highest USN modified by this upgrade
268 :param id: The invocation id of the samba's dc
269 :param replace: A boolean indicating if the range should replace any
270 existing one or appended (default)
275 entry
= samdb
.search(base
="@PROVISION",
276 scope
=ldb
.SCOPE_BASE
,
277 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "dn"])
278 for e
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
279 if not re
.search(';', e
):
280 e
= "%s;%s" % (e
, id)
283 tab
.append("%s-%s;%s" % (low
, high
, id))
284 delta
= ldb
.Message()
285 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
286 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
287 ldb
.FLAG_MOD_REPLACE
, LAST_PROVISION_USN_ATTRIBUTE
)
288 entry
= samdb
.search(expression
='provisionnerID=*',
289 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
290 attrs
=["provisionnerID"])
291 if len(entry
) == 0 or len(entry
[0]) == 0:
292 delta
["provisionnerID"] = ldb
.MessageElement(id, ldb
.FLAG_MOD_ADD
, "provisionnerID")
296 def set_provision_usn(samdb
, low
, high
, id):
297 """Set the field provisionUSN in sam.ldb
298 This field is used to track range of USN modified by provision and
300 This value is used afterward by next provision to figure out if
301 the field have been modified since last provision.
303 :param samdb: An LDB object connect to sam.ldb
304 :param low: The lowest USN modified by this upgrade
305 :param high: The highest USN modified by this upgrade
306 :param id: The invocationId of the provision"""
309 tab
.append("%s-%s;%s" % (low
, high
, id))
311 delta
= ldb
.Message()
312 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
313 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
314 ldb
.FLAG_MOD_ADD
, LAST_PROVISION_USN_ATTRIBUTE
)
318 def get_max_usn(samdb
,basedn
):
319 """ This function return the biggest USN present in the provision
321 :param samdb: A LDB object pointing to the sam.ldb
322 :param basedn: A string containing the base DN of the provision
324 :return: The biggest USN in the provision"""
326 res
= samdb
.search(expression
="objectClass=*",base
=basedn
,
327 scope
=ldb
.SCOPE_SUBTREE
,attrs
=["uSNChanged"],
328 controls
=["search_options:1:2",
329 "server_sort:1:1:uSNChanged",
330 "paged_results:1:1"])
331 return res
[0]["uSNChanged"]
334 def get_last_provision_usn(sam
):
335 """Get USNs ranges modified by a provision or an upgradeprovision
337 :param sam: An LDB object pointing to the sam.ldb
338 :return: a dictionary which keys are invocation id and values are an array
339 of integer representing the different ranges
342 entry
= sam
.search(expression
="%s=*" % LAST_PROVISION_USN_ATTRIBUTE
,
343 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
344 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "provisionnerID"])
345 except ldb
.LdbError
, (ecode
, emsg
):
346 if ecode
== ldb
.ERR_NO_SUCH_OBJECT
:
353 if entry
[0].get("provisionnerID"):
354 for e
in entry
[0]["provisionnerID"]:
356 for r
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
357 tab1
= str(r
).split(';')
362 if (len(myids
) > 0 and id not in myids
):
364 tab2
= p
.split(tab1
[0])
365 if range.get(id) is None:
367 range[id].append(tab2
[0])
368 range[id].append(tab2
[1])
374 class ProvisionResult(object):
375 """Result of a provision.
377 :ivar server_role: The server role
378 :ivar paths: ProvisionPaths instance
379 :ivar domaindn: The domain dn, as string
383 self
.server_role
= None
390 self
.domainsid
= None
391 self
.adminpass_generated
= None
392 self
.adminpass
= None
393 self
.backend_result
= None
395 def report_logger(self
, logger
):
396 """Report this provision result to a logger."""
398 "Once the above files are installed, your Samba4 server will "
400 if self
.adminpass_generated
:
401 logger
.info("Admin password: %s", self
.adminpass
)
402 logger
.info("Server Role: %s", self
.server_role
)
403 logger
.info("Hostname: %s", self
.names
.hostname
)
404 logger
.info("NetBIOS Domain: %s", self
.names
.domain
)
405 logger
.info("DNS Domain: %s", self
.names
.dnsdomain
)
406 logger
.info("DOMAIN SID: %s", self
.domainsid
)
408 if self
.backend_result
:
409 self
.backend_result
.report_logger(logger
)
412 def check_install(lp
, session_info
, credentials
):
413 """Check whether the current install seems ok.
415 :param lp: Loadparm context
416 :param session_info: Session information
417 :param credentials: Credentials
419 if lp
.get("realm") == "":
420 raise Exception("Realm empty")
421 samdb
= Ldb(lp
.samdb_url(), session_info
=session_info
,
422 credentials
=credentials
, lp
=lp
)
423 if len(samdb
.search("(cn=Administrator)")) != 1:
424 raise ProvisioningError("No administrator account found")
427 def findnss(nssfn
, names
):
428 """Find a user or group from a list of possibilities.
430 :param nssfn: NSS Function to try (should raise KeyError if not found)
431 :param names: Names to check.
432 :return: Value return by first names list.
439 raise KeyError("Unable to find user/group in %r" % names
)
442 findnss_uid
= lambda names
: findnss(pwd
.getpwnam
, names
)[2]
443 findnss_gid
= lambda names
: findnss(grp
.getgrnam
, names
)[2]
446 def provision_paths_from_lp(lp
, dnsdomain
):
447 """Set the default paths for provisioning.
449 :param lp: Loadparm context.
450 :param dnsdomain: DNS Domain name
452 paths
= ProvisionPaths()
453 paths
.private_dir
= lp
.get("private dir")
454 paths
.state_dir
= lp
.get("state directory")
456 # This is stored without path prefix for the "privateKeytab" attribute in
457 # "secrets_dns.ldif".
458 paths
.dns_keytab
= "dns.keytab"
459 paths
.keytab
= "secrets.keytab"
461 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
462 paths
.samdb
= os
.path
.join(paths
.private_dir
, "sam.ldb")
463 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, "idmap.ldb")
464 paths
.secrets
= os
.path
.join(paths
.private_dir
, "secrets.ldb")
465 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
466 paths
.dns
= os
.path
.join(paths
.private_dir
, "dns", dnsdomain
+ ".zone")
467 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
468 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
469 paths
.namedconf
= os
.path
.join(paths
.private_dir
, "named.conf")
470 paths
.namedconf_update
= os
.path
.join(paths
.private_dir
, "named.conf.update")
471 paths
.namedtxt
= os
.path
.join(paths
.private_dir
, "named.txt")
472 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
473 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
474 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
475 paths
.hklm
= "hklm.ldb"
476 paths
.hkcr
= "hkcr.ldb"
477 paths
.hkcu
= "hkcu.ldb"
478 paths
.hku
= "hku.ldb"
479 paths
.hkpd
= "hkpd.ldb"
480 paths
.hkpt
= "hkpt.ldb"
481 paths
.sysvol
= lp
.get("path", "sysvol")
482 paths
.netlogon
= lp
.get("path", "netlogon")
483 paths
.smbconf
= lp
.configfile
487 def determine_netbios_name(hostname
):
488 """Determine a netbios name from a hostname."""
489 # remove forbidden chars and force the length to be <16
490 netbiosname
= "".join([x
for x
in hostname
if is_valid_netbios_char(x
)])
491 return netbiosname
[:MAX_NETBIOS_NAME_LEN
].upper()
494 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
495 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
496 schemadn
=None, serverdn
=None, sitename
=None):
497 """Guess configuration settings to use."""
500 hostname
= socket
.gethostname().split(".")[0]
502 netbiosname
= lp
.get("netbios name")
503 if netbiosname
is None:
504 netbiosname
= determine_netbios_name(hostname
)
505 netbiosname
= netbiosname
.upper()
506 if not valid_netbios_name(netbiosname
):
507 raise InvalidNetbiosName(netbiosname
)
509 if dnsdomain
is None:
510 dnsdomain
= lp
.get("realm")
511 if dnsdomain
is None or dnsdomain
== "":
512 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp
.configfile
)
514 dnsdomain
= dnsdomain
.lower()
516 if serverrole
is None:
517 serverrole
= lp
.get("server role")
518 if serverrole
is None:
519 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
521 serverrole
= serverrole
.lower()
523 realm
= dnsdomain
.upper()
525 if lp
.get("realm") == "":
526 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
528 if lp
.get("realm").upper() != realm
:
529 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
))
531 if lp
.get("server role").lower() != serverrole
:
532 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
))
534 if serverrole
== "active directory domain controller":
536 # This will, for better or worse, default to 'WORKGROUP'
537 domain
= lp
.get("workgroup")
538 domain
= domain
.upper()
540 if lp
.get("workgroup").upper() != domain
:
541 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
))
544 domaindn
= samba
.dn_from_dns_name(dnsdomain
)
546 if domain
== netbiosname
:
547 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain
, netbiosname
))
551 domaindn
= "DC=" + netbiosname
553 if not valid_netbios_name(domain
):
554 raise InvalidNetbiosName(domain
)
556 if hostname
.upper() == realm
:
557 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
558 if netbiosname
.upper() == realm
:
559 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm
, netbiosname
))
561 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
567 configdn
= "CN=Configuration," + rootdn
569 schemadn
= "CN=Schema," + configdn
572 sitename
= DEFAULTSITE
574 names
= ProvisionNames()
575 names
.rootdn
= rootdn
576 names
.domaindn
= domaindn
577 names
.configdn
= configdn
578 names
.schemadn
= schemadn
579 names
.ldapmanagerdn
= "CN=Manager," + rootdn
580 names
.dnsdomain
= dnsdomain
581 names
.domain
= domain
583 names
.netbiosname
= netbiosname
584 names
.hostname
= hostname
585 names
.sitename
= sitename
586 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
587 netbiosname
, sitename
, configdn
)
592 def make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
593 serverrole
=None, eadb
=False, use_ntvfs
=False, lp
=None,
595 """Create a new smb.conf file based on a couple of basic settings.
597 assert smbconf
is not None
600 hostname
= socket
.gethostname().split(".")[0]
602 netbiosname
= determine_netbios_name(hostname
)
604 if serverrole
is None:
605 serverrole
= "standalone server"
607 assert domain
is not None
608 domain
= domain
.upper()
610 assert realm
is not None
611 realm
= realm
.upper()
614 "netbios name": netbiosname
,
617 "server role": serverrole
,
621 lp
= samba
.param
.LoadParm()
622 #Load non-existent file
623 if os
.path
.exists(smbconf
):
626 if global_param
is not None:
627 for ent
in global_param
:
628 if global_param
[ent
] is not None:
629 global_settings
[ent
] = " ".join(global_param
[ent
])
631 if targetdir
is not None:
632 global_settings
["private dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "private"))
633 global_settings
["lock dir"] = os
.path
.abspath(targetdir
)
634 global_settings
["state directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "state"))
635 global_settings
["cache directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "cache"))
637 lp
.set("lock dir", os
.path
.abspath(targetdir
))
638 lp
.set("state directory", global_settings
["state directory"])
639 lp
.set("cache directory", global_settings
["cache directory"])
642 if use_ntvfs
and not lp
.get("posix:eadb"):
643 if targetdir
is not None:
644 privdir
= os
.path
.join(targetdir
, "private")
646 privdir
= lp
.get("private dir")
647 lp
.set("posix:eadb", os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
648 elif not use_ntvfs
and not lp
.get("xattr_tdb:file"):
649 if targetdir
is not None:
650 statedir
= os
.path
.join(targetdir
, "state")
652 statedir
= lp
.get("state directory")
653 lp
.set("xattr_tdb:file", os
.path
.abspath(os
.path
.join(statedir
, "xattr.tdb")))
656 if serverrole
== "active directory domain controller":
657 shares
["sysvol"] = os
.path
.join(lp
.get("state directory"), "sysvol")
658 shares
["netlogon"] = os
.path
.join(shares
["sysvol"], realm
.lower(),
661 global_settings
["passdb backend"] = "samba_dsdb"
663 f
= open(smbconf
, 'w')
665 f
.write("[globals]\n")
666 for key
, val
in global_settings
.iteritems():
667 f
.write("\t%s = %s\n" % (key
, val
))
670 for name
, path
in shares
.iteritems():
671 f
.write("[%s]\n" % name
)
672 f
.write("\tpath = %s\n" % path
)
673 f
.write("\tread only = no\n")
677 # reload the smb.conf
680 # and dump it without any values that are the default
681 # this ensures that any smb.conf parameters that were set
682 # on the provision/join command line are set in the resulting smb.conf
683 f
= open(smbconf
, mode
='w')
690 def setup_name_mappings(idmap
, sid
, root_uid
, nobody_uid
,
691 users_gid
, root_gid
):
692 """setup reasonable name mappings for sam names to unix names.
694 :param samdb: SamDB object.
695 :param idmap: IDmap db object.
696 :param sid: The domain sid.
697 :param domaindn: The domain DN.
698 :param root_uid: uid of the UNIX root user.
699 :param nobody_uid: uid of the UNIX nobody user.
700 :param users_gid: gid of the UNIX users group.
701 :param root_gid: gid of the UNIX root group.
703 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
705 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
706 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
709 def setup_samdb_partitions(samdb_path
, logger
, lp
, session_info
,
710 provision_backend
, names
, schema
, serverrole
,
712 """Setup the partitions for the SAM database.
714 Alternatively, provision() may call this, and then populate the database.
716 :note: This will wipe the Sam Database!
718 :note: This function always removes the local SAM LDB file. The erase
719 parameter controls whether to erase the existing data, which
720 may not be stored locally but in LDAP.
723 assert session_info
is not None
725 # We use options=["modules:"] to stop the modules loading - we
726 # just want to wipe and re-initialise the database, not start it up
729 os
.unlink(samdb_path
)
733 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
734 lp
=lp
, options
=["modules:"])
736 ldap_backend_line
= "# No LDAP backend"
737 if provision_backend
.type != "ldb":
738 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldap_uri
740 samdb
.transaction_start()
742 logger
.info("Setting up sam.ldb partitions and settings")
743 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
744 "LDAP_BACKEND_LINE": ldap_backend_line
748 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
749 "BACKEND_TYPE": provision_backend
.type,
750 "SERVER_ROLE": serverrole
753 logger
.info("Setting up sam.ldb rootDSE")
754 setup_samdb_rootdse(samdb
, names
)
756 samdb
.transaction_cancel()
759 samdb
.transaction_commit()
762 def secretsdb_self_join(secretsdb
, domain
,
763 netbiosname
, machinepass
, domainsid
=None,
764 realm
=None, dnsdomain
=None,
766 key_version_number
=1,
767 secure_channel_type
=SEC_CHAN_WKSTA
):
768 """Add domain join-specific bits to a secrets database.
770 :param secretsdb: Ldb Handle to the secrets database
771 :param machinepass: Machine password
773 attrs
= ["whenChanged",
780 if realm
is not None:
781 if dnsdomain
is None:
782 dnsdomain
= realm
.lower()
783 dnsname
= '%s.%s' % (netbiosname
.lower(), dnsdomain
.lower())
786 shortname
= netbiosname
.lower()
788 # We don't need to set msg["flatname"] here, because rdn_name will handle
789 # it, and it causes problems for modifies anyway
790 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
791 msg
["secureChannelType"] = [str(secure_channel_type
)]
792 msg
["objectClass"] = ["top", "primaryDomain"]
793 if dnsname
is not None:
794 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
795 msg
["realm"] = [realm
]
796 msg
["saltPrincipal"] = ["host/%s@%s" % (dnsname
, realm
.upper())]
797 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
798 msg
["privateKeytab"] = ["secrets.keytab"]
800 msg
["secret"] = [machinepass
]
801 msg
["samAccountName"] = ["%s$" % netbiosname
]
802 msg
["secureChannelType"] = [str(secure_channel_type
)]
803 if domainsid
is not None:
804 msg
["objectSid"] = [ndr_pack(domainsid
)]
806 # This complex expression tries to ensure that we don't have more
807 # than one record for this SID, realm or netbios domain at a time,
808 # but we don't delete the old record that we are about to modify,
809 # because that would delete the keytab and previous password.
810 res
= secretsdb
.search(base
="cn=Primary Domains", attrs
=attrs
,
811 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain
, realm
, str(domainsid
), str(msg
.dn
))),
812 scope
=ldb
.SCOPE_ONELEVEL
)
815 secretsdb
.delete(del_msg
.dn
)
817 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
820 msg
["priorSecret"] = [res
[0]["secret"][0]]
821 msg
["priorWhenChanged"] = [res
[0]["whenChanged"][0]]
824 msg
["privateKeytab"] = [res
[0]["privateKeytab"][0]]
829 msg
["krb5Keytab"] = [res
[0]["krb5Keytab"][0]]
835 msg
[el
].set_flags(ldb
.FLAG_MOD_REPLACE
)
836 secretsdb
.modify(msg
)
837 secretsdb
.rename(res
[0].dn
, msg
.dn
)
839 spn
= [ 'HOST/%s' % shortname
]
840 if secure_channel_type
== SEC_CHAN_BDC
and dnsname
is not None:
841 # we are a domain controller then we add servicePrincipalName
842 # entries for the keytab code to update.
843 spn
.extend([ 'HOST/%s' % dnsname
])
844 msg
["servicePrincipalName"] = spn
849 def setup_secretsdb(paths
, session_info
, backend_credentials
, lp
):
850 """Setup the secrets database.
852 :note: This function does not handle exceptions and transaction on purpose,
853 it's up to the caller to do this job.
855 :param path: Path to the secrets database.
856 :param session_info: Session info.
857 :param credentials: Credentials
858 :param lp: Loadparm context
859 :return: LDB handle for the created secrets database
861 if os
.path
.exists(paths
.secrets
):
862 os
.unlink(paths
.secrets
)
864 keytab_path
= os
.path
.join(paths
.private_dir
, paths
.keytab
)
865 if os
.path
.exists(keytab_path
):
866 os
.unlink(keytab_path
)
868 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
869 if os
.path
.exists(dns_keytab_path
):
870 os
.unlink(dns_keytab_path
)
874 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
876 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
877 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
878 secrets_ldb
.transaction_start()
880 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
882 if (backend_credentials
is not None and
883 backend_credentials
.authentication_requested()):
884 if backend_credentials
.get_bind_dn() is not None:
885 setup_add_ldif(secrets_ldb
,
886 setup_path("secrets_simple_ldap.ldif"), {
887 "LDAPMANAGERDN": backend_credentials
.get_bind_dn(),
888 "LDAPMANAGERPASS_B64": b64encode(backend_credentials
.get_password())
891 setup_add_ldif(secrets_ldb
,
892 setup_path("secrets_sasl_ldap.ldif"), {
893 "LDAPADMINUSER": backend_credentials
.get_username(),
894 "LDAPADMINREALM": backend_credentials
.get_realm(),
895 "LDAPADMINPASS_B64": b64encode(backend_credentials
.get_password())
898 secrets_ldb
.transaction_cancel()
903 def setup_privileges(path
, session_info
, lp
):
904 """Setup the privileges database.
906 :param path: Path to the privileges database.
907 :param session_info: Session info.
908 :param credentials: Credentials
909 :param lp: Loadparm context
910 :return: LDB handle for the created secrets database
912 if os
.path
.exists(path
):
914 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
915 privilege_ldb
.erase()
916 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
919 def setup_registry(path
, session_info
, lp
):
920 """Setup the registry.
922 :param path: Path to the registry database
923 :param session_info: Session information
924 :param credentials: Credentials
925 :param lp: Loadparm context
927 reg
= samba
.registry
.Registry()
928 hive
= samba
.registry
.open_ldb(path
, session_info
=session_info
, lp_ctx
=lp
)
929 reg
.mount_hive(hive
, samba
.registry
.HKEY_LOCAL_MACHINE
)
930 provision_reg
= setup_path("provision.reg")
931 assert os
.path
.exists(provision_reg
)
932 reg
.diff_apply(provision_reg
)
935 def setup_idmapdb(path
, session_info
, lp
):
936 """Setup the idmap database.
938 :param path: path to the idmap database
939 :param session_info: Session information
940 :param credentials: Credentials
941 :param lp: Loadparm context
943 if os
.path
.exists(path
):
946 idmap_ldb
= IDmapDB(path
, session_info
=session_info
, lp
=lp
)
948 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
952 def setup_samdb_rootdse(samdb
, names
):
953 """Setup the SamDB rootdse.
955 :param samdb: Sam Database handle
957 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
958 "SCHEMADN": names
.schemadn
,
959 "DOMAINDN": names
.domaindn
,
960 "ROOTDN" : names
.rootdn
,
961 "CONFIGDN": names
.configdn
,
962 "SERVERDN": names
.serverdn
,
966 def setup_self_join(samdb
, admin_session_info
, names
, fill
, machinepass
,
967 dns_backend
, dnspass
, domainsid
, next_rid
, invocationid
,
968 policyguid
, policyguid_dc
,
969 domainControllerFunctionality
, ntdsguid
=None, dc_rid
=None):
970 """Join a host to its own domain."""
971 assert isinstance(invocationid
, str)
972 if ntdsguid
is not None:
973 ntdsguid_line
= "objectGUID: %s\n"%ntdsguid
980 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
981 "CONFIGDN": names
.configdn
,
982 "SCHEMADN": names
.schemadn
,
983 "DOMAINDN": names
.domaindn
,
984 "SERVERDN": names
.serverdn
,
985 "INVOCATIONID": invocationid
,
986 "NETBIOSNAME": names
.netbiosname
,
987 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
988 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
989 "DOMAINSID": str(domainsid
),
990 "DCRID": str(dc_rid
),
991 "SAMBA_VERSION_STRING": version
,
992 "NTDSGUID": ntdsguid_line
,
993 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
994 domainControllerFunctionality
),
995 "RIDALLOCATIONSTART": str(next_rid
+ 100),
996 "RIDALLOCATIONEND": str(next_rid
+ 100 + 499)})
998 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
999 "POLICYGUID": policyguid
,
1000 "POLICYGUID_DC": policyguid_dc
,
1001 "DNSDOMAIN": names
.dnsdomain
,
1002 "DOMAINDN": names
.domaindn
})
1004 # If we are setting up a subdomain, then this has been replicated in, so we
1005 # don't need to add it
1006 if fill
== FILL_FULL
:
1007 setup_add_ldif(samdb
, setup_path("provision_self_join_config.ldif"), {
1008 "CONFIGDN": names
.configdn
,
1009 "SCHEMADN": names
.schemadn
,
1010 "DOMAINDN": names
.domaindn
,
1011 "SERVERDN": names
.serverdn
,
1012 "INVOCATIONID": invocationid
,
1013 "NETBIOSNAME": names
.netbiosname
,
1014 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1015 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1016 "DOMAINSID": str(domainsid
),
1017 "DCRID": str(dc_rid
),
1018 "SAMBA_VERSION_STRING": version
,
1019 "NTDSGUID": ntdsguid_line
,
1020 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1021 domainControllerFunctionality
)})
1023 # Setup fSMORoleOwner entries to point at the newly created DC entry
1024 setup_modify_ldif(samdb
,
1025 setup_path("provision_self_join_modify_config.ldif"), {
1026 "CONFIGDN": names
.configdn
,
1027 "SCHEMADN": names
.schemadn
,
1028 "DEFAULTSITE": names
.sitename
,
1029 "NETBIOSNAME": names
.netbiosname
,
1030 "SERVERDN": names
.serverdn
,
1033 system_session_info
= system_session()
1034 samdb
.set_session_info(system_session_info
)
1035 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1036 # modify a serverReference under cn=config when we are a subdomain, we must
1037 # be system due to ACLs
1038 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
1039 "DOMAINDN": names
.domaindn
,
1040 "SERVERDN": names
.serverdn
,
1041 "NETBIOSNAME": names
.netbiosname
,
1044 samdb
.set_session_info(admin_session_info
)
1046 if dns_backend
!= "SAMBA_INTERNAL":
1047 # This is Samba4 specific and should be replaced by the correct
1048 # DNS AD-style setup
1049 setup_add_ldif(samdb
, setup_path("provision_dns_add_samba.ldif"), {
1050 "DNSDOMAIN": names
.dnsdomain
,
1051 "DOMAINDN": names
.domaindn
,
1052 "DNSPASS_B64": b64encode(dnspass
.encode('utf-16-le')),
1053 "HOSTNAME" : names
.hostname
,
1054 "DNSNAME" : '%s.%s' % (
1055 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
1059 def getpolicypath(sysvolpath
, dnsdomain
, guid
):
1060 """Return the physical path of policy given its guid.
1062 :param sysvolpath: Path to the sysvol folder
1063 :param dnsdomain: DNS name of the AD domain
1064 :param guid: The GUID of the policy
1065 :return: A string with the complete path to the policy folder
1068 guid
= "{%s}" % guid
1069 policy_path
= os
.path
.join(sysvolpath
, dnsdomain
, "Policies", guid
)
1073 def create_gpo_struct(policy_path
):
1074 if not os
.path
.exists(policy_path
):
1075 os
.makedirs(policy_path
, 0775)
1076 f
= open(os
.path
.join(policy_path
, "GPT.INI"), 'w')
1078 f
.write("[General]\r\nVersion=0")
1081 p
= os
.path
.join(policy_path
, "MACHINE")
1082 if not os
.path
.exists(p
):
1083 os
.makedirs(p
, 0775)
1084 p
= os
.path
.join(policy_path
, "USER")
1085 if not os
.path
.exists(p
):
1086 os
.makedirs(p
, 0775)
1089 def create_default_gpo(sysvolpath
, dnsdomain
, policyguid
, policyguid_dc
):
1090 """Create the default GPO for a domain
1092 :param sysvolpath: Physical path for the sysvol folder
1093 :param dnsdomain: DNS domain name of the AD domain
1094 :param policyguid: GUID of the default domain policy
1095 :param policyguid_dc: GUID of the default domain controler policy
1097 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid
)
1098 create_gpo_struct(policy_path
)
1100 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid_dc
)
1101 create_gpo_struct(policy_path
)
1104 def setup_samdb(path
, session_info
, provision_backend
, lp
, names
,
1105 logger
, fill
, serverrole
, schema
, am_rodc
=False):
1106 """Setup a complete SAM Database.
1108 :note: This will wipe the main SAM database file!
1111 # Also wipes the database
1112 setup_samdb_partitions(path
, logger
=logger
, lp
=lp
,
1113 provision_backend
=provision_backend
, session_info
=session_info
,
1114 names
=names
, serverrole
=serverrole
, schema
=schema
)
1116 # Load the database, but don's load the global schema and don't connect
1118 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
1119 credentials
=provision_backend
.credentials
, lp
=lp
,
1120 global_schema
=False, am_rodc
=am_rodc
)
1122 logger
.info("Pre-loading the Samba 4 and AD schema")
1124 # Load the schema from the one we computed earlier
1125 samdb
.set_schema(schema
, write_indices_and_attributes
=False)
1127 # Set the NTDS settings DN manually - in order to have it already around
1128 # before the provisioned tree exists and we connect
1129 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1131 # And now we can connect to the DB - the schema won't be loaded from the
1135 # But we have to give it one more kick to have it use the schema
1136 # during provision - it needs, now that it is connected, to write
1137 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1138 samdb
.set_schema(schema
, write_indices_and_attributes
=True)
1143 def fill_samdb(samdb
, lp
, names
, logger
, domainsid
, domainguid
, policyguid
,
1144 policyguid_dc
, fill
, adminpass
, krbtgtpass
, machinepass
, dns_backend
,
1145 dnspass
, invocationid
, ntdsguid
, serverrole
, am_rodc
=False,
1146 dom_for_fun_level
=None, schema
=None, next_rid
=None, dc_rid
=None):
1148 if next_rid
is None:
1151 # Provision does not make much sense values larger than 1000000000
1152 # as the upper range of the rIDAvailablePool is 1073741823 and
1153 # we don't want to create a domain that cannot allocate rids.
1154 if next_rid
< 1000 or next_rid
> 1000000000:
1155 error
= "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid
)
1156 error
+= "the valid range is %u-%u. The default is %u." % (
1157 1000, 1000000000, 1000)
1158 raise ProvisioningError(error
)
1160 # ATTENTION: Do NOT change these default values without discussion with the
1161 # team and/or release manager. They have a big impact on the whole program!
1162 domainControllerFunctionality
= DS_DOMAIN_FUNCTION_2008_R2
1164 if dom_for_fun_level
is None:
1165 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2003
1167 if dom_for_fun_level
> domainControllerFunctionality
:
1168 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!")
1170 domainFunctionality
= dom_for_fun_level
1171 forestFunctionality
= dom_for_fun_level
1173 # Set the NTDS settings DN manually - in order to have it already around
1174 # before the provisioned tree exists and we connect
1175 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1177 samdb
.transaction_start()
1179 # Set the domain functionality levels onto the database.
1180 # Various module (the password_hash module in particular) need
1181 # to know what level of AD we are emulating.
1183 # These will be fixed into the database via the database
1184 # modifictions below, but we need them set from the start.
1185 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
1186 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
1187 samdb
.set_opaque_integer("domainControllerFunctionality",
1188 domainControllerFunctionality
)
1190 samdb
.set_domain_sid(str(domainsid
))
1191 samdb
.set_invocation_id(invocationid
)
1193 logger
.info("Adding DomainDN: %s" % names
.domaindn
)
1195 # impersonate domain admin
1196 admin_session_info
= admin_session(lp
, str(domainsid
))
1197 samdb
.set_session_info(admin_session_info
)
1198 if domainguid
is not None:
1199 domainguid_line
= "objectGUID: %s\n-" % domainguid
1201 domainguid_line
= ""
1203 descr
= b64encode(get_domain_descriptor(domainsid
))
1204 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
1205 "DOMAINDN": names
.domaindn
,
1206 "DOMAINSID": str(domainsid
),
1207 "DESCRIPTOR": descr
,
1208 "DOMAINGUID": domainguid_line
1211 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
1212 "DOMAINDN": names
.domaindn
,
1213 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1214 "NEXTRID": str(next_rid
),
1215 "DEFAULTSITE": names
.sitename
,
1216 "CONFIGDN": names
.configdn
,
1217 "POLICYGUID": policyguid
,
1218 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1219 "SAMBA_VERSION_STRING": version
1222 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1223 if fill
== FILL_FULL
:
1224 logger
.info("Adding configuration container")
1225 descr
= b64encode(get_config_descriptor(domainsid
))
1226 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
1227 "CONFIGDN": names
.configdn
,
1228 "DESCRIPTOR": descr
,
1231 # The LDIF here was created when the Schema object was constructed
1232 logger
.info("Setting up sam.ldb schema")
1233 samdb
.add_ldif(schema
.schema_dn_add
, controls
=["relax:0"])
1234 samdb
.modify_ldif(schema
.schema_dn_modify
)
1235 samdb
.write_prefixes_from_schema()
1236 samdb
.add_ldif(schema
.schema_data
, controls
=["relax:0"])
1237 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
1238 {"SCHEMADN": names
.schemadn
})
1240 # Now register this container in the root of the forest
1241 msg
= ldb
.Message(ldb
.Dn(samdb
, names
.domaindn
))
1242 msg
["subRefs"] = ldb
.MessageElement(names
.configdn
, ldb
.FLAG_MOD_ADD
,
1246 samdb
.transaction_cancel()
1249 samdb
.transaction_commit()
1251 samdb
.transaction_start()
1253 samdb
.invocation_id
= invocationid
1255 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1256 if fill
== FILL_FULL
:
1257 logger
.info("Setting up sam.ldb configuration data")
1258 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
1259 "CONFIGDN": names
.configdn
,
1260 "NETBIOSNAME": names
.netbiosname
,
1261 "DEFAULTSITE": names
.sitename
,
1262 "DNSDOMAIN": names
.dnsdomain
,
1263 "DOMAIN": names
.domain
,
1264 "SCHEMADN": names
.schemadn
,
1265 "DOMAINDN": names
.domaindn
,
1266 "SERVERDN": names
.serverdn
,
1267 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
1268 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1271 logger
.info("Setting up display specifiers")
1272 display_specifiers_ldif
= read_ms_ldif(
1273 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1274 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
,
1275 {"CONFIGDN": names
.configdn
})
1276 check_all_substituted(display_specifiers_ldif
)
1277 samdb
.add_ldif(display_specifiers_ldif
)
1279 logger
.info("Adding users container")
1280 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
1281 "DOMAINDN": names
.domaindn
})
1282 logger
.info("Modifying users container")
1283 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
1284 "DOMAINDN": names
.domaindn
})
1285 logger
.info("Adding computers container")
1286 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
1287 "DOMAINDN": names
.domaindn
})
1288 logger
.info("Modifying computers container")
1289 setup_modify_ldif(samdb
,
1290 setup_path("provision_computers_modify.ldif"), {
1291 "DOMAINDN": names
.domaindn
})
1292 logger
.info("Setting up sam.ldb data")
1293 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1294 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1295 "DOMAINDN": names
.domaindn
,
1296 "NETBIOSNAME": names
.netbiosname
,
1297 "DEFAULTSITE": names
.sitename
,
1298 "CONFIGDN": names
.configdn
,
1299 "SERVERDN": names
.serverdn
,
1300 "RIDAVAILABLESTART": str(next_rid
+ 600),
1301 "POLICYGUID_DC": policyguid_dc
1304 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1305 if fill
== FILL_FULL
:
1306 setup_modify_ldif(samdb
,
1307 setup_path("provision_configuration_references.ldif"), {
1308 "CONFIGDN": names
.configdn
,
1309 "SCHEMADN": names
.schemadn
})
1311 logger
.info("Setting up well known security principals")
1312 setup_add_ldif(samdb
, setup_path("provision_well_known_sec_princ.ldif"), {
1313 "CONFIGDN": names
.configdn
,
1316 if fill
== FILL_FULL
or fill
== FILL_SUBDOMAIN
:
1317 setup_modify_ldif(samdb
,
1318 setup_path("provision_basedn_references.ldif"),
1319 {"DOMAINDN": names
.domaindn
})
1321 logger
.info("Setting up sam.ldb users and groups")
1322 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1323 "DOMAINDN": names
.domaindn
,
1324 "DOMAINSID": str(domainsid
),
1325 "ADMINPASS_B64": b64encode(adminpass
.encode('utf-16-le')),
1326 "KRBTGTPASS_B64": b64encode(krbtgtpass
.encode('utf-16-le'))
1329 logger
.info("Setting up self join")
1330 setup_self_join(samdb
, admin_session_info
, names
=names
, fill
=fill
,
1331 invocationid
=invocationid
,
1332 dns_backend
=dns_backend
,
1334 machinepass
=machinepass
,
1335 domainsid
=domainsid
,
1338 policyguid
=policyguid
,
1339 policyguid_dc
=policyguid_dc
,
1340 domainControllerFunctionality
=domainControllerFunctionality
,
1343 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1344 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1345 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
)
1346 assert isinstance(names
.ntdsguid
, str)
1348 samdb
.transaction_cancel()
1351 samdb
.transaction_commit()
1356 FILL_SUBDOMAIN
= "SUBDOMAIN"
1357 FILL_NT4SYNC
= "NT4SYNC"
1359 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1360 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)"
1363 def set_dir_acl(path
, acl
, lp
, domsid
, use_ntvfs
, passdb
):
1364 setntacl(lp
, path
, acl
, domsid
, use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1365 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1367 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1368 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1370 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1371 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1374 def set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
):
1375 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1378 :param sysvol: Physical path for the sysvol folder
1379 :param dnsdomain: The DNS name of the domain
1380 :param domainsid: The SID of the domain
1381 :param domaindn: The DN of the domain (ie. DC=...)
1382 :param samdb: An LDB object on the SAM db
1383 :param lp: an LP object
1386 # Set ACL for GPO root folder
1387 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1388 setntacl(lp
, root_policy_path
, POLICIES_ACL
, str(domainsid
),
1389 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
)
1391 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1392 attrs
=["cn", "nTSecurityDescriptor"],
1393 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1396 acl
= ndr_unpack(security
.descriptor
,
1397 str(policy
["nTSecurityDescriptor"])).as_sddl()
1398 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1399 set_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1400 str(domainsid
), use_ntvfs
,
1404 def setsysvolacl(samdb
, netlogon
, sysvol
, uid
, gid
, domainsid
, dnsdomain
,
1405 domaindn
, lp
, use_ntvfs
):
1406 """Set the ACL for the sysvol share and the subfolders
1408 :param samdb: An LDB object on the SAM db
1409 :param netlogon: Physical path for the netlogon folder
1410 :param sysvol: Physical path for the sysvol folder
1411 :param uid: The UID of the "Administrator" user
1412 :param gid: The GID of the "Domain adminstrators" group
1413 :param domainsid: The SID of the domain
1414 :param dnsdomain: The DNS name of the domain
1415 :param domaindn: The DN of the domain (ie. DC=...)
1420 # This will ensure that the smbd code we are running when setting ACLs
1421 # is initialised with the smb.conf
1422 s3conf
= s3param
.get_context()
1423 s3conf
.load(lp
.configfile
)
1424 # ensure we are using the right samba_dsdb passdb backend, no matter what
1425 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1426 passdb
.reload_static_pdb()
1428 # ensure that we init the samba_dsdb backend, so the domain sid is
1429 # marked in secrets.tdb
1430 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1432 # now ensure everything matches correctly, to avoid wierd issues
1433 if passdb
.get_global_sam_sid() != domainsid
:
1434 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
))
1436 domain_info
= s4_passdb
.domain_info()
1437 if domain_info
["dom_sid"] != domainsid
:
1438 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
))
1440 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1441 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()))
1446 os
.chown(sysvol
, -1, gid
)
1452 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1453 setntacl(lp
,sysvol
, SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=s4_passdb
)
1454 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1456 if use_ntvfs
and canchown
:
1457 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1458 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=s4_passdb
)
1460 if use_ntvfs
and canchown
:
1461 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1462 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=s4_passdb
)
1464 # Set acls on Policy folder and policies folders
1465 set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
=s4_passdb
)
1467 def acl_type(direct_db_access
):
1468 if direct_db_access
:
1473 def check_dir_acl(path
, acl
, lp
, domainsid
, direct_db_access
):
1474 fsacl
= getntacl(lp
, path
, direct_db_access
=direct_db_access
)
1475 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1476 if fsacl_sddl
!= acl
:
1477 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
))
1479 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1481 fsacl
= getntacl(lp
, os
.path
.join(root
, name
), direct_db_access
=direct_db_access
)
1483 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1484 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1485 if fsacl_sddl
!= acl
:
1486 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
))
1489 fsacl
= getntacl(lp
, os
.path
.join(root
, name
), direct_db_access
=direct_db_access
)
1491 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1492 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1493 if fsacl_sddl
!= acl
:
1494 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
))
1497 def check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1499 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1502 :param sysvol: Physical path for the sysvol folder
1503 :param dnsdomain: The DNS name of the domain
1504 :param domainsid: The SID of the domain
1505 :param domaindn: The DN of the domain (ie. DC=...)
1506 :param samdb: An LDB object on the SAM db
1507 :param lp: an LP object
1510 # Set ACL for GPO root folder
1511 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1512 fsacl
= getntacl(lp
, root_policy_path
, direct_db_access
=direct_db_access
)
1514 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access
), root_policy_path
))
1515 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1516 if fsacl_sddl
!= POLICIES_ACL
:
1517 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
))
1518 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1519 attrs
=["cn", "nTSecurityDescriptor"],
1520 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1523 acl
= ndr_unpack(security
.descriptor
,
1524 str(policy
["nTSecurityDescriptor"])).as_sddl()
1525 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1526 check_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1527 domainsid
, direct_db_access
)
1530 def checksysvolacl(samdb
, netlogon
, sysvol
, domainsid
, dnsdomain
, domaindn
,
1532 """Set the ACL for the sysvol share and the subfolders
1534 :param samdb: An LDB object on the SAM db
1535 :param netlogon: Physical path for the netlogon folder
1536 :param sysvol: Physical path for the sysvol folder
1537 :param uid: The UID of the "Administrator" user
1538 :param gid: The GID of the "Domain adminstrators" group
1539 :param domainsid: The SID of the domain
1540 :param dnsdomain: The DNS name of the domain
1541 :param domaindn: The DN of the domain (ie. DC=...)
1544 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1545 s3conf
= s3param
.get_context()
1546 s3conf
.load(lp
.configfile
)
1547 # ensure we are using the right samba_dsdb passdb backend, no matter what
1548 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1549 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1550 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1552 # now ensure everything matches correctly, to avoid wierd issues
1553 if passdb
.get_global_sam_sid() != domainsid
:
1554 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
))
1556 domain_info
= s4_passdb
.domain_info()
1557 if domain_info
["dom_sid"] != domainsid
:
1558 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
))
1560 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1561 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()))
1563 # Ensure we can read this directly, and via the smbd VFS
1564 for direct_db_access
in [True, False]:
1565 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1566 for dir_path
in [os
.path
.join(sysvol
, dnsdomain
), netlogon
]:
1567 fsacl
= getntacl(lp
, dir_path
, direct_db_access
=direct_db_access
)
1569 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access
), dir_path
))
1570 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1571 if fsacl_sddl
!= SYSVOL_ACL
:
1572 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
))
1574 # Check acls on Policy folder and policies folders
1575 check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1579 def interface_ips_v4(lp
):
1580 """return only IPv4 IPs"""
1581 ips
= samba
.interface_ips(lp
, False)
1584 if i
.find(':') == -1:
1589 def interface_ips_v6(lp
, linklocal
=False):
1590 """return only IPv6 IPs"""
1591 ips
= samba
.interface_ips(lp
, False)
1594 if i
.find(':') != -1 and (linklocal
or i
.find('%') == -1):
1599 def provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
1600 domainsid
, schema
=None,
1601 targetdir
=None, samdb_fill
=FILL_FULL
,
1602 hostip
=None, hostip6
=None,
1603 next_rid
=1000, dc_rid
=None, adminpass
=None, krbtgtpass
=None,
1604 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1605 invocationid
=None, machinepass
=None, ntdsguid
=None,
1606 dns_backend
=None, dnspass
=None,
1607 serverrole
=None, dom_for_fun_level
=None,
1608 am_rodc
=False, lp
=None, use_ntvfs
=False, skip_sysvolacl
=False):
1609 # create/adapt the group policy GUIDs
1610 # Default GUID for default policy are described at
1611 # "How Core Group Policy Works"
1612 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1613 if policyguid
is None:
1614 policyguid
= DEFAULT_POLICY_GUID
1615 policyguid
= policyguid
.upper()
1616 if policyguid_dc
is None:
1617 policyguid_dc
= DEFAULT_DC_POLICY_GUID
1618 policyguid_dc
= policyguid_dc
.upper()
1620 if invocationid
is None:
1621 invocationid
= str(uuid
.uuid4())
1623 if krbtgtpass
is None:
1624 krbtgtpass
= samba
.generate_random_password(128, 255)
1625 if machinepass
is None:
1626 machinepass
= samba
.generate_random_password(128, 255)
1628 dnspass
= samba
.generate_random_password(128, 255)
1630 samdb
= fill_samdb(samdb
, lp
, names
, logger
=logger
,
1631 domainsid
=domainsid
, schema
=schema
, domainguid
=domainguid
,
1632 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1633 fill
=samdb_fill
, adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1634 invocationid
=invocationid
, machinepass
=machinepass
,
1635 dns_backend
=dns_backend
, dnspass
=dnspass
,
1636 ntdsguid
=ntdsguid
, serverrole
=serverrole
,
1637 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
1638 next_rid
=next_rid
, dc_rid
=dc_rid
)
1640 if serverrole
== "active directory domain controller":
1642 # Set up group policies (domain policy and domain controller
1644 create_default_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
,
1646 if not skip_sysvolacl
:
1647 setsysvolacl(samdb
, paths
.netlogon
, paths
.sysvol
, paths
.root_uid
,
1648 paths
.root_gid
, domainsid
, names
.dnsdomain
,
1649 names
.domaindn
, lp
, use_ntvfs
)
1651 logger
.info("Setting acl on sysvol skipped")
1653 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1654 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1655 netbiosname
=names
.netbiosname
, domainsid
=domainsid
,
1656 machinepass
=machinepass
, secure_channel_type
=SEC_CHAN_BDC
)
1658 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1659 # In future, this might be determined from some configuration
1660 kerberos_enctypes
= str(ENC_ALL_TYPES
)
1663 msg
= ldb
.Message(ldb
.Dn(samdb
,
1664 samdb
.searchone("distinguishedName",
1665 expression
="samAccountName=%s$" % names
.netbiosname
,
1666 scope
=ldb
.SCOPE_SUBTREE
)))
1667 msg
["msDS-SupportedEncryptionTypes"] = ldb
.MessageElement(
1668 elements
=kerberos_enctypes
, flags
=ldb
.FLAG_MOD_REPLACE
,
1669 name
="msDS-SupportedEncryptionTypes")
1671 except ldb
.LdbError
, (enum
, estr
):
1672 if enum
!= ldb
.ERR_NO_SUCH_ATTRIBUTE
:
1673 # It might be that this attribute does not exist in this schema
1676 setup_ad_dns(samdb
, secrets_ldb
, domainsid
, names
, paths
, lp
, logger
,
1677 hostip
=hostip
, hostip6
=hostip6
, dns_backend
=dns_backend
,
1678 dnspass
=dnspass
, os_level
=dom_for_fun_level
,
1679 targetdir
=targetdir
, site
=DEFAULTSITE
)
1681 domainguid
= samdb
.searchone(basedn
=samdb
.get_default_basedn(),
1682 attribute
="objectGUID")
1683 assert isinstance(domainguid
, str)
1685 lastProvisionUSNs
= get_last_provision_usn(samdb
)
1686 maxUSN
= get_max_usn(samdb
, str(names
.rootdn
))
1687 if lastProvisionUSNs
is not None:
1688 update_provision_usn(samdb
, 0, maxUSN
, invocationid
, 1)
1690 set_provision_usn(samdb
, 0, maxUSN
, invocationid
)
1692 logger
.info("Setting up sam.ldb rootDSE marking as synchronized")
1693 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"),
1694 { 'NTDSGUID' : names
.ntdsguid
})
1696 # fix any dangling GUIDs from the provision
1697 logger
.info("Fixing provision GUIDs")
1698 chk
= dbcheck(samdb
, samdb_schema
=samdb
, verbose
=False, fix
=True, yes
=True,
1700 samdb
.transaction_start()
1702 # a small number of GUIDs are missing because of ordering issues in the
1704 for schema_obj
in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1705 chk
.check_database(DN
="%s,%s" % (schema_obj
, names
.schemadn
),
1706 scope
=ldb
.SCOPE_BASE
,
1707 attrs
=['defaultObjectCategory'])
1708 chk
.check_database(DN
="CN=IP Security,CN=System,%s" % names
.domaindn
,
1709 scope
=ldb
.SCOPE_ONELEVEL
,
1710 attrs
=['ipsecOwnersReference',
1711 'ipsecFilterReference',
1712 'ipsecISAKMPReference',
1713 'ipsecNegotiationPolicyReference',
1714 'ipsecNFAReference'])
1716 samdb
.transaction_cancel()
1719 samdb
.transaction_commit()
1723 "ROLE_STANDALONE": "standalone server",
1724 "ROLE_DOMAIN_MEMBER": "member server",
1725 "ROLE_DOMAIN_BDC": "active directory domain controller",
1726 "ROLE_DOMAIN_PDC": "active directory domain controller",
1727 "dc": "active directory domain controller",
1728 "member": "member server",
1729 "domain controller": "active directory domain controller",
1730 "active directory domain controller": "active directory domain controller",
1731 "member server": "member server",
1732 "standalone": "standalone server",
1733 "standalone server": "standalone server",
1737 def sanitize_server_role(role
):
1738 """Sanitize a server role name.
1740 :param role: Server role
1741 :raise ValueError: If the role can not be interpreted
1742 :return: Sanitized server role (one of "member server",
1743 "active directory domain controller", "standalone server")
1746 return _ROLES_MAP
[role
]
1748 raise ValueError(role
)
1751 def provision_fake_ypserver(logger
, samdb
, domaindn
, netbiosname
, nisdomain
,
1753 """Create AD entries for the fake ypserver.
1755 This is needed for being able to manipulate posix attrs via ADUC.
1757 samdb
.transaction_start()
1759 logger
.info("Setting up fake yp server settings")
1760 setup_add_ldif(samdb
, setup_path("ypServ30.ldif"), {
1761 "DOMAINDN": domaindn
,
1762 "NETBIOSNAME": netbiosname
,
1763 "NISDOMAIN": nisdomain
,
1766 samdb
.transaction_cancel()
1769 samdb
.transaction_commit()
1772 def provision(logger
, session_info
, credentials
, smbconf
=None,
1773 targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None, rootdn
=None,
1774 domaindn
=None, schemadn
=None, configdn
=None, serverdn
=None,
1775 domain
=None, hostname
=None, hostip
=None, hostip6
=None, domainsid
=None,
1776 next_rid
=1000, dc_rid
=None, adminpass
=None, ldapadminpass
=None,
1777 krbtgtpass
=None, domainguid
=None, policyguid
=None, policyguid_dc
=None,
1778 dns_backend
=None, dns_forwarder
=None, dnspass
=None,
1779 invocationid
=None, machinepass
=None, ntdsguid
=None,
1780 root
=None, nobody
=None, users
=None, backup
=None, aci
=None,
1781 serverrole
=None, dom_for_fun_level
=None, backend_type
=None,
1782 sitename
=None, ol_mmr_urls
=None, ol_olc
=None, slapd_path
="/bin/false",
1783 useeadb
=False, am_rodc
=False, lp
=None, use_ntvfs
=False,
1784 use_rfc2307
=False, maxuid
=None, maxgid
=None, skip_sysvolacl
=True):
1787 :note: caution, this wipes all existing data!
1791 serverrole
= sanitize_server_role(serverrole
)
1793 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole
)
1795 if ldapadminpass
is None:
1796 # Make a new, random password between Samba and it's LDAP server
1797 ldapadminpass
= samba
.generate_random_password(128, 255)
1799 if backend_type
is None:
1800 backend_type
= "ldb"
1802 if domainsid
is None:
1803 domainsid
= security
.random_sid()
1805 domainsid
= security
.dom_sid(domainsid
)
1807 root_uid
= findnss_uid([root
or "root"])
1808 nobody_uid
= findnss_uid([nobody
or "nobody"])
1809 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
1810 root_gid
= pwd
.getpwuid(root_uid
).pw_gid
1813 bind_gid
= findnss_gid(["bind", "named"])
1817 if targetdir
is not None:
1818 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
1819 elif smbconf
is None:
1820 smbconf
= samba
.param
.default_path()
1821 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
1822 os
.makedirs(os
.path
.dirname(smbconf
))
1824 server_services
= []
1827 global_param
["idmap_ldb:use rfc2307"] = ["yes"]
1829 if dns_backend
!= "SAMBA_INTERNAL":
1830 server_services
.append("-dns")
1832 if dns_forwarder
is not None:
1833 global_param
["dns forwarder"] = [dns_forwarder
]
1836 server_services
.append("+smb")
1837 server_services
.append("-s3fs")
1838 global_param
["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1840 if len(server_services
) > 0:
1841 global_param
["server services"] = server_services
1843 # only install a new smb.conf if there isn't one there already
1844 if os
.path
.exists(smbconf
):
1845 # if Samba Team members can't figure out the weird errors
1846 # loading an empty smb.conf gives, then we need to be smarter.
1847 # Pretend it just didn't exist --abartlet
1848 f
= open(smbconf
, 'r')
1850 data
= f
.read().lstrip()
1853 if data
is None or data
== "":
1854 make_smbconf(smbconf
, hostname
, domain
, realm
,
1855 targetdir
, serverrole
=serverrole
,
1856 eadb
=useeadb
, use_ntvfs
=use_ntvfs
,
1857 lp
=lp
, global_param
=global_param
)
1859 make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
1860 serverrole
=serverrole
,
1861 eadb
=useeadb
, use_ntvfs
=use_ntvfs
, lp
=lp
, global_param
=global_param
)
1864 lp
= samba
.param
.LoadParm()
1866 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
1867 dnsdomain
=realm
, serverrole
=serverrole
, domaindn
=domaindn
,
1868 configdn
=configdn
, schemadn
=schemadn
, serverdn
=serverdn
,
1869 sitename
=sitename
, rootdn
=rootdn
)
1870 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
1872 paths
.bind_gid
= bind_gid
1873 paths
.root_uid
= root_uid
;
1874 paths
.root_gid
= root_gid
1877 logger
.info("Looking up IPv4 addresses")
1878 hostips
= interface_ips_v4(lp
)
1879 if len(hostips
) > 0:
1881 if len(hostips
) > 1:
1882 logger
.warning("More than one IPv4 address found. Using %s",
1884 if hostip
== "127.0.0.1":
1887 logger
.warning("No IPv4 address will be assigned")
1890 logger
.info("Looking up IPv6 addresses")
1891 hostips
= interface_ips_v6(lp
, linklocal
=False)
1893 hostip6
= hostips
[0]
1894 if len(hostips
) > 1:
1895 logger
.warning("More than one IPv6 address found. Using %s", hostip6
)
1897 logger
.warning("No IPv6 address will be assigned")
1899 names
.hostip
= hostip
1900 names
.hostip6
= hostip6
1902 if serverrole
is None:
1903 serverrole
= lp
.get("server role")
1905 if not os
.path
.exists(paths
.private_dir
):
1906 os
.mkdir(paths
.private_dir
)
1907 if not os
.path
.exists(os
.path
.join(paths
.private_dir
, "tls")):
1908 os
.mkdir(os
.path
.join(paths
.private_dir
, "tls"))
1909 if not os
.path
.exists(paths
.state_dir
):
1910 os
.mkdir(paths
.state_dir
)
1912 if paths
.sysvol
and not os
.path
.exists(paths
.sysvol
):
1913 os
.makedirs(paths
.sysvol
, 0775)
1915 if not use_ntvfs
and serverrole
== "active directory domain controller":
1916 s3conf
= s3param
.get_context()
1917 s3conf
.load(lp
.configfile
)
1919 if paths
.sysvol
is None:
1920 raise MissingShareError("sysvol", paths
.smbconf
)
1922 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(paths
.sysvol
))
1925 smbd
.set_simple_acl(file.name
, 0755, root_gid
)
1927 if not smbd
.have_posix_acls():
1928 # This clue is only strictly correct for RPM and
1929 # Debian-like Linux systems, but hopefully other users
1930 # will get enough clue from it.
1931 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.")
1933 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1935 smbd
.chown(file.name
, root_uid
, root_gid
)
1937 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1941 ldapi_url
= "ldapi://%s" % urllib
.quote(paths
.s4_ldapi_path
, safe
="")
1943 schema
= Schema(domainsid
, invocationid
=invocationid
,
1944 schemadn
=names
.schemadn
)
1946 if backend_type
== "ldb":
1947 provision_backend
= LDBBackend(backend_type
, paths
=paths
,
1948 lp
=lp
, credentials
=credentials
,
1949 names
=names
, logger
=logger
)
1950 elif backend_type
== "existing":
1951 # If support for this is ever added back, then the URI will need to be
1953 provision_backend
= ExistingBackend(backend_type
, paths
=paths
,
1954 lp
=lp
, credentials
=credentials
,
1955 names
=names
, logger
=logger
,
1956 ldap_backend_forced_uri
=None)
1957 elif backend_type
== "fedora-ds":
1958 provision_backend
= FDSBackend(backend_type
, paths
=paths
,
1959 lp
=lp
, credentials
=credentials
,
1960 names
=names
, logger
=logger
, domainsid
=domainsid
,
1961 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
1962 slapd_path
=slapd_path
,
1964 elif backend_type
== "openldap":
1965 provision_backend
= OpenLDAPBackend(backend_type
, paths
=paths
,
1966 lp
=lp
, credentials
=credentials
,
1967 names
=names
, logger
=logger
, domainsid
=domainsid
,
1968 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
1969 slapd_path
=slapd_path
, ol_mmr_urls
=ol_mmr_urls
)
1971 raise ValueError("Unknown LDAP backend type selected")
1973 provision_backend
.init()
1974 provision_backend
.start()
1976 # only install a new shares config db if there is none
1977 if not os
.path
.exists(paths
.shareconf
):
1978 logger
.info("Setting up share.ldb")
1979 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
, lp
=lp
)
1980 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
1982 logger
.info("Setting up secrets.ldb")
1983 secrets_ldb
= setup_secretsdb(paths
,
1984 session_info
=session_info
,
1985 backend_credentials
=provision_backend
.secrets_credentials
, lp
=lp
)
1988 logger
.info("Setting up the registry")
1989 setup_registry(paths
.hklm
, session_info
, lp
=lp
)
1991 logger
.info("Setting up the privileges database")
1992 setup_privileges(paths
.privilege
, session_info
, lp
=lp
)
1994 logger
.info("Setting up idmap db")
1995 idmap
= setup_idmapdb(paths
.idmapdb
, session_info
=session_info
, lp
=lp
)
1997 setup_name_mappings(idmap
, sid
=str(domainsid
),
1998 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
1999 users_gid
=users_gid
, root_gid
=root_gid
)
2001 logger
.info("Setting up SAM db")
2002 samdb
= setup_samdb(paths
.samdb
, session_info
,
2003 provision_backend
, lp
, names
, logger
=logger
,
2004 serverrole
=serverrole
,
2005 schema
=schema
, fill
=samdb_fill
, am_rodc
=am_rodc
)
2007 if serverrole
== "active directory domain controller":
2008 if paths
.netlogon
is None:
2009 raise MissingShareError("netlogon", paths
.smbconf
)
2011 if paths
.sysvol
is None:
2012 raise MissingShareError("sysvol", paths
.smbconf
)
2014 if not os
.path
.isdir(paths
.netlogon
):
2015 os
.makedirs(paths
.netlogon
, 0755)
2017 if adminpass
is None:
2018 adminpass
= samba
.generate_random_password(12, 32)
2019 adminpass_generated
= True
2021 adminpass_generated
= False
2023 if samdb_fill
== FILL_FULL
:
2024 provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
2025 schema
=schema
, targetdir
=targetdir
, samdb_fill
=samdb_fill
,
2026 hostip
=hostip
, hostip6
=hostip6
, domainsid
=domainsid
,
2027 next_rid
=next_rid
, dc_rid
=dc_rid
, adminpass
=adminpass
,
2028 krbtgtpass
=krbtgtpass
, domainguid
=domainguid
,
2029 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
2030 invocationid
=invocationid
, machinepass
=machinepass
,
2031 ntdsguid
=ntdsguid
, dns_backend
=dns_backend
,
2032 dnspass
=dnspass
, serverrole
=serverrole
,
2033 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
2034 lp
=lp
, use_ntvfs
=use_ntvfs
,
2035 skip_sysvolacl
=skip_sysvolacl
)
2037 create_krb5_conf(paths
.krb5conf
,
2038 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
2040 logger
.info("A Kerberos configuration suitable for Samba 4 has been "
2041 "generated at %s", paths
.krb5conf
)
2043 if serverrole
== "active directory domain controller":
2044 create_dns_update_list(lp
, logger
, paths
)
2046 backend_result
= provision_backend
.post_setup()
2047 provision_backend
.shutdown()
2050 secrets_ldb
.transaction_cancel()
2053 # Now commit the secrets.ldb to disk
2054 secrets_ldb
.transaction_commit()
2056 # the commit creates the dns.keytab, now chown it
2057 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
2058 if os
.path
.isfile(dns_keytab_path
) and paths
.bind_gid
is not None:
2060 os
.chmod(dns_keytab_path
, 0640)
2061 os
.chown(dns_keytab_path
, -1, paths
.bind_gid
)
2063 if not os
.environ
.has_key('SAMBA_SELFTEST'):
2064 logger
.info("Failed to chown %s to bind gid %u",
2065 dns_keytab_path
, paths
.bind_gid
)
2067 result
= ProvisionResult()
2068 result
.server_role
= serverrole
2069 result
.domaindn
= domaindn
2070 result
.paths
= paths
2071 result
.names
= names
2073 result
.samdb
= samdb
2074 result
.idmap
= idmap
2075 result
.domainsid
= str(domainsid
)
2077 if samdb_fill
== FILL_FULL
:
2078 result
.adminpass_generated
= adminpass_generated
2079 result
.adminpass
= adminpass
2081 result
.adminpass_generated
= False
2082 result
.adminpass
= None
2084 result
.backend_result
= backend_result
2087 provision_fake_ypserver(logger
=logger
, samdb
=samdb
,
2088 domaindn
=names
.domaindn
, netbiosname
=names
.netbiosname
,
2089 nisdomain
=names
.domain
.lower(), maxuid
=maxuid
, maxgid
=maxgid
)
2094 def provision_become_dc(smbconf
=None, targetdir
=None,
2095 realm
=None, rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
2096 serverdn
=None, domain
=None, hostname
=None, domainsid
=None,
2097 adminpass
=None, krbtgtpass
=None, domainguid
=None, policyguid
=None,
2098 policyguid_dc
=None, invocationid
=None, machinepass
=None, dnspass
=None,
2099 dns_backend
=None, root
=None, nobody
=None, users
=None,
2100 backup
=None, serverrole
=None, ldap_backend
=None,
2101 ldap_backend_type
=None, sitename
=None, debuglevel
=1, use_ntvfs
=False):
2103 logger
= logging
.getLogger("provision")
2104 samba
.set_debug_level(debuglevel
)
2106 res
= provision(logger
, system_session(), None,
2107 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
2108 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
2109 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
2110 hostname
=hostname
, hostip
=None, domainsid
=domainsid
,
2111 machinepass
=machinepass
,
2112 serverrole
="active directory domain controller",
2113 sitename
=sitename
, dns_backend
=dns_backend
, dnspass
=dnspass
,
2114 use_ntvfs
=use_ntvfs
)
2115 res
.lp
.set("debuglevel", str(debuglevel
))
2119 def create_krb5_conf(path
, dnsdomain
, hostname
, realm
):
2120 """Write out a file containing zone statements suitable for inclusion in a
2121 named.conf file (including GSS-TSIG configuration).
2123 :param path: Path of the new named.conf file.
2124 :param dnsdomain: DNS Domain name
2125 :param hostname: Local hostname
2126 :param realm: Realm name
2128 setup_file(setup_path("krb5.conf"), path
, {
2129 "DNSDOMAIN": dnsdomain
,
2130 "HOSTNAME": hostname
,
2135 class ProvisioningError(Exception):
2136 """A generic provision error."""
2138 def __init__(self
, value
):
2142 return "ProvisioningError: " + self
.value
2145 class InvalidNetbiosName(Exception):
2146 """A specified name was not a valid NetBIOS name."""
2148 def __init__(self
, name
):
2149 super(InvalidNetbiosName
, self
).__init
__(
2150 "The name '%r' is not a valid NetBIOS name" % name
)
2153 class MissingShareError(ProvisioningError
):
2155 def __init__(self
, name
, smbconf
):
2156 super(MissingShareError
, self
).__init
__(
2157 "Existing smb.conf does not have a [%s] share, but you are "
2158 "configuring a DC. Please remove %s or add the share manually." %