]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Allow $ORIGIN to reference trusted directoreis in SUID binaries.
authorUlrich Drepper <drepper@gmail.com>
Sat, 7 May 2011 15:44:26 +0000 (11:44 -0400)
committerUlrich Drepper <drepper@gmail.com>
Sat, 7 May 2011 15:44:26 +0000 (11:44 -0400)
ChangeLog
NEWS
elf/dl-load.c

index d7bb7340d0b33ee75da2a055947011bbda326224..e93c9a5643ae292dc6903e28879a73302d62cac0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2011-05-07  Petr Baudis  <pasky@suse.cz>
+           Ulrich Drepper  <drepper@gmail.com>
+
+       [BZ #12393]
+       * elf/dl-load.c (fillin_rpath): Move trusted path check...
+       (is_trusted_path): ...to here.
+       (is_norm_trusted_path): Add wrapper for /../ and /./ normalization.
+       (_dl_dst_substitute): Verify expanded $ORIGIN path elements
+       using is_norm_trusted_path() in setuid scripts.
+
 2011-05-06  Paul Pluzhnikov  <ppluzhnikov@google.com>
 
        * sysdeps/unix/sysv/linux/sys/sysmacros.h: Add missing
diff --git a/NEWS b/NEWS
index 5d37da2c107026e8e63f51be8b71fbf29165cfe9..43da5172547559a86bd1d1f891791d580593668c 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -22,8 +22,9 @@ Version 2.14
 
 * The following bugs are resolved with this release:
 
-  11724, 12420, 12445, 12454, 12460, 12469, 12489, 12509, 12510, 12518, 12583,
-  12587, 12597, 12631, 12650, 12653, 12655, 12685, 12714, 12717, 12723
+  11724, 12393, 12420, 12445, 12454, 12460, 12469, 12489, 12509, 12510,
+  12518, 12583, 12587, 12597, 12631, 12650, 12653, 12655, 12685, 12714,
+  12717, 12723
 \f
 Version 2.13
 
index 00ea465e138ea4b628aebc92adaf47faadbc0da0..f2773d5686b3a5d4f2a5839c602f23900d90064c 100644 (file)
@@ -1,5 +1,5 @@
 /* Map in a shared object's segments from the file.
-   Copyright (C) 1995-2005, 2006, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1995-2007, 2009, 2010, 2011 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
@@ -168,6 +168,71 @@ local_strdup (const char *s)
 }
 
 
+static bool
+is_trusted_path (const char *path, size_t len)
+{
+  /* All trusted directories must be complete names.  */
+  if (path[0] != '/')
+    return false;
+
+  const char *trun = system_dirs;
+
+  for (size_t idx = 0; idx < nsystem_dirs_len; ++idx)
+    {
+      if (len == system_dirs_len[idx] && memcmp (trun, path, len) == 0)
+       /* Found it.  */
+       return true;
+
+      trun += system_dirs_len[idx] + 1;
+    }
+
+  return false;
+}
+
+
+static bool
+is_trusted_path_normalize (const char *path, size_t len)
+{
+  char *npath = (char *) alloca (len + 2);
+  char *wnp = npath;
+
+  while (*path != '\0')
+    {
+      if (path[0] == '/')
+       {
+         if (path[1] == '.')
+           {
+             if (path[2] == '.' && (path[3] == '/' || path[3] == '\0'))
+               {
+                 while (wnp > npath && *--wnp != '/')
+                   ;
+                 path += 3;
+                 continue;
+               }
+             else if (path[2] == '/' || path[2] == '\0')
+               {
+                 path += 2;
+                 continue;
+               }
+           }
+
+         if (wnp > npath && wnp[-1] == '/')
+           {
+             ++path;
+             continue;
+           }
+       }
+
+      *wnp++ = *path++;
+    }
+  if (wnp > npath && wnp[-1] != '/')
+    *wnp++ = '/';
+  *wnp = '\0';
+
+  return is_trusted_path (npath, wnp - npath);
+}
+
+
 static size_t
 is_dst (const char *start, const char *name, const char *str,
        int is_path, int secure)
@@ -240,13 +305,14 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result,
                    int is_path)
 {
   const char *const start = name;
-  char *last_elem, *wp;
 
   /* Now fill the result path.  While copying over the string we keep
      track of the start of the last path element.  When we come accross
      a DST we copy over the value or (if the value is not available)
      leave the entire path element out.  */
-  last_elem = wp = result;
+  char *wp = result;
+  char *last_elem = result;
+  bool check_for_trusted = false;
 
   do
     {
@@ -265,6 +331,9 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result,
              else
 #endif
                repl = l->l_origin;
+
+             check_for_trusted = (INTUSE(__libc_enable_secure)
+                                  && l->l_type == lt_executable);
            }
          else if ((len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0)
            repl = GLRO(dl_platform);
@@ -297,11 +366,29 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result,
        {
          *wp++ = *name++;
          if (is_path && *name == ':')
-           last_elem = wp;
+           {
+             /* In SUID/SGID programs, after $ORIGIN expansion the
+                normalized path must be rooted in one of the trusted
+                directories.  */
+             if (__builtin_expect (check_for_trusted, false)
+                 && is_trusted_path_normalize (last_elem, wp - last_elem))
+               {
+                 wp = last_elem;
+                 check_for_trusted = false;
+               }
+             else
+               last_elem = wp;
+           }
        }
     }
   while (*name != '\0');
 
+  /* In SUID/SGID programs, after $ORIGIN expansion the normalized
+     path must be rooted in one of the trusted directories.  */
+  if (__builtin_expect (check_for_trusted, false)
+      && is_trusted_path_normalize (last_elem, wp - last_elem))
+    wp = last_elem;
+
   *wp = '\0';
 
   return result;
@@ -411,33 +498,8 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
        cp[len++] = '/';
 
       /* Make sure we don't use untrusted directories if we run SUID.  */
-      if (__builtin_expect (check_trusted, 0))
-       {
-         const char *trun = system_dirs;
-         size_t idx;
-         int unsecure = 1;
-
-         /* All trusted directories must be complete names.  */
-         if (cp[0] == '/')
-           {
-             for (idx = 0; idx < nsystem_dirs_len; ++idx)
-               {
-                 if (len == system_dirs_len[idx]
-                     && memcmp (trun, cp, len) == 0)
-                   {
-                     /* Found it.  */
-                     unsecure = 0;
-                     break;
-                   }
-
-                 trun += system_dirs_len[idx] + 1;
-               }
-           }
-
-         if (unsecure)
-           /* Simply drop this directory.  */
-           continue;
-       }
+      if (__builtin_expect (check_trusted, 0) && !is_trusted_path (cp, len))
+       continue;
 
       /* See if this directory is already known.  */
       for (dirp = GL(dl_all_dirs); dirp != NULL; dirp = dirp->next)