Files
Arquivotheca.SunOS-4.1.4/lang/rtld/cache_rtld.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

371 lines
9.9 KiB
C

/* @(#)cache_rtld.c 1.1 94/10/31 SMI */
/*
* ld.so directory caching: run-time link-editor specific functions.
*/
/*
* Copyright (c) 1989, 1991 by Sun Microsystems, Inc.
*/
#include <sys/param.h>
#include "link.h"
#include "../ld/dynamic.h"
#include "rtld.h"
#include "cache.h"
#include <string.h>
static struct dd *get_next_dir();
static char *ask_db();
static char *get_next_name();
static struct dd *new_dd();
static secure_directory();
static struct dd *ddhead = NULL; /* master head of list pointer */
static char *es; /* LD_LIBRARY_PATH cache */
static char *default_dirs[] = /* directories searched by default */
{"/usr/lib", "/usr/local/lib", '\0'};
static char *secure_dirs[] = /* directories we trust */
{"/usr/lib","/usr/local/lib","/usr/5lib", '\0'};
static char **also_secure = NULL; /* directories contributed by main */
extern char *library_path; /* alternate path for library search */
extern struct link_map *mlmp; /* main program's link map */
/*
* Lookup a canonical link_object, return its absolute pathname (or NULL).
*/
char *
lo_lookup(lop, lmp)
struct link_object *lop; /* link object searched for */
struct link_map *lmp; /* link map in which it occurs */
{
int i, j, k; /* working temporaries */
char *r; /* rule strings from main link map */
char *cp; /* working pointer */
char *so = NULL; /* return string */
struct dd *ddp; /* directory loop temporary */
static int once = 1; /* once-only flag */
/*
* Once-only processing. Deal with security issues here. Enable
* the main program to specify additional "trusted" directories
* through it's link map rules.
*
* Historical note: until late in the development of 4.1, the
* LD_LIBRARY_PATH environment variable was simply ignored if
* we were running at setxid program. However, this check was
* redundant, as the elements from the path are checked for trust
* anyway, and it inadvertantly prevented a program from allowing
c * the path variable to influence the search among trusted directories.
* Now, the library path variable is allowed, but its elements are
* ignored if they do not specify trusted directories. This assumes
* that the trust relationship among the directories in the path is
* reflexive (i.e., the ordering contributes nothing to the trust.)
*
* Additional note: "ld" allows relative path names to be put into
* the link rules for an executable. This, in the case of setxid
* programs is simply too much rope for people -- as they blithely
* use relative pathnames in construction but then are surprised
* to find them used again at execution. This obscures the basic
* problem of having relative resources when building a secure
* executable, but it's clear that this is just too much rope for
* people. So, "also secure" directories are only those that are
* absolute.
*/
if (once) {
/*
* If we're secure, and the main link_map has been established,
* then see if it has any embedded rules. If so, copy them
* onto the heap, and make a pass through counting the number
* of directories that are in the list. Then allocate an
* array of char *'s for "also_secure" of the appropriate
* length, and step through the processed string to initialize
* the array. Zero the last element. N.B.: only absolute
* directories (those beginning with "/") are included in
* this list.
*/
if ((*is_secure)() && (mlmp != NULL)) {
r = &TEXTBASE(mlmp)[mlmp->lm_ld->ld_un.ld_2->ld_rules];
if (r != TEXTBASE(mlmp)) {
cp = (*heap_malloc)(strlen(r) + 1);
(void) strcpy(cp, r);
for (r = cp, i = 0; get_next_name(&r); i++)
;
also_secure = (char **)(*heap_malloc)((i + 1) *
sizeof (char *));
for (j = k = 0; j < i; j++) {
if (*cp == '/')
also_secure[k++] = cp;
while (*cp++)
;
}
also_secure[k] = NULL;
}
}
/*
* Security checks finished, set up the path contributed
* by the environment. As noted above, we no longer prune
* this if we're running "secure" -- the lower levels take
* care of it for us.
*/
if (library_path && *library_path) {
es = (*heap_malloc)(strlen(library_path) + 1);
(void) strcpy(es, library_path);
} else
es = (char *)NULL;
once = 0;
}
for (ddp = get_next_dir((struct dd *)NULL, lmp);
ddp; ddp = get_next_dir(ddp, lmp))
if (so = ask_db(ddp->dd_db, TEXTBASE(lmp), lop))
break;
return (so);
}
/*
* Delete the databases that came from the mmapped file and foreach
* dd element reset the database pointer
*/
void
lo_flush()
{
struct dd *ddp; /* working dd pointer */
dbd_flush();
for (ddp = ddhead; ddp; ddp = ddp->dd_next)
ddp->dd_db = lo_cache((char *)&AP(ddp->dd_db)[ddp->dd_db->db_name]);
}
/*
* Given a db - find the highest shared versioned object. The
* highest versioned object is the .so with a matching major number
* but the highest minor number
*/
static char *
ask_db(dbp, base, lop)
struct db *dbp;
caddr_t base;
struct link_object *lop;
{
char *name = (char *)&(base[(int)lop->lo_name]);
struct dbe *ep;
struct link_object *tlop;
int index;
char l[20];
/*
* Search appropriate hash bucket for a matching entry.
*/
index = hash(name, strlen(name), lop->lo_major);
for (ep = (struct dbe *)&(dbp->db_hash[index]); (ep && ep->dbe_lop);
ep = ep->dbe_next == 0 ? NULL :
(struct dbe *)&AP(dbp)[ep->dbe_next]) {
tlop = (struct link_object *)&AP(dbp)[ep->dbe_lop];
if (tlop->lo_major == lop->lo_major)
if (!strcmp((char *)&AP(dbp)[tlop->lo_name], name))
break;
}
/*
* If no entry was found, we've lost.
*/
if (!(ep && ep->dbe_lop))
return (NULL);
sprintf(l, "%d", lop->lo_minor);
if (verscmp(l, &AP(dbp)[ep->dbe_name] + tlop->lo_minor) > 0)
fprintf(stderr,
"ld.so: warning: %s has older revision than expected %d\n",
&AP(dbp)[ep->dbe_name], lop->lo_minor);
return (&AP(dbp)[ep->dbe_name]);
}
/*
* Given a directory descriptor, find the next one in a list. List
* is lazily evaluated, and is sensitive to changes in link maps.
*/
static struct dd *
get_next_dir(ddp, lmp)
struct dd *ddp; /* previous directory (or NULL) */
struct link_map *lmp; /* link map in which it occurs */
{
struct dd *rddp; /* return value */
struct dd **ddpp; /* insertion point temporary */
struct db *dbp; /* points to db */
char *ds; /* directory name string */
static char *ldr; /* link_dynamic ld_rules cache */
static int dhused = 0; /* default list inserted */
static int rsused = 0; /* lmp rules have been copied */
static char *rs; /* lmp rules string */
static char **dcpp = default_dirs;
/* default directory list position */
static struct dd *etddp = NULL;
/* environment tail pointer */
static struct dd *dhddp = NULL; /* defaults head pointer */
static struct link_map *plmp = NULL;
/* previous link map pointer */
/*
* If link map has changed, must discard any old values for
* its entries and start over.
*/
if (plmp != lmp) {
if (etddp)
etddp->dd_next = (struct dd *)NULL;
else
ddhead = 0;
plmp = lmp;
dhused = 0;
rs = NULL;
if ((ldr = &TEXTBASE(lmp)[lmp->lm_ld->ld_un.ld_2->ld_rules])
== TEXTBASE(lmp))
rsused = 1;
else
rsused = 0;
}
/*
* Get the next entry on the list if any. If we're starting
* at the front, set pointers accordingly.
*/
if (ddp) {
rddp = ddp->dd_next;
ddpp = &ddp->dd_next;
} else {
rddp = ddhead;
ddpp = &ddhead;
}
/*
* If we have a block, we're done -- return it.
*/
if (rddp)
return (rddp);
/*
* Ran off the end of the list, have to get another block if
* possible. Try first with the environment.
*/
while (es)
if (ds = get_next_name(&es))
if (secure_directory(ds))
if (dbp = lo_cache(ds))
return (etddp = new_dd(ddpp, dbp));
/*
* Environment is exhausted, search the link_map specific rules.
*/
if (!rsused) {
rs = (*heap_malloc)(strlen(ldr) + 1);
(void) strcpy(rs, ldr);
rsused = 1;
}
while (rs)
if (ds = get_next_name(&rs))
if (secure_directory(ds))
if (dbp = lo_cache(ds))
return (new_dd(ddpp, dbp));
/*
* Environment and link_map specific rules exhausted, now examine
* the default directories. If we've got a head and it has not
* yet been inserted, just use that.
*/
if (dhddp && !dhused) {
*ddpp = dhddp;
dhused = 1;
return (dhddp);
}
/*
* Only hope now is that we have more default directories to use.
*/
while (ds = *dcpp++)
if (secure_directory(ds))
if (dbp = lo_cache(ds)) {
rddp = new_dd(ddpp, dbp);
/*
* dhused = 1 since ddhead is also
* dhddp
*/
if (!dhddp) {
dhddp = rddp;
dhused = 1;
}
return (rddp);
}
/*
* Out of directories, they lose.
*/
return ((struct dd *)NULL);
}
/*
* Extract list of directories needed from colon separated string.
*/
static char *
get_next_name(list)
char **list;
{
char *lp = *list;
char *cp = *list;;
if (lp != NULL && *lp != '\0') {
while (*lp != '\0' && *lp != ':')
lp++;
if (*lp == ':') {
*lp = '\0';
*list = lp + 1;
if (**list == '\0')
*list = NULL;
} else
*list = NULL;
}
return(cp);
}
/*
* Allocate a new dd, append it after ddpp and set the dd_dbp to dbp.
*/
static struct dd *
new_dd(ddpp, dbp)
struct dd **ddpp; /* insertion point */
struct db *dbp; /* db associated with this dd */
{
struct dd *ddp; /* working dd ptr. */
ddp = (struct dd *)(*heap_malloc)(sizeof(struct dd));
ddp->dd_db = dbp;
ddp->dd_next = NULL;
*ddpp = ddp;
return (ddp);
}
/*
* Indicate whether "ds" is a directory we trust. If
* security function indicates we don't care, then just return true.
*/
static int
secure_directory(ds)
char *ds; /* directory string */
{
char **cpp; /* working (char **)string */
if ((*is_secure)()) {
for (cpp = secure_dirs; *cpp; cpp++)
if (strcmp(ds, *cpp) == 0)
return (1);
if (also_secure)
for (cpp = also_secure; *cpp; cpp++)
if (strcmp(ds, *cpp) == 0)
return (1);
return (0);
} else
return (1);
}