bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

localcharset: fix security problem


From: Bruno Haible
Subject: localcharset: fix security problem
Date: Sun, 18 Oct 2009 16:58:22 +0200
User-agent: KMail/1.9.9

This fixes a possible symlink attack in localcharset module. The security
hole was probably small, but that's not a reason for not making it even
smaller.


2009-10-18  Bruno Haible  <address@hidden>

        Avoid symlink attack in localcharset module.
        * lib/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.
        * m4/fcntl_h.m4 (gl_FCNTL_O_FLAGS): New macro, extracted from
        gl_FCNTL_H.
        (gl_FCNTL_H): Require it.
        * m4/localcharset.m4 (gl_LOCALCHARSET): Likewise.
        * modules/localcharset (Files): Add m4/fcntl_h.m4.
        Reported by Fergal Glynn <address@hidden>.

*** lib/localcharset.c.orig     2009-10-18 16:53:18.000000000 +0200
--- lib/localcharset.c  2009-10-18 16:50:57.000000000 +0200
***************
*** 23,28 ****
--- 23,29 ----
  /* Specification.  */
  #include "localcharset.h"
  
+ #include <fcntl.h>
  #include <stddef.h>
  #include <stdio.h>
  #include <string.h>
***************
*** 44,49 ****
--- 45,51 ----
  #endif
  
  #if !defined WIN32_NATIVE
+ # include <unistd.h>
  # if HAVE_LANGINFO_CODESET
  #  include <langinfo.h>
  # else
***************
*** 75,80 ****
--- 77,87 ----
  # 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) == '\\')
***************
*** 117,123 ****
    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;
--- 124,129 ----
***************
*** 143,219 ****
          }
        }
  
!       if (file_name == NULL || (fp = fopen (file_name, "r")) == NULL)
!       /* Out of memory or file not found, treat it as empty.  */
        cp = "";
        else
        {
!         /* 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 (file_name != NULL)
!       free (file_name);
  
  #else
  
--- 149,253 ----
          }
        }
  
!       if (file_name == NULL)
!       /* Out of memory.  Treat the file as empty.  */
        cp = "";
        else
        {
!         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
            {
!             FILE *fp;
! 
!             fp = fdopen (fd, "r");
!             if (fp == NULL)
                {
!                 /* Out of memory.  Treat the file as empty.  */
!                 close (fd);
!                 cp = "";
                }
              else
                {
!                 /* 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;
!                   }
                }
            }
  
!         free (file_name);
!       }
  
  #else
  
*** m4/fcntl_h.m4.orig  2009-10-18 16:53:19.000000000 +0200
--- m4/fcntl_h.m4       2009-10-18 16:37:25.000000000 +0200
***************
*** 1,4 ****
! # serial 5
  # Configure fcntl.h.
  dnl Copyright (C) 2006, 2007, 2009 Free Software Foundation, Inc.
  dnl This file is free software; the Free Software Foundation
--- 1,4 ----
! # 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
***************
*** 10,15 ****
--- 10,26 ----
  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],
***************
*** 77,86 ****
    esac
    AC_DEFINE_UNQUOTED([HAVE_WORKING_O_NOFOLLOW], [$ac_val],
      [Define to 1 if O_NOFOLLOW works.])
- 
-   gl_CHECK_NEXT_HEADERS([fcntl.h])
-   FCNTL_H='fcntl.h'
-   AC_SUBST([FCNTL_H])
  ])
  
  AC_DEFUN([gl_FCNTL_MODULE_INDICATOR],
--- 88,93 ----
*** m4/localcharset.m4.orig     2009-10-18 16:53:19.000000000 +0200
--- m4/localcharset.m4  2009-10-18 16:37:43.000000000 +0200
***************
*** 1,4 ****
! # localcharset.m4 serial 6
  dnl Copyright (C) 2002, 2004, 2006, 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,
--- 1,4 ----
! # localcharset.m4 serial 7
  dnl Copyright (C) 2002, 2004, 2006, 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,
***************
*** 8,13 ****
--- 8,14 ----
  [
    dnl Prerequisites of lib/localcharset.c.
    AC_REQUIRE([AM_LANGINFO_CODESET])
+   AC_REQUIRE([gl_FCNTL_O_FLAGS])
    AC_CHECK_DECLS_ONCE([getc_unlocked])
  
    dnl Prerequisites of the lib/Makefile.am snippet.
*** modules/localcharset.orig   2009-10-18 16:53:19.000000000 +0200
--- modules/localcharset        2009-10-18 16:37:07.000000000 +0200
***************
*** 14,19 ****
--- 14,20 ----
  lib/ref-add.sin
  lib/ref-del.sin
  m4/codeset.m4
+ m4/fcntl_h.m4
  m4/glibc21.m4
  m4/localcharset.m4
  




reply via email to

[Prev in Thread] Current Thread [Next in Thread]