Init
This commit is contained in:
370
lang/rtld/cache_rtld.c
Normal file
370
lang/rtld/cache_rtld.c
Normal file
@@ -0,0 +1,370 @@
|
||||
/* @(#)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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user