This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Never leave $ORIGIN unexpanded


2011-05-13  Andreas Schwab  <schwab@redhat.com>

	* elf/dl-load.c (is_dst): Remove parameter secure, all callers
	changed.  Move check for valid use of $ORIGIN ...
	(_dl_dst_substitute): ... here.  Reset check_for_trusted when a
	path element is skipped.

---
 This change started its life as commit 207e77fd3f0a94acdf0557608dd4f10ce0e0f22f,
 it's in wide use, it was rebased and reviewed several times.

 elf/dl-load.c | 37 ++++++++++++++++++-------------------
 1 file changed, 18 insertions(+), 19 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 6fb615e..0938b70 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -197,43 +197,36 @@ is_trusted_path_normalize (const char *path, size_t len)
 
 
 static size_t
-is_dst (const char *start, const char *name, const char *str,
-	int is_path, int secure)
+is_dst (const char *start, const char *name, const char *str, int is_path)
 {
   size_t len;
   bool is_curly = false;
 
   if (name[0] == '{')
     {
       is_curly = true;
       ++name;
     }
 
   len = 0;
   while (name[len] == str[len] && name[len] != '\0')
     ++len;
 
   if (is_curly)
     {
       if (name[len] != '}')
 	return 0;
 
       /* Point again at the beginning of the name.  */
       --name;
       /* Skip over closing curly brace and adjust for the --name.  */
       len += 2;
     }
   else if (name[len] != '\0' && name[len] != '/'
 	   && (!is_path || name[len] != ':'))
     return 0;
 
-  if (__glibc_unlikely (secure)
-      && ((name[len] != '\0' && name[len] != '/'
-	   && (!is_path || name[len] != ':'))
-	  || (name != start + 1 && (!is_path || name[-2] != ':'))))
-    return 0;
-
   return len;
 }
 
 
@@ -241,26 +234,23 @@ size_t
 _dl_dst_count (const char *name, int is_path)
 {
   const char *const start = name;
   size_t cnt = 0;
 
   do
     {
       size_t len;
 
-      /* $ORIGIN is not expanded for SUID/GUID programs (except if it
-	 is $ORIGIN alone) and it must always appear first in path.  */
       ++name;
-      if ((len = is_dst (start, name, "ORIGIN", is_path,
-			 __libc_enable_secure)) != 0
-	  || (len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0
-	  || (len = is_dst (start, name, "LIB", is_path, 0)) != 0)
+      if ((len = is_dst (start, name, "ORIGIN", is_path)) != 0
+	  || (len = is_dst (start, name, "PLATFORM", is_path)) != 0
+	  || (len = is_dst (start, name, "LIB", is_path)) != 0)
 	++cnt;
 
       name = strchr (name + len, '$');
     }
   while (name != NULL);
 
   return cnt;
 }
 
 
@@ -268,92 +258,101 @@ char *
 _dl_dst_substitute (struct link_map *l, const char *name, char *result,
 		    int is_path)
 {
   const char *const start = name;
 
   /* Now fill the result path.  While copying over the string we keep
      track of the start of the last path element.  When we come across
      a DST we copy over the value or (if the value is not available)
      leave the entire path element out.  */
   char *wp = result;
   char *last_elem = result;
   bool check_for_trusted = false;
 
   do
     {
       if (__glibc_unlikely (*name == '$'))
 	{
 	  const char *repl = NULL;
 	  size_t len;
 
 	  ++name;
-	  if ((len = is_dst (start, name, "ORIGIN", is_path,
-			     __libc_enable_secure)) != 0)
+	  if ((len = is_dst (start, name, "ORIGIN", is_path)) != 0)
 	    {
-	      repl = l->l_origin;
+	      /* For SUID/GUID programs $ORIGIN must always appear
+		 first in a path element.  */
+	      if (__glibc_unlikely (__libc_enable_secure)
+		  && ((name[len] != '\0' && name[len] != '/'
+		       && (!is_path || name[len] != ':'))
+		      || (name != start + 1 && (!is_path || name[-2] != ':'))))
+		repl = (const char *) -1;
+	      else
+		repl = l->l_origin;
+
 	      check_for_trusted = (__libc_enable_secure
 				   && l->l_type == lt_executable);
 	    }
-	  else if ((len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0)
+	  else if ((len = is_dst (start, name, "PLATFORM", is_path)) != 0)
 	    repl = GLRO(dl_platform);
-	  else if ((len = is_dst (start, name, "LIB", is_path, 0)) != 0)
+	  else if ((len = is_dst (start, name, "LIB", is_path)) != 0)
 	    repl = DL_DST_LIB;
 
 	  if (repl != NULL && repl != (const char *) -1)
 	    {
 	      wp = __stpcpy (wp, repl);
 	      name += len;
 	    }
 	  else if (len > 1)
 	    {
 	      /* We cannot use this path element, the value of the
 		 replacement is unknown.  */
 	      wp = last_elem;
 	      name += len;
 	      while (*name != '\0' && (!is_path || *name != ':'))
 		++name;
 	      /* Also skip following colon if this is the first rpath
 		 element, but keep an empty element at the end.  */
 	      if (wp == result && is_path && *name == ':' && name[1] != '\0')
 		++name;
+	      check_for_trusted = false;
 	    }
 	  else
 	    /* No DST we recognize.  */
 	    *wp++ = '$';
 	}
       else
 	{
 	  *wp++ = *name++;
 	  if (is_path && *name == ':')
 	    {
 	      /* In SUID/SGID programs, after $ORIGIN expansion the
 		 normalized path must be rooted in one of the trusted
 		 directories.  */
 	      if (__glibc_unlikely (check_for_trusted)
 		  && !is_trusted_path_normalize (last_elem, wp - last_elem))
 		wp = last_elem;
 	      else
 		last_elem = wp;
 
 	      check_for_trusted = false;
 	    }
 	}
     }
   while (*name != '\0');
 
   /* In SUID/SGID programs, after $ORIGIN expansion the normalized
      path must be rooted in one of the trusted directories.  */
   if (__glibc_unlikely (check_for_trusted)
       && !is_trusted_path_normalize (last_elem, wp - last_elem))
     wp = last_elem;
 
   *wp = '\0';
 
   return result;
 }
 
 
 /* Return copy of argument with all recognized dynamic string tokens
    ($ORIGIN and $PLATFORM for now) replaced.  On some platforms it
    might not be possible to determine the path from which the object
    belonging to the map is loaded.  In this case the path element
    containing $ORIGIN is left out.  */

-- 
ldv


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]