diff options
Diffstat (limited to 'patches/glibc-2.20/0005-CVE-2014-8121-Do-not-close-NSS-files-database-during.patch')
-rw-r--r-- | patches/glibc-2.20/0005-CVE-2014-8121-Do-not-close-NSS-files-database-during.patch | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/patches/glibc-2.20/0005-CVE-2014-8121-Do-not-close-NSS-files-database-during.patch b/patches/glibc-2.20/0005-CVE-2014-8121-Do-not-close-NSS-files-database-during.patch new file mode 100644 index 0000000..17861f7 --- /dev/null +++ b/patches/glibc-2.20/0005-CVE-2014-8121-Do-not-close-NSS-files-database-during.patch @@ -0,0 +1,226 @@ +From: Florian Weimer <fweimer@redhat.com> +Date: Wed, 29 Apr 2015 14:41:25 +0200 +Subject: [PATCH] CVE-2014-8121: Do not close NSS files database during + iteration [BZ #18007] +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Robin Hack discovered Samba would enter an infinite loop processing +certain quota-related requests. We eventually tracked this down to a +glibc issue. + +Running a (simplified) test case under strace shows that /etc/passwd +is continuously opened and closed: + +… +open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 3 +lseek(3, 0, SEEK_CUR) = 0 +read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 2717 +lseek(3, 2717, SEEK_SET) = 2717 +close(3) = 0 +open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 3 +lseek(3, 0, SEEK_CUR) = 0 +lseek(3, 0, SEEK_SET) = 0 +read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 2717 +lseek(3, 2717, SEEK_SET) = 2717 +close(3) = 0 +open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 3 +lseek(3, 0, SEEK_CUR) = 0 +… + +The lookup function implementation in +nss/nss_files/files-XXX.c:DB_LOOKUP has code to prevent that. It is +supposed skip closing the input file if it was already open. + + /* Reset file pointer to beginning or open file. */ \ + status = internal_setent (keep_stream); \ + \ + if (status == NSS_STATUS_SUCCESS) \ + { \ + /* Tell getent function that we have repositioned the file pointer. */ \ + last_use = getby; \ + \ + while ((status = internal_getent (result, buffer, buflen, errnop \ + H_ERRNO_ARG EXTRA_ARGS_VALUE)) \ + == NSS_STATUS_SUCCESS) \ + { break_if_match } \ + \ + if (! keep_stream) \ + internal_endent (); \ + } \ + +keep_stream is initialized from the stayopen flag in internal_setent. +internal_setent is called from the set*ent implementation as: + + status = internal_setent (stayopen); + +However, for non-host database, this flag is always 0, per the +STAYOPEN magic in nss/getXXent_r.c. + +Thus, the fix is this: + +- status = internal_setent (stayopen); ++ status = internal_setent (1); + +This is not a behavioral change even for the hosts database (where the +application can specify the stayopen flag) because with a call to +sethostent(0), the file handle is still not closed in the +implementation of gethostent. +--- + nss/Makefile | 2 +- + nss/nss_files/files-XXX.c | 2 +- + nss/tst-nss-getpwent.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 120 insertions(+), 2 deletions(-) + create mode 100644 nss/tst-nss-getpwent.c + +diff --git a/nss/Makefile b/nss/Makefile +index 1fa7f1f397f5..d6f0139bf5c8 100644 +--- a/nss/Makefile ++++ b/nss/Makefile +@@ -39,7 +39,7 @@ install-bin := getent makedb + makedb-modules = xmalloc hash-string + extra-objs += $(makedb-modules:=.o) + +-tests = test-netdb tst-nss-test1 test-digits-dots ++tests = test-netdb tst-nss-test1 test-digits-dots tst-nss-getpwent + xtests = bug-erange + + # Specify rules for the nss_* modules. We have some services. +diff --git a/nss/nss_files/files-XXX.c b/nss/nss_files/files-XXX.c +index 212b938fdf39..cc38174df0c2 100644 +--- a/nss/nss_files/files-XXX.c ++++ b/nss/nss_files/files-XXX.c +@@ -134,7 +134,7 @@ CONCAT(_nss_files_set,ENTNAME) (int stayopen) + + __libc_lock_lock (lock); + +- status = internal_setent (stayopen); ++ status = internal_setent (1); + + if (status == NSS_STATUS_SUCCESS && fgetpos (stream, &position) < 0) + { +diff --git a/nss/tst-nss-getpwent.c b/nss/tst-nss-getpwent.c +new file mode 100644 +index 000000000000..f2e8abce6098 +--- /dev/null ++++ b/nss/tst-nss-getpwent.c +@@ -0,0 +1,118 @@ ++/* Copyright (C) 2015 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 <pwd.h> ++#include <stdbool.h> ++#include <stdio.h> ++#include <stdlib.h> ++#include <string.h> ++ ++int ++do_test (void) ++{ ++ /* Count the number of entries in the password database, and fetch ++ data from the first and last entries. */ ++ size_t count = 0; ++ struct passwd * pw; ++ char *first_name = NULL; ++ uid_t first_uid = 0; ++ char *last_name = NULL; ++ uid_t last_uid = 0; ++ setpwent (); ++ while ((pw = getpwent ()) != NULL) ++ { ++ if (first_name == NULL) ++ { ++ first_name = strdup (pw->pw_name); ++ if (first_name == NULL) ++ { ++ printf ("strdup: %m\n"); ++ return 1; ++ } ++ first_uid = pw->pw_uid; ++ } ++ ++ free (last_name); ++ last_name = strdup (pw->pw_name); ++ if (last_name == NULL) ++ { ++ printf ("strdup: %m\n"); ++ return 1; ++ } ++ last_uid = pw->pw_uid; ++ ++count; ++ } ++ endpwent (); ++ ++ if (count == 0) ++ { ++ printf ("No entries in the password database.\n"); ++ return 0; ++ } ++ ++ /* Try again, this time interleaving with name-based and UID-based ++ lookup operations. The counts do not match if the interleaved ++ lookups affected the enumeration. */ ++ size_t new_count = 0; ++ setpwent (); ++ while ((pw = getpwent ()) != NULL) ++ { ++ if (new_count == count) ++ { ++ printf ("Additional entry in the password database.\n"); ++ return 1; ++ } ++ ++new_count; ++ struct passwd *pw2 = getpwnam (first_name); ++ if (pw2 == NULL) ++ { ++ printf ("getpwnam (%s) failed: %m\n", first_name); ++ return 1; ++ } ++ pw2 = getpwnam (last_name); ++ if (pw2 == NULL) ++ { ++ printf ("getpwnam (%s) failed: %m\n", last_name); ++ return 1; ++ } ++ pw2 = getpwuid (first_uid); ++ if (pw2 == NULL) ++ { ++ printf ("getpwuid (%llu) failed: %m\n", ++ (unsigned long long) first_uid); ++ return 1; ++ } ++ pw2 = getpwuid (last_uid); ++ if (pw2 == NULL) ++ { ++ printf ("getpwuid (%llu) failed: %m\n", ++ (unsigned long long) last_uid); ++ return 1; ++ } ++ } ++ endpwent (); ++ if (new_count < count) ++ { ++ printf ("Missing entry in the password database.\n"); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++#define TEST_FUNCTION do_test () ++#include "../test-skeleton.c" |