1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba4 server
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 __docformat__
= "restructuredText"
29 from base64
import b64encode
44 from samba
.auth
import system_session
, admin_session
46 from samba
.samba3
import smbd
, passdb
47 from samba
.samba3
import param
as s3param
48 from samba
.dsdb
import DS_DOMAIN_FUNCTION_2000
52 check_all_substituted
,
53 is_valid_netbios_char
,
59 from samba
.dcerpc
import security
, misc
60 from samba
.dcerpc
.misc
import (
64 from samba
.dsdb
import (
65 DS_DOMAIN_FUNCTION_2003
,
66 DS_DOMAIN_FUNCTION_2008_R2
,
69 from samba
.idmap
import IDmapDB
70 from samba
.ms_display_specifiers
import read_ms_ldif
71 from samba
.ntacls
import setntacl
, getntacl
, dsacl2fsacl
72 from samba
.ndr
import ndr_pack
, ndr_unpack
73 from samba
.provision
.backend
import (
79 from samba
.provision
.descriptor
import (
81 get_config_descriptor
,
82 get_config_partitions_descriptor
,
83 get_config_sites_descriptor
,
84 get_config_delete_protected1_descriptor
,
85 get_config_delete_protected1wd_descriptor
,
86 get_config_delete_protected2_descriptor
,
87 get_domain_descriptor
,
88 get_domain_infrastructure_descriptor
,
89 get_domain_builtin_descriptor
,
90 get_domain_computers_descriptor
,
91 get_domain_users_descriptor
,
92 get_domain_controllers_descriptor
,
93 get_domain_delete_protected1_descriptor
,
94 get_domain_delete_protected2_descriptor
,
95 get_dns_partition_descriptor
,
97 from samba
.provision
.common
import (
102 from samba
.provision
.sambadns
import (
104 create_dns_update_list
108 import samba
.registry
109 from samba
.schema
import Schema
110 from samba
.samdb
import SamDB
111 from samba
.dbchecker
import dbcheck
114 DEFAULT_POLICY_GUID
= "31B2F340-016D-11D2-945F-00C04FB984F9"
115 DEFAULT_DC_POLICY_GUID
= "6AC1786C-016F-11D2-945F-00C04fB984F9"
116 DEFAULTSITE
= "Default-First-Site-Name"
117 LAST_PROVISION_USN_ATTRIBUTE
= "lastProvisionUSN"
120 class ProvisionPaths(object):
123 self
.shareconf
= None
134 self
.dns_keytab
= None
137 self
.private_dir
= None
138 self
.state_dir
= None
141 class ProvisionNames(object):
149 self
.dnsforestdn
= None
150 self
.dnsdomaindn
= None
151 self
.ldapmanagerdn
= None
152 self
.dnsdomain
= None
154 self
.netbiosname
= None
161 def find_provision_key_parameters(samdb
, secretsdb
, idmapdb
, paths
, smbconf
,
163 """Get key provision parameters (realm, domain, ...) from a given provision
165 :param samdb: An LDB object connected to the sam.ldb file
166 :param secretsdb: An LDB object connected to the secrets.ldb file
167 :param idmapdb: An LDB object connected to the idmap.ldb file
168 :param paths: A list of path to provision object
169 :param smbconf: Path to the smb.conf file
170 :param lp: A LoadParm object
171 :return: A list of key provision parameters
173 names
= ProvisionNames()
174 names
.adminpass
= None
176 # NT domain, kerberos realm, root dn, domain dn, domain dns name
177 names
.domain
= string
.upper(lp
.get("workgroup"))
178 names
.realm
= lp
.get("realm")
179 names
.dnsdomain
= names
.realm
.lower()
180 basedn
= samba
.dn_from_dns_name(names
.dnsdomain
)
181 names
.realm
= string
.upper(names
.realm
)
183 # Get the netbiosname first (could be obtained from smb.conf in theory)
184 res
= secretsdb
.search(expression
="(flatname=%s)" %
185 names
.domain
,base
="CN=Primary Domains",
186 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
187 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
189 names
.smbconf
= smbconf
191 # That's a bit simplistic but it's ok as long as we have only 3
193 current
= samdb
.search(expression
="(objectClass=*)",
194 base
="", scope
=ldb
.SCOPE_BASE
,
195 attrs
=["defaultNamingContext", "schemaNamingContext",
196 "configurationNamingContext","rootDomainNamingContext",
199 names
.configdn
= current
[0]["configurationNamingContext"]
200 configdn
= str(names
.configdn
)
201 names
.schemadn
= current
[0]["schemaNamingContext"]
202 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
,
203 current
[0]["defaultNamingContext"][0]))):
204 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
205 "is not the same ..." % (paths
.samdb
,
206 str(current
[0]["defaultNamingContext"][0]),
207 paths
.smbconf
, basedn
)))
209 names
.domaindn
=current
[0]["defaultNamingContext"]
210 names
.rootdn
=current
[0]["rootDomainNamingContext"]
211 names
.ncs
=current
[0]["namingContexts"]
212 names
.dnsforestdn
= None
213 names
.dnsdomaindn
= None
215 for i
in range(0, len(names
.ncs
)):
218 dnsforestdn
= "DC=ForestDnsZones,%s" % (str(names
.rootdn
))
219 if nc
== dnsforestdn
:
220 names
.dnsforestdn
= dnsforestdn
223 dnsdomaindn
= "DC=DomainDnsZones,%s" % (str(names
.domaindn
))
224 if nc
== dnsdomaindn
:
225 names
.dnsdomaindn
= dnsdomaindn
229 res3
= samdb
.search(expression
="(objectClass=site)",
230 base
="CN=Sites," + configdn
, scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn"])
231 names
.sitename
= str(res3
[0]["cn"])
233 # dns hostname and server dn
234 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
235 base
="OU=Domain Controllers,%s" % basedn
,
236 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
237 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("." + names
.dnsdomain
, "")
239 server_res
= samdb
.search(expression
="serverReference=%s" % res4
[0].dn
,
240 attrs
=[], base
=configdn
)
241 names
.serverdn
= server_res
[0].dn
243 # invocation id/objectguid
244 res5
= samdb
.search(expression
="(objectClass=*)",
245 base
="CN=NTDS Settings,%s" % str(names
.serverdn
),
246 scope
=ldb
.SCOPE_BASE
,
247 attrs
=["invocationID", "objectGUID"])
248 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
249 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
252 res6
= samdb
.search(expression
="(objectClass=*)", base
=basedn
,
253 scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID",
254 "objectSid","msDS-Behavior-Version" ])
255 names
.domainguid
= str(ndr_unpack(misc
.GUID
, res6
[0]["objectGUID"][0]))
256 names
.domainsid
= ndr_unpack( security
.dom_sid
, res6
[0]["objectSid"][0])
257 if res6
[0].get("msDS-Behavior-Version") is None or \
258 int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
259 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
261 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
264 res7
= samdb
.search(expression
="(displayName=Default Domain Policy)",
265 base
="CN=Policies,CN=System," + basedn
,
266 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
267 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
269 res8
= samdb
.search(expression
="(displayName=Default Domain Controllers"
271 base
="CN=Policies,CN=System," + basedn
,
272 scope
=ldb
.SCOPE_ONELEVEL
,
273 attrs
=["cn","displayName"])
275 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
277 names
.policyid_dc
= None
279 res9
= idmapdb
.search(expression
="(cn=%s-%s)" %
280 (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
),
281 attrs
=["xidNumber", "type"])
283 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
))
284 if res9
[0]["type"][0] == "ID_TYPE_BOTH":
285 names
.root_gid
= res9
[0]["xidNumber"][0]
287 names
.root_gid
= pwd
.getpwuid(int(res9
[0]["xidNumber"][0])).pw_gid
291 def update_provision_usn(samdb
, low
, high
, id, replace
=False):
292 """Update the field provisionUSN in sam.ldb
294 This field is used to track range of USN modified by provision and
296 This value is used afterward by next provision to figure out if
297 the field have been modified since last provision.
299 :param samdb: An LDB object connect to sam.ldb
300 :param low: The lowest USN modified by this upgrade
301 :param high: The highest USN modified by this upgrade
302 :param id: The invocation id of the samba's dc
303 :param replace: A boolean indicating if the range should replace any
304 existing one or appended (default)
309 entry
= samdb
.search(base
="@PROVISION",
310 scope
=ldb
.SCOPE_BASE
,
311 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "dn"])
312 for e
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
313 if not re
.search(';', e
):
314 e
= "%s;%s" % (e
, id)
317 tab
.append("%s-%s;%s" % (low
, high
, id))
318 delta
= ldb
.Message()
319 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
320 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
321 ldb
.FLAG_MOD_REPLACE
, LAST_PROVISION_USN_ATTRIBUTE
)
322 entry
= samdb
.search(expression
='provisionnerID=*',
323 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
324 attrs
=["provisionnerID"])
325 if len(entry
) == 0 or len(entry
[0]) == 0:
326 delta
["provisionnerID"] = ldb
.MessageElement(id, ldb
.FLAG_MOD_ADD
, "provisionnerID")
330 def set_provision_usn(samdb
, low
, high
, id):
331 """Set the field provisionUSN in sam.ldb
332 This field is used to track range of USN modified by provision and
334 This value is used afterward by next provision to figure out if
335 the field have been modified since last provision.
337 :param samdb: An LDB object connect to sam.ldb
338 :param low: The lowest USN modified by this upgrade
339 :param high: The highest USN modified by this upgrade
340 :param id: The invocationId of the provision"""
343 tab
.append("%s-%s;%s" % (low
, high
, id))
345 delta
= ldb
.Message()
346 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
347 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
348 ldb
.FLAG_MOD_ADD
, LAST_PROVISION_USN_ATTRIBUTE
)
352 def get_max_usn(samdb
,basedn
):
353 """ This function return the biggest USN present in the provision
355 :param samdb: A LDB object pointing to the sam.ldb
356 :param basedn: A string containing the base DN of the provision
358 :return: The biggest USN in the provision"""
360 res
= samdb
.search(expression
="objectClass=*",base
=basedn
,
361 scope
=ldb
.SCOPE_SUBTREE
,attrs
=["uSNChanged"],
362 controls
=["search_options:1:2",
363 "server_sort:1:1:uSNChanged",
364 "paged_results:1:1"])
365 return res
[0]["uSNChanged"]
368 def get_last_provision_usn(sam
):
369 """Get USNs ranges modified by a provision or an upgradeprovision
371 :param sam: An LDB object pointing to the sam.ldb
372 :return: a dictionary which keys are invocation id and values are an array
373 of integer representing the different ranges
376 entry
= sam
.search(expression
="%s=*" % LAST_PROVISION_USN_ATTRIBUTE
,
377 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
378 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "provisionnerID"])
379 except ldb
.LdbError
, (ecode
, emsg
):
380 if ecode
== ldb
.ERR_NO_SUCH_OBJECT
:
387 if entry
[0].get("provisionnerID"):
388 for e
in entry
[0]["provisionnerID"]:
390 for r
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
391 tab1
= str(r
).split(';')
396 if (len(myids
) > 0 and id not in myids
):
398 tab2
= p
.split(tab1
[0])
399 if range.get(id) is None:
401 range[id].append(tab2
[0])
402 range[id].append(tab2
[1])
408 class ProvisionResult(object):
409 """Result of a provision.
411 :ivar server_role: The server role
412 :ivar paths: ProvisionPaths instance
413 :ivar domaindn: The domain dn, as string
417 self
.server_role
= None
424 self
.domainsid
= None
425 self
.adminpass_generated
= None
426 self
.adminpass
= None
427 self
.backend_result
= None
429 def report_logger(self
, logger
):
430 """Report this provision result to a logger."""
432 "Once the above files are installed, your Samba4 server will "
434 if self
.adminpass_generated
:
435 logger
.info("Admin password: %s", self
.adminpass
)
436 logger
.info("Server Role: %s", self
.server_role
)
437 logger
.info("Hostname: %s", self
.names
.hostname
)
438 logger
.info("NetBIOS Domain: %s", self
.names
.domain
)
439 logger
.info("DNS Domain: %s", self
.names
.dnsdomain
)
440 logger
.info("DOMAIN SID: %s", self
.domainsid
)
442 if self
.backend_result
:
443 self
.backend_result
.report_logger(logger
)
446 def check_install(lp
, session_info
, credentials
):
447 """Check whether the current install seems ok.
449 :param lp: Loadparm context
450 :param session_info: Session information
451 :param credentials: Credentials
453 if lp
.get("realm") == "":
454 raise Exception("Realm empty")
455 samdb
= Ldb(lp
.samdb_url(), session_info
=session_info
,
456 credentials
=credentials
, lp
=lp
)
457 if len(samdb
.search("(cn=Administrator)")) != 1:
458 raise ProvisioningError("No administrator account found")
461 def findnss(nssfn
, names
):
462 """Find a user or group from a list of possibilities.
464 :param nssfn: NSS Function to try (should raise KeyError if not found)
465 :param names: Names to check.
466 :return: Value return by first names list.
473 raise KeyError("Unable to find user/group in %r" % names
)
476 findnss_uid
= lambda names
: findnss(pwd
.getpwnam
, names
)[2]
477 findnss_gid
= lambda names
: findnss(grp
.getgrnam
, names
)[2]
480 def provision_paths_from_lp(lp
, dnsdomain
):
481 """Set the default paths for provisioning.
483 :param lp: Loadparm context.
484 :param dnsdomain: DNS Domain name
486 paths
= ProvisionPaths()
487 paths
.private_dir
= lp
.get("private dir")
488 paths
.state_dir
= lp
.get("state directory")
490 # This is stored without path prefix for the "privateKeytab" attribute in
491 # "secrets_dns.ldif".
492 paths
.dns_keytab
= "dns.keytab"
493 paths
.keytab
= "secrets.keytab"
495 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
496 paths
.samdb
= os
.path
.join(paths
.private_dir
, "sam.ldb")
497 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, "idmap.ldb")
498 paths
.secrets
= os
.path
.join(paths
.private_dir
, "secrets.ldb")
499 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
500 paths
.dns
= os
.path
.join(paths
.private_dir
, "dns", dnsdomain
+ ".zone")
501 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
502 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
503 paths
.namedconf
= os
.path
.join(paths
.private_dir
, "named.conf")
504 paths
.namedconf_update
= os
.path
.join(paths
.private_dir
, "named.conf.update")
505 paths
.namedtxt
= os
.path
.join(paths
.private_dir
, "named.txt")
506 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
507 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
508 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
509 paths
.hklm
= "hklm.ldb"
510 paths
.hkcr
= "hkcr.ldb"
511 paths
.hkcu
= "hkcu.ldb"
512 paths
.hku
= "hku.ldb"
513 paths
.hkpd
= "hkpd.ldb"
514 paths
.hkpt
= "hkpt.ldb"
515 paths
.sysvol
= lp
.get("path", "sysvol")
516 paths
.netlogon
= lp
.get("path", "netlogon")
517 paths
.smbconf
= lp
.configfile
521 def determine_netbios_name(hostname
):
522 """Determine a netbios name from a hostname."""
523 # remove forbidden chars and force the length to be <16
524 netbiosname
= "".join([x
for x
in hostname
if is_valid_netbios_char(x
)])
525 return netbiosname
[:MAX_NETBIOS_NAME_LEN
].upper()
528 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
529 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
530 schemadn
=None, serverdn
=None, sitename
=None):
531 """Guess configuration settings to use."""
534 hostname
= socket
.gethostname().split(".")[0]
536 netbiosname
= lp
.get("netbios name")
537 if netbiosname
is None:
538 netbiosname
= determine_netbios_name(hostname
)
539 netbiosname
= netbiosname
.upper()
540 if not valid_netbios_name(netbiosname
):
541 raise InvalidNetbiosName(netbiosname
)
543 if dnsdomain
is None:
544 dnsdomain
= lp
.get("realm")
545 if dnsdomain
is None or dnsdomain
== "":
546 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp
.configfile
)
548 dnsdomain
= dnsdomain
.lower()
550 if serverrole
is None:
551 serverrole
= lp
.get("server role")
552 if serverrole
is None:
553 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
555 serverrole
= serverrole
.lower()
557 realm
= dnsdomain
.upper()
559 if lp
.get("realm") == "":
560 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
562 if lp
.get("realm").upper() != realm
:
563 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
))
565 if lp
.get("server role").lower() != serverrole
:
566 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
))
568 if serverrole
== "active directory domain controller":
570 # This will, for better or worse, default to 'WORKGROUP'
571 domain
= lp
.get("workgroup")
572 domain
= domain
.upper()
574 if lp
.get("workgroup").upper() != domain
:
575 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
))
578 domaindn
= samba
.dn_from_dns_name(dnsdomain
)
580 if domain
== netbiosname
:
581 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain
, netbiosname
))
585 domaindn
= "DC=" + netbiosname
587 if not valid_netbios_name(domain
):
588 raise InvalidNetbiosName(domain
)
590 if hostname
.upper() == realm
:
591 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
592 if netbiosname
.upper() == realm
:
593 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm
, netbiosname
))
595 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
601 configdn
= "CN=Configuration," + rootdn
603 schemadn
= "CN=Schema," + configdn
606 sitename
= DEFAULTSITE
608 names
= ProvisionNames()
609 names
.rootdn
= rootdn
610 names
.domaindn
= domaindn
611 names
.configdn
= configdn
612 names
.schemadn
= schemadn
613 names
.ldapmanagerdn
= "CN=Manager," + rootdn
614 names
.dnsdomain
= dnsdomain
615 names
.domain
= domain
617 names
.netbiosname
= netbiosname
618 names
.hostname
= hostname
619 names
.sitename
= sitename
620 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
621 netbiosname
, sitename
, configdn
)
626 def make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
627 serverrole
=None, eadb
=False, use_ntvfs
=False, lp
=None,
629 """Create a new smb.conf file based on a couple of basic settings.
631 assert smbconf
is not None
634 hostname
= socket
.gethostname().split(".")[0]
636 netbiosname
= determine_netbios_name(hostname
)
638 if serverrole
is None:
639 serverrole
= "standalone server"
641 assert domain
is not None
642 domain
= domain
.upper()
644 assert realm
is not None
645 realm
= realm
.upper()
648 "netbios name": netbiosname
,
651 "server role": serverrole
,
655 lp
= samba
.param
.LoadParm()
656 #Load non-existent file
657 if os
.path
.exists(smbconf
):
660 if global_param
is not None:
661 for ent
in global_param
:
662 if global_param
[ent
] is not None:
663 global_settings
[ent
] = " ".join(global_param
[ent
])
665 if targetdir
is not None:
666 global_settings
["private dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "private"))
667 global_settings
["lock dir"] = os
.path
.abspath(targetdir
)
668 global_settings
["state directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "state"))
669 global_settings
["cache directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "cache"))
671 lp
.set("lock dir", os
.path
.abspath(targetdir
))
672 lp
.set("state directory", global_settings
["state directory"])
673 lp
.set("cache directory", global_settings
["cache directory"])
676 if use_ntvfs
and not lp
.get("posix:eadb"):
677 if targetdir
is not None:
678 privdir
= os
.path
.join(targetdir
, "private")
680 privdir
= lp
.get("private dir")
681 lp
.set("posix:eadb", os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
682 elif not use_ntvfs
and not lp
.get("xattr_tdb:file"):
683 if targetdir
is not None:
684 statedir
= os
.path
.join(targetdir
, "state")
686 statedir
= lp
.get("state directory")
687 lp
.set("xattr_tdb:file", os
.path
.abspath(os
.path
.join(statedir
, "xattr.tdb")))
690 if serverrole
== "active directory domain controller":
691 shares
["sysvol"] = os
.path
.join(lp
.get("state directory"), "sysvol")
692 shares
["netlogon"] = os
.path
.join(shares
["sysvol"], realm
.lower(),
695 global_settings
["passdb backend"] = "samba_dsdb"
697 f
= open(smbconf
, 'w')
699 f
.write("[globals]\n")
700 for key
, val
in global_settings
.iteritems():
701 f
.write("\t%s = %s\n" % (key
, val
))
704 for name
, path
in shares
.iteritems():
705 f
.write("[%s]\n" % name
)
706 f
.write("\tpath = %s\n" % path
)
707 f
.write("\tread only = no\n")
711 # reload the smb.conf
714 # and dump it without any values that are the default
715 # this ensures that any smb.conf parameters that were set
716 # on the provision/join command line are set in the resulting smb.conf
717 f
= open(smbconf
, mode
='w')
724 def setup_name_mappings(idmap
, sid
, root_uid
, nobody_uid
,
725 users_gid
, root_gid
):
726 """setup reasonable name mappings for sam names to unix names.
728 :param samdb: SamDB object.
729 :param idmap: IDmap db object.
730 :param sid: The domain sid.
731 :param domaindn: The domain DN.
732 :param root_uid: uid of the UNIX root user.
733 :param nobody_uid: uid of the UNIX nobody user.
734 :param users_gid: gid of the UNIX users group.
735 :param root_gid: gid of the UNIX root group.
737 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
739 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
740 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
743 def setup_samdb_partitions(samdb_path
, logger
, lp
, session_info
,
744 provision_backend
, names
, schema
, serverrole
,
746 """Setup the partitions for the SAM database.
748 Alternatively, provision() may call this, and then populate the database.
750 :note: This will wipe the Sam Database!
752 :note: This function always removes the local SAM LDB file. The erase
753 parameter controls whether to erase the existing data, which
754 may not be stored locally but in LDAP.
757 assert session_info
is not None
759 # We use options=["modules:"] to stop the modules loading - we
760 # just want to wipe and re-initialise the database, not start it up
763 os
.unlink(samdb_path
)
767 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
768 lp
=lp
, options
=["modules:"])
770 ldap_backend_line
= "# No LDAP backend"
771 if provision_backend
.type != "ldb":
772 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldap_uri
774 samdb
.transaction_start()
776 logger
.info("Setting up sam.ldb partitions and settings")
777 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
778 "LDAP_BACKEND_LINE": ldap_backend_line
782 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
783 "BACKEND_TYPE": provision_backend
.type,
784 "SERVER_ROLE": serverrole
787 logger
.info("Setting up sam.ldb rootDSE")
788 setup_samdb_rootdse(samdb
, names
)
790 samdb
.transaction_cancel()
793 samdb
.transaction_commit()
796 def secretsdb_self_join(secretsdb
, domain
,
797 netbiosname
, machinepass
, domainsid
=None,
798 realm
=None, dnsdomain
=None,
800 key_version_number
=1,
801 secure_channel_type
=SEC_CHAN_WKSTA
):
802 """Add domain join-specific bits to a secrets database.
804 :param secretsdb: Ldb Handle to the secrets database
805 :param machinepass: Machine password
807 attrs
= ["whenChanged",
814 if realm
is not None:
815 if dnsdomain
is None:
816 dnsdomain
= realm
.lower()
817 dnsname
= '%s.%s' % (netbiosname
.lower(), dnsdomain
.lower())
820 shortname
= netbiosname
.lower()
822 # We don't need to set msg["flatname"] here, because rdn_name will handle
823 # it, and it causes problems for modifies anyway
824 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
825 msg
["secureChannelType"] = [str(secure_channel_type
)]
826 msg
["objectClass"] = ["top", "primaryDomain"]
827 if dnsname
is not None:
828 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
829 msg
["realm"] = [realm
]
830 msg
["saltPrincipal"] = ["host/%s@%s" % (dnsname
, realm
.upper())]
831 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
832 msg
["privateKeytab"] = ["secrets.keytab"]
834 msg
["secret"] = [machinepass
]
835 msg
["samAccountName"] = ["%s$" % netbiosname
]
836 msg
["secureChannelType"] = [str(secure_channel_type
)]
837 if domainsid
is not None:
838 msg
["objectSid"] = [ndr_pack(domainsid
)]
840 # This complex expression tries to ensure that we don't have more
841 # than one record for this SID, realm or netbios domain at a time,
842 # but we don't delete the old record that we are about to modify,
843 # because that would delete the keytab and previous password.
844 res
= secretsdb
.search(base
="cn=Primary Domains", attrs
=attrs
,
845 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain
, realm
, str(domainsid
), str(msg
.dn
))),
846 scope
=ldb
.SCOPE_ONELEVEL
)
849 secretsdb
.delete(del_msg
.dn
)
851 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
854 msg
["priorSecret"] = [res
[0]["secret"][0]]
855 msg
["priorWhenChanged"] = [res
[0]["whenChanged"][0]]
858 msg
["privateKeytab"] = [res
[0]["privateKeytab"][0]]
863 msg
["krb5Keytab"] = [res
[0]["krb5Keytab"][0]]
869 msg
[el
].set_flags(ldb
.FLAG_MOD_REPLACE
)
870 secretsdb
.modify(msg
)
871 secretsdb
.rename(res
[0].dn
, msg
.dn
)
873 spn
= [ 'HOST/%s' % shortname
]
874 if secure_channel_type
== SEC_CHAN_BDC
and dnsname
is not None:
875 # we are a domain controller then we add servicePrincipalName
876 # entries for the keytab code to update.
877 spn
.extend([ 'HOST/%s' % dnsname
])
878 msg
["servicePrincipalName"] = spn
883 def setup_secretsdb(paths
, session_info
, backend_credentials
, lp
):
884 """Setup the secrets database.
886 :note: This function does not handle exceptions and transaction on purpose,
887 it's up to the caller to do this job.
889 :param path: Path to the secrets database.
890 :param session_info: Session info.
891 :param credentials: Credentials
892 :param lp: Loadparm context
893 :return: LDB handle for the created secrets database
895 if os
.path
.exists(paths
.secrets
):
896 os
.unlink(paths
.secrets
)
898 keytab_path
= os
.path
.join(paths
.private_dir
, paths
.keytab
)
899 if os
.path
.exists(keytab_path
):
900 os
.unlink(keytab_path
)
902 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
903 if os
.path
.exists(dns_keytab_path
):
904 os
.unlink(dns_keytab_path
)
908 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
910 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
911 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
912 secrets_ldb
.transaction_start()
914 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
916 if (backend_credentials
is not None and
917 backend_credentials
.authentication_requested()):
918 if backend_credentials
.get_bind_dn() is not None:
919 setup_add_ldif(secrets_ldb
,
920 setup_path("secrets_simple_ldap.ldif"), {
921 "LDAPMANAGERDN": backend_credentials
.get_bind_dn(),
922 "LDAPMANAGERPASS_B64": b64encode(backend_credentials
.get_password())
925 setup_add_ldif(secrets_ldb
,
926 setup_path("secrets_sasl_ldap.ldif"), {
927 "LDAPADMINUSER": backend_credentials
.get_username(),
928 "LDAPADMINREALM": backend_credentials
.get_realm(),
929 "LDAPADMINPASS_B64": b64encode(backend_credentials
.get_password())
932 secrets_ldb
.transaction_cancel()
937 def setup_privileges(path
, session_info
, lp
):
938 """Setup the privileges database.
940 :param path: Path to the privileges database.
941 :param session_info: Session info.
942 :param credentials: Credentials
943 :param lp: Loadparm context
944 :return: LDB handle for the created secrets database
946 if os
.path
.exists(path
):
948 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
949 privilege_ldb
.erase()
950 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
953 def setup_registry(path
, session_info
, lp
):
954 """Setup the registry.
956 :param path: Path to the registry database
957 :param session_info: Session information
958 :param credentials: Credentials
959 :param lp: Loadparm context
961 reg
= samba
.registry
.Registry()
962 hive
= samba
.registry
.open_ldb(path
, session_info
=session_info
, lp_ctx
=lp
)
963 reg
.mount_hive(hive
, samba
.registry
.HKEY_LOCAL_MACHINE
)
964 provision_reg
= setup_path("provision.reg")
965 assert os
.path
.exists(provision_reg
)
966 reg
.diff_apply(provision_reg
)
969 def setup_idmapdb(path
, session_info
, lp
):
970 """Setup the idmap database.
972 :param path: path to the idmap database
973 :param session_info: Session information
974 :param credentials: Credentials
975 :param lp: Loadparm context
977 if os
.path
.exists(path
):
980 idmap_ldb
= IDmapDB(path
, session_info
=session_info
, lp
=lp
)
982 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
986 def setup_samdb_rootdse(samdb
, names
):
987 """Setup the SamDB rootdse.
989 :param samdb: Sam Database handle
991 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
992 "SCHEMADN": names
.schemadn
,
993 "DOMAINDN": names
.domaindn
,
994 "ROOTDN" : names
.rootdn
,
995 "CONFIGDN": names
.configdn
,
996 "SERVERDN": names
.serverdn
,
1000 def setup_self_join(samdb
, admin_session_info
, names
, fill
, machinepass
,
1001 dns_backend
, dnspass
, domainsid
, next_rid
, invocationid
,
1002 policyguid
, policyguid_dc
,
1003 domainControllerFunctionality
, ntdsguid
=None, dc_rid
=None):
1004 """Join a host to its own domain."""
1005 assert isinstance(invocationid
, str)
1006 if ntdsguid
is not None:
1007 ntdsguid_line
= "objectGUID: %s\n"%ntdsguid
1014 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
1015 "CONFIGDN": names
.configdn
,
1016 "SCHEMADN": names
.schemadn
,
1017 "DOMAINDN": names
.domaindn
,
1018 "SERVERDN": names
.serverdn
,
1019 "INVOCATIONID": invocationid
,
1020 "NETBIOSNAME": names
.netbiosname
,
1021 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1022 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1023 "DOMAINSID": str(domainsid
),
1024 "DCRID": str(dc_rid
),
1025 "SAMBA_VERSION_STRING": version
,
1026 "NTDSGUID": ntdsguid_line
,
1027 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1028 domainControllerFunctionality
),
1029 "RIDALLOCATIONSTART": str(next_rid
+ 100),
1030 "RIDALLOCATIONEND": str(next_rid
+ 100 + 499)})
1032 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
1033 "POLICYGUID": policyguid
,
1034 "POLICYGUID_DC": policyguid_dc
,
1035 "DNSDOMAIN": names
.dnsdomain
,
1036 "DOMAINDN": names
.domaindn
})
1038 # If we are setting up a subdomain, then this has been replicated in, so we
1039 # don't need to add it
1040 if fill
== FILL_FULL
:
1041 setup_add_ldif(samdb
, setup_path("provision_self_join_config.ldif"), {
1042 "CONFIGDN": names
.configdn
,
1043 "SCHEMADN": names
.schemadn
,
1044 "DOMAINDN": names
.domaindn
,
1045 "SERVERDN": names
.serverdn
,
1046 "INVOCATIONID": invocationid
,
1047 "NETBIOSNAME": names
.netbiosname
,
1048 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1049 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1050 "DOMAINSID": str(domainsid
),
1051 "DCRID": str(dc_rid
),
1052 "SAMBA_VERSION_STRING": version
,
1053 "NTDSGUID": ntdsguid_line
,
1054 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1055 domainControllerFunctionality
)})
1057 # Setup fSMORoleOwner entries to point at the newly created DC entry
1058 setup_modify_ldif(samdb
,
1059 setup_path("provision_self_join_modify_config.ldif"), {
1060 "CONFIGDN": names
.configdn
,
1061 "SCHEMADN": names
.schemadn
,
1062 "DEFAULTSITE": names
.sitename
,
1063 "NETBIOSNAME": names
.netbiosname
,
1064 "SERVERDN": names
.serverdn
,
1067 system_session_info
= system_session()
1068 samdb
.set_session_info(system_session_info
)
1069 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1070 # modify a serverReference under cn=config when we are a subdomain, we must
1071 # be system due to ACLs
1072 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
1073 "DOMAINDN": names
.domaindn
,
1074 "SERVERDN": names
.serverdn
,
1075 "NETBIOSNAME": names
.netbiosname
,
1078 samdb
.set_session_info(admin_session_info
)
1080 if dns_backend
!= "SAMBA_INTERNAL":
1081 # This is Samba4 specific and should be replaced by the correct
1082 # DNS AD-style setup
1083 setup_add_ldif(samdb
, setup_path("provision_dns_add_samba.ldif"), {
1084 "DNSDOMAIN": names
.dnsdomain
,
1085 "DOMAINDN": names
.domaindn
,
1086 "DNSPASS_B64": b64encode(dnspass
.encode('utf-16-le')),
1087 "HOSTNAME" : names
.hostname
,
1088 "DNSNAME" : '%s.%s' % (
1089 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
1093 def getpolicypath(sysvolpath
, dnsdomain
, guid
):
1094 """Return the physical path of policy given its guid.
1096 :param sysvolpath: Path to the sysvol folder
1097 :param dnsdomain: DNS name of the AD domain
1098 :param guid: The GUID of the policy
1099 :return: A string with the complete path to the policy folder
1102 guid
= "{%s}" % guid
1103 policy_path
= os
.path
.join(sysvolpath
, dnsdomain
, "Policies", guid
)
1107 def create_gpo_struct(policy_path
):
1108 if not os
.path
.exists(policy_path
):
1109 os
.makedirs(policy_path
, 0775)
1110 f
= open(os
.path
.join(policy_path
, "GPT.INI"), 'w')
1112 f
.write("[General]\r\nVersion=0")
1115 p
= os
.path
.join(policy_path
, "MACHINE")
1116 if not os
.path
.exists(p
):
1117 os
.makedirs(p
, 0775)
1118 p
= os
.path
.join(policy_path
, "USER")
1119 if not os
.path
.exists(p
):
1120 os
.makedirs(p
, 0775)
1123 def create_default_gpo(sysvolpath
, dnsdomain
, policyguid
, policyguid_dc
):
1124 """Create the default GPO for a domain
1126 :param sysvolpath: Physical path for the sysvol folder
1127 :param dnsdomain: DNS domain name of the AD domain
1128 :param policyguid: GUID of the default domain policy
1129 :param policyguid_dc: GUID of the default domain controler policy
1131 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid
)
1132 create_gpo_struct(policy_path
)
1134 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid_dc
)
1135 create_gpo_struct(policy_path
)
1138 def setup_samdb(path
, session_info
, provision_backend
, lp
, names
,
1139 logger
, fill
, serverrole
, schema
, am_rodc
=False):
1140 """Setup a complete SAM Database.
1142 :note: This will wipe the main SAM database file!
1145 # Also wipes the database
1146 setup_samdb_partitions(path
, logger
=logger
, lp
=lp
,
1147 provision_backend
=provision_backend
, session_info
=session_info
,
1148 names
=names
, serverrole
=serverrole
, schema
=schema
)
1150 # Load the database, but don's load the global schema and don't connect
1152 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
1153 credentials
=provision_backend
.credentials
, lp
=lp
,
1154 global_schema
=False, am_rodc
=am_rodc
)
1156 logger
.info("Pre-loading the Samba 4 and AD schema")
1158 # Load the schema from the one we computed earlier
1159 samdb
.set_schema(schema
, write_indices_and_attributes
=False)
1161 # Set the NTDS settings DN manually - in order to have it already around
1162 # before the provisioned tree exists and we connect
1163 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1165 # And now we can connect to the DB - the schema won't be loaded from the
1169 # But we have to give it one more kick to have it use the schema
1170 # during provision - it needs, now that it is connected, to write
1171 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1172 samdb
.set_schema(schema
, write_indices_and_attributes
=True)
1177 def fill_samdb(samdb
, lp
, names
, logger
, domainsid
, domainguid
, policyguid
,
1178 policyguid_dc
, fill
, adminpass
, krbtgtpass
, machinepass
, dns_backend
,
1179 dnspass
, invocationid
, ntdsguid
, serverrole
, am_rodc
=False,
1180 dom_for_fun_level
=None, schema
=None, next_rid
=None, dc_rid
=None):
1182 if next_rid
is None:
1185 # Provision does not make much sense values larger than 1000000000
1186 # as the upper range of the rIDAvailablePool is 1073741823 and
1187 # we don't want to create a domain that cannot allocate rids.
1188 if next_rid
< 1000 or next_rid
> 1000000000:
1189 error
= "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid
)
1190 error
+= "the valid range is %u-%u. The default is %u." % (
1191 1000, 1000000000, 1000)
1192 raise ProvisioningError(error
)
1194 # ATTENTION: Do NOT change these default values without discussion with the
1195 # team and/or release manager. They have a big impact on the whole program!
1196 domainControllerFunctionality
= DS_DOMAIN_FUNCTION_2008_R2
1198 if dom_for_fun_level
is None:
1199 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2003
1201 if dom_for_fun_level
> domainControllerFunctionality
:
1202 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!")
1204 domainFunctionality
= dom_for_fun_level
1205 forestFunctionality
= dom_for_fun_level
1207 # Set the NTDS settings DN manually - in order to have it already around
1208 # before the provisioned tree exists and we connect
1209 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1211 samdb
.transaction_start()
1213 # Set the domain functionality levels onto the database.
1214 # Various module (the password_hash module in particular) need
1215 # to know what level of AD we are emulating.
1217 # These will be fixed into the database via the database
1218 # modifictions below, but we need them set from the start.
1219 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
1220 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
1221 samdb
.set_opaque_integer("domainControllerFunctionality",
1222 domainControllerFunctionality
)
1224 samdb
.set_domain_sid(str(domainsid
))
1225 samdb
.set_invocation_id(invocationid
)
1227 logger
.info("Adding DomainDN: %s" % names
.domaindn
)
1229 # impersonate domain admin
1230 admin_session_info
= admin_session(lp
, str(domainsid
))
1231 samdb
.set_session_info(admin_session_info
)
1232 if domainguid
is not None:
1233 domainguid_line
= "objectGUID: %s\n-" % domainguid
1235 domainguid_line
= ""
1237 descr
= b64encode(get_domain_descriptor(domainsid
))
1238 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
1239 "DOMAINDN": names
.domaindn
,
1240 "DOMAINSID": str(domainsid
),
1241 "DESCRIPTOR": descr
,
1242 "DOMAINGUID": domainguid_line
1245 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
1246 "DOMAINDN": names
.domaindn
,
1247 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1248 "NEXTRID": str(next_rid
),
1249 "DEFAULTSITE": names
.sitename
,
1250 "CONFIGDN": names
.configdn
,
1251 "POLICYGUID": policyguid
,
1252 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1253 "SAMBA_VERSION_STRING": version
1256 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1257 if fill
== FILL_FULL
:
1258 logger
.info("Adding configuration container")
1259 descr
= b64encode(get_config_descriptor(domainsid
))
1260 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
1261 "CONFIGDN": names
.configdn
,
1262 "DESCRIPTOR": descr
,
1265 # The LDIF here was created when the Schema object was constructed
1266 logger
.info("Setting up sam.ldb schema")
1267 samdb
.add_ldif(schema
.schema_dn_add
, controls
=["relax:0"])
1268 samdb
.modify_ldif(schema
.schema_dn_modify
)
1269 samdb
.write_prefixes_from_schema()
1270 samdb
.add_ldif(schema
.schema_data
, controls
=["relax:0"])
1271 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
1272 {"SCHEMADN": names
.schemadn
})
1274 # Now register this container in the root of the forest
1275 msg
= ldb
.Message(ldb
.Dn(samdb
, names
.domaindn
))
1276 msg
["subRefs"] = ldb
.MessageElement(names
.configdn
, ldb
.FLAG_MOD_ADD
,
1280 samdb
.transaction_cancel()
1283 samdb
.transaction_commit()
1285 samdb
.transaction_start()
1287 samdb
.invocation_id
= invocationid
1289 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1290 if fill
== FILL_FULL
:
1291 logger
.info("Setting up sam.ldb configuration data")
1292 partitions_descr
= b64encode(get_config_partitions_descriptor(domainsid
))
1293 sites_descr
= b64encode(get_config_sites_descriptor(domainsid
))
1294 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
1295 "CONFIGDN": names
.configdn
,
1296 "NETBIOSNAME": names
.netbiosname
,
1297 "DEFAULTSITE": names
.sitename
,
1298 "DNSDOMAIN": names
.dnsdomain
,
1299 "DOMAIN": names
.domain
,
1300 "SCHEMADN": names
.schemadn
,
1301 "DOMAINDN": names
.domaindn
,
1302 "SERVERDN": names
.serverdn
,
1303 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
1304 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1305 "PARTITIONS_DESCRIPTOR": partitions_descr
,
1306 "SITES_DESCRIPTOR": sites_descr
,
1309 logger
.info("Setting up display specifiers")
1310 display_specifiers_ldif
= read_ms_ldif(
1311 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1312 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
,
1313 {"CONFIGDN": names
.configdn
})
1314 check_all_substituted(display_specifiers_ldif
)
1315 samdb
.add_ldif(display_specifiers_ldif
)
1317 logger
.info("Adding users container")
1318 users_desc
= b64encode(get_domain_users_descriptor(domainsid
))
1319 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
1320 "DOMAINDN": names
.domaindn
,
1321 "USERS_DESCRIPTOR": users_desc
1323 logger
.info("Modifying users container")
1324 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
1325 "DOMAINDN": names
.domaindn
})
1326 logger
.info("Adding computers container")
1327 computers_desc
= b64encode(get_domain_computers_descriptor(domainsid
))
1328 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
1329 "DOMAINDN": names
.domaindn
,
1330 "COMPUTERS_DESCRIPTOR": computers_desc
1332 logger
.info("Modifying computers container")
1333 setup_modify_ldif(samdb
,
1334 setup_path("provision_computers_modify.ldif"), {
1335 "DOMAINDN": names
.domaindn
})
1336 logger
.info("Setting up sam.ldb data")
1337 infrastructure_desc
= b64encode(get_domain_infrastructure_descriptor(domainsid
))
1338 builtin_desc
= b64encode(get_domain_builtin_descriptor(domainsid
))
1339 controllers_desc
= b64encode(get_domain_controllers_descriptor(domainsid
))
1340 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1341 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1342 "DOMAINDN": names
.domaindn
,
1343 "NETBIOSNAME": names
.netbiosname
,
1344 "DEFAULTSITE": names
.sitename
,
1345 "CONFIGDN": names
.configdn
,
1346 "SERVERDN": names
.serverdn
,
1347 "RIDAVAILABLESTART": str(next_rid
+ 600),
1348 "POLICYGUID_DC": policyguid_dc
,
1349 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc
,
1350 "BUILTIN_DESCRIPTOR": builtin_desc
,
1351 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc
,
1354 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1355 if fill
== FILL_FULL
:
1356 setup_modify_ldif(samdb
,
1357 setup_path("provision_configuration_references.ldif"), {
1358 "CONFIGDN": names
.configdn
,
1359 "SCHEMADN": names
.schemadn
})
1361 logger
.info("Setting up well known security principals")
1362 setup_add_ldif(samdb
, setup_path("provision_well_known_sec_princ.ldif"), {
1363 "CONFIGDN": names
.configdn
,
1366 if fill
== FILL_FULL
or fill
== FILL_SUBDOMAIN
:
1367 setup_modify_ldif(samdb
,
1368 setup_path("provision_basedn_references.ldif"),
1369 {"DOMAINDN": names
.domaindn
})
1371 logger
.info("Setting up sam.ldb users and groups")
1372 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1373 "DOMAINDN": names
.domaindn
,
1374 "DOMAINSID": str(domainsid
),
1375 "ADMINPASS_B64": b64encode(adminpass
.encode('utf-16-le')),
1376 "KRBTGTPASS_B64": b64encode(krbtgtpass
.encode('utf-16-le'))
1379 logger
.info("Setting up self join")
1380 setup_self_join(samdb
, admin_session_info
, names
=names
, fill
=fill
,
1381 invocationid
=invocationid
,
1382 dns_backend
=dns_backend
,
1384 machinepass
=machinepass
,
1385 domainsid
=domainsid
,
1388 policyguid
=policyguid
,
1389 policyguid_dc
=policyguid_dc
,
1390 domainControllerFunctionality
=domainControllerFunctionality
,
1393 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1394 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1395 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
)
1396 assert isinstance(names
.ntdsguid
, str)
1398 samdb
.transaction_cancel()
1401 samdb
.transaction_commit()
1406 FILL_SUBDOMAIN
= "SUBDOMAIN"
1407 FILL_NT4SYNC
= "NT4SYNC"
1409 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1410 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)"
1411 SYSVOL_SERVICE
="sysvol"
1413 def set_dir_acl(path
, acl
, lp
, domsid
, use_ntvfs
, passdb
, service
=SYSVOL_SERVICE
):
1414 setntacl(lp
, path
, acl
, domsid
, use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1415 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1417 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1418 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1420 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1421 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1424 def set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
):
1425 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1428 :param sysvol: Physical path for the sysvol folder
1429 :param dnsdomain: The DNS name of the domain
1430 :param domainsid: The SID of the domain
1431 :param domaindn: The DN of the domain (ie. DC=...)
1432 :param samdb: An LDB object on the SAM db
1433 :param lp: an LP object
1436 # Set ACL for GPO root folder
1437 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1438 setntacl(lp
, root_policy_path
, POLICIES_ACL
, str(domainsid
),
1439 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=SYSVOL_SERVICE
)
1441 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1442 attrs
=["cn", "nTSecurityDescriptor"],
1443 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1446 acl
= ndr_unpack(security
.descriptor
,
1447 str(policy
["nTSecurityDescriptor"])).as_sddl()
1448 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1449 set_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1450 str(domainsid
), use_ntvfs
,
1454 def setsysvolacl(samdb
, netlogon
, sysvol
, uid
, gid
, domainsid
, dnsdomain
,
1455 domaindn
, lp
, use_ntvfs
):
1456 """Set the ACL for the sysvol share and the subfolders
1458 :param samdb: An LDB object on the SAM db
1459 :param netlogon: Physical path for the netlogon folder
1460 :param sysvol: Physical path for the sysvol folder
1461 :param uid: The UID of the "Administrator" user
1462 :param gid: The GID of the "Domain adminstrators" group
1463 :param domainsid: The SID of the domain
1464 :param dnsdomain: The DNS name of the domain
1465 :param domaindn: The DN of the domain (ie. DC=...)
1470 # This will ensure that the smbd code we are running when setting ACLs
1471 # is initialised with the smb.conf
1472 s3conf
= s3param
.get_context()
1473 s3conf
.load(lp
.configfile
)
1474 # ensure we are using the right samba_dsdb passdb backend, no matter what
1475 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1476 passdb
.reload_static_pdb()
1478 # ensure that we init the samba_dsdb backend, so the domain sid is
1479 # marked in secrets.tdb
1480 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1482 # now ensure everything matches correctly, to avoid wierd issues
1483 if passdb
.get_global_sam_sid() != domainsid
:
1484 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
))
1486 domain_info
= s4_passdb
.domain_info()
1487 if domain_info
["dom_sid"] != domainsid
:
1488 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
))
1490 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1491 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()))
1496 os
.chown(sysvol
, -1, gid
)
1502 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1503 setntacl(lp
,sysvol
, SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
,
1504 skip_invalid_chown
=True, passdb
=s4_passdb
,
1505 service
=SYSVOL_SERVICE
)
1506 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1508 if use_ntvfs
and canchown
:
1509 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1510 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
),
1511 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True,
1512 passdb
=s4_passdb
, service
=SYSVOL_SERVICE
)
1514 if use_ntvfs
and canchown
:
1515 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1516 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
),
1517 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True,
1518 passdb
=s4_passdb
, service
=SYSVOL_SERVICE
)
1520 # Set acls on Policy folder and policies folders
1521 set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
=s4_passdb
)
1523 def acl_type(direct_db_access
):
1524 if direct_db_access
:
1529 def check_dir_acl(path
, acl
, lp
, domainsid
, direct_db_access
):
1530 fsacl
= getntacl(lp
, path
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1531 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1532 if fsacl_sddl
!= acl
:
1533 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
))
1535 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1537 fsacl
= getntacl(lp
, os
.path
.join(root
, name
),
1538 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1540 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1541 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1542 if fsacl_sddl
!= acl
:
1543 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
))
1546 fsacl
= getntacl(lp
, os
.path
.join(root
, name
),
1547 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1549 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1550 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1551 if fsacl_sddl
!= acl
:
1552 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
))
1555 def check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1557 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1560 :param sysvol: Physical path for the sysvol folder
1561 :param dnsdomain: The DNS name of the domain
1562 :param domainsid: The SID of the domain
1563 :param domaindn: The DN of the domain (ie. DC=...)
1564 :param samdb: An LDB object on the SAM db
1565 :param lp: an LP object
1568 # Set ACL for GPO root folder
1569 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1570 fsacl
= getntacl(lp
, root_policy_path
,
1571 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1573 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access
), root_policy_path
))
1574 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1575 if fsacl_sddl
!= POLICIES_ACL
:
1576 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
))
1577 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1578 attrs
=["cn", "nTSecurityDescriptor"],
1579 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1582 acl
= ndr_unpack(security
.descriptor
,
1583 str(policy
["nTSecurityDescriptor"])).as_sddl()
1584 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1585 check_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1586 domainsid
, direct_db_access
)
1589 def checksysvolacl(samdb
, netlogon
, sysvol
, domainsid
, dnsdomain
, domaindn
,
1591 """Set the ACL for the sysvol share and the subfolders
1593 :param samdb: An LDB object on the SAM db
1594 :param netlogon: Physical path for the netlogon folder
1595 :param sysvol: Physical path for the sysvol folder
1596 :param uid: The UID of the "Administrator" user
1597 :param gid: The GID of the "Domain adminstrators" group
1598 :param domainsid: The SID of the domain
1599 :param dnsdomain: The DNS name of the domain
1600 :param domaindn: The DN of the domain (ie. DC=...)
1603 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1604 s3conf
= s3param
.get_context()
1605 s3conf
.load(lp
.configfile
)
1606 # ensure we are using the right samba_dsdb passdb backend, no matter what
1607 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1608 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1609 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1611 # now ensure everything matches correctly, to avoid wierd issues
1612 if passdb
.get_global_sam_sid() != domainsid
:
1613 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
))
1615 domain_info
= s4_passdb
.domain_info()
1616 if domain_info
["dom_sid"] != domainsid
:
1617 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
))
1619 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1620 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()))
1622 # Ensure we can read this directly, and via the smbd VFS
1623 for direct_db_access
in [True, False]:
1624 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1625 for dir_path
in [os
.path
.join(sysvol
, dnsdomain
), netlogon
]:
1626 fsacl
= getntacl(lp
, dir_path
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1628 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access
), dir_path
))
1629 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1630 if fsacl_sddl
!= SYSVOL_ACL
:
1631 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
))
1633 # Check acls on Policy folder and policies folders
1634 check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1638 def interface_ips_v4(lp
):
1639 """return only IPv4 IPs"""
1640 ips
= samba
.interface_ips(lp
, False)
1643 if i
.find(':') == -1:
1648 def interface_ips_v6(lp
, linklocal
=False):
1649 """return only IPv6 IPs"""
1650 ips
= samba
.interface_ips(lp
, False)
1653 if i
.find(':') != -1 and (linklocal
or i
.find('%') == -1):
1658 def provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
1659 domainsid
, schema
=None,
1660 targetdir
=None, samdb_fill
=FILL_FULL
,
1661 hostip
=None, hostip6
=None,
1662 next_rid
=1000, dc_rid
=None, adminpass
=None, krbtgtpass
=None,
1663 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1664 invocationid
=None, machinepass
=None, ntdsguid
=None,
1665 dns_backend
=None, dnspass
=None,
1666 serverrole
=None, dom_for_fun_level
=None,
1667 am_rodc
=False, lp
=None, use_ntvfs
=False, skip_sysvolacl
=False):
1668 # create/adapt the group policy GUIDs
1669 # Default GUID for default policy are described at
1670 # "How Core Group Policy Works"
1671 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1672 if policyguid
is None:
1673 policyguid
= DEFAULT_POLICY_GUID
1674 policyguid
= policyguid
.upper()
1675 if policyguid_dc
is None:
1676 policyguid_dc
= DEFAULT_DC_POLICY_GUID
1677 policyguid_dc
= policyguid_dc
.upper()
1679 if invocationid
is None:
1680 invocationid
= str(uuid
.uuid4())
1682 if krbtgtpass
is None:
1683 krbtgtpass
= samba
.generate_random_password(128, 255)
1684 if machinepass
is None:
1685 machinepass
= samba
.generate_random_password(128, 255)
1687 dnspass
= samba
.generate_random_password(128, 255)
1689 samdb
= fill_samdb(samdb
, lp
, names
, logger
=logger
,
1690 domainsid
=domainsid
, schema
=schema
, domainguid
=domainguid
,
1691 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1692 fill
=samdb_fill
, adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1693 invocationid
=invocationid
, machinepass
=machinepass
,
1694 dns_backend
=dns_backend
, dnspass
=dnspass
,
1695 ntdsguid
=ntdsguid
, serverrole
=serverrole
,
1696 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
1697 next_rid
=next_rid
, dc_rid
=dc_rid
)
1699 if serverrole
== "active directory domain controller":
1701 # Set up group policies (domain policy and domain controller
1703 create_default_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
,
1705 if not skip_sysvolacl
:
1706 setsysvolacl(samdb
, paths
.netlogon
, paths
.sysvol
, paths
.root_uid
,
1707 paths
.root_gid
, domainsid
, names
.dnsdomain
,
1708 names
.domaindn
, lp
, use_ntvfs
)
1710 logger
.info("Setting acl on sysvol skipped")
1712 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1713 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1714 netbiosname
=names
.netbiosname
, domainsid
=domainsid
,
1715 machinepass
=machinepass
, secure_channel_type
=SEC_CHAN_BDC
)
1717 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1718 # In future, this might be determined from some configuration
1719 kerberos_enctypes
= str(ENC_ALL_TYPES
)
1722 msg
= ldb
.Message(ldb
.Dn(samdb
,
1723 samdb
.searchone("distinguishedName",
1724 expression
="samAccountName=%s$" % names
.netbiosname
,
1725 scope
=ldb
.SCOPE_SUBTREE
)))
1726 msg
["msDS-SupportedEncryptionTypes"] = ldb
.MessageElement(
1727 elements
=kerberos_enctypes
, flags
=ldb
.FLAG_MOD_REPLACE
,
1728 name
="msDS-SupportedEncryptionTypes")
1730 except ldb
.LdbError
, (enum
, estr
):
1731 if enum
!= ldb
.ERR_NO_SUCH_ATTRIBUTE
:
1732 # It might be that this attribute does not exist in this schema
1735 setup_ad_dns(samdb
, secrets_ldb
, domainsid
, names
, paths
, lp
, logger
,
1736 hostip
=hostip
, hostip6
=hostip6
, dns_backend
=dns_backend
,
1737 dnspass
=dnspass
, os_level
=dom_for_fun_level
,
1738 targetdir
=targetdir
, site
=DEFAULTSITE
)
1740 domainguid
= samdb
.searchone(basedn
=samdb
.get_default_basedn(),
1741 attribute
="objectGUID")
1742 assert isinstance(domainguid
, str)
1744 lastProvisionUSNs
= get_last_provision_usn(samdb
)
1745 maxUSN
= get_max_usn(samdb
, str(names
.rootdn
))
1746 if lastProvisionUSNs
is not None:
1747 update_provision_usn(samdb
, 0, maxUSN
, invocationid
, 1)
1749 set_provision_usn(samdb
, 0, maxUSN
, invocationid
)
1751 logger
.info("Setting up sam.ldb rootDSE marking as synchronized")
1752 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"),
1753 { 'NTDSGUID' : names
.ntdsguid
})
1755 # fix any dangling GUIDs from the provision
1756 logger
.info("Fixing provision GUIDs")
1757 chk
= dbcheck(samdb
, samdb_schema
=samdb
, verbose
=False, fix
=True, yes
=True,
1759 samdb
.transaction_start()
1761 # a small number of GUIDs are missing because of ordering issues in the
1763 for schema_obj
in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1764 chk
.check_database(DN
="%s,%s" % (schema_obj
, names
.schemadn
),
1765 scope
=ldb
.SCOPE_BASE
,
1766 attrs
=['defaultObjectCategory'])
1767 chk
.check_database(DN
="CN=IP Security,CN=System,%s" % names
.domaindn
,
1768 scope
=ldb
.SCOPE_ONELEVEL
,
1769 attrs
=['ipsecOwnersReference',
1770 'ipsecFilterReference',
1771 'ipsecISAKMPReference',
1772 'ipsecNegotiationPolicyReference',
1773 'ipsecNFAReference'])
1775 samdb
.transaction_cancel()
1778 samdb
.transaction_commit()
1782 "ROLE_STANDALONE": "standalone server",
1783 "ROLE_DOMAIN_MEMBER": "member server",
1784 "ROLE_DOMAIN_BDC": "active directory domain controller",
1785 "ROLE_DOMAIN_PDC": "active directory domain controller",
1786 "dc": "active directory domain controller",
1787 "member": "member server",
1788 "domain controller": "active directory domain controller",
1789 "active directory domain controller": "active directory domain controller",
1790 "member server": "member server",
1791 "standalone": "standalone server",
1792 "standalone server": "standalone server",
1796 def sanitize_server_role(role
):
1797 """Sanitize a server role name.
1799 :param role: Server role
1800 :raise ValueError: If the role can not be interpreted
1801 :return: Sanitized server role (one of "member server",
1802 "active directory domain controller", "standalone server")
1805 return _ROLES_MAP
[role
]
1807 raise ValueError(role
)
1810 def provision_fake_ypserver(logger
, samdb
, domaindn
, netbiosname
, nisdomain
,
1812 """Create AD entries for the fake ypserver.
1814 This is needed for being able to manipulate posix attrs via ADUC.
1816 samdb
.transaction_start()
1818 logger
.info("Setting up fake yp server settings")
1819 setup_add_ldif(samdb
, setup_path("ypServ30.ldif"), {
1820 "DOMAINDN": domaindn
,
1821 "NETBIOSNAME": netbiosname
,
1822 "NISDOMAIN": nisdomain
,
1825 samdb
.transaction_cancel()
1828 samdb
.transaction_commit()
1831 def provision(logger
, session_info
, credentials
, smbconf
=None,
1832 targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None, rootdn
=None,
1833 domaindn
=None, schemadn
=None, configdn
=None, serverdn
=None,
1834 domain
=None, hostname
=None, hostip
=None, hostip6
=None, domainsid
=None,
1835 next_rid
=1000, dc_rid
=None, adminpass
=None, ldapadminpass
=None,
1836 krbtgtpass
=None, domainguid
=None, policyguid
=None, policyguid_dc
=None,
1837 dns_backend
=None, dns_forwarder
=None, dnspass
=None,
1838 invocationid
=None, machinepass
=None, ntdsguid
=None,
1839 root
=None, nobody
=None, users
=None, backup
=None, aci
=None,
1840 serverrole
=None, dom_for_fun_level
=None, backend_type
=None,
1841 sitename
=None, ol_mmr_urls
=None, ol_olc
=None, slapd_path
="/bin/false",
1842 useeadb
=False, am_rodc
=False, lp
=None, use_ntvfs
=False,
1843 use_rfc2307
=False, maxuid
=None, maxgid
=None, skip_sysvolacl
=True):
1846 :note: caution, this wipes all existing data!
1850 serverrole
= sanitize_server_role(serverrole
)
1852 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole
)
1854 if ldapadminpass
is None:
1855 # Make a new, random password between Samba and it's LDAP server
1856 ldapadminpass
= samba
.generate_random_password(128, 255)
1858 if backend_type
is None:
1859 backend_type
= "ldb"
1861 if domainsid
is None:
1862 domainsid
= security
.random_sid()
1864 domainsid
= security
.dom_sid(domainsid
)
1866 root_uid
= findnss_uid([root
or "root"])
1867 nobody_uid
= findnss_uid([nobody
or "nobody"])
1868 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
1869 root_gid
= pwd
.getpwuid(root_uid
).pw_gid
1872 bind_gid
= findnss_gid(["bind", "named"])
1876 if targetdir
is not None:
1877 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
1878 elif smbconf
is None:
1879 smbconf
= samba
.param
.default_path()
1880 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
1881 os
.makedirs(os
.path
.dirname(smbconf
))
1883 server_services
= []
1886 global_param
["idmap_ldb:use rfc2307"] = ["yes"]
1888 if dns_backend
!= "SAMBA_INTERNAL":
1889 server_services
.append("-dns")
1891 if dns_forwarder
is not None:
1892 global_param
["dns forwarder"] = [dns_forwarder
]
1895 server_services
.append("+smb")
1896 server_services
.append("-s3fs")
1897 global_param
["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1899 if len(server_services
) > 0:
1900 global_param
["server services"] = server_services
1902 # only install a new smb.conf if there isn't one there already
1903 if os
.path
.exists(smbconf
):
1904 # if Samba Team members can't figure out the weird errors
1905 # loading an empty smb.conf gives, then we need to be smarter.
1906 # Pretend it just didn't exist --abartlet
1907 f
= open(smbconf
, 'r')
1909 data
= f
.read().lstrip()
1912 if data
is None or data
== "":
1913 make_smbconf(smbconf
, hostname
, domain
, realm
,
1914 targetdir
, serverrole
=serverrole
,
1915 eadb
=useeadb
, use_ntvfs
=use_ntvfs
,
1916 lp
=lp
, global_param
=global_param
)
1918 make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
1919 serverrole
=serverrole
,
1920 eadb
=useeadb
, use_ntvfs
=use_ntvfs
, lp
=lp
, global_param
=global_param
)
1923 lp
= samba
.param
.LoadParm()
1925 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
1926 dnsdomain
=realm
, serverrole
=serverrole
, domaindn
=domaindn
,
1927 configdn
=configdn
, schemadn
=schemadn
, serverdn
=serverdn
,
1928 sitename
=sitename
, rootdn
=rootdn
)
1929 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
1931 paths
.bind_gid
= bind_gid
1932 paths
.root_uid
= root_uid
;
1933 paths
.root_gid
= root_gid
1936 logger
.info("Looking up IPv4 addresses")
1937 hostips
= interface_ips_v4(lp
)
1938 if len(hostips
) > 0:
1940 if len(hostips
) > 1:
1941 logger
.warning("More than one IPv4 address found. Using %s",
1943 if hostip
== "127.0.0.1":
1946 logger
.warning("No IPv4 address will be assigned")
1949 logger
.info("Looking up IPv6 addresses")
1950 hostips
= interface_ips_v6(lp
, linklocal
=False)
1952 hostip6
= hostips
[0]
1953 if len(hostips
) > 1:
1954 logger
.warning("More than one IPv6 address found. Using %s", hostip6
)
1956 logger
.warning("No IPv6 address will be assigned")
1958 names
.hostip
= hostip
1959 names
.hostip6
= hostip6
1961 if serverrole
is None:
1962 serverrole
= lp
.get("server role")
1964 if not os
.path
.exists(paths
.private_dir
):
1965 os
.mkdir(paths
.private_dir
)
1966 if not os
.path
.exists(os
.path
.join(paths
.private_dir
, "tls")):
1967 os
.mkdir(os
.path
.join(paths
.private_dir
, "tls"))
1968 if not os
.path
.exists(paths
.state_dir
):
1969 os
.mkdir(paths
.state_dir
)
1971 if paths
.sysvol
and not os
.path
.exists(paths
.sysvol
):
1972 os
.makedirs(paths
.sysvol
, 0775)
1974 if not use_ntvfs
and serverrole
== "active directory domain controller":
1975 s3conf
= s3param
.get_context()
1976 s3conf
.load(lp
.configfile
)
1978 if paths
.sysvol
is None:
1979 raise MissingShareError("sysvol", paths
.smbconf
)
1981 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(paths
.sysvol
))
1984 smbd
.set_simple_acl(file.name
, 0755, root_gid
)
1986 if not smbd
.have_posix_acls():
1987 # This clue is only strictly correct for RPM and
1988 # Debian-like Linux systems, but hopefully other users
1989 # will get enough clue from it.
1990 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.")
1992 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1994 smbd
.chown(file.name
, root_uid
, root_gid
)
1996 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
2000 ldapi_url
= "ldapi://%s" % urllib
.quote(paths
.s4_ldapi_path
, safe
="")
2002 schema
= Schema(domainsid
, invocationid
=invocationid
,
2003 schemadn
=names
.schemadn
)
2005 if backend_type
== "ldb":
2006 provision_backend
= LDBBackend(backend_type
, paths
=paths
,
2007 lp
=lp
, credentials
=credentials
,
2008 names
=names
, logger
=logger
)
2009 elif backend_type
== "existing":
2010 # If support for this is ever added back, then the URI will need to be
2012 provision_backend
= ExistingBackend(backend_type
, paths
=paths
,
2013 lp
=lp
, credentials
=credentials
,
2014 names
=names
, logger
=logger
,
2015 ldap_backend_forced_uri
=None)
2016 elif backend_type
== "fedora-ds":
2017 provision_backend
= FDSBackend(backend_type
, paths
=paths
,
2018 lp
=lp
, credentials
=credentials
,
2019 names
=names
, logger
=logger
, domainsid
=domainsid
,
2020 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
2021 slapd_path
=slapd_path
,
2023 elif backend_type
== "openldap":
2024 provision_backend
= OpenLDAPBackend(backend_type
, paths
=paths
,
2025 lp
=lp
, credentials
=credentials
,
2026 names
=names
, logger
=logger
, domainsid
=domainsid
,
2027 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
2028 slapd_path
=slapd_path
, ol_mmr_urls
=ol_mmr_urls
)
2030 raise ValueError("Unknown LDAP backend type selected")
2032 provision_backend
.init()
2033 provision_backend
.start()
2035 # only install a new shares config db if there is none
2036 if not os
.path
.exists(paths
.shareconf
):
2037 logger
.info("Setting up share.ldb")
2038 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
, lp
=lp
)
2039 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
2041 logger
.info("Setting up secrets.ldb")
2042 secrets_ldb
= setup_secretsdb(paths
,
2043 session_info
=session_info
,
2044 backend_credentials
=provision_backend
.secrets_credentials
, lp
=lp
)
2047 logger
.info("Setting up the registry")
2048 setup_registry(paths
.hklm
, session_info
, lp
=lp
)
2050 logger
.info("Setting up the privileges database")
2051 setup_privileges(paths
.privilege
, session_info
, lp
=lp
)
2053 logger
.info("Setting up idmap db")
2054 idmap
= setup_idmapdb(paths
.idmapdb
, session_info
=session_info
, lp
=lp
)
2056 setup_name_mappings(idmap
, sid
=str(domainsid
),
2057 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
2058 users_gid
=users_gid
, root_gid
=root_gid
)
2060 logger
.info("Setting up SAM db")
2061 samdb
= setup_samdb(paths
.samdb
, session_info
,
2062 provision_backend
, lp
, names
, logger
=logger
,
2063 serverrole
=serverrole
,
2064 schema
=schema
, fill
=samdb_fill
, am_rodc
=am_rodc
)
2066 if serverrole
== "active directory domain controller":
2067 if paths
.netlogon
is None:
2068 raise MissingShareError("netlogon", paths
.smbconf
)
2070 if paths
.sysvol
is None:
2071 raise MissingShareError("sysvol", paths
.smbconf
)
2073 if not os
.path
.isdir(paths
.netlogon
):
2074 os
.makedirs(paths
.netlogon
, 0755)
2076 if adminpass
is None:
2077 adminpass
= samba
.generate_random_password(12, 32)
2078 adminpass_generated
= True
2080 adminpass_generated
= False
2082 if samdb_fill
== FILL_FULL
:
2083 provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
2084 schema
=schema
, targetdir
=targetdir
, samdb_fill
=samdb_fill
,
2085 hostip
=hostip
, hostip6
=hostip6
, domainsid
=domainsid
,
2086 next_rid
=next_rid
, dc_rid
=dc_rid
, adminpass
=adminpass
,
2087 krbtgtpass
=krbtgtpass
, domainguid
=domainguid
,
2088 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
2089 invocationid
=invocationid
, machinepass
=machinepass
,
2090 ntdsguid
=ntdsguid
, dns_backend
=dns_backend
,
2091 dnspass
=dnspass
, serverrole
=serverrole
,
2092 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
2093 lp
=lp
, use_ntvfs
=use_ntvfs
,
2094 skip_sysvolacl
=skip_sysvolacl
)
2096 create_krb5_conf(paths
.krb5conf
,
2097 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
2099 logger
.info("A Kerberos configuration suitable for Samba 4 has been "
2100 "generated at %s", paths
.krb5conf
)
2102 if serverrole
== "active directory domain controller":
2103 create_dns_update_list(lp
, logger
, paths
)
2105 backend_result
= provision_backend
.post_setup()
2106 provision_backend
.shutdown()
2109 secrets_ldb
.transaction_cancel()
2112 # Now commit the secrets.ldb to disk
2113 secrets_ldb
.transaction_commit()
2115 # the commit creates the dns.keytab, now chown it
2116 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
2117 if os
.path
.isfile(dns_keytab_path
) and paths
.bind_gid
is not None:
2119 os
.chmod(dns_keytab_path
, 0640)
2120 os
.chown(dns_keytab_path
, -1, paths
.bind_gid
)
2122 if not os
.environ
.has_key('SAMBA_SELFTEST'):
2123 logger
.info("Failed to chown %s to bind gid %u",
2124 dns_keytab_path
, paths
.bind_gid
)
2126 result
= ProvisionResult()
2127 result
.server_role
= serverrole
2128 result
.domaindn
= domaindn
2129 result
.paths
= paths
2130 result
.names
= names
2132 result
.samdb
= samdb
2133 result
.idmap
= idmap
2134 result
.domainsid
= str(domainsid
)
2136 if samdb_fill
== FILL_FULL
:
2137 result
.adminpass_generated
= adminpass_generated
2138 result
.adminpass
= adminpass
2140 result
.adminpass_generated
= False
2141 result
.adminpass
= None
2143 result
.backend_result
= backend_result
2146 provision_fake_ypserver(logger
=logger
, samdb
=samdb
,
2147 domaindn
=names
.domaindn
, netbiosname
=names
.netbiosname
,
2148 nisdomain
=names
.domain
.lower(), maxuid
=maxuid
, maxgid
=maxgid
)
2153 def provision_become_dc(smbconf
=None, targetdir
=None,
2154 realm
=None, rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
2155 serverdn
=None, domain
=None, hostname
=None, domainsid
=None,
2156 adminpass
=None, krbtgtpass
=None, domainguid
=None, policyguid
=None,
2157 policyguid_dc
=None, invocationid
=None, machinepass
=None, dnspass
=None,
2158 dns_backend
=None, root
=None, nobody
=None, users
=None,
2159 backup
=None, serverrole
=None, ldap_backend
=None,
2160 ldap_backend_type
=None, sitename
=None, debuglevel
=1, use_ntvfs
=False):
2162 logger
= logging
.getLogger("provision")
2163 samba
.set_debug_level(debuglevel
)
2165 res
= provision(logger
, system_session(), None,
2166 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
2167 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
2168 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
2169 hostname
=hostname
, hostip
=None, domainsid
=domainsid
,
2170 machinepass
=machinepass
,
2171 serverrole
="active directory domain controller",
2172 sitename
=sitename
, dns_backend
=dns_backend
, dnspass
=dnspass
,
2173 use_ntvfs
=use_ntvfs
)
2174 res
.lp
.set("debuglevel", str(debuglevel
))
2178 def create_krb5_conf(path
, dnsdomain
, hostname
, realm
):
2179 """Write out a file containing zone statements suitable for inclusion in a
2180 named.conf file (including GSS-TSIG configuration).
2182 :param path: Path of the new named.conf file.
2183 :param dnsdomain: DNS Domain name
2184 :param hostname: Local hostname
2185 :param realm: Realm name
2187 setup_file(setup_path("krb5.conf"), path
, {
2188 "DNSDOMAIN": dnsdomain
,
2189 "HOSTNAME": hostname
,
2194 class ProvisioningError(Exception):
2195 """A generic provision error."""
2197 def __init__(self
, value
):
2201 return "ProvisioningError: " + self
.value
2204 class InvalidNetbiosName(Exception):
2205 """A specified name was not a valid NetBIOS name."""
2207 def __init__(self
, name
):
2208 super(InvalidNetbiosName
, self
).__init
__(
2209 "The name '%r' is not a valid NetBIOS name" % name
)
2212 class MissingShareError(ProvisioningError
):
2214 def __init__(self
, name
, smbconf
):
2215 super(MissingShareError
, self
).__init
__(
2216 "Existing smb.conf does not have a [%s] share, but you are "
2217 "configuring a DC. Please remove %s or add the share manually." %