ce426f
commit TBD
ce426f
Author: Florian Weimer <fweimer@redhat.com>
ce426f
Date:   Fri May 19 17:46:47 2017 +0200
ce426f
ce426f
    rtld: Reject overly long LD_AUDIT path elements
ce426f
ce426f
Also only process the last LD_AUDIT entry.
ce426f
ce426f
Index: b/elf/rtld.c
ce426f
===================================================================
ce426f
--- a/elf/rtld.c
ce426f
+++ b/elf/rtld.c
ce426f
@@ -116,13 +116,91 @@ dso_name_valid_for_suid (const char *p)
ce426f
   return *p != '\0';
ce426f
 }
ce426f
 
ce426f
-/* List of auditing DSOs.  */
ce426f
+/* LD_AUDIT variable contents.  Must be processed before the
ce426f
+   audit_list below.  */
ce426f
+const char *audit_list_string;
ce426f
+
ce426f
+/* Cyclic list of auditing DSOs.  audit_list->next is the first
ce426f
+   element.  */
ce426f
 static struct audit_list
ce426f
 {
ce426f
   const char *name;
ce426f
   struct audit_list *next;
ce426f
 } *audit_list;
ce426f
 
ce426f
+/* Iterator for audit_list_string followed by audit_list.  */
ce426f
+struct audit_list_iter
ce426f
+{
ce426f
+  /* Tail of audit_list_string still needing processing, or NULL.  */
ce426f
+  const char *audit_list_tail;
ce426f
+
ce426f
+  /* The list element returned in the previous iteration.  NULL before
ce426f
+     the first element.  */
ce426f
+  struct audit_list *previous;
ce426f
+
ce426f
+  /* Scratch buffer for returning a name which is part of
ce426f
+     audit_list_string.  */
ce426f
+  char fname[PATH_MAX];
ce426f
+};
ce426f
+
ce426f
+/* Initialize an audit list iterator.  */
ce426f
+static void
ce426f
+audit_list_iter_init (struct audit_list_iter *iter)
ce426f
+{
ce426f
+  iter->audit_list_tail = audit_list_string;
ce426f
+  iter->previous = NULL;
ce426f
+}
ce426f
+
ce426f
+/* Iterate through both audit_list_string and audit_list.  */
ce426f
+static const char *
ce426f
+audit_list_iter_next (struct audit_list_iter *iter)
ce426f
+{
ce426f
+  if (iter->audit_list_tail != NULL)
ce426f
+    {
ce426f
+      /* First iterate over audit_list_string.  */
ce426f
+      while (*iter->audit_list_tail != '\0')
ce426f
+	{
ce426f
+	  /* Split audit list at colon.  */
ce426f
+	  size_t len = strcspn (iter->audit_list_tail, ":");
ce426f
+	  if (len > 0 && len < PATH_MAX)
ce426f
+	    {
ce426f
+	      memcpy (iter->fname, iter->audit_list_tail, len);
ce426f
+	      iter->fname[len] = '\0';
ce426f
+	    }
ce426f
+	  else
ce426f
+	    /* Do not return this name to the caller.  */
ce426f
+	    iter->fname[0] = '\0';
ce426f
+
ce426f
+	  /* Skip over the substring and the following delimiter.  */
ce426f
+	  iter->audit_list_tail += len;
ce426f
+	  if (*iter->audit_list_tail == ':')
ce426f
+	    ++iter->audit_list_tail;
ce426f
+
ce426f
+	  /* If the name is valid, return it.  */
ce426f
+	  if (dso_name_valid_for_suid (iter->fname))
ce426f
+	    return iter->fname;
ce426f
+	  /* Otherwise, wrap around and try the next name.  */
ce426f
+	}
ce426f
+      /* Fall through to the procesing of audit_list.  */
ce426f
+    }
ce426f
+
ce426f
+  if (iter->previous == NULL)
ce426f
+    {
ce426f
+      if (audit_list == NULL)
ce426f
+	/* No pre-parsed audit list.  */
ce426f
+	return NULL;
ce426f
+      /* Start of audit list.  The first list element is at
ce426f
+	 audit_list->next (cyclic list).  */
ce426f
+      iter->previous = audit_list->next;
ce426f
+      return iter->previous->name;
ce426f
+    }
ce426f
+  if (iter->previous == audit_list)
ce426f
+    /* Cyclic list wrap-around.  */
ce426f
+    return NULL;
ce426f
+  iter->previous = iter->previous->next;
ce426f
+  return iter->previous->name;
ce426f
+}
ce426f
+
ce426f
 /* Set nonzero during loading and initialization of executable and
ce426f
    libraries, cleared before the executable's entry point runs.  This
ce426f
    must not be initialized to nonzero, because the unused dynamic
ce426f
@@ -1441,11 +1519,13 @@ of this helper program; chances are you
ce426f
     GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid ();
ce426f
 
ce426f
   /* If we have auditing DSOs to load, do it now.  */
ce426f
-  if (__builtin_expect (audit_list != NULL, 0))
ce426f
+  bool need_security_init = true;
ce426f
+  if (__builtin_expect (audit_list != NULL, 0)
ce426f
+      || __builtin_expect (audit_list_string != NULL, 0))
ce426f
     {
ce426f
-      /* Iterate over all entries in the list.  The order is important.  */
ce426f
       struct audit_ifaces *last_audit = NULL;
ce426f
-      struct audit_list *al = audit_list->next;
ce426f
+      struct audit_list_iter al_iter;
ce426f
+      audit_list_iter_init (&al_iter);
ce426f
 
ce426f
       /* Since we start using the auditing DSOs right away we need to
ce426f
 	 initialize the data structures now.  */
ce426f
@@ -1456,9 +1536,14 @@ of this helper program; chances are you
ce426f
 	 use different values (especially the pointer guard) and will
ce426f
 	 fail later on.  */
ce426f
       security_init ();
ce426f
+      need_security_init = false;
ce426f
 
ce426f
-      do
ce426f
+      while (true)
ce426f
 	{
ce426f
+	  const char *name = audit_list_iter_next (&al_iter);
ce426f
+	  if (name == NULL)
ce426f
+	    break;
ce426f
+
ce426f
 	  int tls_idx = GL(dl_tls_max_dtv_idx);
ce426f
 
ce426f
 	  /* Now it is time to determine the layout of the static TLS
ce426f
@@ -1467,7 +1552,7 @@ of this helper program; chances are you
ce426f
 	     no DF_STATIC_TLS bit is set.  The reason is that we know
ce426f
 	     glibc will use the static model.  */
ce426f
 	  struct dlmopen_args dlmargs;
ce426f
-	  dlmargs.fname = al->name;
ce426f
+	  dlmargs.fname = name;
ce426f
 	  dlmargs.map = NULL;
ce426f
 
ce426f
 	  const char *objname;
ce426f
@@ -1480,7 +1565,7 @@ of this helper program; chances are you
ce426f
 	    not_loaded:
ce426f
 	      _dl_error_printf ("\
ce426f
 ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
ce426f
-				al->name, err_str);
ce426f
+				name, err_str);
ce426f
 	      if (malloced)
ce426f
 		free ((char *) err_str);
ce426f
 	    }
ce426f
@@ -1584,10 +1669,7 @@ ERROR: ld.so: object '%s' cannot be load
ce426f
 		  goto not_loaded;
ce426f
 		}
ce426f
 	    }
ce426f
-
ce426f
-	  al = al->next;
ce426f
 	}
ce426f
-      while (al != audit_list->next);
ce426f
 
ce426f
       /* If we have any auditing modules, announce that we already
ce426f
 	 have two objects loaded.  */
ce426f
@@ -1851,7 +1933,7 @@ ERROR: ld.so: object '%s' cannot be load
ce426f
   if (tcbp == NULL)
ce426f
     tcbp = init_tls ();
ce426f
 
ce426f
-  if (__builtin_expect (audit_list == NULL, 1))
ce426f
+  if (need_security_init)
ce426f
     /* Initialize security features.  But only if we have not done it
ce426f
        earlier.  */
ce426f
     security_init ();
ce426f
@@ -2495,9 +2577,7 @@ process_dl_audit (char *str)
ce426f
   char *p;
ce426f
 
ce426f
   while ((p = (strsep) (&str, ":")) != NULL)
ce426f
-    if (p[0] != '\0'
ce426f
-	&& (__builtin_expect (! INTUSE(__libc_enable_secure), 1)
ce426f
-	    || strchr (p, '/') == NULL))
ce426f
+    if (dso_name_valid_for_suid (p))
ce426f
       {
ce426f
 	/* This is using the local malloc, not the system malloc.  The
ce426f
 	   memory can never be freed.  */
ce426f
@@ -2561,7 +2641,7 @@ process_envvars (enum mode *modep)
ce426f
 	      break;
ce426f
 	    }
ce426f
 	  if (memcmp (envline, "AUDIT", 5) == 0)
ce426f
-	    process_dl_audit (&envline[6]);
ce426f
+	    audit_list_string = &envline[6];
ce426f
 	  break;
ce426f
 
ce426f
 	case 7: