forked from pool/glibc
321 lines
9.2 KiB
Diff
321 lines
9.2 KiB
Diff
|
2011-05-11 Ulrich Drepper <drepper@gmail.com>
|
||
|
|
||
|
[BZ #12393]
|
||
|
* elf/dl-load.c (is_trusted_path): Remove unnecessary test.
|
||
|
(is_trusted_path_normalize): Skip initial colon. Append slash
|
||
|
to empty buffer. Duplicate is_trusted_path code but allow
|
||
|
constructed patch to be prefix.
|
||
|
(is_dst): Allow $ORIGIN followed by /.
|
||
|
(_dl_dst_substitute): Correct clearing of check_for_trusted.
|
||
|
Correct testing of result of is_trusted_path_normalize
|
||
|
(decompose_rpath): Fix warning.
|
||
|
|
||
|
2011-05-07 Petr Baudis <pasky@suse.cz>
|
||
|
Ulrich Drepper <drepper@gmail.com>
|
||
|
|
||
|
[BZ #12393]
|
||
|
* elf/dl-load.c (fillin_rpath): Move trusted path check...
|
||
|
(is_trusted_path): ...to here.
|
||
|
(is_trusted_path_normalize): Wrapper for /../ and /./ normalization.
|
||
|
(_dl_dst_substitute): Verify expanded $ORIGIN path elements
|
||
|
using is_trusted_path_normalize() in setuid scripts.
|
||
|
|
||
|
2011-03-14 Andreas Schwab <schwab@redhat.com>
|
||
|
|
||
|
* elf/dl-load.c (_dl_dst_substitute): When skipping the first
|
||
|
rpath element also skip the following colon.
|
||
|
(expand_dynamic_string_token): Add is_path parameter and pass
|
||
|
down to DL_DST_REQUIRED and _dl_dst_substitute.
|
||
|
(decompose_rpath): Call expand_dynamic_string_token with
|
||
|
non-zero is_path. Ignore empty rpaths.
|
||
|
(_dl_map_object_from_fd): Call expand_dynamic_string_token
|
||
|
with zero is_path.
|
||
|
|
||
|
2011-03-06 Ulrich Drepper <drepper@gmail.com>
|
||
|
|
||
|
* elf/dl-load.c (_dl_map_object): If we are looking for the first
|
||
|
to-be-loaded object along a path to loader is ld.so.
|
||
|
|
||
|
|
||
|
--- glibc-2.13/elf/dl-load.c 2011-05-20 21:53:43.766426054 +0200
|
||
|
+++ glibc-2.14/elf/dl-load.c 2011-05-31 09:59:16.781617374 +0200
|
||
|
@@ -1,5 +1,5 @@
|
||
|
/* Map in a shared object's segments from the file.
|
||
|
- Copyright (C) 1995-2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
|
||
|
+ Copyright (C) 1995-2007, 2009, 2010, 2011 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
|
||
|
@@ -168,6 +168,87 @@ local_strdup (const char *s)
|
||
|
}
|
||
|
|
||
|
|
||
|
+static bool
|
||
|
+is_trusted_path (const char *path, size_t len)
|
||
|
+{
|
||
|
+ const char *trun = system_dirs;
|
||
|
+
|
||
|
+ for (size_t idx = 0; idx < nsystem_dirs_len; ++idx)
|
||
|
+ {
|
||
|
+ if (len == system_dirs_len[idx] && memcmp (trun, path, len) == 0)
|
||
|
+ /* Found it. */
|
||
|
+ return true;
|
||
|
+
|
||
|
+ trun += system_dirs_len[idx] + 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ return false;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+static bool
|
||
|
+is_trusted_path_normalize (const char *path, size_t len)
|
||
|
+{
|
||
|
+ if (len == 0)
|
||
|
+ return false;
|
||
|
+
|
||
|
+ if (*path == ':')
|
||
|
+ {
|
||
|
+ ++path;
|
||
|
+ --len;
|
||
|
+ }
|
||
|
+
|
||
|
+ char *npath = (char *) alloca (len + 2);
|
||
|
+ char *wnp = npath;
|
||
|
+ while (*path != '\0')
|
||
|
+ {
|
||
|
+ if (path[0] == '/')
|
||
|
+ {
|
||
|
+ if (path[1] == '.')
|
||
|
+ {
|
||
|
+ if (path[2] == '.' && (path[3] == '/' || path[3] == '\0'))
|
||
|
+ {
|
||
|
+ while (wnp > npath && *--wnp != '/')
|
||
|
+ ;
|
||
|
+ path += 3;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+ else if (path[2] == '/' || path[2] == '\0')
|
||
|
+ {
|
||
|
+ path += 2;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (wnp > npath && wnp[-1] == '/')
|
||
|
+ {
|
||
|
+ ++path;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ *wnp++ = *path++;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (wnp == npath || wnp[-1] != '/')
|
||
|
+ *wnp++ = '/';
|
||
|
+
|
||
|
+ const char *trun = system_dirs;
|
||
|
+
|
||
|
+ for (size_t idx = 0; idx < nsystem_dirs_len; ++idx)
|
||
|
+ {
|
||
|
+ if (wnp - npath >= system_dirs_len[idx]
|
||
|
+ && memcmp (trun, npath, system_dirs_len[idx]) == 0)
|
||
|
+ /* Found it. */
|
||
|
+ return true;
|
||
|
+
|
||
|
+ trun += system_dirs_len[idx] + 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ return false;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
static size_t
|
||
|
is_dst (const char *start, const char *name, const char *str,
|
||
|
int is_path, int secure)
|
||
|
@@ -200,7 +281,8 @@ is_dst (const char *start, const char *n
|
||
|
return 0;
|
||
|
|
||
|
if (__builtin_expect (secure, 0)
|
||
|
- && ((name[len] != '\0' && (!is_path || name[len] != ':'))
|
||
|
+ && ((name[len] != '\0' && name[len] != '/'
|
||
|
+ && (!is_path || name[len] != ':'))
|
||
|
|| (name != start + 1 && (!is_path || name[-2] != ':'))))
|
||
|
return 0;
|
||
|
|
||
|
@@ -240,13 +322,14 @@ _dl_dst_substitute (struct link_map *l,
|
||
|
int is_path)
|
||
|
{
|
||
|
const char *const start = name;
|
||
|
- char *last_elem, *wp;
|
||
|
|
||
|
/* Now fill the result path. While copying over the string we keep
|
||
|
track of the start of the last path element. When we come accross
|
||
|
a DST we copy over the value or (if the value is not available)
|
||
|
leave the entire path element out. */
|
||
|
- last_elem = wp = result;
|
||
|
+ char *wp = result;
|
||
|
+ char *last_elem = result;
|
||
|
+ bool check_for_trusted = false;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
@@ -265,6 +348,9 @@ _dl_dst_substitute (struct link_map *l,
|
||
|
else
|
||
|
#endif
|
||
|
repl = l->l_origin;
|
||
|
+
|
||
|
+ check_for_trusted = (INTUSE(__libc_enable_secure)
|
||
|
+ && l->l_type == lt_executable);
|
||
|
}
|
||
|
else if ((len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0)
|
||
|
repl = GLRO(dl_platform);
|
||
|
@@ -284,6 +370,10 @@ _dl_dst_substitute (struct link_map *l,
|
||
|
name += len;
|
||
|
while (*name != '\0' && (!is_path || *name != ':'))
|
||
|
++name;
|
||
|
+ /* Also skip following colon if this is the first rpath
|
||
|
+ element, but keep an empty element at the end. */
|
||
|
+ if (wp == result && is_path && *name == ':' && name[1] != '\0')
|
||
|
+ ++name;
|
||
|
}
|
||
|
else
|
||
|
/* No DST we recognize. */
|
||
|
@@ -293,11 +383,28 @@ _dl_dst_substitute (struct link_map *l,
|
||
|
{
|
||
|
*wp++ = *name++;
|
||
|
if (is_path && *name == ':')
|
||
|
- last_elem = wp;
|
||
|
+ {
|
||
|
+ /* In SUID/SGID programs, after $ORIGIN expansion the
|
||
|
+ normalized path must be rooted in one of the trusted
|
||
|
+ directories. */
|
||
|
+ if (__builtin_expect (check_for_trusted, false)
|
||
|
+ && !is_trusted_path_normalize (last_elem, wp - last_elem))
|
||
|
+ wp = last_elem;
|
||
|
+ else
|
||
|
+ last_elem = wp;
|
||
|
+
|
||
|
+ check_for_trusted = false;
|
||
|
+ }
|
||
|
}
|
||
|
}
|
||
|
while (*name != '\0');
|
||
|
|
||
|
+ /* In SUID/SGID programs, after $ORIGIN expansion the normalized
|
||
|
+ path must be rooted in one of the trusted directories. */
|
||
|
+ if (__builtin_expect (check_for_trusted, false)
|
||
|
+ && !is_trusted_path_normalize (last_elem, wp - last_elem))
|
||
|
+ wp = last_elem;
|
||
|
+
|
||
|
*wp = '\0';
|
||
|
|
||
|
return result;
|
||
|
@@ -310,7 +417,7 @@ _dl_dst_substitute (struct link_map *l,
|
||
|
belonging to the map is loaded. In this case the path element
|
||
|
containing $ORIGIN is left out. */
|
||
|
static char *
|
||
|
-expand_dynamic_string_token (struct link_map *l, const char *s)
|
||
|
+expand_dynamic_string_token (struct link_map *l, const char *s, int is_path)
|
||
|
{
|
||
|
/* We make two runs over the string. First we determine how large the
|
||
|
resulting string is and then we copy it over. Since this is no
|
||
|
@@ -321,7 +428,7 @@ expand_dynamic_string_token (struct link
|
||
|
char *result;
|
||
|
|
||
|
/* Determine the number of DST elements. */
|
||
|
- cnt = DL_DST_COUNT (s, 1);
|
||
|
+ cnt = DL_DST_COUNT (s, is_path);
|
||
|
|
||
|
/* If we do not have to replace anything simply copy the string. */
|
||
|
if (__builtin_expect (cnt, 0) == 0)
|
||
|
@@ -335,7 +442,7 @@ expand_dynamic_string_token (struct link
|
||
|
if (result == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
- return _dl_dst_substitute (l, s, result, 1);
|
||
|
+ return _dl_dst_substitute (l, s, result, is_path);
|
||
|
}
|
||
|
|
||
|
|
||
|
@@ -407,33 +514,8 @@ fillin_rpath (char *rpath, struct r_sear
|
||
|
cp[len++] = '/';
|
||
|
|
||
|
/* Make sure we don't use untrusted directories if we run SUID. */
|
||
|
- if (__builtin_expect (check_trusted, 0))
|
||
|
- {
|
||
|
- const char *trun = system_dirs;
|
||
|
- size_t idx;
|
||
|
- int unsecure = 1;
|
||
|
-
|
||
|
- /* All trusted directories must be complete names. */
|
||
|
- if (cp[0] == '/')
|
||
|
- {
|
||
|
- for (idx = 0; idx < nsystem_dirs_len; ++idx)
|
||
|
- {
|
||
|
- if (len == system_dirs_len[idx]
|
||
|
- && memcmp (trun, cp, len) == 0)
|
||
|
- {
|
||
|
- /* Found it. */
|
||
|
- unsecure = 0;
|
||
|
- break;
|
||
|
- }
|
||
|
-
|
||
|
- trun += system_dirs_len[idx] + 1;
|
||
|
- }
|
||
|
- }
|
||
|
-
|
||
|
- if (unsecure)
|
||
|
- /* Simply drop this directory. */
|
||
|
- continue;
|
||
|
- }
|
||
|
+ if (__builtin_expect (check_trusted, 0) && !is_trusted_path (cp, len))
|
||
|
+ continue;
|
||
|
|
||
|
/* See if this directory is already known. */
|
||
|
for (dirp = GL(dl_all_dirs); dirp != NULL; dirp = dirp->next)
|
||
|
@@ -551,13 +633,21 @@ decompose_rpath (struct r_search_path_st
|
||
|
|
||
|
/* Make a writable copy. At the same time expand possible dynamic
|
||
|
string tokens. */
|
||
|
- copy = expand_dynamic_string_token (l, rpath);
|
||
|
+ copy = expand_dynamic_string_token (l, rpath, 1);
|
||
|
if (copy == NULL)
|
||
|
{
|
||
|
errstring = N_("cannot create RUNPATH/RPATH copy");
|
||
|
goto signal_error;
|
||
|
}
|
||
|
|
||
|
+ /* Ignore empty rpaths. */
|
||
|
+ if (*copy == 0)
|
||
|
+ {
|
||
|
+ free (copy);
|
||
|
+ sps->dirs = (struct r_search_path_elem **) -1;
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
/* Count the number of necessary elements in the result array. */
|
||
|
nelems = 0;
|
||
|
for (cp = copy; *cp != '\0'; ++cp)
|
||
|
@@ -2109,7 +2201,9 @@ _dl_map_object (struct link_map *loader,
|
||
|
{
|
||
|
#ifdef SHARED
|
||
|
// XXX Correct to unconditionally default to namespace 0?
|
||
|
- l = loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded;
|
||
|
+ l = (loader
|
||
|
+ ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded
|
||
|
+ ?: &GL(dl_rtld_map));
|
||
|
#else
|
||
|
l = loader;
|
||
|
#endif
|
||
|
@@ -2175,7 +2269,7 @@ _dl_map_object (struct link_map *loader,
|
||
|
{
|
||
|
/* The path may contain dynamic string tokens. */
|
||
|
realname = (loader
|
||
|
- ? expand_dynamic_string_token (loader, name)
|
||
|
+ ? expand_dynamic_string_token (loader, name, 0)
|
||
|
: local_strdup (name));
|
||
|
if (realname == NULL)
|
||
|
fd = -1;
|