diff options
-rw-r--r-- | README.google | 7 | ||||
-rw-r--r-- | locale/findlocale.c | 74 | ||||
-rw-r--r-- | locale/setlocale.c | 14 |
3 files changed, 80 insertions, 15 deletions
diff --git a/README.google b/README.google index b65b4ee..49ab22c 100644 --- a/README.google +++ b/README.google @@ -581,3 +581,10 @@ resolv/res_send.c under high load (BZ15946, CVE-2013-7423) https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=f9d2d03254a58d92635a311a42253eeed5a40a47 (stanshebs, backport) + +locale/findlocale.c +locale/setlocale.c + For b/29130905, fix locale vulnerabilities (BZ1737, CVE-2014-0475) + https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=4e8f95a0df7c2300b830ec12c0ae1e161bc8a8a3 + https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=d183645616b0533b3acee28f1a95570bffbdf50f + (stanshebs, backport) diff --git a/locale/findlocale.c b/locale/findlocale.c index 0c42b99..faeee61 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/locale/setlocale.c b/locale/setlocale.c index b70fa6c..a4c5983 100644 --- a/locale/setlocale.c +++ b/locale/setlocale.c @@ -272,6 +272,8 @@ setlocale (int category, const char *locale) of entries of the form `CATEGORY=VALUE'. */ const char *newnames[__LC_LAST]; struct __locale_data *newdata[__LC_LAST]; + /* Copy of the locale argument, for in-place splitting. */ + char *locale_copy = NULL; /* Set all name pointers to the argument name. */ for (category = 0; category < __LC_LAST; ++category) @@ -281,7 +283,13 @@ setlocale (int category, const char *locale) if (__builtin_expect (strchr (locale, ';') != NULL, 0)) { /* This is a composite name. Make a copy and split it up. */ - char *np = strdupa (locale); + locale_copy = strdup (locale); + if (__glibc_unlikely (locale_copy == NULL)) + { + __libc_rwlock_unlock (__libc_setlocale_lock); + return NULL; + } + char *np = locale_copy; char *cp; int cnt; @@ -299,6 +307,7 @@ setlocale (int category, const char *locale) { error_return: __libc_rwlock_unlock (__libc_setlocale_lock); + free (locale_copy); /* Bogus category name. */ ERROR_RETURN; @@ -391,8 +400,9 @@ setlocale (int category, const char *locale) /* Critical section left. */ __libc_rwlock_unlock (__libc_setlocale_lock); - /* Free the resources (the locale path variable). */ + /* Free the resources. */ free (locale_path); + free (locale_copy); return composite; } |