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_ntds_quotas_descriptor
,
85 get_config_delete_protected1_descriptor
,
86 get_config_delete_protected1wd_descriptor
,
87 get_config_delete_protected2_descriptor
,
88 get_domain_descriptor
,
89 get_domain_infrastructure_descriptor
,
90 get_domain_builtin_descriptor
,
91 get_domain_computers_descriptor
,
92 get_domain_users_descriptor
,
93 get_domain_controllers_descriptor
,
94 get_domain_delete_protected1_descriptor
,
95 get_domain_delete_protected2_descriptor
,
96 get_dns_partition_descriptor
,
97 get_dns_forest_microsoft_dns_descriptor
,
98 get_dns_domain_microsoft_dns_descriptor
,
100 from samba
.provision
.common
import (
105 from samba
.provision
.sambadns
import (
108 create_dns_update_list
112 import samba
.registry
113 from samba
.schema
import Schema
114 from samba
.samdb
import SamDB
115 from samba
.dbchecker
import dbcheck
118 DEFAULT_POLICY_GUID
= "31B2F340-016D-11D2-945F-00C04FB984F9"
119 DEFAULT_DC_POLICY_GUID
= "6AC1786C-016F-11D2-945F-00C04fB984F9"
120 DEFAULTSITE
= "Default-First-Site-Name"
121 LAST_PROVISION_USN_ATTRIBUTE
= "lastProvisionUSN"
124 class ProvisionPaths(object):
127 self
.shareconf
= None
138 self
.dns_keytab
= None
141 self
.private_dir
= None
142 self
.state_dir
= None
145 class ProvisionNames(object):
153 self
.dnsforestdn
= None
154 self
.dnsdomaindn
= None
155 self
.ldapmanagerdn
= None
156 self
.dnsdomain
= None
158 self
.netbiosname
= None
166 def find_provision_key_parameters(samdb
, secretsdb
, idmapdb
, paths
, smbconf
,
168 """Get key provision parameters (realm, domain, ...) from a given provision
170 :param samdb: An LDB object connected to the sam.ldb file
171 :param secretsdb: An LDB object connected to the secrets.ldb file
172 :param idmapdb: An LDB object connected to the idmap.ldb file
173 :param paths: A list of path to provision object
174 :param smbconf: Path to the smb.conf file
175 :param lp: A LoadParm object
176 :return: A list of key provision parameters
178 names
= ProvisionNames()
179 names
.adminpass
= None
181 # NT domain, kerberos realm, root dn, domain dn, domain dns name
182 names
.domain
= string
.upper(lp
.get("workgroup"))
183 names
.realm
= lp
.get("realm")
184 names
.dnsdomain
= names
.realm
.lower()
185 basedn
= samba
.dn_from_dns_name(names
.dnsdomain
)
186 names
.realm
= string
.upper(names
.realm
)
188 # Get the netbiosname first (could be obtained from smb.conf in theory)
189 res
= secretsdb
.search(expression
="(flatname=%s)" %
190 names
.domain
,base
="CN=Primary Domains",
191 scope
=ldb
.SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
192 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
194 names
.smbconf
= smbconf
196 # That's a bit simplistic but it's ok as long as we have only 3
198 current
= samdb
.search(expression
="(objectClass=*)",
199 base
="", scope
=ldb
.SCOPE_BASE
,
200 attrs
=["defaultNamingContext", "schemaNamingContext",
201 "configurationNamingContext","rootDomainNamingContext",
204 names
.configdn
= current
[0]["configurationNamingContext"]
205 configdn
= str(names
.configdn
)
206 names
.schemadn
= current
[0]["schemaNamingContext"]
207 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
,
208 current
[0]["defaultNamingContext"][0]))):
209 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
210 "is not the same ..." % (paths
.samdb
,
211 str(current
[0]["defaultNamingContext"][0]),
212 paths
.smbconf
, basedn
)))
214 names
.domaindn
=current
[0]["defaultNamingContext"]
215 names
.rootdn
=current
[0]["rootDomainNamingContext"]
216 names
.ncs
=current
[0]["namingContexts"]
217 names
.dnsforestdn
= None
218 names
.dnsdomaindn
= None
220 for i
in range(0, len(names
.ncs
)):
223 dnsforestdn
= "DC=ForestDnsZones,%s" % (str(names
.rootdn
))
224 if nc
== dnsforestdn
:
225 names
.dnsforestdn
= dnsforestdn
228 dnsdomaindn
= "DC=DomainDnsZones,%s" % (str(names
.domaindn
))
229 if nc
== dnsdomaindn
:
230 names
.dnsdomaindn
= dnsdomaindn
234 res3
= samdb
.search(expression
="(objectClass=site)",
235 base
="CN=Sites," + configdn
, scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn"])
236 names
.sitename
= str(res3
[0]["cn"])
238 # dns hostname and server dn
239 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
240 base
="OU=Domain Controllers,%s" % basedn
,
241 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
242 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("." + names
.dnsdomain
, "")
244 server_res
= samdb
.search(expression
="serverReference=%s" % res4
[0].dn
,
245 attrs
=[], base
=configdn
)
246 names
.serverdn
= server_res
[0].dn
248 # invocation id/objectguid
249 res5
= samdb
.search(expression
="(objectClass=*)",
250 base
="CN=NTDS Settings,%s" % str(names
.serverdn
),
251 scope
=ldb
.SCOPE_BASE
,
252 attrs
=["invocationID", "objectGUID"])
253 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
254 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
257 res6
= samdb
.search(expression
="(objectClass=*)", base
=basedn
,
258 scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID",
259 "objectSid","msDS-Behavior-Version" ])
260 names
.domainguid
= str(ndr_unpack(misc
.GUID
, res6
[0]["objectGUID"][0]))
261 names
.domainsid
= ndr_unpack( security
.dom_sid
, res6
[0]["objectSid"][0])
262 if res6
[0].get("msDS-Behavior-Version") is None or \
263 int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
264 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
266 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
269 res7
= samdb
.search(expression
="(displayName=Default Domain Policy)",
270 base
="CN=Policies,CN=System," + basedn
,
271 scope
=ldb
.SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
272 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
274 res8
= samdb
.search(expression
="(displayName=Default Domain Controllers"
276 base
="CN=Policies,CN=System," + basedn
,
277 scope
=ldb
.SCOPE_ONELEVEL
,
278 attrs
=["cn","displayName"])
280 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
282 names
.policyid_dc
= None
284 res9
= idmapdb
.search(expression
="(cn=%s-%s)" %
285 (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
),
286 attrs
=["xidNumber", "type"])
288 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names
.domainsid
), security
.DOMAIN_RID_ADMINISTRATOR
))
289 if res9
[0]["type"][0] == "ID_TYPE_BOTH":
290 names
.root_gid
= res9
[0]["xidNumber"][0]
292 names
.root_gid
= pwd
.getpwuid(int(res9
[0]["xidNumber"][0])).pw_gid
294 dns_admins_sid
= get_dnsadmins_sid(samdb
, names
.domaindn
)
295 names
.name_map
['DnsAdmins'] = str(dns_admins_sid
)
300 def update_provision_usn(samdb
, low
, high
, id, replace
=False):
301 """Update the field provisionUSN in sam.ldb
303 This field is used to track range of USN modified by provision and
305 This value is used afterward by next provision to figure out if
306 the field have been modified since last provision.
308 :param samdb: An LDB object connect to sam.ldb
309 :param low: The lowest USN modified by this upgrade
310 :param high: The highest USN modified by this upgrade
311 :param id: The invocation id of the samba's dc
312 :param replace: A boolean indicating if the range should replace any
313 existing one or appended (default)
318 entry
= samdb
.search(base
="@PROVISION",
319 scope
=ldb
.SCOPE_BASE
,
320 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "dn"])
321 for e
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
322 if not re
.search(';', e
):
323 e
= "%s;%s" % (e
, id)
326 tab
.append("%s-%s;%s" % (low
, high
, id))
327 delta
= ldb
.Message()
328 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
329 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
330 ldb
.FLAG_MOD_REPLACE
, LAST_PROVISION_USN_ATTRIBUTE
)
331 entry
= samdb
.search(expression
='provisionnerID=*',
332 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
333 attrs
=["provisionnerID"])
334 if len(entry
) == 0 or len(entry
[0]) == 0:
335 delta
["provisionnerID"] = ldb
.MessageElement(id, ldb
.FLAG_MOD_ADD
, "provisionnerID")
339 def set_provision_usn(samdb
, low
, high
, id):
340 """Set the field provisionUSN in sam.ldb
341 This field is used to track range of USN modified by provision and
343 This value is used afterward by next provision to figure out if
344 the field have been modified since last provision.
346 :param samdb: An LDB object connect to sam.ldb
347 :param low: The lowest USN modified by this upgrade
348 :param high: The highest USN modified by this upgrade
349 :param id: The invocationId of the provision"""
352 tab
.append("%s-%s;%s" % (low
, high
, id))
354 delta
= ldb
.Message()
355 delta
.dn
= ldb
.Dn(samdb
, "@PROVISION")
356 delta
[LAST_PROVISION_USN_ATTRIBUTE
] = ldb
.MessageElement(tab
,
357 ldb
.FLAG_MOD_ADD
, LAST_PROVISION_USN_ATTRIBUTE
)
361 def get_max_usn(samdb
,basedn
):
362 """ This function return the biggest USN present in the provision
364 :param samdb: A LDB object pointing to the sam.ldb
365 :param basedn: A string containing the base DN of the provision
367 :return: The biggest USN in the provision"""
369 res
= samdb
.search(expression
="objectClass=*",base
=basedn
,
370 scope
=ldb
.SCOPE_SUBTREE
,attrs
=["uSNChanged"],
371 controls
=["search_options:1:2",
372 "server_sort:1:1:uSNChanged",
373 "paged_results:1:1"])
374 return res
[0]["uSNChanged"]
377 def get_last_provision_usn(sam
):
378 """Get USNs ranges modified by a provision or an upgradeprovision
380 :param sam: An LDB object pointing to the sam.ldb
381 :return: a dictionary which keys are invocation id and values are an array
382 of integer representing the different ranges
385 entry
= sam
.search(expression
="%s=*" % LAST_PROVISION_USN_ATTRIBUTE
,
386 base
="@PROVISION", scope
=ldb
.SCOPE_BASE
,
387 attrs
=[LAST_PROVISION_USN_ATTRIBUTE
, "provisionnerID"])
388 except ldb
.LdbError
, (ecode
, emsg
):
389 if ecode
== ldb
.ERR_NO_SUCH_OBJECT
:
396 if entry
[0].get("provisionnerID"):
397 for e
in entry
[0]["provisionnerID"]:
399 for r
in entry
[0][LAST_PROVISION_USN_ATTRIBUTE
]:
400 tab1
= str(r
).split(';')
405 if (len(myids
) > 0 and id not in myids
):
407 tab2
= p
.split(tab1
[0])
408 if range.get(id) is None:
410 range[id].append(tab2
[0])
411 range[id].append(tab2
[1])
417 class ProvisionResult(object):
418 """Result of a provision.
420 :ivar server_role: The server role
421 :ivar paths: ProvisionPaths instance
422 :ivar domaindn: The domain dn, as string
426 self
.server_role
= None
433 self
.domainsid
= None
434 self
.adminpass_generated
= None
435 self
.adminpass
= None
436 self
.backend_result
= None
438 def report_logger(self
, logger
):
439 """Report this provision result to a logger."""
441 "Once the above files are installed, your Samba4 server will "
443 if self
.adminpass_generated
:
444 logger
.info("Admin password: %s", self
.adminpass
)
445 logger
.info("Server Role: %s", self
.server_role
)
446 logger
.info("Hostname: %s", self
.names
.hostname
)
447 logger
.info("NetBIOS Domain: %s", self
.names
.domain
)
448 logger
.info("DNS Domain: %s", self
.names
.dnsdomain
)
449 logger
.info("DOMAIN SID: %s", self
.domainsid
)
451 if self
.backend_result
:
452 self
.backend_result
.report_logger(logger
)
455 def check_install(lp
, session_info
, credentials
):
456 """Check whether the current install seems ok.
458 :param lp: Loadparm context
459 :param session_info: Session information
460 :param credentials: Credentials
462 if lp
.get("realm") == "":
463 raise Exception("Realm empty")
464 samdb
= Ldb(lp
.samdb_url(), session_info
=session_info
,
465 credentials
=credentials
, lp
=lp
)
466 if len(samdb
.search("(cn=Administrator)")) != 1:
467 raise ProvisioningError("No administrator account found")
470 def findnss(nssfn
, names
):
471 """Find a user or group from a list of possibilities.
473 :param nssfn: NSS Function to try (should raise KeyError if not found)
474 :param names: Names to check.
475 :return: Value return by first names list.
482 raise KeyError("Unable to find user/group in %r" % names
)
485 findnss_uid
= lambda names
: findnss(pwd
.getpwnam
, names
)[2]
486 findnss_gid
= lambda names
: findnss(grp
.getgrnam
, names
)[2]
489 def provision_paths_from_lp(lp
, dnsdomain
):
490 """Set the default paths for provisioning.
492 :param lp: Loadparm context.
493 :param dnsdomain: DNS Domain name
495 paths
= ProvisionPaths()
496 paths
.private_dir
= lp
.get("private dir")
497 paths
.state_dir
= lp
.get("state directory")
499 # This is stored without path prefix for the "privateKeytab" attribute in
500 # "secrets_dns.ldif".
501 paths
.dns_keytab
= "dns.keytab"
502 paths
.keytab
= "secrets.keytab"
504 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
505 paths
.samdb
= os
.path
.join(paths
.private_dir
, "sam.ldb")
506 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, "idmap.ldb")
507 paths
.secrets
= os
.path
.join(paths
.private_dir
, "secrets.ldb")
508 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
509 paths
.dns
= os
.path
.join(paths
.private_dir
, "dns", dnsdomain
+ ".zone")
510 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
511 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
512 paths
.namedconf
= os
.path
.join(paths
.private_dir
, "named.conf")
513 paths
.namedconf_update
= os
.path
.join(paths
.private_dir
, "named.conf.update")
514 paths
.namedtxt
= os
.path
.join(paths
.private_dir
, "named.txt")
515 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
516 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
517 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
518 paths
.hklm
= "hklm.ldb"
519 paths
.hkcr
= "hkcr.ldb"
520 paths
.hkcu
= "hkcu.ldb"
521 paths
.hku
= "hku.ldb"
522 paths
.hkpd
= "hkpd.ldb"
523 paths
.hkpt
= "hkpt.ldb"
524 paths
.sysvol
= lp
.get("path", "sysvol")
525 paths
.netlogon
= lp
.get("path", "netlogon")
526 paths
.smbconf
= lp
.configfile
530 def determine_netbios_name(hostname
):
531 """Determine a netbios name from a hostname."""
532 # remove forbidden chars and force the length to be <16
533 netbiosname
= "".join([x
for x
in hostname
if is_valid_netbios_char(x
)])
534 return netbiosname
[:MAX_NETBIOS_NAME_LEN
].upper()
537 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
538 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
539 schemadn
=None, serverdn
=None, sitename
=None):
540 """Guess configuration settings to use."""
543 hostname
= socket
.gethostname().split(".")[0]
545 netbiosname
= lp
.get("netbios name")
546 if netbiosname
is None:
547 netbiosname
= determine_netbios_name(hostname
)
548 netbiosname
= netbiosname
.upper()
549 if not valid_netbios_name(netbiosname
):
550 raise InvalidNetbiosName(netbiosname
)
552 if dnsdomain
is None:
553 dnsdomain
= lp
.get("realm")
554 if dnsdomain
is None or dnsdomain
== "":
555 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp
.configfile
)
557 dnsdomain
= dnsdomain
.lower()
559 if serverrole
is None:
560 serverrole
= lp
.get("server role")
561 if serverrole
is None:
562 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
564 serverrole
= serverrole
.lower()
566 realm
= dnsdomain
.upper()
568 if lp
.get("realm") == "":
569 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
571 if lp
.get("realm").upper() != realm
:
572 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
))
574 if lp
.get("server role").lower() != serverrole
:
575 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
))
577 if serverrole
== "active directory domain controller":
579 # This will, for better or worse, default to 'WORKGROUP'
580 domain
= lp
.get("workgroup")
581 domain
= domain
.upper()
583 if lp
.get("workgroup").upper() != domain
:
584 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
))
587 domaindn
= samba
.dn_from_dns_name(dnsdomain
)
589 if domain
== netbiosname
:
590 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain
, netbiosname
))
594 domaindn
= "DC=" + netbiosname
596 if not valid_netbios_name(domain
):
597 raise InvalidNetbiosName(domain
)
599 if hostname
.upper() == realm
:
600 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
601 if netbiosname
.upper() == realm
:
602 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm
, netbiosname
))
604 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
610 configdn
= "CN=Configuration," + rootdn
612 schemadn
= "CN=Schema," + configdn
615 sitename
= DEFAULTSITE
617 names
= ProvisionNames()
618 names
.rootdn
= rootdn
619 names
.domaindn
= domaindn
620 names
.configdn
= configdn
621 names
.schemadn
= schemadn
622 names
.ldapmanagerdn
= "CN=Manager," + rootdn
623 names
.dnsdomain
= dnsdomain
624 names
.domain
= domain
626 names
.netbiosname
= netbiosname
627 names
.hostname
= hostname
628 names
.sitename
= sitename
629 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
630 netbiosname
, sitename
, configdn
)
635 def make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
636 serverrole
=None, eadb
=False, use_ntvfs
=False, lp
=None,
638 """Create a new smb.conf file based on a couple of basic settings.
640 assert smbconf
is not None
643 hostname
= socket
.gethostname().split(".")[0]
645 netbiosname
= determine_netbios_name(hostname
)
647 if serverrole
is None:
648 serverrole
= "standalone server"
650 assert domain
is not None
651 domain
= domain
.upper()
653 assert realm
is not None
654 realm
= realm
.upper()
657 "netbios name": netbiosname
,
660 "server role": serverrole
,
664 lp
= samba
.param
.LoadParm()
665 #Load non-existent file
666 if os
.path
.exists(smbconf
):
669 if global_param
is not None:
670 for ent
in global_param
:
671 if global_param
[ent
] is not None:
672 global_settings
[ent
] = " ".join(global_param
[ent
])
674 if targetdir
is not None:
675 global_settings
["private dir"] = os
.path
.abspath(os
.path
.join(targetdir
, "private"))
676 global_settings
["lock dir"] = os
.path
.abspath(targetdir
)
677 global_settings
["state directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "state"))
678 global_settings
["cache directory"] = os
.path
.abspath(os
.path
.join(targetdir
, "cache"))
680 lp
.set("lock dir", os
.path
.abspath(targetdir
))
681 lp
.set("state directory", global_settings
["state directory"])
682 lp
.set("cache directory", global_settings
["cache directory"])
685 if use_ntvfs
and not lp
.get("posix:eadb"):
686 if targetdir
is not None:
687 privdir
= os
.path
.join(targetdir
, "private")
689 privdir
= lp
.get("private dir")
690 lp
.set("posix:eadb", os
.path
.abspath(os
.path
.join(privdir
, "eadb.tdb")))
691 elif not use_ntvfs
and not lp
.get("xattr_tdb:file"):
692 if targetdir
is not None:
693 statedir
= os
.path
.join(targetdir
, "state")
695 statedir
= lp
.get("state directory")
696 lp
.set("xattr_tdb:file", os
.path
.abspath(os
.path
.join(statedir
, "xattr.tdb")))
699 if serverrole
== "active directory domain controller":
700 shares
["sysvol"] = os
.path
.join(lp
.get("state directory"), "sysvol")
701 shares
["netlogon"] = os
.path
.join(shares
["sysvol"], realm
.lower(),
704 global_settings
["passdb backend"] = "samba_dsdb"
706 f
= open(smbconf
, 'w')
708 f
.write("[globals]\n")
709 for key
, val
in global_settings
.iteritems():
710 f
.write("\t%s = %s\n" % (key
, val
))
713 for name
, path
in shares
.iteritems():
714 f
.write("[%s]\n" % name
)
715 f
.write("\tpath = %s\n" % path
)
716 f
.write("\tread only = no\n")
720 # reload the smb.conf
723 # and dump it without any values that are the default
724 # this ensures that any smb.conf parameters that were set
725 # on the provision/join command line are set in the resulting smb.conf
726 f
= open(smbconf
, mode
='w')
733 def setup_name_mappings(idmap
, sid
, root_uid
, nobody_uid
,
734 users_gid
, root_gid
):
735 """setup reasonable name mappings for sam names to unix names.
737 :param samdb: SamDB object.
738 :param idmap: IDmap db object.
739 :param sid: The domain sid.
740 :param domaindn: The domain DN.
741 :param root_uid: uid of the UNIX root user.
742 :param nobody_uid: uid of the UNIX nobody user.
743 :param users_gid: gid of the UNIX users group.
744 :param root_gid: gid of the UNIX root group.
746 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
748 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
749 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
752 def setup_samdb_partitions(samdb_path
, logger
, lp
, session_info
,
753 provision_backend
, names
, schema
, serverrole
,
755 """Setup the partitions for the SAM database.
757 Alternatively, provision() may call this, and then populate the database.
759 :note: This will wipe the Sam Database!
761 :note: This function always removes the local SAM LDB file. The erase
762 parameter controls whether to erase the existing data, which
763 may not be stored locally but in LDAP.
766 assert session_info
is not None
768 # We use options=["modules:"] to stop the modules loading - we
769 # just want to wipe and re-initialise the database, not start it up
772 os
.unlink(samdb_path
)
776 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
777 lp
=lp
, options
=["modules:"])
779 ldap_backend_line
= "# No LDAP backend"
780 if provision_backend
.type != "ldb":
781 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldap_uri
783 samdb
.transaction_start()
785 logger
.info("Setting up sam.ldb partitions and settings")
786 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
787 "LDAP_BACKEND_LINE": ldap_backend_line
791 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
792 "BACKEND_TYPE": provision_backend
.type,
793 "SERVER_ROLE": serverrole
796 logger
.info("Setting up sam.ldb rootDSE")
797 setup_samdb_rootdse(samdb
, names
)
799 samdb
.transaction_cancel()
802 samdb
.transaction_commit()
805 def secretsdb_self_join(secretsdb
, domain
,
806 netbiosname
, machinepass
, domainsid
=None,
807 realm
=None, dnsdomain
=None,
809 key_version_number
=1,
810 secure_channel_type
=SEC_CHAN_WKSTA
):
811 """Add domain join-specific bits to a secrets database.
813 :param secretsdb: Ldb Handle to the secrets database
814 :param machinepass: Machine password
816 attrs
= ["whenChanged",
823 if realm
is not None:
824 if dnsdomain
is None:
825 dnsdomain
= realm
.lower()
826 dnsname
= '%s.%s' % (netbiosname
.lower(), dnsdomain
.lower())
829 shortname
= netbiosname
.lower()
831 # We don't need to set msg["flatname"] here, because rdn_name will handle
832 # it, and it causes problems for modifies anyway
833 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
834 msg
["secureChannelType"] = [str(secure_channel_type
)]
835 msg
["objectClass"] = ["top", "primaryDomain"]
836 if dnsname
is not None:
837 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
838 msg
["realm"] = [realm
]
839 msg
["saltPrincipal"] = ["host/%s@%s" % (dnsname
, realm
.upper())]
840 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
841 msg
["privateKeytab"] = ["secrets.keytab"]
843 msg
["secret"] = [machinepass
]
844 msg
["samAccountName"] = ["%s$" % netbiosname
]
845 msg
["secureChannelType"] = [str(secure_channel_type
)]
846 if domainsid
is not None:
847 msg
["objectSid"] = [ndr_pack(domainsid
)]
849 # This complex expression tries to ensure that we don't have more
850 # than one record for this SID, realm or netbios domain at a time,
851 # but we don't delete the old record that we are about to modify,
852 # because that would delete the keytab and previous password.
853 res
= secretsdb
.search(base
="cn=Primary Domains", attrs
=attrs
,
854 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain
, realm
, str(domainsid
), str(msg
.dn
))),
855 scope
=ldb
.SCOPE_ONELEVEL
)
858 secretsdb
.delete(del_msg
.dn
)
860 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
863 msg
["priorSecret"] = [res
[0]["secret"][0]]
864 msg
["priorWhenChanged"] = [res
[0]["whenChanged"][0]]
867 msg
["privateKeytab"] = [res
[0]["privateKeytab"][0]]
872 msg
["krb5Keytab"] = [res
[0]["krb5Keytab"][0]]
878 msg
[el
].set_flags(ldb
.FLAG_MOD_REPLACE
)
879 secretsdb
.modify(msg
)
880 secretsdb
.rename(res
[0].dn
, msg
.dn
)
882 spn
= [ 'HOST/%s' % shortname
]
883 if secure_channel_type
== SEC_CHAN_BDC
and dnsname
is not None:
884 # we are a domain controller then we add servicePrincipalName
885 # entries for the keytab code to update.
886 spn
.extend([ 'HOST/%s' % dnsname
])
887 msg
["servicePrincipalName"] = spn
892 def setup_secretsdb(paths
, session_info
, backend_credentials
, lp
):
893 """Setup the secrets database.
895 :note: This function does not handle exceptions and transaction on purpose,
896 it's up to the caller to do this job.
898 :param path: Path to the secrets database.
899 :param session_info: Session info.
900 :param credentials: Credentials
901 :param lp: Loadparm context
902 :return: LDB handle for the created secrets database
904 if os
.path
.exists(paths
.secrets
):
905 os
.unlink(paths
.secrets
)
907 keytab_path
= os
.path
.join(paths
.private_dir
, paths
.keytab
)
908 if os
.path
.exists(keytab_path
):
909 os
.unlink(keytab_path
)
911 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
912 if os
.path
.exists(dns_keytab_path
):
913 os
.unlink(dns_keytab_path
)
917 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
919 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
920 secrets_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
921 secrets_ldb
.transaction_start()
923 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
925 if (backend_credentials
is not None and
926 backend_credentials
.authentication_requested()):
927 if backend_credentials
.get_bind_dn() is not None:
928 setup_add_ldif(secrets_ldb
,
929 setup_path("secrets_simple_ldap.ldif"), {
930 "LDAPMANAGERDN": backend_credentials
.get_bind_dn(),
931 "LDAPMANAGERPASS_B64": b64encode(backend_credentials
.get_password())
934 setup_add_ldif(secrets_ldb
,
935 setup_path("secrets_sasl_ldap.ldif"), {
936 "LDAPADMINUSER": backend_credentials
.get_username(),
937 "LDAPADMINREALM": backend_credentials
.get_realm(),
938 "LDAPADMINPASS_B64": b64encode(backend_credentials
.get_password())
941 secrets_ldb
.transaction_cancel()
946 def setup_privileges(path
, session_info
, lp
):
947 """Setup the privileges database.
949 :param path: Path to the privileges database.
950 :param session_info: Session info.
951 :param credentials: Credentials
952 :param lp: Loadparm context
953 :return: LDB handle for the created secrets database
955 if os
.path
.exists(path
):
957 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
958 privilege_ldb
.erase()
959 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
962 def setup_registry(path
, session_info
, lp
):
963 """Setup the registry.
965 :param path: Path to the registry database
966 :param session_info: Session information
967 :param credentials: Credentials
968 :param lp: Loadparm context
970 reg
= samba
.registry
.Registry()
971 hive
= samba
.registry
.open_ldb(path
, session_info
=session_info
, lp_ctx
=lp
)
972 reg
.mount_hive(hive
, samba
.registry
.HKEY_LOCAL_MACHINE
)
973 provision_reg
= setup_path("provision.reg")
974 assert os
.path
.exists(provision_reg
)
975 reg
.diff_apply(provision_reg
)
978 def setup_idmapdb(path
, session_info
, lp
):
979 """Setup the idmap database.
981 :param path: path to the idmap database
982 :param session_info: Session information
983 :param credentials: Credentials
984 :param lp: Loadparm context
986 if os
.path
.exists(path
):
989 idmap_ldb
= IDmapDB(path
, session_info
=session_info
, lp
=lp
)
991 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
995 def setup_samdb_rootdse(samdb
, names
):
996 """Setup the SamDB rootdse.
998 :param samdb: Sam Database handle
1000 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
1001 "SCHEMADN": names
.schemadn
,
1002 "DOMAINDN": names
.domaindn
,
1003 "ROOTDN" : names
.rootdn
,
1004 "CONFIGDN": names
.configdn
,
1005 "SERVERDN": names
.serverdn
,
1009 def setup_self_join(samdb
, admin_session_info
, names
, fill
, machinepass
,
1010 dns_backend
, dnspass
, domainsid
, next_rid
, invocationid
,
1011 policyguid
, policyguid_dc
,
1012 domainControllerFunctionality
, ntdsguid
=None, dc_rid
=None):
1013 """Join a host to its own domain."""
1014 assert isinstance(invocationid
, str)
1015 if ntdsguid
is not None:
1016 ntdsguid_line
= "objectGUID: %s\n"%ntdsguid
1023 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
1024 "CONFIGDN": names
.configdn
,
1025 "SCHEMADN": names
.schemadn
,
1026 "DOMAINDN": names
.domaindn
,
1027 "SERVERDN": names
.serverdn
,
1028 "INVOCATIONID": invocationid
,
1029 "NETBIOSNAME": names
.netbiosname
,
1030 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1031 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1032 "DOMAINSID": str(domainsid
),
1033 "DCRID": str(dc_rid
),
1034 "SAMBA_VERSION_STRING": version
,
1035 "NTDSGUID": ntdsguid_line
,
1036 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1037 domainControllerFunctionality
),
1038 "RIDALLOCATIONSTART": str(next_rid
+ 100),
1039 "RIDALLOCATIONEND": str(next_rid
+ 100 + 499)})
1041 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
1042 "POLICYGUID": policyguid
,
1043 "POLICYGUID_DC": policyguid_dc
,
1044 "DNSDOMAIN": names
.dnsdomain
,
1045 "DOMAINDN": names
.domaindn
})
1047 # If we are setting up a subdomain, then this has been replicated in, so we
1048 # don't need to add it
1049 if fill
== FILL_FULL
:
1050 setup_add_ldif(samdb
, setup_path("provision_self_join_config.ldif"), {
1051 "CONFIGDN": names
.configdn
,
1052 "SCHEMADN": names
.schemadn
,
1053 "DOMAINDN": names
.domaindn
,
1054 "SERVERDN": names
.serverdn
,
1055 "INVOCATIONID": invocationid
,
1056 "NETBIOSNAME": names
.netbiosname
,
1057 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
1058 "MACHINEPASS_B64": b64encode(machinepass
.encode('utf-16-le')),
1059 "DOMAINSID": str(domainsid
),
1060 "DCRID": str(dc_rid
),
1061 "SAMBA_VERSION_STRING": version
,
1062 "NTDSGUID": ntdsguid_line
,
1063 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1064 domainControllerFunctionality
)})
1066 # Setup fSMORoleOwner entries to point at the newly created DC entry
1067 setup_modify_ldif(samdb
,
1068 setup_path("provision_self_join_modify_config.ldif"), {
1069 "CONFIGDN": names
.configdn
,
1070 "SCHEMADN": names
.schemadn
,
1071 "DEFAULTSITE": names
.sitename
,
1072 "NETBIOSNAME": names
.netbiosname
,
1073 "SERVERDN": names
.serverdn
,
1076 system_session_info
= system_session()
1077 samdb
.set_session_info(system_session_info
)
1078 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1079 # modify a serverReference under cn=config when we are a subdomain, we must
1080 # be system due to ACLs
1081 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
1082 "DOMAINDN": names
.domaindn
,
1083 "SERVERDN": names
.serverdn
,
1084 "NETBIOSNAME": names
.netbiosname
,
1087 samdb
.set_session_info(admin_session_info
)
1089 if dns_backend
!= "SAMBA_INTERNAL":
1090 # This is Samba4 specific and should be replaced by the correct
1091 # DNS AD-style setup
1092 setup_add_ldif(samdb
, setup_path("provision_dns_add_samba.ldif"), {
1093 "DNSDOMAIN": names
.dnsdomain
,
1094 "DOMAINDN": names
.domaindn
,
1095 "DNSPASS_B64": b64encode(dnspass
.encode('utf-16-le')),
1096 "HOSTNAME" : names
.hostname
,
1097 "DNSNAME" : '%s.%s' % (
1098 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
1102 def getpolicypath(sysvolpath
, dnsdomain
, guid
):
1103 """Return the physical path of policy given its guid.
1105 :param sysvolpath: Path to the sysvol folder
1106 :param dnsdomain: DNS name of the AD domain
1107 :param guid: The GUID of the policy
1108 :return: A string with the complete path to the policy folder
1111 guid
= "{%s}" % guid
1112 policy_path
= os
.path
.join(sysvolpath
, dnsdomain
, "Policies", guid
)
1116 def create_gpo_struct(policy_path
):
1117 if not os
.path
.exists(policy_path
):
1118 os
.makedirs(policy_path
, 0775)
1119 f
= open(os
.path
.join(policy_path
, "GPT.INI"), 'w')
1121 f
.write("[General]\r\nVersion=0")
1124 p
= os
.path
.join(policy_path
, "MACHINE")
1125 if not os
.path
.exists(p
):
1126 os
.makedirs(p
, 0775)
1127 p
= os
.path
.join(policy_path
, "USER")
1128 if not os
.path
.exists(p
):
1129 os
.makedirs(p
, 0775)
1132 def create_default_gpo(sysvolpath
, dnsdomain
, policyguid
, policyguid_dc
):
1133 """Create the default GPO for a domain
1135 :param sysvolpath: Physical path for the sysvol folder
1136 :param dnsdomain: DNS domain name of the AD domain
1137 :param policyguid: GUID of the default domain policy
1138 :param policyguid_dc: GUID of the default domain controler policy
1140 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid
)
1141 create_gpo_struct(policy_path
)
1143 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid_dc
)
1144 create_gpo_struct(policy_path
)
1147 def setup_samdb(path
, session_info
, provision_backend
, lp
, names
,
1148 logger
, fill
, serverrole
, schema
, am_rodc
=False):
1149 """Setup a complete SAM Database.
1151 :note: This will wipe the main SAM database file!
1154 # Also wipes the database
1155 setup_samdb_partitions(path
, logger
=logger
, lp
=lp
,
1156 provision_backend
=provision_backend
, session_info
=session_info
,
1157 names
=names
, serverrole
=serverrole
, schema
=schema
)
1159 # Load the database, but don's load the global schema and don't connect
1161 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
1162 credentials
=provision_backend
.credentials
, lp
=lp
,
1163 global_schema
=False, am_rodc
=am_rodc
)
1165 logger
.info("Pre-loading the Samba 4 and AD schema")
1167 # Load the schema from the one we computed earlier
1168 samdb
.set_schema(schema
, write_indices_and_attributes
=False)
1170 # Set the NTDS settings DN manually - in order to have it already around
1171 # before the provisioned tree exists and we connect
1172 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1174 # And now we can connect to the DB - the schema won't be loaded from the
1178 # But we have to give it one more kick to have it use the schema
1179 # during provision - it needs, now that it is connected, to write
1180 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1181 samdb
.set_schema(schema
, write_indices_and_attributes
=True)
1186 def fill_samdb(samdb
, lp
, names
, logger
, domainsid
, domainguid
, policyguid
,
1187 policyguid_dc
, fill
, adminpass
, krbtgtpass
, machinepass
, dns_backend
,
1188 dnspass
, invocationid
, ntdsguid
, serverrole
, am_rodc
=False,
1189 dom_for_fun_level
=None, schema
=None, next_rid
=None, dc_rid
=None):
1191 if next_rid
is None:
1194 # Provision does not make much sense values larger than 1000000000
1195 # as the upper range of the rIDAvailablePool is 1073741823 and
1196 # we don't want to create a domain that cannot allocate rids.
1197 if next_rid
< 1000 or next_rid
> 1000000000:
1198 error
= "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid
)
1199 error
+= "the valid range is %u-%u. The default is %u." % (
1200 1000, 1000000000, 1000)
1201 raise ProvisioningError(error
)
1203 # ATTENTION: Do NOT change these default values without discussion with the
1204 # team and/or release manager. They have a big impact on the whole program!
1205 domainControllerFunctionality
= DS_DOMAIN_FUNCTION_2008_R2
1207 if dom_for_fun_level
is None:
1208 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2003
1210 if dom_for_fun_level
> domainControllerFunctionality
:
1211 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!")
1213 domainFunctionality
= dom_for_fun_level
1214 forestFunctionality
= dom_for_fun_level
1216 # Set the NTDS settings DN manually - in order to have it already around
1217 # before the provisioned tree exists and we connect
1218 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
1220 samdb
.transaction_start()
1222 # Set the domain functionality levels onto the database.
1223 # Various module (the password_hash module in particular) need
1224 # to know what level of AD we are emulating.
1226 # These will be fixed into the database via the database
1227 # modifictions below, but we need them set from the start.
1228 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
1229 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
1230 samdb
.set_opaque_integer("domainControllerFunctionality",
1231 domainControllerFunctionality
)
1233 samdb
.set_domain_sid(str(domainsid
))
1234 samdb
.set_invocation_id(invocationid
)
1236 logger
.info("Adding DomainDN: %s" % names
.domaindn
)
1238 # impersonate domain admin
1239 admin_session_info
= admin_session(lp
, str(domainsid
))
1240 samdb
.set_session_info(admin_session_info
)
1241 if domainguid
is not None:
1242 domainguid_line
= "objectGUID: %s\n-" % domainguid
1244 domainguid_line
= ""
1246 descr
= b64encode(get_domain_descriptor(domainsid
))
1247 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
1248 "DOMAINDN": names
.domaindn
,
1249 "DOMAINSID": str(domainsid
),
1250 "DESCRIPTOR": descr
,
1251 "DOMAINGUID": domainguid_line
1254 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
1255 "DOMAINDN": names
.domaindn
,
1256 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1257 "NEXTRID": str(next_rid
),
1258 "DEFAULTSITE": names
.sitename
,
1259 "CONFIGDN": names
.configdn
,
1260 "POLICYGUID": policyguid
,
1261 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1262 "SAMBA_VERSION_STRING": version
1265 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1266 if fill
== FILL_FULL
:
1267 logger
.info("Adding configuration container")
1268 descr
= b64encode(get_config_descriptor(domainsid
))
1269 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
1270 "CONFIGDN": names
.configdn
,
1271 "DESCRIPTOR": descr
,
1274 # The LDIF here was created when the Schema object was constructed
1275 logger
.info("Setting up sam.ldb schema")
1276 samdb
.add_ldif(schema
.schema_dn_add
, controls
=["relax:0"])
1277 samdb
.modify_ldif(schema
.schema_dn_modify
)
1278 samdb
.write_prefixes_from_schema()
1279 samdb
.add_ldif(schema
.schema_data
, controls
=["relax:0"])
1280 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
1281 {"SCHEMADN": names
.schemadn
})
1283 # Now register this container in the root of the forest
1284 msg
= ldb
.Message(ldb
.Dn(samdb
, names
.domaindn
))
1285 msg
["subRefs"] = ldb
.MessageElement(names
.configdn
, ldb
.FLAG_MOD_ADD
,
1289 samdb
.transaction_cancel()
1292 samdb
.transaction_commit()
1294 samdb
.transaction_start()
1296 samdb
.invocation_id
= invocationid
1298 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1299 if fill
== FILL_FULL
:
1300 logger
.info("Setting up sam.ldb configuration data")
1302 partitions_descr
= b64encode(get_config_partitions_descriptor(domainsid
))
1303 sites_descr
= b64encode(get_config_sites_descriptor(domainsid
))
1304 ntdsquotas_descr
= b64encode(get_config_ntds_quotas_descriptor(domainsid
))
1305 protected1_descr
= b64encode(get_config_delete_protected1_descriptor(domainsid
))
1306 protected1wd_descr
= b64encode(get_config_delete_protected1wd_descriptor(domainsid
))
1307 protected2_descr
= b64encode(get_config_delete_protected2_descriptor(domainsid
))
1309 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
1310 "CONFIGDN": names
.configdn
,
1311 "NETBIOSNAME": names
.netbiosname
,
1312 "DEFAULTSITE": names
.sitename
,
1313 "DNSDOMAIN": names
.dnsdomain
,
1314 "DOMAIN": names
.domain
,
1315 "SCHEMADN": names
.schemadn
,
1316 "DOMAINDN": names
.domaindn
,
1317 "SERVERDN": names
.serverdn
,
1318 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
1319 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
1320 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr
,
1321 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr
,
1322 "SERVICES_DESCRIPTOR": protected1_descr
,
1323 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr
,
1324 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr
,
1325 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr
,
1326 "PARTITIONS_DESCRIPTOR": partitions_descr
,
1327 "SITES_DESCRIPTOR": sites_descr
,
1330 logger
.info("Setting up display specifiers")
1331 display_specifiers_ldif
= read_ms_ldif(
1332 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1333 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
,
1334 {"CONFIGDN": names
.configdn
})
1335 check_all_substituted(display_specifiers_ldif
)
1336 samdb
.add_ldif(display_specifiers_ldif
)
1338 logger
.info("Modifying display specifiers")
1339 setup_modify_ldif(samdb
,
1340 setup_path("provision_configuration_modify.ldif"), {
1341 "CONFIGDN": names
.configdn
,
1342 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1345 logger
.info("Adding users container")
1346 users_desc
= b64encode(get_domain_users_descriptor(domainsid
))
1347 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
1348 "DOMAINDN": names
.domaindn
,
1349 "USERS_DESCRIPTOR": users_desc
1351 logger
.info("Modifying users container")
1352 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
1353 "DOMAINDN": names
.domaindn
})
1354 logger
.info("Adding computers container")
1355 computers_desc
= b64encode(get_domain_computers_descriptor(domainsid
))
1356 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
1357 "DOMAINDN": names
.domaindn
,
1358 "COMPUTERS_DESCRIPTOR": computers_desc
1360 logger
.info("Modifying computers container")
1361 setup_modify_ldif(samdb
,
1362 setup_path("provision_computers_modify.ldif"), {
1363 "DOMAINDN": names
.domaindn
})
1364 logger
.info("Setting up sam.ldb data")
1365 infrastructure_desc
= b64encode(get_domain_infrastructure_descriptor(domainsid
))
1366 lostandfound_desc
= b64encode(get_domain_delete_protected2_descriptor(domainsid
))
1367 system_desc
= b64encode(get_domain_delete_protected1_descriptor(domainsid
))
1368 builtin_desc
= b64encode(get_domain_builtin_descriptor(domainsid
))
1369 controllers_desc
= b64encode(get_domain_controllers_descriptor(domainsid
))
1370 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1371 "CREATTIME": str(samba
.unix2nttime(int(time
.time()))),
1372 "DOMAINDN": names
.domaindn
,
1373 "NETBIOSNAME": names
.netbiosname
,
1374 "DEFAULTSITE": names
.sitename
,
1375 "CONFIGDN": names
.configdn
,
1376 "SERVERDN": names
.serverdn
,
1377 "RIDAVAILABLESTART": str(next_rid
+ 600),
1378 "POLICYGUID_DC": policyguid_dc
,
1379 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc
,
1380 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc
,
1381 "SYSTEM_DESCRIPTOR": system_desc
,
1382 "BUILTIN_DESCRIPTOR": builtin_desc
,
1383 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc
,
1386 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1387 if fill
== FILL_FULL
:
1388 setup_modify_ldif(samdb
,
1389 setup_path("provision_configuration_references.ldif"), {
1390 "CONFIGDN": names
.configdn
,
1391 "SCHEMADN": names
.schemadn
})
1393 logger
.info("Setting up well known security principals")
1394 protected1wd_descr
= b64encode(get_config_delete_protected1wd_descriptor(domainsid
))
1395 setup_add_ldif(samdb
, setup_path("provision_well_known_sec_princ.ldif"), {
1396 "CONFIGDN": names
.configdn
,
1397 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr
,
1400 if fill
== FILL_FULL
or fill
== FILL_SUBDOMAIN
:
1401 setup_modify_ldif(samdb
,
1402 setup_path("provision_basedn_references.ldif"),
1403 {"DOMAINDN": names
.domaindn
})
1405 logger
.info("Setting up sam.ldb users and groups")
1406 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1407 "DOMAINDN": names
.domaindn
,
1408 "DOMAINSID": str(domainsid
),
1409 "ADMINPASS_B64": b64encode(adminpass
.encode('utf-16-le')),
1410 "KRBTGTPASS_B64": b64encode(krbtgtpass
.encode('utf-16-le'))
1413 logger
.info("Setting up self join")
1414 setup_self_join(samdb
, admin_session_info
, names
=names
, fill
=fill
,
1415 invocationid
=invocationid
,
1416 dns_backend
=dns_backend
,
1418 machinepass
=machinepass
,
1419 domainsid
=domainsid
,
1422 policyguid
=policyguid
,
1423 policyguid_dc
=policyguid_dc
,
1424 domainControllerFunctionality
=domainControllerFunctionality
,
1427 ntds_dn
= "CN=NTDS Settings,%s" % names
.serverdn
1428 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1429 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
)
1430 assert isinstance(names
.ntdsguid
, str)
1432 samdb
.transaction_cancel()
1435 samdb
.transaction_commit()
1440 FILL_SUBDOMAIN
= "SUBDOMAIN"
1441 FILL_NT4SYNC
= "NT4SYNC"
1443 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1444 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)"
1445 SYSVOL_SERVICE
="sysvol"
1447 def set_dir_acl(path
, acl
, lp
, domsid
, use_ntvfs
, passdb
, service
=SYSVOL_SERVICE
):
1448 setntacl(lp
, path
, acl
, domsid
, use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1449 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1451 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1452 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1454 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
,
1455 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=service
)
1458 def set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
):
1459 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1462 :param sysvol: Physical path for the sysvol folder
1463 :param dnsdomain: The DNS name of the domain
1464 :param domainsid: The SID of the domain
1465 :param domaindn: The DN of the domain (ie. DC=...)
1466 :param samdb: An LDB object on the SAM db
1467 :param lp: an LP object
1470 # Set ACL for GPO root folder
1471 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1472 setntacl(lp
, root_policy_path
, POLICIES_ACL
, str(domainsid
),
1473 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True, passdb
=passdb
, service
=SYSVOL_SERVICE
)
1475 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1476 attrs
=["cn", "nTSecurityDescriptor"],
1477 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1480 acl
= ndr_unpack(security
.descriptor
,
1481 str(policy
["nTSecurityDescriptor"])).as_sddl()
1482 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1483 set_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1484 str(domainsid
), use_ntvfs
,
1488 def setsysvolacl(samdb
, netlogon
, sysvol
, uid
, gid
, domainsid
, dnsdomain
,
1489 domaindn
, lp
, use_ntvfs
):
1490 """Set the ACL for the sysvol share and the subfolders
1492 :param samdb: An LDB object on the SAM db
1493 :param netlogon: Physical path for the netlogon folder
1494 :param sysvol: Physical path for the sysvol folder
1495 :param uid: The UID of the "Administrator" user
1496 :param gid: The GID of the "Domain adminstrators" group
1497 :param domainsid: The SID of the domain
1498 :param dnsdomain: The DNS name of the domain
1499 :param domaindn: The DN of the domain (ie. DC=...)
1504 # This will ensure that the smbd code we are running when setting ACLs
1505 # is initialised with the smb.conf
1506 s3conf
= s3param
.get_context()
1507 s3conf
.load(lp
.configfile
)
1508 # ensure we are using the right samba_dsdb passdb backend, no matter what
1509 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1510 passdb
.reload_static_pdb()
1512 # ensure that we init the samba_dsdb backend, so the domain sid is
1513 # marked in secrets.tdb
1514 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1516 # now ensure everything matches correctly, to avoid wierd issues
1517 if passdb
.get_global_sam_sid() != domainsid
:
1518 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
))
1520 domain_info
= s4_passdb
.domain_info()
1521 if domain_info
["dom_sid"] != domainsid
:
1522 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
))
1524 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1525 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()))
1530 os
.chown(sysvol
, -1, gid
)
1536 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1537 setntacl(lp
,sysvol
, SYSVOL_ACL
, str(domainsid
), use_ntvfs
=use_ntvfs
,
1538 skip_invalid_chown
=True, passdb
=s4_passdb
,
1539 service
=SYSVOL_SERVICE
)
1540 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1542 if use_ntvfs
and canchown
:
1543 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1544 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
),
1545 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True,
1546 passdb
=s4_passdb
, service
=SYSVOL_SERVICE
)
1548 if use_ntvfs
and canchown
:
1549 os
.chown(os
.path
.join(root
, name
), -1, gid
)
1550 setntacl(lp
, os
.path
.join(root
, name
), SYSVOL_ACL
, str(domainsid
),
1551 use_ntvfs
=use_ntvfs
, skip_invalid_chown
=True,
1552 passdb
=s4_passdb
, service
=SYSVOL_SERVICE
)
1554 # Set acls on Policy folder and policies folders
1555 set_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
, use_ntvfs
, passdb
=s4_passdb
)
1557 def acl_type(direct_db_access
):
1558 if direct_db_access
:
1563 def check_dir_acl(path
, acl
, lp
, domainsid
, direct_db_access
):
1564 fsacl
= getntacl(lp
, path
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1565 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1566 if fsacl_sddl
!= acl
:
1567 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
))
1569 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1571 fsacl
= getntacl(lp
, os
.path
.join(root
, name
),
1572 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1574 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1575 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1576 if fsacl_sddl
!= acl
:
1577 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
))
1580 fsacl
= getntacl(lp
, os
.path
.join(root
, name
),
1581 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1583 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access
), os
.path
.join(root
, name
)))
1584 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1585 if fsacl_sddl
!= acl
:
1586 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
))
1589 def check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1591 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1594 :param sysvol: Physical path for the sysvol folder
1595 :param dnsdomain: The DNS name of the domain
1596 :param domainsid: The SID of the domain
1597 :param domaindn: The DN of the domain (ie. DC=...)
1598 :param samdb: An LDB object on the SAM db
1599 :param lp: an LP object
1602 # Set ACL for GPO root folder
1603 root_policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1604 fsacl
= getntacl(lp
, root_policy_path
,
1605 direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1607 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access
), root_policy_path
))
1608 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1609 if fsacl_sddl
!= POLICIES_ACL
:
1610 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
))
1611 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1612 attrs
=["cn", "nTSecurityDescriptor"],
1613 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1616 acl
= ndr_unpack(security
.descriptor
,
1617 str(policy
["nTSecurityDescriptor"])).as_sddl()
1618 policy_path
= getpolicypath(sysvol
, dnsdomain
, str(policy
["cn"]))
1619 check_dir_acl(policy_path
, dsacl2fsacl(acl
, domainsid
), lp
,
1620 domainsid
, direct_db_access
)
1623 def checksysvolacl(samdb
, netlogon
, sysvol
, domainsid
, dnsdomain
, domaindn
,
1625 """Set the ACL for the sysvol share and the subfolders
1627 :param samdb: An LDB object on the SAM db
1628 :param netlogon: Physical path for the netlogon folder
1629 :param sysvol: Physical path for the sysvol folder
1630 :param uid: The UID of the "Administrator" user
1631 :param gid: The GID of the "Domain adminstrators" group
1632 :param domainsid: The SID of the domain
1633 :param dnsdomain: The DNS name of the domain
1634 :param domaindn: The DN of the domain (ie. DC=...)
1637 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1638 s3conf
= s3param
.get_context()
1639 s3conf
.load(lp
.configfile
)
1640 # ensure we are using the right samba_dsdb passdb backend, no matter what
1641 s3conf
.set("passdb backend", "samba_dsdb:%s" % samdb
.url
)
1642 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1643 s4_passdb
= passdb
.PDB(s3conf
.get("passdb backend"))
1645 # now ensure everything matches correctly, to avoid wierd issues
1646 if passdb
.get_global_sam_sid() != domainsid
:
1647 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
))
1649 domain_info
= s4_passdb
.domain_info()
1650 if domain_info
["dom_sid"] != domainsid
:
1651 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
))
1653 if domain_info
["dns_domain"].upper() != dnsdomain
.upper():
1654 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()))
1656 # Ensure we can read this directly, and via the smbd VFS
1657 for direct_db_access
in [True, False]:
1658 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1659 for dir_path
in [os
.path
.join(sysvol
, dnsdomain
), netlogon
]:
1660 fsacl
= getntacl(lp
, dir_path
, direct_db_access
=direct_db_access
, service
=SYSVOL_SERVICE
)
1662 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access
), dir_path
))
1663 fsacl_sddl
= fsacl
.as_sddl(domainsid
)
1664 if fsacl_sddl
!= SYSVOL_ACL
:
1665 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
))
1667 # Check acls on Policy folder and policies folders
1668 check_gpos_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
,
1672 def interface_ips_v4(lp
):
1673 """return only IPv4 IPs"""
1674 ips
= samba
.interface_ips(lp
, False)
1677 if i
.find(':') == -1:
1682 def interface_ips_v6(lp
, linklocal
=False):
1683 """return only IPv6 IPs"""
1684 ips
= samba
.interface_ips(lp
, False)
1687 if i
.find(':') != -1 and (linklocal
or i
.find('%') == -1):
1692 def provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
1693 domainsid
, schema
=None,
1694 targetdir
=None, samdb_fill
=FILL_FULL
,
1695 hostip
=None, hostip6
=None,
1696 next_rid
=1000, dc_rid
=None, adminpass
=None, krbtgtpass
=None,
1697 domainguid
=None, policyguid
=None, policyguid_dc
=None,
1698 invocationid
=None, machinepass
=None, ntdsguid
=None,
1699 dns_backend
=None, dnspass
=None,
1700 serverrole
=None, dom_for_fun_level
=None,
1701 am_rodc
=False, lp
=None, use_ntvfs
=False, skip_sysvolacl
=False):
1702 # create/adapt the group policy GUIDs
1703 # Default GUID for default policy are described at
1704 # "How Core Group Policy Works"
1705 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1706 if policyguid
is None:
1707 policyguid
= DEFAULT_POLICY_GUID
1708 policyguid
= policyguid
.upper()
1709 if policyguid_dc
is None:
1710 policyguid_dc
= DEFAULT_DC_POLICY_GUID
1711 policyguid_dc
= policyguid_dc
.upper()
1713 if invocationid
is None:
1714 invocationid
= str(uuid
.uuid4())
1716 if krbtgtpass
is None:
1717 krbtgtpass
= samba
.generate_random_password(128, 255)
1718 if machinepass
is None:
1719 machinepass
= samba
.generate_random_password(128, 255)
1721 dnspass
= samba
.generate_random_password(128, 255)
1723 samdb
= fill_samdb(samdb
, lp
, names
, logger
=logger
,
1724 domainsid
=domainsid
, schema
=schema
, domainguid
=domainguid
,
1725 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1726 fill
=samdb_fill
, adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1727 invocationid
=invocationid
, machinepass
=machinepass
,
1728 dns_backend
=dns_backend
, dnspass
=dnspass
,
1729 ntdsguid
=ntdsguid
, serverrole
=serverrole
,
1730 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
1731 next_rid
=next_rid
, dc_rid
=dc_rid
)
1733 if serverrole
== "active directory domain controller":
1735 # Set up group policies (domain policy and domain controller
1737 create_default_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
,
1739 if not skip_sysvolacl
:
1740 setsysvolacl(samdb
, paths
.netlogon
, paths
.sysvol
, paths
.root_uid
,
1741 paths
.root_gid
, domainsid
, names
.dnsdomain
,
1742 names
.domaindn
, lp
, use_ntvfs
)
1744 logger
.info("Setting acl on sysvol skipped")
1746 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1747 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1748 netbiosname
=names
.netbiosname
, domainsid
=domainsid
,
1749 machinepass
=machinepass
, secure_channel_type
=SEC_CHAN_BDC
)
1751 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1752 # In future, this might be determined from some configuration
1753 kerberos_enctypes
= str(ENC_ALL_TYPES
)
1756 msg
= ldb
.Message(ldb
.Dn(samdb
,
1757 samdb
.searchone("distinguishedName",
1758 expression
="samAccountName=%s$" % names
.netbiosname
,
1759 scope
=ldb
.SCOPE_SUBTREE
)))
1760 msg
["msDS-SupportedEncryptionTypes"] = ldb
.MessageElement(
1761 elements
=kerberos_enctypes
, flags
=ldb
.FLAG_MOD_REPLACE
,
1762 name
="msDS-SupportedEncryptionTypes")
1764 except ldb
.LdbError
, (enum
, estr
):
1765 if enum
!= ldb
.ERR_NO_SUCH_ATTRIBUTE
:
1766 # It might be that this attribute does not exist in this schema
1769 setup_ad_dns(samdb
, secrets_ldb
, domainsid
, names
, paths
, lp
, logger
,
1770 hostip
=hostip
, hostip6
=hostip6
, dns_backend
=dns_backend
,
1771 dnspass
=dnspass
, os_level
=dom_for_fun_level
,
1772 targetdir
=targetdir
, site
=DEFAULTSITE
)
1774 domainguid
= samdb
.searchone(basedn
=samdb
.get_default_basedn(),
1775 attribute
="objectGUID")
1776 assert isinstance(domainguid
, str)
1778 lastProvisionUSNs
= get_last_provision_usn(samdb
)
1779 maxUSN
= get_max_usn(samdb
, str(names
.rootdn
))
1780 if lastProvisionUSNs
is not None:
1781 update_provision_usn(samdb
, 0, maxUSN
, invocationid
, 1)
1783 set_provision_usn(samdb
, 0, maxUSN
, invocationid
)
1785 logger
.info("Setting up sam.ldb rootDSE marking as synchronized")
1786 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"),
1787 { 'NTDSGUID' : names
.ntdsguid
})
1789 # fix any dangling GUIDs from the provision
1790 logger
.info("Fixing provision GUIDs")
1791 chk
= dbcheck(samdb
, samdb_schema
=samdb
, verbose
=False, fix
=True, yes
=True,
1793 samdb
.transaction_start()
1795 # a small number of GUIDs are missing because of ordering issues in the
1797 for schema_obj
in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1798 chk
.check_database(DN
="%s,%s" % (schema_obj
, names
.schemadn
),
1799 scope
=ldb
.SCOPE_BASE
,
1800 attrs
=['defaultObjectCategory'])
1801 chk
.check_database(DN
="CN=IP Security,CN=System,%s" % names
.domaindn
,
1802 scope
=ldb
.SCOPE_ONELEVEL
,
1803 attrs
=['ipsecOwnersReference',
1804 'ipsecFilterReference',
1805 'ipsecISAKMPReference',
1806 'ipsecNegotiationPolicyReference',
1807 'ipsecNFAReference'])
1809 samdb
.transaction_cancel()
1812 samdb
.transaction_commit()
1816 "ROLE_STANDALONE": "standalone server",
1817 "ROLE_DOMAIN_MEMBER": "member server",
1818 "ROLE_DOMAIN_BDC": "active directory domain controller",
1819 "ROLE_DOMAIN_PDC": "active directory domain controller",
1820 "dc": "active directory domain controller",
1821 "member": "member server",
1822 "domain controller": "active directory domain controller",
1823 "active directory domain controller": "active directory domain controller",
1824 "member server": "member server",
1825 "standalone": "standalone server",
1826 "standalone server": "standalone server",
1830 def sanitize_server_role(role
):
1831 """Sanitize a server role name.
1833 :param role: Server role
1834 :raise ValueError: If the role can not be interpreted
1835 :return: Sanitized server role (one of "member server",
1836 "active directory domain controller", "standalone server")
1839 return _ROLES_MAP
[role
]
1841 raise ValueError(role
)
1844 def provision_fake_ypserver(logger
, samdb
, domaindn
, netbiosname
, nisdomain
,
1846 """Create AD entries for the fake ypserver.
1848 This is needed for being able to manipulate posix attrs via ADUC.
1850 samdb
.transaction_start()
1852 logger
.info("Setting up fake yp server settings")
1853 setup_add_ldif(samdb
, setup_path("ypServ30.ldif"), {
1854 "DOMAINDN": domaindn
,
1855 "NETBIOSNAME": netbiosname
,
1856 "NISDOMAIN": nisdomain
,
1859 samdb
.transaction_cancel()
1862 samdb
.transaction_commit()
1865 def provision(logger
, session_info
, credentials
, smbconf
=None,
1866 targetdir
=None, samdb_fill
=FILL_FULL
, realm
=None, rootdn
=None,
1867 domaindn
=None, schemadn
=None, configdn
=None, serverdn
=None,
1868 domain
=None, hostname
=None, hostip
=None, hostip6
=None, domainsid
=None,
1869 next_rid
=1000, dc_rid
=None, adminpass
=None, ldapadminpass
=None,
1870 krbtgtpass
=None, domainguid
=None, policyguid
=None, policyguid_dc
=None,
1871 dns_backend
=None, dns_forwarder
=None, dnspass
=None,
1872 invocationid
=None, machinepass
=None, ntdsguid
=None,
1873 root
=None, nobody
=None, users
=None, backup
=None, aci
=None,
1874 serverrole
=None, dom_for_fun_level
=None, backend_type
=None,
1875 sitename
=None, ol_mmr_urls
=None, ol_olc
=None, slapd_path
="/bin/false",
1876 useeadb
=False, am_rodc
=False, lp
=None, use_ntvfs
=False,
1877 use_rfc2307
=False, maxuid
=None, maxgid
=None, skip_sysvolacl
=True):
1880 :note: caution, this wipes all existing data!
1884 serverrole
= sanitize_server_role(serverrole
)
1886 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole
)
1888 if ldapadminpass
is None:
1889 # Make a new, random password between Samba and it's LDAP server
1890 ldapadminpass
= samba
.generate_random_password(128, 255)
1892 if backend_type
is None:
1893 backend_type
= "ldb"
1895 if domainsid
is None:
1896 domainsid
= security
.random_sid()
1898 domainsid
= security
.dom_sid(domainsid
)
1900 root_uid
= findnss_uid([root
or "root"])
1901 nobody_uid
= findnss_uid([nobody
or "nobody"])
1902 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
1903 root_gid
= pwd
.getpwuid(root_uid
).pw_gid
1906 bind_gid
= findnss_gid(["bind", "named"])
1910 if targetdir
is not None:
1911 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
1912 elif smbconf
is None:
1913 smbconf
= samba
.param
.default_path()
1914 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
1915 os
.makedirs(os
.path
.dirname(smbconf
))
1917 server_services
= []
1920 global_param
["idmap_ldb:use rfc2307"] = ["yes"]
1922 if dns_backend
!= "SAMBA_INTERNAL":
1923 server_services
.append("-dns")
1925 if dns_forwarder
is not None:
1926 global_param
["dns forwarder"] = [dns_forwarder
]
1929 server_services
.append("+smb")
1930 server_services
.append("-s3fs")
1931 global_param
["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1933 if len(server_services
) > 0:
1934 global_param
["server services"] = server_services
1936 # only install a new smb.conf if there isn't one there already
1937 if os
.path
.exists(smbconf
):
1938 # if Samba Team members can't figure out the weird errors
1939 # loading an empty smb.conf gives, then we need to be smarter.
1940 # Pretend it just didn't exist --abartlet
1941 f
= open(smbconf
, 'r')
1943 data
= f
.read().lstrip()
1946 if data
is None or data
== "":
1947 make_smbconf(smbconf
, hostname
, domain
, realm
,
1948 targetdir
, serverrole
=serverrole
,
1949 eadb
=useeadb
, use_ntvfs
=use_ntvfs
,
1950 lp
=lp
, global_param
=global_param
)
1952 make_smbconf(smbconf
, hostname
, domain
, realm
, targetdir
,
1953 serverrole
=serverrole
,
1954 eadb
=useeadb
, use_ntvfs
=use_ntvfs
, lp
=lp
, global_param
=global_param
)
1957 lp
= samba
.param
.LoadParm()
1959 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
1960 dnsdomain
=realm
, serverrole
=serverrole
, domaindn
=domaindn
,
1961 configdn
=configdn
, schemadn
=schemadn
, serverdn
=serverdn
,
1962 sitename
=sitename
, rootdn
=rootdn
)
1963 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
1965 paths
.bind_gid
= bind_gid
1966 paths
.root_uid
= root_uid
;
1967 paths
.root_gid
= root_gid
1970 logger
.info("Looking up IPv4 addresses")
1971 hostips
= interface_ips_v4(lp
)
1972 if len(hostips
) > 0:
1974 if len(hostips
) > 1:
1975 logger
.warning("More than one IPv4 address found. Using %s",
1977 if hostip
== "127.0.0.1":
1980 logger
.warning("No IPv4 address will be assigned")
1983 logger
.info("Looking up IPv6 addresses")
1984 hostips
= interface_ips_v6(lp
, linklocal
=False)
1986 hostip6
= hostips
[0]
1987 if len(hostips
) > 1:
1988 logger
.warning("More than one IPv6 address found. Using %s", hostip6
)
1990 logger
.warning("No IPv6 address will be assigned")
1992 names
.hostip
= hostip
1993 names
.hostip6
= hostip6
1995 if serverrole
is None:
1996 serverrole
= lp
.get("server role")
1998 if not os
.path
.exists(paths
.private_dir
):
1999 os
.mkdir(paths
.private_dir
)
2000 if not os
.path
.exists(os
.path
.join(paths
.private_dir
, "tls")):
2001 os
.mkdir(os
.path
.join(paths
.private_dir
, "tls"))
2002 if not os
.path
.exists(paths
.state_dir
):
2003 os
.mkdir(paths
.state_dir
)
2005 if paths
.sysvol
and not os
.path
.exists(paths
.sysvol
):
2006 os
.makedirs(paths
.sysvol
, 0775)
2008 if not use_ntvfs
and serverrole
== "active directory domain controller":
2009 s3conf
= s3param
.get_context()
2010 s3conf
.load(lp
.configfile
)
2012 if paths
.sysvol
is None:
2013 raise MissingShareError("sysvol", paths
.smbconf
)
2015 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(paths
.sysvol
))
2018 smbd
.set_simple_acl(file.name
, 0755, root_gid
)
2020 if not smbd
.have_posix_acls():
2021 # This clue is only strictly correct for RPM and
2022 # Debian-like Linux systems, but hopefully other users
2023 # will get enough clue from it.
2024 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.")
2026 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
2028 smbd
.chown(file.name
, root_uid
, root_gid
)
2030 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
2034 ldapi_url
= "ldapi://%s" % urllib
.quote(paths
.s4_ldapi_path
, safe
="")
2036 schema
= Schema(domainsid
, invocationid
=invocationid
,
2037 schemadn
=names
.schemadn
)
2039 if backend_type
== "ldb":
2040 provision_backend
= LDBBackend(backend_type
, paths
=paths
,
2041 lp
=lp
, credentials
=credentials
,
2042 names
=names
, logger
=logger
)
2043 elif backend_type
== "existing":
2044 # If support for this is ever added back, then the URI will need to be
2046 provision_backend
= ExistingBackend(backend_type
, paths
=paths
,
2047 lp
=lp
, credentials
=credentials
,
2048 names
=names
, logger
=logger
,
2049 ldap_backend_forced_uri
=None)
2050 elif backend_type
== "fedora-ds":
2051 provision_backend
= FDSBackend(backend_type
, paths
=paths
,
2052 lp
=lp
, credentials
=credentials
,
2053 names
=names
, logger
=logger
, domainsid
=domainsid
,
2054 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
2055 slapd_path
=slapd_path
,
2057 elif backend_type
== "openldap":
2058 provision_backend
= OpenLDAPBackend(backend_type
, paths
=paths
,
2059 lp
=lp
, credentials
=credentials
,
2060 names
=names
, logger
=logger
, domainsid
=domainsid
,
2061 schema
=schema
, hostname
=hostname
, ldapadminpass
=ldapadminpass
,
2062 slapd_path
=slapd_path
, ol_mmr_urls
=ol_mmr_urls
)
2064 raise ValueError("Unknown LDAP backend type selected")
2066 provision_backend
.init()
2067 provision_backend
.start()
2069 # only install a new shares config db if there is none
2070 if not os
.path
.exists(paths
.shareconf
):
2071 logger
.info("Setting up share.ldb")
2072 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
, lp
=lp
)
2073 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
2075 logger
.info("Setting up secrets.ldb")
2076 secrets_ldb
= setup_secretsdb(paths
,
2077 session_info
=session_info
,
2078 backend_credentials
=provision_backend
.secrets_credentials
, lp
=lp
)
2081 logger
.info("Setting up the registry")
2082 setup_registry(paths
.hklm
, session_info
, lp
=lp
)
2084 logger
.info("Setting up the privileges database")
2085 setup_privileges(paths
.privilege
, session_info
, lp
=lp
)
2087 logger
.info("Setting up idmap db")
2088 idmap
= setup_idmapdb(paths
.idmapdb
, session_info
=session_info
, lp
=lp
)
2090 setup_name_mappings(idmap
, sid
=str(domainsid
),
2091 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
2092 users_gid
=users_gid
, root_gid
=root_gid
)
2094 logger
.info("Setting up SAM db")
2095 samdb
= setup_samdb(paths
.samdb
, session_info
,
2096 provision_backend
, lp
, names
, logger
=logger
,
2097 serverrole
=serverrole
,
2098 schema
=schema
, fill
=samdb_fill
, am_rodc
=am_rodc
)
2100 if serverrole
== "active directory domain controller":
2101 if paths
.netlogon
is None:
2102 raise MissingShareError("netlogon", paths
.smbconf
)
2104 if paths
.sysvol
is None:
2105 raise MissingShareError("sysvol", paths
.smbconf
)
2107 if not os
.path
.isdir(paths
.netlogon
):
2108 os
.makedirs(paths
.netlogon
, 0755)
2110 if adminpass
is None:
2111 adminpass
= samba
.generate_random_password(12, 32)
2112 adminpass_generated
= True
2114 adminpass_generated
= False
2116 if samdb_fill
== FILL_FULL
:
2117 provision_fill(samdb
, secrets_ldb
, logger
, names
, paths
,
2118 schema
=schema
, targetdir
=targetdir
, samdb_fill
=samdb_fill
,
2119 hostip
=hostip
, hostip6
=hostip6
, domainsid
=domainsid
,
2120 next_rid
=next_rid
, dc_rid
=dc_rid
, adminpass
=adminpass
,
2121 krbtgtpass
=krbtgtpass
, domainguid
=domainguid
,
2122 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
2123 invocationid
=invocationid
, machinepass
=machinepass
,
2124 ntdsguid
=ntdsguid
, dns_backend
=dns_backend
,
2125 dnspass
=dnspass
, serverrole
=serverrole
,
2126 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
,
2127 lp
=lp
, use_ntvfs
=use_ntvfs
,
2128 skip_sysvolacl
=skip_sysvolacl
)
2130 create_krb5_conf(paths
.krb5conf
,
2131 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
2133 logger
.info("A Kerberos configuration suitable for Samba 4 has been "
2134 "generated at %s", paths
.krb5conf
)
2136 if serverrole
== "active directory domain controller":
2137 create_dns_update_list(lp
, logger
, paths
)
2139 backend_result
= provision_backend
.post_setup()
2140 provision_backend
.shutdown()
2143 secrets_ldb
.transaction_cancel()
2146 # Now commit the secrets.ldb to disk
2147 secrets_ldb
.transaction_commit()
2149 # the commit creates the dns.keytab, now chown it
2150 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
2151 if os
.path
.isfile(dns_keytab_path
) and paths
.bind_gid
is not None:
2153 os
.chmod(dns_keytab_path
, 0640)
2154 os
.chown(dns_keytab_path
, -1, paths
.bind_gid
)
2156 if not os
.environ
.has_key('SAMBA_SELFTEST'):
2157 logger
.info("Failed to chown %s to bind gid %u",
2158 dns_keytab_path
, paths
.bind_gid
)
2160 result
= ProvisionResult()
2161 result
.server_role
= serverrole
2162 result
.domaindn
= domaindn
2163 result
.paths
= paths
2164 result
.names
= names
2166 result
.samdb
= samdb
2167 result
.idmap
= idmap
2168 result
.domainsid
= str(domainsid
)
2170 if samdb_fill
== FILL_FULL
:
2171 result
.adminpass_generated
= adminpass_generated
2172 result
.adminpass
= adminpass
2174 result
.adminpass_generated
= False
2175 result
.adminpass
= None
2177 result
.backend_result
= backend_result
2180 provision_fake_ypserver(logger
=logger
, samdb
=samdb
,
2181 domaindn
=names
.domaindn
, netbiosname
=names
.netbiosname
,
2182 nisdomain
=names
.domain
.lower(), maxuid
=maxuid
, maxgid
=maxgid
)
2187 def provision_become_dc(smbconf
=None, targetdir
=None,
2188 realm
=None, rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
2189 serverdn
=None, domain
=None, hostname
=None, domainsid
=None,
2190 adminpass
=None, krbtgtpass
=None, domainguid
=None, policyguid
=None,
2191 policyguid_dc
=None, invocationid
=None, machinepass
=None, dnspass
=None,
2192 dns_backend
=None, root
=None, nobody
=None, users
=None,
2193 backup
=None, serverrole
=None, ldap_backend
=None,
2194 ldap_backend_type
=None, sitename
=None, debuglevel
=1, use_ntvfs
=False):
2196 logger
= logging
.getLogger("provision")
2197 samba
.set_debug_level(debuglevel
)
2199 res
= provision(logger
, system_session(), None,
2200 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
2201 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
2202 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
2203 hostname
=hostname
, hostip
=None, domainsid
=domainsid
,
2204 machinepass
=machinepass
,
2205 serverrole
="active directory domain controller",
2206 sitename
=sitename
, dns_backend
=dns_backend
, dnspass
=dnspass
,
2207 use_ntvfs
=use_ntvfs
)
2208 res
.lp
.set("debuglevel", str(debuglevel
))
2212 def create_krb5_conf(path
, dnsdomain
, hostname
, realm
):
2213 """Write out a file containing zone statements suitable for inclusion in a
2214 named.conf file (including GSS-TSIG configuration).
2216 :param path: Path of the new named.conf file.
2217 :param dnsdomain: DNS Domain name
2218 :param hostname: Local hostname
2219 :param realm: Realm name
2221 setup_file(setup_path("krb5.conf"), path
, {
2222 "DNSDOMAIN": dnsdomain
,
2223 "HOSTNAME": hostname
,
2228 class ProvisioningError(Exception):
2229 """A generic provision error."""
2231 def __init__(self
, value
):
2235 return "ProvisioningError: " + self
.value
2238 class InvalidNetbiosName(Exception):
2239 """A specified name was not a valid NetBIOS name."""
2241 def __init__(self
, name
):
2242 super(InvalidNetbiosName
, self
).__init
__(
2243 "The name '%r' is not a valid NetBIOS name" % name
)
2246 class MissingShareError(ProvisioningError
):
2248 def __init__(self
, name
, smbconf
):
2249 super(MissingShareError
, self
).__init
__(
2250 "Existing smb.conf does not have a [%s] share, but you are "
2251 "configuring a DC. Please remove %s or add the share manually." %