From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 102891 invoked by alias); 6 Jun 2018 05:03:03 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Received: (qmail 102869 invoked by uid 89); 6 Jun 2018 05:03:02 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-24.9 required=5.0 tests=AWL,BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,RCVD_IN_DNSWL_NONE,URIBL_RED autolearn=ham version=3.3.2 spammy=greedy, belonging X-HELO: mail-io0-f170.google.com X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:to:from:subject:message-id:date:user-agent :mime-version:content-language; bh=GMDLlcyyXrROPXQ6nG0Ec+gYap+Q+XrWCB4jsdI7OvI=; b=ERI6nHxAuTPotHTb1KKkxb0sgmKBhiwmK+dINQ8jMzZlAgoGXDMfElHRVlTedelWxJ qHiuPTLmIoGfJx/ttONxm9hpuP3av4v5/Sjs2UWyuCVl5XGBgTbWlCO9cd0XoeCmC6l+ jqs5NYPDu1yJ4g/ULix0a20sKbNUs+FpXXn5/E1UtH/W/Sa87SPtO/4GMITVcsx8hVp1 c6w2SkEDJ6qJE6Mvy5rVPYdMv4Wbvqy6hJqQ7dDNx3Dx6Y8rGgq7EpgMzJ6505KVLyQ9 JzJRRm/XuiZmN2kK5bAfd9yefTJNf+IF9//URvXxBr/j3j973XZgP5MSb6qszGDSW5iS qjcg== X-Gm-Message-State: APt69E0G5f93gHIVj+GjGpdB61B59kWnkFtopNVU2enZzXEgURfAtvXm jBwkN7i+o1O+HKjhndhVIu/zSw== X-Google-Smtp-Source: ADUXVKIwGZwMbrhk8kFQjgN78CUatse0QWJE3SalEhKpF+UboOSVCOXP1mMKXQAzSBnuUpM9RF7pww== X-Received: by 2002:a6b:290a:: with SMTP id p10-v6mr1403672iop.1.1528261375628; Tue, 05 Jun 2018 22:02:55 -0700 (PDT) To: GNU C Library , Andreas Schwab , "Dmitry V. Levin" , Florian Weimer From: Carlos O'Donell Subject: [PATCH] Improve DST handling (Bug 23102, Bug 21942, Bug 18018, Bug, 23259, CVE-2011-0536 ). Message-ID: <9cf43cb6-511c-ec6c-9a87-e89a467238d9@redhat.com> Date: Wed, 06 Jun 2018 05:03:00 -0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.8.0 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------EC84823A755D780531E32B12" X-SW-Source: 2018-06/txt/msg00073.txt.bz2 This is a multi-part message in MIME format. --------------EC84823A755D780531E32B12 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Content-length: 15102 This commit improves DST handling significantly in the following ways: firstly is_dst () is overhauled to correctly process DST sequences that would be accepted given the ELF gABI. This means that we actually now accept slightly more sequences than before. Now we accept $ORIGIN$ORIGIN, but in the past we accepted only $ORIGIN\0 or $ORIGIN/..., but this kind of behaviour results in unexpected and uninterpreted DST sequences being used as literal search paths leading to security defects. Therefore the first step in correcting this defect is making is_dst () properly account for all DSTs and making the function context free in the sense that it counts DSTs without knowledge of path, or AT_SECURE. Next, _dl_dst_count () is also simplified to count all DSTs regardless of context. Then in _dl_dst_substitute () we reintroduce context-dependent processing for such things as AT_SECURE handling. At the level of _dl_dst_substitute we can have access to things like the true start of the string sequence to validate $ORIGIN-based paths rooted in trusted directories. Lastly, callers of _dl_dst_substitute () are adjusted to pass in the start of their string sequences, this includes expand_dynamic_string_token () and fillin_rpath (). Verified with a sequence of 47 tests on x86_64 that cover non-AT_SECURE and AT_SECURE testing using a sysroot (requires root to run). The tests cover cases for bug 23102, bug 21942, bug 18018, and bug 23259. These tests are not yet appropriate for the glibc regression testsuite, but with the upcoming test-in-container testing framework it should be possible to include these tests upstream soon. See the mailing list for the tests: [insert final URL of post containing reference to swbz23259.tar.gz (test cases)] OK to checkin? --- ChangeLog | 20 +++++++ NEWS | 10 ++++ elf/dl-deps.c | 2 +- elf/dl-dst.h | 3 +- elf/dl-load.c | 141 +++++++++++++++++++++++++++++++-------------- sysdeps/generic/ldsodefs.h | 5 +- 6 files changed, 133 insertions(+), 48 deletions(-) diff --git a/ChangeLog b/ChangeLog index a3bc2bf31e..5ece817a39 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2018-06-06 Carlos O'Donell + Andreas Schwab + Dmitry V. Levin + + [BZ #23102] + [BZ #21942] + [BZ #18018] + [BZ #23259] + CVE-2011-0536 + * elf/dl-load.c (is_dst): Comment. Support ELF gABI. + (_dl_dst_count): Comment. Simplify and count DSTs. + (_dl_dst_substitute): Comment. Support __libc_enable_secure handling. + Add string start to arguments. + (expand_dybamic_string_token): Comment. Accept path start. + (fillin_rpath): Pass string start to expand_dynamic_string_token. + * sysdeps/generic/ldsodefs.h: _dl_dst_substitute takes additiional + string start argument. + * elf/dl-deps.c (expand_dst): Adjust call to _dl_dst_substitute. + * elf/dl-dst.h: Fix comment. + 2018-06-05 Joseph Myers * sysdeps/unix/sysv/linux/aarch64/bits/hwcap.h (HWCAP_DIT): New diff --git a/NEWS b/NEWS index e2a6f45121..0d0bc9ad4c 100644 --- a/NEWS +++ b/NEWS @@ -41,6 +41,16 @@ Major new features: NI_IDN_ALLOW_UNASSIGNED, NI_IDN_USE_STD3_ASCII_RULES) have been deprecated. They no longer have any effect. +* Parsing of dynamic string tokens in DT_RPATH, DT_RUNPATH, and DT_NEEDED + has been expanded to support the full range of ELF gABI expressions + including such constructs as '$ORIGIN$ORIGIN' (if valid). For SUID/GUID + applications the rules have been further restricted, and where in the + past a dynamic string token sequence may have been interpreted as a + literal string it will now cause a load failure. These load failures + were always considered unspecified behaviour from the perspective of the + dynamic loader, and for safety are now load errors e.g. /foo/${ORIGIN}.so + in DT_NEEDED results in a load failure now. + Deprecated and removed features, and other changes affecting compatibility: * The nonstandard header files and <_G_config.h> are no longer diff --git a/elf/dl-deps.c b/elf/dl-deps.c index c975fcffd7..2ec434a1ff 100644 --- a/elf/dl-deps.c +++ b/elf/dl-deps.c @@ -114,7 +114,7 @@ DST not allowed in SUID/SGID programs")); \ __newp = (char *) alloca (DL_DST_REQUIRED (l, __str, strlen (__str), \ __dst_cnt)); \ \ - __result = _dl_dst_substitute (l, __str, __newp); \ + __result = _dl_dst_substitute (l, __str, __str, __newp); \ \ if (*__result == '\0') \ { \ diff --git a/elf/dl-dst.h b/elf/dl-dst.h index 32de5d225a..ee7254f3c3 100644 --- a/elf/dl-dst.h +++ b/elf/dl-dst.h @@ -18,8 +18,7 @@ #include "trusted-dirs.h" -/* Determine the number of DST elements in the name. Only if IS_PATH is - nonzero paths are recognized (i.e., multiple, ':' separated filenames). */ +/* Determine the number of DST elements in the name. */ #define DL_DST_COUNT(name) \ ({ \ size_t __cnt = 0; \ diff --git a/elf/dl-load.c b/elf/dl-load.c index 431236920f..13263212d5 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -177,63 +177,89 @@ is_trusted_path_normalize (const char *path, size_t len) return false; } +/* Given a substring starting at NAME, just after the DST '$' start + token, determine if NAME contains dynamic string token STR, + following the ELF gABI rules for dynamic string tokens: + * Longest possible sequence using the rules (greedy). + + * Must start with a $ (enforced by caller). + + * Must follow $ with one underscore or ASCII [A-Za-z] (enforced by + caller via STR comparison) or '{' (start curly quoted name). + + * Must follow first two characters with zero or more [A-Za-z0-9_] + (enforced by caller) or '}' (end curly quoted name). + + If the sequence is a dynamic string token matching STR then + the length of the DST is returned, otherwise 0. */ static size_t -is_dst (const char *start, const char *name, const char *str, int secure) +is_dst (const char *name, const char *str) { - size_t len; + size_t nlen, slen; bool is_curly = false; + /* Is a ${...} name sequence? */ if (name[0] == '{') { is_curly = true; ++name; } - len = 0; - while (name[len] == str[len] && name[len] != '\0') - ++len; + /* Find longest valid name sequence. */ + nlen = 0; + while ((name[nlen] >= 'A' && name[nlen] <= 'Z') + || (name[nlen] >= 'a' && name[nlen] <= 'z') + || (name[nlen] >= '0' && name[nlen] <= '9') + || (name[nlen] == '_')) + ++nlen; + + slen = strlen (str); + + /* Can't be the DST we are looking for. */ + if (slen != nlen) + return 0; if (is_curly) { - if (name[len] != '}') + /* Invalid curly sequence! */ + if (name[nlen] != '}') return 0; /* Point again at the beginning of the name. */ --name; - /* Skip over closing curly brace and adjust for the --name. */ - len += 2; + /* Count the two curly braces. */ + nlen += 2; } - else if (name[len] != '\0' && name[len] != '/') - return 0; - if (__glibc_unlikely (secure) - && ((name[len] != '\0' && name[len] != '/') - || (name != start + 1))) - return 0; - - return len; + return nlen; } - +/* Passed the start of a DST sequence at the first '$' occurrence. + See the DL_DST_COUNT macro which inlines the strchr to find the + first occurrence of '$' and optimizes that likely case that there + is no DST. If there is a DST we call into _dl_dst_count to count + the number of DSTs. We count all known DSTs regardless of + __libc_enable_secure; the caller is responsible for enforcing + the security of the substitution rules (usually + _dl_dst_substitute). */ size_t _dl_dst_count (const char *name) { - const char *const start = name; size_t cnt = 0; do { size_t len; - /* $ORIGIN is not expanded for SUID/GUID programs (except if it - is $ORIGIN alone) and it must always appear first in path. */ ++name; - if ((len = is_dst (start, name, "ORIGIN", __libc_enable_secure)) != 0 - || (len = is_dst (start, name, "PLATFORM", 0)) != 0 - || (len = is_dst (start, name, "LIB", 0)) != 0) + /* All DSTs must follow ELF gABI rules, see is_dst (). */ + if ((len = is_dst (name, "ORIGIN")) != 0 + || (len = is_dst (name, "PLATFORM")) != 0 + || (len = is_dst (name, "LIB")) != 0) ++cnt; + /* There may be more than one DST in the sequence. */ name = strchr (name + len, '$'); } while (name != NULL); @@ -241,12 +267,14 @@ _dl_dst_count (const char *name) return cnt; } - +/* Process NAME for DSTs and store in RESULT using the information from + link map L to resolve the DSTs. The value of START must equal the + start of the parent string if NAME is a substring sequence being + parsed with path separators e.g. $ORIGIN:$PLATFORM. */ char * -_dl_dst_substitute (struct link_map *l, const char *name, char *result) +_dl_dst_substitute (struct link_map *l, const char *start, + const char *name, char *result) { - const char *const start = name; - /* Now fill the result path. While copying over the string we keep track of the start of the last path element. When we come across a DST we copy over the value or (if the value is not available) @@ -263,15 +291,36 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result) size_t len; ++name; - if ((len = is_dst (start, name, "ORIGIN", __libc_enable_secure)) != 0) + if ((len = is_dst (name, "ORIGIN")) != 0) { - repl = l->l_origin; + /* For SUID/GUID programs we normally ignore the path with + a DST in DT_RUNPATH, or DT_RPATH. However, there is one + exception to this rule, and it is: + + * $ORIGIN appears first in the path element. + * The path element is rooted in a trusted directory. + + This exception allows such programs to reference + shared libraries in subdirectories of trusted + directories. The use case is one of general + organization and deployment flexibility. + Trusted directories are usually such paths as "/lib64" + or "/lib". */ + if (__glibc_unlikely (__libc_enable_secure) + && ((name[len] != '\0' && name[len] != '/' + && name[len] != ':') + || (name != start + 1 + || (name > start + 2 && name[-2] != ':')))) + repl = (const char *) -1; + else + repl = l->l_origin; + check_for_trusted = (__libc_enable_secure && l->l_type == lt_executable); } - else if ((len = is_dst (start, name, "PLATFORM", 0)) != 0) + else if ((len = is_dst (name, "PLATFORM")) != 0) repl = GLRO(dl_platform); - else if ((len = is_dst (start, name, "LIB", 0)) != 0) + else if ((len = is_dst (name, "LIB")) != 0) repl = DL_DST_LIB; if (repl != NULL && repl != (const char *) -1) @@ -283,6 +332,7 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result) { /* We cannot use this path element, the value of the replacement is unknown. */ + check_for_trusted = false; wp = last_elem; break; } @@ -309,13 +359,17 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result) } -/* Return copy of argument with all recognized dynamic string tokens - ($ORIGIN and $PLATFORM for now) replaced. On some platforms it - might not be possible to determine the path from which the object - belonging to the map is loaded. In this case the path element - containing $ORIGIN is left out. */ +/* Return a malloc allocated copy of NAME with all recognized DSTs + ($ORIGIN and $PLATFORM for now) replaced. The value of START must + equal the start of the parent string if NAME is a substring + sequence being parsed with path separators e.g. $ORIGIN:$PLATFORM. + On some platforms it might not be possible to determine the path + from which the object belonging to the map is loaded. In this case + the path element containing $ORIGIN is left out. On error NULL is + returned. */ static char * -expand_dynamic_string_token (struct link_map *l, const char *s) +expand_dynamic_string_token (struct link_map *l, const char *start, + const char *name) { /* 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 @@ -326,21 +380,21 @@ expand_dynamic_string_token (struct link_map *l, const char *s) char *result; /* Determine the number of DST elements. */ - cnt = DL_DST_COUNT (s); + cnt = DL_DST_COUNT (name); /* If we do not have to replace anything simply copy the string. */ if (__glibc_likely (cnt == 0)) - return __strdup (s); + return __strdup (name); /* Determine the length of the substituted string. */ - total = DL_DST_REQUIRED (l, s, strlen (s), cnt); + total = DL_DST_REQUIRED (l, name, strlen (name), cnt); /* Allocate the necessary memory. */ result = (char *) malloc (total + 1); if (result == NULL) return NULL; - return _dl_dst_substitute (l, s, result); + return _dl_dst_substitute (l, start, name, result); } @@ -387,6 +441,7 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep, const char *what, const char *where, struct link_map *l) { char *cp; + char *start = rpath; size_t nelems = 0; while ((cp = __strsep (&rpath, sep)) != NULL) @@ -398,7 +453,7 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep, /* `strsep' can pass an empty string. */ if (*cp != '\0') { - to_free = cp = expand_dynamic_string_token (l, cp); + to_free = cp = expand_dynamic_string_token (l, start, cp); /* expand_dynamic_string_token can return NULL in case of empty path or memory allocation failure. */ @@ -2091,7 +2146,7 @@ _dl_map_object (struct link_map *loader, const char *name, { /* The path may contain dynamic string tokens. */ realname = (loader - ? expand_dynamic_string_token (loader, name) + ? expand_dynamic_string_token (loader, name, name) : __strdup (name)); if (realname == NULL) fd = -1; diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 95dc87519b..688ff60785 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -1108,8 +1108,9 @@ extern const char *_dl_get_origin (void) attribute_hidden; extern size_t _dl_dst_count (const char *name) attribute_hidden; /* Substitute DST values. */ -extern char *_dl_dst_substitute (struct link_map *l, const char *name, - char *result) attribute_hidden; +extern char *_dl_dst_substitute (struct link_map *l, const char *start, + const char *name, char *result) + attribute_hidden; /* Open the shared object NAME, relocate it, and run its initializer if it hasn't already been run. MODE is as for `dlopen' (see ). If -- 2.14.3 --------------EC84823A755D780531E32B12 Content-Type: application/gzip; name="swbz23259.tar.gz" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="swbz23259.tar.gz" Content-length: 5800 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 AA== --------------EC84823A755D780531E32B12--