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