diff options
Diffstat (limited to 'elf')
-rw-r--r-- | elf/Makefile | 13 | ||||
-rw-r--r-- | elf/dl-error.c | 33 | ||||
-rw-r--r-- | elf/dl-load.c | 70 | ||||
-rw-r--r-- | elf/ldd.bash.in | 58 | ||||
-rw-r--r-- | elf/ldd.sh.in | 54 | ||||
-rw-r--r-- | elf/link.h | 20 | ||||
-rw-r--r-- | elf/rtld.c | 124 |
7 files changed, 297 insertions, 75 deletions
diff --git a/elf/Makefile b/elf/Makefile index 8f98cb8406..87f2d0d67d 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -41,6 +41,8 @@ extra-libs = libdl extra-libs-others = $(extra-libs) libdl-routines := dlopen dlclose dlsym dlerror dladdr +before-compile = $(objpfx)trusted-dirs.h + all: # Make this the default target; it will be defined in Rules. @@ -92,6 +94,17 @@ $(objpfx)$(rtld-installed-name): $(objpfx)ld.so ln -s $(<F) $@ endif +# Build a file mentioning all trustworthy directories to look for shared +# libraries when using LD_LIBRARY_PATH in a setuid program. The user can +# add directories to the list by defining $(user-defined-trusted-dirs) +# before starting make. +$(objpfx)trusted-dirs.h: Makefile + (for dir in `echo "$(default-rpath) $(user-defined-trusted-dirs)" | \ + sed 's/:/ /g'`; do \ + echo " \"$$dir\","; \ + done;) > $@T + mv -f $@T $@ +CFLAGS-dl-load.c = -I$(objdir)/$(subdir) # Specify the dependencies of libdl.so; its commands come from the generic # rule to build a shared library. diff --git a/elf/dl-error.c b/elf/dl-error.c index 52eb577eb0..a19ccff626 100644 --- a/elf/dl-error.c +++ b/elf/dl-error.c @@ -37,6 +37,11 @@ struct catch this is null. */ static struct catch *catch; +/* This points to a function which is called when an error is + received. Unlike the handling of `catch' this function may return. + The arguments will be the `errstring' and `objname'. */ +static receiver_fct receiver; + void _dl_signal_error (int errcode, @@ -58,6 +63,13 @@ _dl_signal_error (int errcode, catch->objname = objname; longjmp (catch->env, errcode ?: -1); } + else if (receiver) + { + /* We are inside _dl_receive_error. Call the user supplied + handler and resume the work. The receiver will still + installed. */ + (*receiver) (errstring, objname); + } else { /* Lossage while resolving the program's own symbols is always fatal. */ @@ -77,6 +89,8 @@ _dl_catch_error (char **errstring, { int errcode; struct catch *old, c = { errstring: NULL, objname: NULL }; + /* We need not handle `receiver' since setting a `catch' is handle + before it. */ old = catch; errcode = setjmp (c.env); @@ -96,3 +110,22 @@ _dl_catch_error (char **errstring, *objname = c.objname; return errcode == -1 ? 0 : errcode; } + +void +_dl_receive_error (receiver_fct fct, void (*operate) (void)) +{ + struct catch *old_catch; + receiver_fct old_receiver; + + old_catch = catch; + old_receiver = receiver; + + /* Set the new values. */ + catch = NULL; + receiver = fct; + + (*operate) (); + + catch = old_catch; + receiver = old_receiver; +} diff --git a/elf/dl-load.c b/elf/dl-load.c index 7dc6d91a02..6a3d919976 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -1,5 +1,5 @@ /* _dl_map_object -- Map in a shared object's segments from the file. - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -409,7 +409,8 @@ _dl_map_object_from_fd (char *name, int fd, char *realname, static int open_path (const char *name, size_t namelen, const char *dirpath, - char **realname) + char **realname, + const char *trusted_dirs[]) { char *buf; const char *p; @@ -426,13 +427,42 @@ open_path (const char *name, size_t namelen, do { size_t buflen; + size_t this_len; dirpath = p; p = strpbrk (dirpath, ":;"); if (p == NULL) p = strchr (dirpath, '\0'); - if (p == dirpath) + this_len = p - dirpath; + + /* When we run a setuid program we do not accept any directory. */ + if (__libc_enable_secure) + { + /* All trusted directory must be complete name. */ + if (dirpath[0] != '/') + continue; + + /* If we got a list of trusted directories only accept one + of these. */ + if (trusted_dirs != NULL) + { + const char **trust = trusted_dirs; + + while (*trust != NULL) + if (memcmp (dirpath, *trust, this_len) == 0 + && (*trust)[this_len] == '\0') + break; + else + ++trust; + + /* If directory is not trusted, ignore this directory. */ + if (*trust == NULL) + continue; + } + } + + if (this_len == 0) { /* Two adjacent colons, or a colon at the beginning or the end of the path means to search the current directory. */ @@ -442,10 +472,10 @@ open_path (const char *name, size_t namelen, else { /* Construct the pathname to try. */ - (void) memcpy (buf, dirpath, p - dirpath); - buf[p - dirpath] = '/'; - (void) memcpy (&buf[(p - dirpath) + 1], name, namelen); - buflen = p - dirpath + 1 + namelen; + (void) memcpy (buf, dirpath, this_len); + buf[this_len] = '/'; + (void) memcpy (&buf[this_len + 1], name, namelen); + buflen = this_len + 1 + namelen; } fd = __open (buf, O_RDONLY); @@ -508,9 +538,9 @@ _dl_map_object (struct link_map *loader, const char *name, int type, size_t namelen = strlen (name) + 1; - inline void trypath (const char *dirpath) + inline void trypath (const char *dirpath, const char *trusted[]) { - fd = open_path (name, namelen, dirpath, &realname); + fd = open_path (name, namelen, dirpath, &realname, trusted); } fd = -1; @@ -521,16 +551,24 @@ _dl_map_object (struct link_map *loader, const char *name, int type, if (l && l->l_info[DT_RPATH]) trypath ((const char *) (l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr + - l->l_info[DT_RPATH]->d_un.d_val)); + l->l_info[DT_RPATH]->d_un.d_val), NULL); /* If dynamically linked, try the DT_RPATH of the executable itself. */ l = _dl_loaded; if (fd == -1 && l && l->l_type != lt_loaded && l->l_info[DT_RPATH]) trypath ((const char *) (l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr + - l->l_info[DT_RPATH]->d_un.d_val)); + l->l_info[DT_RPATH]->d_un.d_val), NULL); /* Try an environment variable (unless setuid). */ if (fd == -1 && ! __libc_enable_secure) - trypath (getenv ("LD_LIBRARY_PATH")); + { + static const char *trusted_dirs[] = + { +#include "trusted-dirs.h" + NULL + }; + + trypath (getenv ("LD_LIBRARY_PATH"), trusted_dirs); + } if (fd == -1) { /* Check the list of libraries in the file /etc/ld.so.cache, @@ -555,7 +593,7 @@ _dl_map_object (struct link_map *loader, const char *name, int type, if (fd == -1) { extern const char *_dl_rpath; /* Set in rtld.c. */ - trypath (_dl_rpath); + trypath (_dl_rpath, NULL); } } else @@ -590,6 +628,7 @@ _dl_map_object (struct link_map *loader, const char *name, int type, are only interested in the list of libraries this isn't so severe. Fake an entry with all the information we have (in fact only the name). */ + static const ElfW(Symndx) dummy_bucket = STN_UNDEF; /* Enter the new object in the list of loaded objects. */ if ((name_copy = local_strdup (name)) == NULL @@ -599,6 +638,11 @@ _dl_map_object (struct link_map *loader, const char *name, int type, /* We use an opencount of 0 as a sign for the faked entry. */ l->l_opencount = 0; l->l_reserved = 0; + l->l_buckets = &dummy_bucket; + l->l_nbuckets = 1; + l->l_relocated = 1; + + return l; } else _dl_signal_error (errno, name, "cannot open shared object file"); diff --git a/elf/ldd.bash.in b/elf/ldd.bash.in index 6e2b51591a..d174709380 100644 --- a/elf/ldd.bash.in +++ b/elf/ldd.bash.in @@ -29,23 +29,39 @@ TEXTDOMAIN=libc TEXTDOMAINDIR=@TEXTDOMAINDIR@ RTLD=@RTLD@ +RELOCS= while test $# -gt 0; do case "$1" in - --v*) + --v | --ve | --ver | --vers | --versi | --versio | --version) echo $"ldd (GNU libc) @VERSION@ Copyright (C) 1996, 1997 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." exit 0 ;; - --h*) + --h | --he | --hel | --help) echo $"ldd [OPTION]... FILE... - --help print this help and exit - --version print version information and exit + --help print this help and exit + --version print version information and exit + -d, --data-relocs process data relocations + -r, --function-relocs process data and function relocations Report bugs using the \`glibcbug' script to <bugs@gnu.ai.mit.edu>." exit 0 ;; + -d | --d | --da | --dat | --data | --data- | --data-r | --data-re | \ + --data-rel | --data-relo | --data-reloc | --data-relocs) + RELOCS='--data-relocs' + shift ;; + -r | --f | --fu | --fun | --func | --funct | --functi | --functio | \ + --function | --function- | --function-r | --function-re | --function-rel | \ + --function-relo | --function-reloc | --function-relocs) + RELOCS='--function-relocs' + shift ;; --) # Stop option processing. shift; break ;; + -*) + echo >&2 $"ldd: unrecognized option" "\`$1'" + echo >&2 $"Try \`ldd --help' for more information." + exit 1 ;; *) break ;; esac @@ -53,9 +69,8 @@ done case $# in 0) - echo >&2 $"\ -ldd: missing file arguments -Try \`ldd --help' for more information." + echo >&2 $"ldd: missing file arguments" + echo >&2 $"Try \`ldd --help' for more information." exit 1 ;; 1) # We don't list the file name when there is only one. @@ -65,14 +80,21 @@ Try \`ldd --help' for more information." esac if test ! -f "$file"; then echo "${file}:" $"no such file" - elif ${RTLD} --verify "$file"; then - LD_TRACE_LOADED_OBJECTS=1 exec ${RTLD} "$file" && exit 1 + exit 1 else - echo $" not a dynamic executable" + test -x "$file" || + echo $"warning: you do not have execution permission for" "\`$file'" + if ${RTLD} --verify "$file"; then + LD_TRACE_LOADED_OBJECTS=1 exec ${RTLD} ${RELOCS} "$file" || exit 1 + else + echo $" not a dynamic executable" + exit 1 + fi fi exit ;; *) set -e # Bail out immediately if ${RTLD} loses on any argument. + result=0 for file; do echo "${file}:" case "$file" in @@ -80,16 +102,22 @@ Try \`ldd --help' for more information." *) file="./$file" ;; esac if test ! -f "$file"; then - echo "$file:" $"no such file" - elif ${RTLD} --verify "$file"; then - LD_TRACE_LOADED_OBJECTS=1 ${RTLD} "$file" + echo "${file}:" $"no such file" + result=1 else - echo $" not a dynamic executable" + test -x "$file" || + echo $"warning: you do not have execution permission for" "\`$file'" + if ${RTLD} --verify "$file"; then + LD_TRACE_LOADED_OBJECTS=1 ${RTLD} ${RELOCS} "$file" || result=1 + else + echo $" not a dynamic executable" + result=1 + fi fi done esac -exit 0 +exit $result # Local Variables: # mode:ksh # End: diff --git a/elf/ldd.sh.in b/elf/ldd.sh.in index 5b6cc3a75d..4351578b6d 100644 --- a/elf/ldd.sh.in +++ b/elf/ldd.sh.in @@ -25,23 +25,40 @@ # variable LD_TRACE_LOADED_OBJECTS to a non-empty value. RTLD=@RTLD@ +RELOCS= while test $# -gt 0; do case "$1" in - --v*) + --v | --ve | --ver | --vers | --versi | --versio | --version) echo 'ldd (GNU libc) @VERSION@ Copyright (C) 1996, 1997 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.' exit 0 ;; - --h*) + --h | --he | --hel | --help) echo "ldd [OPTION]... FILE... - --help print this help and exit - --version print version information and exit + --help print this help and exit + --version print version information and exit + -d, --data-relocs process data relocations + -r, --function-relocs process data and function relocations Report bugs using the \`glibcbug' script to <bugs@gnu.ai.mit.edu>." exit 0 ;; + -d | --d | --da | --dat | --data | --data- | --data-r | --data-re | \ + --data-rel | --data-relo | --data-reloc | --data-relocs) + RELOCS='--data-relocs' + shift ;; + -r | --f | --fu | --fun | --func | --funct | --functi | --functio | \ + --function | --function- | --function-r | --function-re | --function-rel | \ + --function-relo | --function-reloc | --function-relocs) + RELOCS='--function-relocs' + shift ;; --) # Stop option processing. shift; break ;; + -*) + echo >&2 "\ +ldd: unrecognized option \`$1' +Try \`ldd --help' for more information." + exit 1 ;; *) break ;; esac @@ -61,14 +78,21 @@ Try \`ldd --help' for more information." esac if test ! -f "$file"; then echo "${file}: no such file" - elif ${RTLD} --verify "$file"; then - LD_TRACE_LOADED_OBJECTS=1 exec ${RTLD} "$file" && exit 1 + exit 1 else - echo ' not a dynamic executable' + test -x "$file" || + echo "warning: you do not have execution permission for \`$file'" + if ${RTLD} --verify "$file"; then + LD_TRACE_LOADED_OBJECTS=1 exec ${RTLD} ${RELOCS} "$file" || exit 1 + else + echo ' not a dynamic executable' + exit 1 + fi fi exit ;; *) set -e # Bail out immediately if ${RTLD} loses on any argument. + result=0 for file; do echo "${file}:" case "$file" in @@ -76,13 +100,19 @@ Try \`ldd --help' for more information." *) file="./$file" ;; esac if test ! -f "$file"; then - echo "$file: no such file" - elif ${RTLD} --verify "$file"; then - LD_TRACE_LOADED_OBJECTS=1 ${RTLD} "$file" + echo "${file}: no such file" + result=1 else - echo ' not a dynamic executable' + test -x "$file" || + echo "warning: you do not have execution permission for \`$file'" + if ${RTLD} --verify "$file"; then + LD_TRACE_LOADED_OBJECTS=1 ${RTLD} ${RELOCS} "$file" || result=1 + else + echo ' not a dynamic executable' + result=1 + fi fi done esac -exit 0 +exit $result diff --git a/elf/link.h b/elf/link.h index 7e0b60793f..95d8f0912d 100644 --- a/elf/link.h +++ b/elf/link.h @@ -137,6 +137,12 @@ struct link_map unsigned int l_global:1; /* Nonzero if object in _dl_global_scope. */ unsigned int l_reserved:2; /* Reserved for internal use. */ }; + + +/* Function used as argument for `_dl_receive_error' function. The + arguments are the error string and the objname the error occurred + in. */ +typedef void (*receiver_fct) (const char *, const char *); /* Internal functions of the run-time dynamic linker. These can be accessed if you link again the dynamic linker @@ -161,6 +167,11 @@ extern int _dl_sysdep_open_zero_fill (void); /* dl-sysdep.c */ are concatenated to form the message to print. */ extern void _dl_sysdep_message (const char *string, ...); +/* OS-dependent function to write a message on the standard error. + All arguments are `const char *'; args until a null pointer + are concatenated to form the message to print. */ +extern void _dl_sysdep_error (const char *string, ...); + /* OS-dependent function to give a fatal error message and exit when the dynamic linker fails before the program is fully linked. All arguments are `const char *'; args until a null pointer @@ -177,11 +188,9 @@ extern int _dl_secure; zero; OBJECT is the name of the problematical shared object, or null if it is a general problem; ERRSTRING is a string describing the specific problem. */ - extern void _dl_signal_error (int errcode, const char *object, - const char *errstring) - __attribute__ ((__noreturn__)); + const char *errstring); /* Call OPERATE, catching errors from `dl_signal_error'. If there is no error, *ERRSTRING is set to null. If there is an error, *ERRSTRING and @@ -192,6 +201,11 @@ extern int _dl_catch_error (char **errstring, const char **object, void (*operate) (void)); +/* Call OPERATE, receiving errors from `dl_signal_error'. Unlike + `_dl_catch_error' the operation is resumed after the OPERATE + function returns. */ +extern void _dl_receive_error (receiver_fct fct, void (*operate) (void)); + /* Helper function for <dlfcn.h> functions. Runs the OPERATE function via _dl_catch_error. Returns zero for success, nonzero for failure; and diff --git a/elf/rtld.c b/elf/rtld.c index 39435f8243..f9a2cd3d03 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -44,6 +44,10 @@ extern void *_dl_sysdep_read_whole_file (const char *filename, size_t *filesize_ptr, int mmap_prot); +/* Helper function to handle errors while resolving symbols. */ +static void print_unresolved (const char *errstring, const char *objname); + + int _dl_argc; char **_dl_argv; const char *_dl_rpath; @@ -142,11 +146,19 @@ dl_main (const ElfW(Phdr) *phdr, enum { normal, list, verify, trace } mode; struct link_map **preloads; unsigned int npreloads; + const char *preloadlist; size_t file_size; char *file; mode = getenv ("LD_TRACE_LOADED_OBJECTS") != NULL ? trace : normal; + /* LAZY is determined by the parameters --datadeps and --function-deps + if we trace the binary. */ + if (mode == trace) + lazy = -1; + else + lazy = !__libc_enable_secure && *(getenv ("LD_BIND_NOW") ?: "") == '\0'; + /* Set up a flag which tells we are just starting. */ _dl_starting_up = 1; @@ -186,22 +198,44 @@ of this helper program; chances are you did not intend to run this program.\n", /* Note the place where the dynamic linker actually came from. */ _dl_rtld_map.l_name = _dl_argv[0]; - if (! strcmp (_dl_argv[1], "--list")) - { - mode = list; + while (_dl_argc > 1) + if (! strcmp (_dl_argv[1], "--list")) + { + mode = list; + lazy = -1; /* This means do no dependency analysis. */ - ++_dl_skip_args; - --_dl_argc; - ++_dl_argv; - } - else if (! strcmp (_dl_argv[1], "--verify")) - { - mode = verify; + ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } + else if (! strcmp (_dl_argv[1], "--verify")) + { + mode = verify; - ++_dl_skip_args; - --_dl_argc; - ++_dl_argv; - } + ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } + else if (! strcmp (_dl_argv[1], "--data-relocs")) + { + mode = trace; + lazy = 1; /* This means do only data relocation analysis. */ + + ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } + else if (! strcmp (_dl_argv[1], "--function-relocs")) + { + mode = trace; + lazy = 0; /* This means do also function relocation analysis. */ + + ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } + else + break; ++_dl_skip_args; --_dl_argc; @@ -311,23 +345,22 @@ of this helper program; chances are you did not intend to run this program.\n", preloads = NULL; npreloads = 0; - if (! __libc_enable_secure) + preloadlist = getenv ("LD_PRELOAD"); + if (preloadlist) { - const char *preloadlist = getenv ("LD_PRELOAD"); - if (preloadlist) - { - /* The LD_PRELOAD environment variable gives a white space - separated list of libraries that are loaded before the - executable's dependencies and prepended to the global - scope list. */ - char *list = strdupa (preloadlist); - char *p; - while ((p = strsep (&list, " ")) != NULL) - { - (void) _dl_map_object (NULL, p, lt_library, 0); - ++npreloads; - } - } + /* The LD_PRELOAD environment variable gives a white space + separated list of libraries that are loaded before the + executable's dependencies and prepended to the global scope + list. If the binary is running setuid all elements + containing a '/' are ignored since it is insecure. */ + char *list = strdupa (preloadlist); + char *p; + while ((p = strsep (&list, " ")) != NULL) + if (! __libc_enable_secure || strchr (p, '/') == NULL) + { + (void) _dl_map_object (NULL, p, lt_library, 0); + ++npreloads; + } } /* Read the contents of the file. */ @@ -496,12 +529,31 @@ of this helper program; chances are you did not intend to run this program.\n", *--bp = '0'; _dl_sysdep_message (" in object at 0x", bp, "\n", NULL); } + else if (lazy >= 0) + { + /* We have to do symbol dependency testing. */ + void doit (void) + { + _dl_relocate_object (l, _dl_object_relocation_scope (l), lazy); + } + + l = _dl_loaded; + while (l->l_next) + l = l->l_next; + do + { + if (l != &_dl_rtld_map && l->l_opencount > 0) + { + _dl_receive_error (print_unresolved, doit); + *_dl_global_scope_end = NULL; + } + l = l->l_prev; + } while (l); + } _exit (0); } - lazy = !__libc_enable_secure && *(getenv ("LD_BIND_NOW") ?: "") == '\0'; - { /* Now we have all the objects loaded. Relocate them all except for the dynamic linker itself. We do this in reverse order so that copy @@ -573,3 +625,11 @@ of this helper program; chances are you did not intend to run this program.\n", /* Once we return, _dl_sysdep_start will invoke the DT_INIT functions and then *USER_ENTRY. */ } + +/* This is a little helper function for resolving symbols while + tracing the binary. */ +static void +print_unresolved (const char *errstring, const char *objname) +{ + _dl_sysdep_error (errstring, " (", objname, ")\n", NULL); +} |