/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <debug.h>
#include <conv.h>
#include <elfcap.h>
#include "_rtld.h"
#include "_elf.h"
#include "_audit.h"
#include "msg.h"
/*
* qsort(3c) capability comparison function.
*/
static int
{
/*
* First, investigate any platform capability.
*/
return (-1);
return (1);
/*
* Second, investigate any machine capability.
*/
return (-1);
return (1);
/*
* Third, investigate any CA_SUNW_HW_2 hardware capabilities.
*/
return (-1);
return (1);
/*
* Finally, investigate any CA_SUNW_HW_1 hardware capabilities.
*/
return (-1);
return (1);
/*
* Normally, a capabilities directory contains one or more capabilities
* files, each with different capabilities. The role of ld.so.1 is to
* select the best candidate from these variants. However, we've come
* across cases where files containing the same capabilities have been
* placed in the same capabilities directory. As we can't tell which
* file is the best, we select neither, and diagnose this suspicious
* scenario.
*/
return (0);
}
/*
* Determine whether HWCAP1 capabilities value is supported.
*/
int
{
/*
* Ensure that the kernel can cope with the required capabilities.
*/
if ((rtld_flags2 & RT_FL2_HWCAP) &&
if (rej) {
}
return (0);
}
return (1);
}
/*
* Determine whether HWCAP2 capabilities value is supported.
*/
int
{
/*
* Ensure that the kernel can cope with the required capabilities.
*/
if (rej) {
}
return (0);
}
return (1);
}
/*
* Process any software capabilities.
*/
/* ARGSUSED0 */
int
{
#if defined(_ELF64)
/*
* A 64-bit executable that started the process can be restricted to a
* 32-bit address space. A 64-bit dependency that is restricted to a
* 32-bit address space can not be loaded unless the executable has
* established this requirement.
*/
if (rej) {
}
return (0);
}
#endif
return (1);
}
/*
* Process any platform capability.
*/
int
{
/*
* If the platform name hasn't been set, try and obtain it.
*/
if (rej) {
/*
* Note, the platform name points to a string within an
* objects string table, and if that object can't be
* loaded, it will be unloaded and thus invalidate the
* string. Duplicate the string here for rejection
* message inheritance.
*/
}
return (0);
}
return (1);
}
/*
* Process any machine capability.
*/
int
{
/*
* If the machine name hasn't been set, try and obtain it.
*/
if (rej) {
/*
* Note, the machine name points to a string within an
* objects string table, and if that object can't be
* loaded, it will be unloaded and thus invalidate the
* string. Duplicate the string here for rejection
* message inheritance.
*/
}
return (0);
}
return (1);
}
/*
* Generic front-end to capabilities validation.
*/
static int
{
/*
* If the caller has no capabilities, then the object is valid.
*/
return (1);
if (alt)
else
char *str;
case CA_SUNW_HW_1:
/*
* Remove any historic values that should not be
* involved with any validation.
*/
val &= ~AV_HW1_IGNORE;
return (0);
if (fdp)
break;
case CA_SUNW_SF_1:
return (0);
if (fdp)
break;
case CA_SUNW_HW_2:
return (0);
if (fdp)
break;
case CA_SUNW_PLAT:
/*
* A capabilities group can define multiple platform
* names that are appropriate. Only if all the names
* are deemed invalid is the group determined
* inappropriate.
*/
totplat++;
ivlplat++;
else if (fdp)
}
break;
case CA_SUNW_MACH:
/*
* A capabilities group can define multiple machine
* names that are appropriate. Only if all the names
* are deemed invalid is the group determined
* inappropriate.
*/
totmach++;
ivlmach++;
else if (fdp)
}
break;
case CA_SUNW_ID:
/*
* Capabilities identifiers provide for diagnostics,
* but are not attributes that must be compared with
* the system. They are ignored.
*/
break;
default:
return (0);
}
cptr++;
}
/*
* If any platform names, or machine names were found, and all were
* invalid, indicate that the object is inappropriate.
*/
return (0);
return (1);
}
/*
* Determine whether a link-map should use alternative system capabilities.
*/
static void
{
int alt = 0;
/*
* If an alternative set of system capabilities have been established,
* and only specific files should use these alternative system
* capabilities, determine whether this file is one of those specified.
*/
if (capavl) {
const char *file;
/*
* The simplest way to reference a file is to use its file name
* (soname), however try all of the names that this file is
* known by.
*/
file++;
else
alt = 1;
if (alt == 0) {
const char *cp;
break;
}
}
}
/*
* Indicate if this link-map should use alternative system capabilities,
* and that the alternative system capabilities check has been carried
* out.
*/
}
/*
* Validate the capabilities requirements of a link-map.
*
* This routine is called for main, where a link-map is constructed from the
* mappings returned from exec(), and for any symbol capabilities comparisons.
*/
int
{
}
/*
* Validate the capabilities requirements of a file under inspection.
* This file is still under the early stages of loading, and has no link-map
* yet. The file must have an object capabilities definition (PT_SUNWCAP), to
* have gotten us here. The logic here is the same as cap_check_lmp().
*/
int
{
int alt = 0;
/*
* If an alternative set of system capabilities have been established,
* and only specific files should use these alternative system
* capabilities, determine whether this file is one of those specified.
*/
if (capavl) {
const char *file;
/*
* The simplest way to reference a file is to use its file name
* (soname), however try all of the names that this file is
* known by.
*/
file++;
else
alt = 1;
}
/*
* Indicate if this file descriptor should use alternative system
* capabilities, and that the alternative system capabilities check has
* been carried out.
*/
/*
* Verify that the required capabilities are supported by the reference.
*/
}
/*
* Free a file descriptor list. As part of building this list, the original
* names for each capabilities candidate were duplicated for use in later
* diagnostics. These names need to be freed.
*/
void
{
if (fdalp) {
}
}
}
/*
* When $CAPABILITY (or $HWCAP) is used to represent dependencies, take the
* associated directory and analyze all the files it contains.
*/
static int
{
const char *src;
int error = 0;
/*
* Access the directory in preparation for reading its entries. If
* successful, establish the initial pathname.
*/
return (0);
}
*dst++ = '/';
/*
* Read each entry from the directory and determine whether it is a
* valid ELF file.
*/
char *_dst;
/*
* Ignore "." and ".." entries.
*/
continue;
/*
* Complete the full pathname.
*/
*_dst = '\0';
/*
* Trace the inspection of this file, and determine any
* auditor substitution.
*/
continue;
/*
* Note, all directory entries are processed by find_path(),
* even entries that are directories themselves. This single
* point for control keeps the number of stat()'s down, and
* provides a single point for error diagnostics.
*/
continue;
}
/*
* If this object has already been loaded, save the capabilities
* for later sorting. Otherwise we have a new candidate.
*/
/*
* Duplicate the original name, as this may be required for
* later diagnostics. Keep a copy of the file descriptor for
* analysis once all capabilities candidates have been
* determined.
*/
AL_CNT_CAP) == NULL)) {
error = 1;
break;
}
}
/*
* If no objects have been found, we're done. Also, if an allocation
* error occurred while processing any object, remove any objects that
* had already been added to the list and return.
*/
if (fdalp)
return (0);
}
/*
* Having processed and retained all candidates from this directory,
* sort them, based on the precedence of their hardware capabilities.
*/
/*
* If any objects were found to have the same capabilities, then these
* objects must be rejected, as we can't tell which object is more
* appropriate.
*/
}
return (0);
}
return (1);
}
int
int *in_nfavl)
{
int unused = 0;
return (0);
/*
* Now complete the mapping of each of the ordered objects, adding
* each object to a new pathname descriptor.
*/
int audit = 0;
if (unused)
continue;
/*
* Complete mapping the file, obtaining a handle, and continue
* to analyze the object, establishing dependencies and
* relocating. Remove the file descriptor at this point, as it
* is no longer required.
*/
continue;
/*
* Create a new pathname descriptor to represent this filtee,
* and insert this descriptor in the Alist following the
* hardware descriptor that seeded this processing.
*/
if (ghp)
return (0);
}
/*
* Establish the filter handle to prevent any recursion.
*/
}
/*
* indicates the auditor wishes to ignore this filtee.
*/
audit = 1;
}
}
/*
* Finish processing the objects associated with this request.
*/
/*
* If the filtee has been successfully processed, then create
* an association between the filter and the filtee. This
* association provides sufficient information to tear down the
* filter and filtee if necessary.
*/
/*
* If this object is marked an end-filtee, we're done.
*/
unused = 1;
/*
* If this filtee loading has failed, generate a diagnostic.
* Null out the path name descriptor entry, and continue the
* search.
*/
/*
* If attempting to load this filtee required a new
* link-map control list to which this request has
* added objects, then remove all the objects that
* have been associated to this request.
*/
if (nlmco != ALIST_OFF_DATA)
}
}
return (1);
}
/*
* Load an individual capabilities object.
*/
Rt_map *
{
int found = 0;
/*
* Obtain the sorted list of hardware capabilities objects available.
*/
return (NULL);
/*
* From the list of hardware capability objects, use the first and
* discard the rest.
*/
found++;
}
return (lmp);
}
/*
* Use a case insensitive string match when looking up capability mask
* values by name, and omit the AV_ prefix.
*/
/*
* To aid in the development and testing of capabilities, an alternative system
* capabilities group can be specified. This alternative set is initialized
* from the system capabilities that are normally used to validate all object
* loading. However, the user can disable, enable or override flags within
* this alternative set, and thus affect object loading.
*
* This technique is usually combined with defining the family of objects
* that should be compared against this alternative set. Without defining the
* family of objects, all objects loaded by ld.so.1 are validated against the
* alternative set. This can prevent the loading of critical system objects
* like libc, and thus prevent process execution.
*/
typedef enum {
} cap_mode;
static struct {
/* update */
{ { 0, 0, 0 }, { 0, 0, 0 }, NULL }, /* CA_SUNW_HW_1 */
{ { 0, 0, 0 }, { 0, 0, 0 }, NULL }, /* CA_SUNW_SF_1 */
{ { 0, 0, 0 }, { 0, 0, 0 }, NULL } /* CA_SUNW_HW_2 */
};
static int
{
return (0);
/*
* Determine whether this token should be enabled (+),
* disabled (-), or override any existing settings.
*/
if (*ptr == '+') {
mode = CAP_ENABLE;
ptr++;
} else if (*ptr == '-') {
mode = CAP_DISABLE;
ptr++;
}
/*
* Process the capabilities as directed by the calling tag.
*/
switch (tag) {
case CA_SUNW_HW_1:
/*
* Determine whether the capabilities string matches
* a known hardware capability mask. Note, the caller
* indicates that these are hardware capabilities by
* passing in the CA_SUNW_HW_1 tag. However, the
* tokens could be CA_SUNW_HW_1 or CA_SUNW_HW_2.
*/
ndx = CA_SUNW_HW_2;
break;
}
ndx = CA_SUNW_HW_1;
break;
case CA_SUNW_SF_1:
/*
* Determine whether the capabilities string matches a
* known software capability mask. Note, the callers
* indication of what capabilities to process are
* triggered by a tag of CA_SUNW_SF_1, but the tokens
* processed could be CA_SUNW_SF_1, CA_SUNW_SF_2, etc.
*/
ndx = CA_SUNW_SF_1;
break;
}
/*
* If a capabilities token has not been matched, interpret the
* string as a number. To provide for setting the various
* families (CA_SUNW_HW_1, CA_SUNW_HW_2), the number can be
* prefixed with the (bracketed) family index.
*
* LD_HWCAP=[1]0x40 sets CA_SUNW_HW_1 with 0x40
* LD_HWCAP=[2]0x80 sets CA_SUNW_HW_2 with 0x80
*
* Invalid indexes are ignored.
*/
if (val == 0) {
char *end;
ptr += 3;
if (tag == CA_SUNW_HW_1) {
ndx = CA_SUNW_HW_2;
ptr += 3;
} else {
/* invalid index */
continue;
}
} else {
/* invalid index */
continue;
}
} else
errno = 0;
continue;
/*
* If the value wasn't an entirely valid hexadecimal
* integer, assume it was intended as a capability
* name and skip it.
*/
if (*end != '\0') {
continue;
}
}
}
/*
* If the "override" token was supplied, set the alternative
* system capabilities, then enable or disable others.
*/
}
return (1);
}
/*
* Create an AVL tree of objects that are to be validated against an alternative
* system capabilities value.
*/
static int
{
return (0);
/*
* Determine whether this pathname has already been recorded.
*/
continue;
}
}
return (1);
}
/*
* Set alternative system capabilities. A user can establish alternative system
* capabilities from the environment, or from a configuration file. This
* routine is called in each instance. Environment variables only set the
* replaceable (rpl) variables. Configuration files can set both replaceable
* (rpl) and permanent (prm) variables.
*/
int
cap_alternative(void)
{
/*
* If no capabilities have been set, we're done.
*/
return (1);
/*
* If the user has requested to modify any capabilities, establish a
* unique set from the present system capabilities.
*/
return (0);
*alt_scapset = *org_scapset;
/*
* Process any replaceable variables.
*/
return (0);
return (0);
if (rpl_platcap) {
}
if (rpl_machcap) {
}
return (0);
/*
* Process any permanent variables.
*/
return (0);
return (0);
if (prm_platcap) {
}
if (prm_machcap) {
}
return (0);
/*
* Reset the replaceable variables. If this is the environment variable
* processing, these variables are now available for configuration file
* initialization.
*/
return (1);
}
/*
* Take the index from a Capinfo entry and determine the associated capabilities
* set. Verify that the capabilities are available for this system.
*/
static int
{
/*
* Determine whether this file requires validation against alternative
* system capabilities.
*/
else
/*
* A capabilities index points to a capabilities group that can consist
* of one or more capabilities, terminated with a CA_SUNW_NULL entry.
*/
char *str;
case CA_SUNW_HW_1:
/*
* Remove any historic values that should not be
* involved with any validation.
*/
val &= ~AV_HW1_IGNORE;
capfail++;
break;
case CA_SUNW_SF_1:
capfail++;
break;
case CA_SUNW_HW_2:
capfail++;
break;
case CA_SUNW_PLAT:
/*
* A capabilities set can define multiple platform names
* that are appropriate. Only if all the names are
* deemed invalid is the group determined inappropriate.
*/
totplat++;
ivlplat++;
}
break;
case CA_SUNW_MACH:
/*
* A capabilities set can define multiple machine names
* that are appropriate. Only if all the names are
* deemed invalid is the group determined inappropriate.
*/
totmach++;
ivlmach++;
}
break;
default:
break;
}
}
/*
* If any platform definitions, or machine definitions were found, and
* all were invalid, indicate that the object is inappropriate.
*/
return (0);
}
return (1);
}
/*
* Determine whether a symbols capabilities are more significant than any that
* have already been validated. The precedence of capabilities are:
*
* PLATCAP -> MACHCAP -> HWCAP_2 -> HWCAP_1
*
*
* Presently we make no comparisons of software capabilities. However, should
* this symbol capability have required the SF1_SUNW_ADDR32 attribute, then
* this would have been validated as appropriate or not.
*
* bestcapset is the presently available 'best' capabilities group, and
* symcapset is the present capabilities group under investigation. Return 0
* if the bestcapset should remain in affect, or 1 if the symcapset is better.
*/
inline static int
{
/*
* Check any platform capability. If the new symbol isn't associated
* with a CA_SUNW_PLAT capability, and the best symbol is, then retain
* the best capabilities group. If the new symbol is associated with a
* CA_SUNW_PLAT capability, and the best symbol isn't, then the new
* symbol needs to be taken.
*/
return (0);
return (1);
/*
* Check any machine name capability. If the new symbol isn't
* associated with a CA_SUNW_MACH capability, and the best symbol is,
* then retain the best capabilities group. If the new symbol is
* associated with a CA_SUNW_MACH capability, and the best symbol isn't,
* then the new symbol needs to be taken.
*/
return (0);
return (1);
/*
* Check the hardware capabilities. If the best symbols CA_SUNW_HW_2
* capabilities are greater than the new symbols capabilities, then
* retain the best capabilities group. If the new symbols CA_SUNW_HW_2
* capabilities are greater than the best symbol, then the new symbol
* needs to be taken.
*/
return (0);
return (1);
/*
* Check the remaining hardware capabilities. If the best symbols
* CA_SUNW_HW_1 capabilities are greater than the new symbols
* capabilities, then retain the best capabilities group. If the new
* symbols CA_SUNW_HW_1 capabilities are greater than the best symbol,
* then the new symbol needs to be taken.
*/
return (0);
return (1);
/*
* Both capabilities are the same. Retain the best on a first-come
* first-served basis.
*/
return (0);
}
/*
* Initiate symbol capabilities processing. If an initial symbol lookup
* results in binding to a symbol that has an associated SUNW_capinfo entry,
* we arrive here.
*
* The standard model is that this initial symbol is the lead capabilities
* symbol (defined as CAPINFO_SUNW_GLOB) of a capabilities family. This lead
* symbol's SUNW_capinfo information points to the SUNW_capchain entry that
* provides the family symbol indexes. We traverse this chain, looking at
* each family member, to discover the best capabilities instance. This
* instance name and symbol information is returned to establish the final
* symbol binding.
*
* If the symbol that got us here is not CAPINFO_SUNW_GLOB, then we've bound
* directly to a capabilities symbol which must be verified. This is not the
* model created by ld(1) using -z symbolcap, but might be created directly
* within a relocatable object by the compilation system.
*/
int
{
const char *bname;
/*
* If this symbols capability group is not a lead symbol, then simply
* verify the symbol.
*/
if (grpndx != CAPINFO_SUNW_GLOB) {
}
/*
* If there is no capabilities chain, return the lead symbol.
*/
return (1);
/*
* If there is only one member for this family, take it. Once a family
* has been processed, the best family instance is written to the head
* of the chain followed by a null entry. This caching ensures that the
* same family comparison doesn't have to be undertaken more than once.
*/
return (1);
}
/*
* As this symbol is the lead symbol of a capabilities family, it is
* considered the generic member, and therefore forms the basic
* fall-back for the capabilities family.
*/
/*
* Traverse the capabilities chain analyzing each family member.
*/
if ((grpndx =
continue;
continue;
/*
* Determine whether a symbol's capabilities are more
* significant than any that have already been validated.
*/
}
}
/*
* Having found the best symbol, cache the results by overriding the
* first element of the associated chain.
*/
/*
* Update the symbol result information for return to the user.
*/
return (1);
}