]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Avoid symlink attack in localcharset module. Use fcntl_h.m4 from gnulib.
authorBruno Haible <bruno@clisp.org>
Sun, 18 Oct 2009 15:17:48 +0000 (17:17 +0200)
committerBruno Haible <bruno@clisp.org>
Sun, 18 Oct 2009 15:17:48 +0000 (17:17 +0200)
15 files changed:
ChangeLog
Makefile.am
PACKAGING
gettext-runtime/intl/ChangeLog
gettext-runtime/intl/localcharset.c
gettext-runtime/m4/ChangeLog
gettext-runtime/m4/Makefile.am
gettext-runtime/m4/fcntl_h.m4 [new file with mode: 0644]
gettext-runtime/m4/intl.m4
gettext-tools/doc/ChangeLog
gettext-tools/doc/gettext.texi
gettext-tools/m4/ChangeLog
gettext-tools/m4/Makefile.am
gettext-tools/misc/ChangeLog
gettext-tools/misc/gettextize.in

index a5db476f76eeb6ee7284e5c66d363b96a7d5a5b2..3a56eb5bc91f6be9ff3bdbd3c37e8e1dcf2da4cd 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2009-10-18  Bruno Haible  <bruno@clisp.org>
+
+       * Makefile.am (distcheck-hook): Compare fcntl_h.m4.
+       * PACKAGING: Mention also fcntl_h.m4.
+
 2009-09-27  Bruno Haible  <bruno@clisp.org>
 
        * NEWS: Mention configure options --without-cvs and --with-git.
index 03ef081a8e6b01b1da8fa742ae457bc9b20bcc9b..6b5449b303e22a85db26472bf9c622f754cbddbe 100644 (file)
@@ -43,6 +43,7 @@ distcheck-hook:
        cmp -s gettext-runtime/po/remove-potcdate.sin gettext-tools/po/remove-potcdate.sin
        cmp -s gettext-runtime/po/remove-potcdate.sin gettext-tools/examples/po/remove-potcdate.sin
        cmp -s gettext-runtime/m4/codeset.m4 gettext-tools/gnulib-m4/codeset.m4
+       cmp -s gettext-runtime/m4/fcntl_h.m4 gettext-tools/gnulib-m4/fcntl_h.m4
        cmp -s gettext-runtime/m4/gettext.m4 gettext-tools/gnulib-m4/gettext.m4
        cmp -s gettext-runtime/m4/glibc2.m4 gettext-tools/gnulib-m4/glibc2.m4
        cmp -s gettext-runtime/m4/glibc21.m4 gettext-tools/gnulib-m4/glibc21.m4
index 3e7772b615a1ed1313ad0189d2421e3cdfbf3e70..1ba83831cc6fe3b633a880e7b76fbd2bbb6412b6 100644 (file)
--- a/PACKAGING
+++ b/PACKAGING
@@ -117,6 +117,7 @@ following file list.
       $prefix/share/gettext/archive.git.tar.gz   (only installed if --with-git specified)
       $prefix/share/gettext/archive.dir.tar.gz   (only installed if --without-cvs specified)
       $prefix/share/aclocal/codeset.m4
+      $prefix/share/aclocal/fcntl_h.m4
       $prefix/share/aclocal/gettext.m4
       $prefix/share/aclocal/glibc2.m4
       $prefix/share/aclocal/glibc21.m4
index 0fefa04d2ce0218540584ed5dd5eeabe55615e2a..6dd1b7f847389652d370fd2e48bfd38a1fafdf1a 100644 (file)
@@ -1,3 +1,11 @@
+2009-10-18  Bruno Haible  <bruno@clisp.org>
+
+       Avoid symlink attack in localcharset module.
+       * localcharset.c: Include <fcntl.h>, <unistd.h>.
+       (O_NOFOLLOW): Define fallback.
+       (get_charset_aliases): Don't open the file if it is a symbolic link.
+       Reported by Fergal Glynn <fglynn@veracode.com>.
+
 2009-08-20  Eric Blake  <ebb9@byu.net>
 
        * vasnprintf.c (decimal_point_char): Avoid warning on old-style
index 434fc7c16208f36d0a59c9e9d9614cf65145f091..e808967989395f79ef71a55702ea775bcf34e9da 100644 (file)
@@ -24,6 +24,7 @@
 /* Specification.  */
 #include "localcharset.h"
 
+#include <fcntl.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <string.h>
@@ -45,6 +46,7 @@
 #endif
 
 #if !defined WIN32_NATIVE
+# include <unistd.h>
 # if HAVE_LANGINFO_CODESET
 #  include <langinfo.h>
 # else
 # include "configmake.h"
 #endif
 
+/* Define O_NOFOLLOW to 0 on platforms where it does not exist.  */
+#ifndef O_NOFOLLOW
+# define O_NOFOLLOW 0
+#endif
+
 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
   /* Win32, Cygwin, OS/2, DOS */
 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
@@ -118,7 +125,6 @@ get_charset_aliases (void)
   if (cp == NULL)
     {
 #if !(defined DARWIN7 || defined VMS || defined WIN32_NATIVE || defined __CYGWIN__)
-      FILE *fp;
       const char *dir;
       const char *base = "charset.alias";
       char *file_name;
@@ -144,77 +150,105 @@ get_charset_aliases (void)
          }
       }
 
-      if (file_name == NULL || (fp = fopen (file_name, "r")) == NULL)
-       /* Out of memory or file not found, treat it as empty.  */
+      if (file_name == NULL)
+       /* Out of memory.  Treat the file as empty.  */
        cp = "";
       else
        {
-         /* Parse the file's contents.  */
-         char *res_ptr = NULL;
-         size_t res_size = 0;
-
-         for (;;)
+         int fd;
+
+         /* Open the file.  Reject symbolic links on platforms that support
+            O_NOFOLLOW.  This is a security feature.  Without it, an attacker
+            could retrieve parts of the contents (namely, the tail of the
+            first line that starts with "* ") of an arbitrary file by placing
+            a symbolic link to that file under the name "charset.alias" in
+            some writable directory and defining the environment variable
+            CHARSETALIASDIR to point to that directory.  */
+         fd = open (file_name,
+                    O_RDONLY | (HAVE_WORKING_O_NOFOLLOW ? O_NOFOLLOW : 0));
+         if (fd < 0)
+           /* File not found.  Treat it as empty.  */
+           cp = "";
+         else
            {
-             int c;
-             char buf1[50+1];
-             char buf2[50+1];
-             size_t l1, l2;
-             char *old_res_ptr;
-
-             c = getc (fp);
-             if (c == EOF)
-               break;
-             if (c == '\n' || c == ' ' || c == '\t')
-               continue;
-             if (c == '#')
-               {
-                 /* Skip comment, to end of line.  */
-                 do
-                   c = getc (fp);
-                 while (!(c == EOF || c == '\n'));
-                 if (c == EOF)
-                   break;
-                 continue;
-               }
-             ungetc (c, fp);
-             if (fscanf (fp, "%50s %50s", buf1, buf2) < 2)
-               break;
-             l1 = strlen (buf1);
-             l2 = strlen (buf2);
-             old_res_ptr = res_ptr;
-             if (res_size == 0)
+             FILE *fp;
+
+             fp = fdopen (fd, "r");
+             if (fp == NULL)
                {
-                 res_size = l1 + 1 + l2 + 1;
-                 res_ptr = (char *) malloc (res_size + 1);
+                 /* Out of memory.  Treat the file as empty.  */
+                 close (fd);
+                 cp = "";
                }
              else
                {
-                 res_size += l1 + 1 + l2 + 1;
-                 res_ptr = (char *) realloc (res_ptr, res_size + 1);
+                 /* Parse the file's contents.  */
+                 char *res_ptr = NULL;
+                 size_t res_size = 0;
+
+                 for (;;)
+                   {
+                     int c;
+                     char buf1[50+1];
+                     char buf2[50+1];
+                     size_t l1, l2;
+                     char *old_res_ptr;
+
+                     c = getc (fp);
+                     if (c == EOF)
+                       break;
+                     if (c == '\n' || c == ' ' || c == '\t')
+                       continue;
+                     if (c == '#')
+                       {
+                         /* Skip comment, to end of line.  */
+                         do
+                           c = getc (fp);
+                         while (!(c == EOF || c == '\n'));
+                         if (c == EOF)
+                           break;
+                         continue;
+                       }
+                     ungetc (c, fp);
+                     if (fscanf (fp, "%50s %50s", buf1, buf2) < 2)
+                       break;
+                     l1 = strlen (buf1);
+                     l2 = strlen (buf2);
+                     old_res_ptr = res_ptr;
+                     if (res_size == 0)
+                       {
+                         res_size = l1 + 1 + l2 + 1;
+                         res_ptr = (char *) malloc (res_size + 1);
+                       }
+                     else
+                       {
+                         res_size += l1 + 1 + l2 + 1;
+                         res_ptr = (char *) realloc (res_ptr, res_size + 1);
+                       }
+                     if (res_ptr == NULL)
+                       {
+                         /* Out of memory. */
+                         res_size = 0;
+                         if (old_res_ptr != NULL)
+                           free (old_res_ptr);
+                         break;
+                       }
+                     strcpy (res_ptr + res_size - (l2 + 1) - (l1 + 1), buf1);
+                     strcpy (res_ptr + res_size - (l2 + 1), buf2);
+                   }
+                 fclose (fp);
+                 if (res_size == 0)
+                   cp = "";
+                 else
+                   {
+                     *(res_ptr + res_size) = '\0';
+                     cp = res_ptr;
+                   }
                }
-             if (res_ptr == NULL)
-               {
-                 /* Out of memory. */
-                 res_size = 0;
-                 if (old_res_ptr != NULL)
-                   free (old_res_ptr);
-                 break;
-               }
-             strcpy (res_ptr + res_size - (l2 + 1) - (l1 + 1), buf1);
-             strcpy (res_ptr + res_size - (l2 + 1), buf2);
-           }
-         fclose (fp);
-         if (res_size == 0)
-           cp = "";
-         else
-           {
-             *(res_ptr + res_size) = '\0';
-             cp = res_ptr;
            }
-       }
 
-      if (file_name != NULL)
-       free (file_name);
+         free (file_name);
+       }
 
 #else
 
index 8d699b9419c7f973cad4d5bd6bf67c5b77be73a9..f854b705adc6e949d9afcdc3bdd30b9584104c3f 100644 (file)
@@ -1,3 +1,9 @@
+2009-10-18  Bruno Haible  <bruno@clisp.org>
+
+       * fcntl_h.m4: New file, from gnulib.
+       * intl.m4 (AM_INTL_SUBDIR): Require gl_FCNTL_O_FLAGS.
+       * Makefile.am (EXTRA_DIST): Add fcntl_h.m4.
+
 2009-08-14  Bruno Haible  <bruno@clisp.org>
 
        * eoverflow.m4: Remove file. Obsoleted by gnulib's 'errno' module.
index 5dcfc68270669e33d7427c260426e791e5680c1d..ccf0f2e442dc2448b02343ae517b185e47941d9e 100644 (file)
@@ -5,6 +5,7 @@
 EXTRA_DIST = README \
 ansi-c++.m4 \
 codeset.m4 \
+fcntl_h.m4 \
 gettext.m4 \
 glibc2.m4 \
 glibc21.m4 \
diff --git a/gettext-runtime/m4/fcntl_h.m4 b/gettext-runtime/m4/fcntl_h.m4
new file mode 100644 (file)
index 0000000..223fa48
--- /dev/null
@@ -0,0 +1,108 @@
+# serial 6
+# Configure fcntl.h.
+dnl Copyright (C) 2006, 2007, 2009 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Written by Paul Eggert.
+
+AC_DEFUN([gl_FCNTL_H],
+[
+  AC_REQUIRE([gl_FCNTL_H_DEFAULTS])
+  AC_REQUIRE([gl_FCNTL_O_FLAGS])
+  gl_CHECK_NEXT_HEADERS([fcntl.h])
+  FCNTL_H='fcntl.h'
+  AC_SUBST([FCNTL_H])
+])
+
+# Test whether the flags O_NOATIME and O_NOFOLLOW actually work.
+# Define HAVE_WORKING_O_NOATIME to 1 if O_NOATIME works, or to 0 otherwise.
+# Define HAVE_WORKING_O_NOFOLLOW to 1 if O_NOFOLLOW works, or to 0 otherwise.
+AC_DEFUN([gl_FCNTL_O_FLAGS],
+[
+  dnl Persuade glibc <fcntl.h> to define O_NOATIME and O_NOFOLLOW.
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+  AC_CACHE_CHECK([for working fcntl.h], [gl_cv_header_working_fcntl_h],
+    [AC_RUN_IFELSE(
+       [AC_LANG_PROGRAM(
+         [[#include <sys/types.h>
+          #include <sys/stat.h>
+          #include <unistd.h>
+          #include <fcntl.h>
+          #ifndef O_NOATIME
+           #define O_NOATIME 0
+          #endif
+          #ifndef O_NOFOLLOW
+           #define O_NOFOLLOW 0
+          #endif
+          static int const constants[] =
+           {
+             O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC, O_APPEND,
+             O_NONBLOCK, O_SYNC, O_ACCMODE, O_RDONLY, O_RDWR, O_WRONLY
+           };
+         ]],
+         [[
+           int status = !constants;
+           {
+             static char const sym[] = "conftest.sym";
+             if (symlink (".", sym) != 0
+                 || close (open (sym, O_RDONLY | O_NOFOLLOW)) == 0)
+               status |= 32;
+             unlink (sym);
+           }
+           {
+             static char const file[] = "confdefs.h";
+             int fd = open (file, O_RDONLY | O_NOATIME);
+             char c;
+             struct stat st0, st1;
+             if (fd < 0
+                 || fstat (fd, &st0) != 0
+                 || sleep (1) != 0
+                 || read (fd, &c, 1) != 1
+                 || close (fd) != 0
+                 || stat (file, &st1) != 0
+                 || st0.st_atime != st1.st_atime)
+               status |= 64;
+           }
+           return status;]])],
+       [gl_cv_header_working_fcntl_h=yes],
+       [case $? in #(
+       32) gl_cv_header_working_fcntl_h='no (bad O_NOFOLLOW)';; #(
+       64) gl_cv_header_working_fcntl_h='no (bad O_NOATIME)';; #(
+       96) gl_cv_header_working_fcntl_h='no (bad O_NOATIME, O_NOFOLLOW)';; #(
+        *) gl_cv_header_working_fcntl_h='no';;
+       esac],
+       [gl_cv_header_working_fcntl_h=cross-compiling])])
+
+  case $gl_cv_header_working_fcntl_h in #(
+  *O_NOATIME* | no | cross-compiling) ac_val=0;; #(
+  *) ac_val=1;;
+  esac
+  AC_DEFINE_UNQUOTED([HAVE_WORKING_O_NOATIME], [$ac_val],
+    [Define to 1 if O_NOATIME works.])
+
+  case $gl_cv_header_working_fcntl_h in #(
+  *O_NOFOLLOW* | no | cross-compiling) ac_val=0;; #(
+  *) ac_val=1;;
+  esac
+  AC_DEFINE_UNQUOTED([HAVE_WORKING_O_NOFOLLOW], [$ac_val],
+    [Define to 1 if O_NOFOLLOW works.])
+])
+
+AC_DEFUN([gl_FCNTL_MODULE_INDICATOR],
+[
+  dnl Use AC_REQUIRE here, so that the default settings are expanded once only.
+  AC_REQUIRE([gl_FCNTL_H_DEFAULTS])
+  GNULIB_[]m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./-],[ABCDEFGHIJKLMNOPQRSTUVWXYZ___])=1
+])
+
+AC_DEFUN([gl_FCNTL_H_DEFAULTS],
+[
+  GNULIB_OPEN=0;    AC_SUBST([GNULIB_OPEN])
+  GNULIB_OPENAT=0;  AC_SUBST([GNULIB_OPENAT])
+  dnl Assume proper GNU behavior unless another module says otherwise.
+  HAVE_OPENAT=1;    AC_SUBST([HAVE_OPENAT])
+  REPLACE_OPEN=0;   AC_SUBST([REPLACE_OPEN])
+  REPLACE_OPENAT=0; AC_SUBST([REPLACE_OPENAT])
+])
index 2b446d2cd4375cf3e09db2c034cda3dc6f6985ee..a0325c3de6111ae6d86ef300117ea77532a4e3fb 100644 (file)
@@ -1,4 +1,4 @@
-# intl.m4 serial 13 (gettext-0.18)
+# intl.m4 serial 14 (gettext-0.18)
 dnl Copyright (C) 1995-2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -40,6 +40,7 @@ AC_DEFUN([AM_INTL_SUBDIR],
   AC_REQUIRE([gt_PRINTF_POSIX])
   AC_REQUIRE([gl_GLIBC21])dnl
   AC_REQUIRE([gl_XSIZE])dnl
+  AC_REQUIRE([gl_FCNTL_O_FLAGS])dnl
   AC_REQUIRE([gt_INTL_MACOSX])dnl
 
   dnl Support for automake's --enable-silent-rules.
index 017622960d391e150179e8cf0bab2275638a6c06..fa0e163112b202f1807b28f6352b016d5210f402 100644 (file)
@@ -1,3 +1,7 @@
+2009-10-18  Bruno Haible  <bruno@clisp.org>
+
+       * gettext.texi (aclocal): Add fcntl_h.m4 to the file list.
+
 2009-09-20  Bruno Haible  <bruno@clisp.org>
 
        * gettext.texi (src/Makefile): Update recommendations for autoconf
index b09bcbadbe3d6b613ac747a6b17ac86c7f1c2e92..68ac5caca2cfcacfd8a9eaf6653a5d81177b0064 100644 (file)
@@ -7934,7 +7934,7 @@ automake 1.9.
 @cindex @file{aclocal.m4} file
 
 If you do not have an @file{aclocal.m4} file in your distribution,
-the simplest is to concatenate the files @file{codeset.m4},
+the simplest is to concatenate the files @file{codeset.m4}, @file{fcntl_h.m4},
 @file{gettext.m4}, @file{glibc2.m4}, @file{glibc21.m4}, @file{iconv.m4},
 @file{intdiv0.m4}, @file{intl.m4}, @file{intldir.m4}, @file{intlmacosx.m4},
 @file{intmax.m4}, @file{inttypes_h.m4}, @file{inttypes-pri.m4},
index dfbf8f5677412d95df3753b3e590784fb213c980..0923c53cc96fa3d4f045010d8f66c839f0972cb0 100644 (file)
@@ -1,3 +1,7 @@
+2009-10-18  Bruno Haible  <bruno@clisp.org>
+
+       * Makefile.am (aclocal_DATA): Add fcntl_h.m4.
+
 2009-08-15  Bruno Haible  <bruno@clisp.org>
 
        Stop using gnulib module 'strdup'.
index 99a11e958a60c54bc5e30e383f3be4223854661e..fe6795a56566b92c913951e284c9d2dbad864e72 100644 (file)
@@ -8,6 +8,7 @@ aclocal_DATA = \
   ../../autoconf-lib-link/m4/lib-link.m4 \
   ../../autoconf-lib-link/m4/lib-prefix.m4 \
   ../../gettext-runtime/m4/codeset.m4 \
+  ../../gettext-runtime/m4/fcntl_h.m4 \
   ../../gettext-runtime/m4/gettext.m4 \
   ../../gettext-runtime/m4/glibc2.m4 \
   ../../gettext-runtime/m4/glibc21.m4 \
index 0d0508140d4105bbc821013c44bc2c9e10a34b2c..5f0aa94a8d09497c958b7212c3bdcf4d229b404e 100644 (file)
@@ -1,3 +1,8 @@
+2009-10-18  Bruno Haible  <bruno@clisp.org>
+
+       * gettextize.in (m4filelist): Add fcntl_h.m4 to the list.
+       (func_version): Bump copyright year.
+
 2009-09-27  Bruno Haible  <bruno@clisp.org>
 
        * add-to-archive: Pass option -fPIC to gcc. Clean up cvsuser.so.
index bdccb55395e4673c056a4e49603b2159958c63be..8c30f74a46fd6fef8374e0ba8557e06b65fea1a3 100644 (file)
@@ -1,6 +1,6 @@
 #! /bin/sh
 #
-# Copyright (C) 1995-1998, 2000-2008 Free Software Foundation, Inc.
+# Copyright (C) 1995-1998, 2000-2009 Free Software Foundation, Inc.
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -174,7 +174,7 @@ Report bugs to <bug-gnu-gettext@gnu.org>."
 func_version ()
 {
   echo "$progname (GNU $package) $version"
-  echo "Copyright (C) 1995-1998, 2000-2008 Free Software Foundation, Inc.
+  echo "Copyright (C) 1995-1998, 2000-2009 Free Software Foundation, Inc.
 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
 This is free software: you are free to change and redistribute it.
 There is NO WARRANTY, to the extent permitted by law."
@@ -768,8 +768,8 @@ m4filelist='gettext.m4 iconv.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 nls.m4
 min_automake_version=1.9
 if test -n "$intldir" || test -z "$have_automake19"; then
   # Add intldir.m4, intl.m4 and its dependencies.
-  m4filelist=$m4filelist' codeset.m4 glibc2.m4 glibc21.m4 intdiv0.m4 intl.m4
-   intldir.m4 intlmacosx.m4 intmax.m4 inttypes_h.m4 inttypes-pri.m4
+  m4filelist=$m4filelist' codeset.m4 fcntl_h.m4 glibc2.m4 glibc21.m4 intdiv0.m4
+   intl.m4 intldir.m4 intlmacosx.m4 intmax.m4 inttypes_h.m4 inttypes-pri.m4
    lcmessage.m4 lock.m4 longlong.m4 printf-posix.m4 size_max.m4 stdint_h.m4
    threadlib.m4 uintmax_t.m4 visibility.m4 wchar_t.m4 wint_t.m4 xsize.m4'
   min_automake_version=1.8