diff options
author | Florian Weimer <fweimer@redhat.com> | 2017-06-19 22:31:04 +0200 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2017-06-19 22:46:18 +0200 |
commit | aab04ca5d359150e17631e6a9b44b65e93bdc467 (patch) | |
tree | 90b698c674fd58131664c34e5bc3a8387dd3f8cb | |
parent | 87bd4186da10371f46e2f1a7bf7c0a45bb04f1ac (diff) | |
download | glibc-aab04ca5d359150e17631e6a9b44b65e93bdc467.tar.gz glibc-aab04ca5d359150e17631e6a9b44b65e93bdc467.tar.xz glibc-aab04ca5d359150e17631e6a9b44b65e93bdc467.zip |
ld.so: Reject overly long LD_PRELOAD path elements
(cherry picked from commit 6d0ba622891bed9d8394eef1935add53003b12e8)
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | elf/rtld.c | 82 |
2 files changed, 73 insertions, 16 deletions
diff --git a/ChangeLog b/ChangeLog index e37f14f02a..07c95fe8d0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2017-06-19 Florian Weimer <fweimer@redhat.com> + * elf/rtld.c (SECURE_NAME_LIMIT, SECURE_PATH_LIMIT): Define. + (dso_name_valid_for_suid): New function. + (handle_ld_preload): Likewise. + (dl_main): Call it. Remove alloca. + +2017-06-19 Florian Weimer <fweimer@redhat.com> + [BZ #21624] CVE-2017-1000366 * elf/rtld.c (process_envvars): Ignore LD_LIBRARY_PATH for diff --git a/elf/rtld.c b/elf/rtld.c index 215a9aec8f..1d8eab9fe2 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -99,6 +99,35 @@ uintptr_t __pointer_chk_guard_local strong_alias (__pointer_chk_guard_local, __pointer_chk_guard) #endif +/* Length limits for names and paths, to protect the dynamic linker, + particularly when __libc_enable_secure is active. */ +#ifdef NAME_MAX +# define SECURE_NAME_LIMIT NAME_MAX +#else +# define SECURE_NAME_LIMIT 255 +#endif +#ifdef PATH_MAX +# define SECURE_PATH_LIMIT PATH_MAX +#else +# define SECURE_PATH_LIMIT 1024 +#endif + +/* Check that AT_SECURE=0, or that the passed name does not contain + directories and is not overly long. Reject empty names + unconditionally. */ +static bool +dso_name_valid_for_suid (const char *p) +{ + if (__glibc_unlikely (__libc_enable_secure)) + { + /* Ignore pathnames with directories for AT_SECURE=1 + programs, and also skip overlong names. */ + size_t len = strlen (p); + if (len >= SECURE_NAME_LIMIT || memchr (p, '/', len) != NULL) + return false; + } + return *p != '\0'; +} /* List of auditing DSOs. */ static struct audit_list @@ -730,6 +759,42 @@ static const char *preloadlist attribute_relro; /* Nonzero if information about versions has to be printed. */ static int version_info attribute_relro; +/* The LD_PRELOAD environment variable gives list of libraries + separated by white space or colons 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.) Return the number of preloads + performed. */ +unsigned int +handle_ld_preload (const char *preloadlist, struct link_map *main_map) +{ + unsigned int npreloads = 0; + const char *p = preloadlist; + char fname[SECURE_PATH_LIMIT]; + + while (*p != '\0') + { + /* Split preload list at space/colon. */ + size_t len = strcspn (p, " :"); + if (len > 0 && len < sizeof (fname)) + { + memcpy (fname, p, len); + fname[len] = '\0'; + } + else + fname[0] = '\0'; + + /* Skip over the substring and the following delimiter. */ + p += len; + if (*p != '\0') + ++p; + + if (dso_name_valid_for_suid (fname)) + npreloads += do_preload (fname, main_map, "LD_PRELOAD"); + } + return npreloads; +} + static void dl_main (const ElfW(Phdr) *phdr, ElfW(Word) phnum, @@ -1481,23 +1546,8 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", if (__glibc_unlikely (preloadlist != NULL)) { - /* The LD_PRELOAD environment variable gives list of libraries - separated by white space or colons 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; - HP_TIMING_NOW (start); - - /* Prevent optimizing strsep. Speed is not important here. */ - while ((p = (strsep) (&list, " :")) != NULL) - if (p[0] != '\0' - && (__builtin_expect (! __libc_enable_secure, 1) - || strchr (p, '/') == NULL)) - npreloads += do_preload (p, main_map, "LD_PRELOAD"); - + npreloads += handle_ld_preload (preloadlist, main_map); HP_TIMING_NOW (stop); HP_TIMING_DIFF (diff, start, stop); HP_TIMING_ACCUM_NT (load_time, diff); |