Login
[x]
Log in using an account from:
Fedora Account System
Red Hat Associate
Red Hat Customer
Or login using a Red Hat Bugzilla account
Forgot Password
Login:
Hide Forgot
Create an Account
Red Hat Bugzilla – Attachment 914283 Details for
Bug 1102353
CVE-2014-0475 glibc: directory traversal in LC_* locale handling
[?]
New
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
|
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh83 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
This site requires JavaScript to be enabled to function correctly, please enable it.
[patch]
0002-_nl_find_locale-Improve-handling-of-crafted-locale-n.patch
0002-_nl_find_locale-Improve-handling-of-crafted-locale-n.patch (text/plain), 12.94 KB, created by
Florian Weimer
on 2014-07-02 19:42:36 UTC
(
hide
)
Description:
0002-_nl_find_locale-Improve-handling-of-crafted-locale-n.patch
Filename:
MIME Type:
Creator:
Florian Weimer
Created:
2014-07-02 19:42:36 UTC
Size:
12.94 KB
patch
obsolete
>From 7f3e0956170a0ce04f0ff087f6333419631b0638 Mon Sep 17 00:00:00 2001 >From: Florian Weimer <fweimer@redhat.com> >Date: Mon, 12 May 2014 15:24:12 +0200 >Subject: [PATCH 2/3] _nl_find_locale: Improve handling of crafted locale names > >--- > ChangeLog | 8 ++ > NEWS | 9 ++ > locale/findlocale.c | 74 +++++++++++++--- > localedata/ChangeLog | 6 ++ > localedata/Makefile | 3 +- > localedata/tst-setlocale3.c | 203 ++++++++++++++++++++++++++++++++++++++++++++ > 6 files changed, 289 insertions(+), 14 deletions(-) > create mode 100644 localedata/tst-setlocale3.c > >diff --git a/ChangeLog b/ChangeLog >index 457b821..207597c 100644 >--- a/ChangeLog >+++ b/ChangeLog >@@ -1,5 +1,13 @@ > 2014-07-02 Florian Weimer <fweimer@redhat.com> > >+ * locale/findlocale.c (name_present, valid_locale_name): New >+ functions. >+ (_nl_find_locale): Use the loc_name variable to store name >+ candidates. Call name_present and valid_locale_name to check and >+ validate locale names. Return an error if the locale is invalid. >+ >+2014-07-02 Florian Weimer <fweimer@redhat.com> >+ > * locale/setlocale.c (setlocale): Use strdup for allocating > composite name copy. > >diff --git a/NEWS b/NEWS >index a07ea66..c61d121 100644 >--- a/NEWS >+++ b/NEWS >@@ -65,6 +65,15 @@ Version 2.20 > * All supported architectures now use the main glibc sysdeps directory > instead of some being in a separate "ports" directory (which was > distributed separately before glibc 2.17). >+ >+* Locale names, including those obtained from environment variables (LANG >+ and the LC_* variables), are more tightly checked for proper syntax. >+ setlocale will now fail (with EINVAL) for locale names that are overly >+ long, contain slashes without starting with a slash, or contain ".." path >+ components. (CVE-2014-0475) Previously, some valid locale names were >+ silently replaced with the "C" locale when running in AT_SECURE mode >+ (e.g., in a SUID program). This is no longer necessary because of the >+ additional checks. > > Version 2.19 > >diff --git a/locale/findlocale.c b/locale/findlocale.c >index bbaf708..22e8b53 100644 >--- a/locale/findlocale.c >+++ b/locale/findlocale.c >@@ -17,6 +17,7 @@ > <http://www.gnu.org/licenses/>. */ > > #include <assert.h> >+#include <errno.h> > #include <locale.h> > #include <stdlib.h> > #include <string.h> >@@ -57,6 +58,45 @@ struct loaded_l10nfile *_nl_locale_file_list[__LC_LAST]; > > const char _nl_default_locale_path[] attribute_hidden = LOCALEDIR; > >+/* Checks if the name is actually present, that is, not NULL and not >+ empty. */ >+static inline int >+name_present (const char *name) >+{ >+ return name != NULL && name[0] != '\0'; >+} >+ >+/* Checks that the locale name neither extremely long, nor contains a >+ ".." path component (to prevent directory traversal). */ >+static inline int >+valid_locale_name (const char *name) >+{ >+ /* Not set. */ >+ size_t namelen = strlen (name); >+ /* Name too long. The limit is arbitrary and prevents stack overflow >+ issues later. */ >+ if (__glibc_unlikely (namelen > 255)) >+ return 0; >+ /* Directory traversal attempt. */ >+ static const char slashdot[4] = {'/', '.', '.', '/'}; >+ if (__glibc_unlikely (memmem (name, namelen, >+ slashdot, sizeof (slashdot)) != NULL)) >+ return 0; >+ if (namelen == 2 && __glibc_unlikely (name[0] == '.' && name [1] == '.')) >+ return 0; >+ if (namelen >= 3 >+ && __glibc_unlikely (((name[0] == '.' >+ && name[1] == '.' >+ && name[2] == '/') >+ || (name[namelen - 3] == '/' >+ && name[namelen - 2] == '.' >+ && name[namelen - 1] == '.')))) >+ return 0; >+ /* If there is a slash in the name, it must start with one. */ >+ if (__glibc_unlikely (memchr (name, '/', namelen) != NULL) && name[0] != '/') >+ return 0; >+ return 1; >+} > > struct __locale_data * > internal_function >@@ -65,7 +105,7 @@ _nl_find_locale (const char *locale_path, size_t locale_path_len, > { > int mask; > /* Name of the locale for this category. */ >- char *loc_name; >+ char *loc_name = (char *) *name; > const char *language; > const char *modifier; > const char *territory; >@@ -73,31 +113,39 @@ _nl_find_locale (const char *locale_path, size_t locale_path_len, > const char *normalized_codeset; > struct loaded_l10nfile *locale_file; > >- if ((*name)[0] == '\0') >+ if (loc_name[0] == '\0') > { > /* The user decides which locale to use by setting environment > variables. */ >- *name = getenv ("LC_ALL"); >- if (*name == NULL || (*name)[0] == '\0') >- *name = getenv (_nl_category_names.str >+ loc_name = getenv ("LC_ALL"); >+ if (!name_present (loc_name)) >+ loc_name = getenv (_nl_category_names.str > + _nl_category_name_idxs[category]); >- if (*name == NULL || (*name)[0] == '\0') >- *name = getenv ("LANG"); >+ if (!name_present (loc_name)) >+ loc_name = getenv ("LANG"); >+ if (!name_present (loc_name)) >+ loc_name = (char *) _nl_C_name; > } > >- if (*name == NULL || (*name)[0] == '\0' >- || (__builtin_expect (__libc_enable_secure, 0) >- && strchr (*name, '/') != NULL)) >- *name = (char *) _nl_C_name; >+ /* We used to fall back to the C locale if the name contains a slash >+ character '/', but we now check for directory traversal in >+ valid_locale_name, so this is no longer necessary. */ > >- if (__builtin_expect (strcmp (*name, _nl_C_name), 1) == 0 >- || __builtin_expect (strcmp (*name, _nl_POSIX_name), 1) == 0) >+ if (__builtin_expect (strcmp (loc_name, _nl_C_name), 1) == 0 >+ || __builtin_expect (strcmp (loc_name, _nl_POSIX_name), 1) == 0) > { > /* We need not load anything. The needed data is contained in > the library itself. */ > *name = (char *) _nl_C_name; > return _nl_C[category]; > } >+ else if (!valid_locale_name (loc_name)) >+ { >+ __set_errno (EINVAL); >+ return NULL; >+ } >+ >+ *name = loc_name; > > /* We really have to load some data. First we try the archive, > but only if there was no LOCPATH environment variable specified. */ >diff --git a/localedata/ChangeLog b/localedata/ChangeLog >index 9dd3cf2..c22d279 100644 >--- a/localedata/ChangeLog >+++ b/localedata/ChangeLog >@@ -1,3 +1,9 @@ >+2014-07-02 Florian Weimer <fweimer@redhat.com> >+ >+ * tst-setlocale3.c: New file. >+ * Makefile (tests): Add tst-setlocale3. >+ (tst-setlocale3-ENV): New variable. >+ > 2014-06-20 Stefan Liebler <stli@linux.vnet.ibm.com> > > * Makefile (LOCALES): Add en_GB.UTF-8. >diff --git a/localedata/Makefile b/localedata/Makefile >index e8fe10f..b6235f2 100644 >--- a/localedata/Makefile >+++ b/localedata/Makefile >@@ -74,7 +74,8 @@ locale_test_suite := tst_iswalnum tst_iswalpha tst_iswcntrl \ > tests = $(locale_test_suite) tst-digits tst-setlocale bug-iconv-trans \ > tst-leaks tst-mbswcs1 tst-mbswcs2 tst-mbswcs3 tst-mbswcs4 tst-mbswcs5 \ > tst-mbswcs6 tst-xlocale1 tst-xlocale2 bug-usesetlocale \ >- tst-strfmon1 tst-sscanf bug-setlocale1 tst-setlocale2 tst-wctype >+ tst-strfmon1 tst-sscanf bug-setlocale1 tst-setlocale2 tst-setlocale3 \ >+ tst-wctype > tests-static = bug-setlocale1-static > tests += $(tests-static) > ifeq (yes,$(build-shared)) >diff --git a/localedata/tst-setlocale3.c b/localedata/tst-setlocale3.c >new file mode 100644 >index 0000000..e3b21a9 >--- /dev/null >+++ b/localedata/tst-setlocale3.c >@@ -0,0 +1,203 @@ >+/* Regression test for setlocale invalid environment variable handling. >+ Copyright (C) 2014 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 >+ modify it under the terms of the GNU Lesser General Public >+ License as published by the Free Software Foundation; either >+ version 2.1 of the License, or (at your option) any later version. >+ >+ The GNU C Library is distributed in the hope that it will be useful, >+ but WITHOUT ANY WARRANTY; without even the implied warranty of >+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >+ Lesser General Public License for more details. >+ >+ You should have received a copy of the GNU Lesser General Public >+ License along with the GNU C Library; if not, see >+ <http://www.gnu.org/licenses/>. */ >+ >+#include <locale.h> >+#include <stdio.h> >+#include <stdlib.h> >+#include <string.h> >+ >+/* The result of setlocale may be overwritten by subsequent calls, so >+ this wrapper makes a copy. */ >+static char * >+setlocale_copy (int category, const char *locale) >+{ >+ const char *result = setlocale (category, locale); >+ if (result == NULL) >+ return NULL; >+ return strdup (result); >+} >+ >+static char *de_locale; >+ >+static void >+setlocale_fail (const char *envstring) >+{ >+ setenv ("LC_CTYPE", envstring, 1); >+ if (setlocale (LC_CTYPE, "") != NULL) >+ { >+ printf ("unexpected setlocale success for \"%s\" locale\n", envstring); >+ exit (1); >+ } >+ const char *newloc = setlocale (LC_CTYPE, NULL); >+ if (strcmp (newloc, de_locale) != 0) >+ { >+ printf ("failed setlocale call \"%s\" changed locale to \"%s\"\n", >+ envstring, newloc); >+ exit (1); >+ } >+} >+ >+static void >+setlocale_success (const char *envstring) >+{ >+ setenv ("LC_CTYPE", envstring, 1); >+ char *newloc = setlocale_copy (LC_CTYPE, ""); >+ if (newloc == NULL) >+ { >+ printf ("setlocale for \"%s\": %m\n", envstring); >+ exit (1); >+ } >+ if (strcmp (newloc, de_locale) == 0) >+ { >+ printf ("setlocale with LC_CTYPE=\"%s\" left locale at \"%s\"\n", >+ envstring, de_locale); >+ exit (1); >+ } >+ if (setlocale (LC_CTYPE, de_locale) == NULL) >+ { >+ printf ("restoring locale \"%s\" with LC_CTYPE=\"%s\": %m\n", >+ de_locale, envstring); >+ exit (1); >+ } >+ char *newloc2 = setlocale_copy (LC_CTYPE, newloc); >+ if (newloc2 == NULL) >+ { >+ printf ("restoring locale \"%s\" following \"%s\": %m\n", >+ newloc, envstring); >+ exit (1); >+ } >+ if (strcmp (newloc, newloc2) != 0) >+ { >+ printf ("representation of locale \"%s\" changed from \"%s\" to \"%s\"", >+ envstring, newloc, newloc2); >+ exit (1); >+ } >+ free (newloc); >+ free (newloc2); >+ >+ if (setlocale (LC_CTYPE, de_locale) == NULL) >+ { >+ printf ("restoring locale \"%s\" with LC_CTYPE=\"%s\": %m\n", >+ de_locale, envstring); >+ exit (1); >+ } >+} >+ >+/* Checks that a known-good locale still works if LC_ALL contains a >+ value which should be ignored. */ >+static void >+setlocale_ignore (const char *to_ignore) >+{ >+ const char *fr_locale = "fr_FR.UTF-8"; >+ setenv ("LC_CTYPE", fr_locale, 1); >+ char *expected_locale = setlocale_copy (LC_CTYPE, ""); >+ if (expected_locale == NULL) >+ { >+ printf ("setlocale with LC_CTYPE=\"%s\" failed: %m\n", fr_locale); >+ exit (1); >+ } >+ if (setlocale (LC_CTYPE, de_locale) == NULL) >+ { >+ printf ("failed to restore locale: %m\n"); >+ exit (1); >+ } >+ unsetenv ("LC_CTYPE"); >+ >+ setenv ("LC_ALL", to_ignore, 1); >+ setenv ("LC_CTYPE", fr_locale, 1); >+ const char *actual_locale = setlocale (LC_CTYPE, ""); >+ if (actual_locale == NULL) >+ { >+ printf ("setlocale with LC_ALL, LC_CTYPE=\"%s\" failed: %m\n", >+ fr_locale); >+ exit (1); >+ } >+ if (strcmp (actual_locale, expected_locale) != 0) >+ { >+ printf ("setlocale under LC_ALL failed: got \"%s\", expected \"%s\"\n", >+ actual_locale, expected_locale); >+ exit (1); >+ } >+ unsetenv ("LC_CTYPE"); >+ setlocale_success (fr_locale); >+ unsetenv ("LC_ALL"); >+ free (expected_locale); >+} >+ >+static int >+do_test (void) >+{ >+ /* The glibc test harness sets this environment variable >+ uncondionally. */ >+ unsetenv ("LC_ALL"); >+ >+ de_locale = setlocale_copy (LC_CTYPE, "de_DE.UTF-8"); >+ if (de_locale == NULL) >+ { >+ printf ("setlocale (LC_CTYPE, \"de_DE.UTF-8\"): %m\n"); >+ return 1; >+ } >+ setlocale_success ("C"); >+ setlocale_success ("en_US.UTF-8"); >+ setlocale_success ("/en_US.UTF-8"); >+ setlocale_success ("//en_US.UTF-8"); >+ setlocale_ignore (""); >+ >+ setlocale_fail ("does-not-exist"); >+ setlocale_fail ("/"); >+ setlocale_fail ("/../localedata/en_US.UTF-8"); >+ setlocale_fail ("en_US.UTF-8/"); >+ setlocale_fail ("en_US.UTF-8/.."); >+ setlocale_fail ("en_US.UTF-8/../en_US.UTF-8"); >+ setlocale_fail ("../localedata/en_US.UTF-8"); >+ { >+ size_t large_length = 1024; >+ char *large_name = malloc (large_length + 1); >+ if (large_name == NULL) >+ { >+ puts ("malloc failure"); >+ return 1; >+ } >+ memset (large_name, '/', large_length); >+ const char *suffix = "en_US.UTF-8"; >+ strcpy (large_name + large_length - strlen (suffix), suffix); >+ setlocale_fail (large_name); >+ free (large_name); >+ } >+ { >+ size_t huge_length = 64 * 1024 * 1024; >+ char *huge_name = malloc (huge_length + 1); >+ if (huge_name == NULL) >+ { >+ puts ("malloc failure"); >+ return 1; >+ } >+ memset (huge_name, 'X', huge_length); >+ huge_name[huge_length] = '\0'; >+ /* Construct a composite locale specification. */ >+ const char *prefix = "LC_CTYPE=de_DE.UTF-8;LC_TIME="; >+ memcpy (huge_name, prefix, strlen (prefix)); >+ setlocale_fail (huge_name); >+ free (huge_name); >+ } >+ >+ return 0; >+} >+ >+#define TEST_FUNCTION do_test () >+#include "../test-skeleton.c" >-- >1.9.3 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 1102353
:
914282
| 914283 |
914284