Răsfoiți Sursa

eliminate theoretical stack overflow

Christian Grothoff 12 ani în urmă
părinte
comite
4245c6e9c3
2 a modificat fișierele cu 101 adăugiri și 84 ștergeri
  1. 7 0
      ChangeLog
  2. 94 84
      src/microhttpd/digestauth.c

+ 7 - 0
ChangeLog

@@ -1,3 +1,10 @@
+Fri Nov 29 20:17:03 CET 2013
+	Eliminating theoretical stack overflow by limiting length
+	of URIs in authentication headers to 32k (only applicable
+	if the application explicitly raised the memroy limits,
+	and only applies to MHD_digest_auth_check). Issue was
+	reported by Florian Weimer. -CG
+
 Tue Nov 26 01:26:15 CET 2013
 	Fix race on shutdown signal with thread pool on non-Linux
 	systems by signalling n times for n threads. -CG

+ 94 - 84
src/microhttpd/digestauth.c

@@ -51,7 +51,7 @@
 
 
 /**
- * convert bin to hex 
+ * convert bin to hex
  *
  * @param bin binary data
  * @param len number of bytes in bin
@@ -64,12 +64,12 @@ cvthex (const unsigned char *bin,
 {
   size_t i;
   unsigned int j;
-  
-  for (i = 0; i < len; ++i) 
+
+  for (i = 0; i < len; ++i)
     {
-      j = (bin[i] >> 4) & 0x0f;      
-      hex[i * 2] = j <= 9 ? (j + '0') : (j + 'a' - 10);    
-      j = bin[i] & 0x0f;    
+      j = (bin[i] >> 4) & 0x0f;
+      hex[i * 2] = j <= 9 ? (j + '0') : (j + 'a' - 10);
+      j = bin[i] & 0x0f;
       hex[i * 2 + 1] = j <= 9 ? (j + '0') : (j + 'a' - 10);
     }
   hex[len * 2] = '\0';
@@ -99,7 +99,7 @@ digest_calc_ha1 (const char *alg,
 {
   struct MD5Context md5;
   unsigned char ha1[MD5_DIGEST_SIZE];
-  
+
   MD5Init (&md5);
   MD5Update (&md5, username, strlen (username));
   MD5Update (&md5, ":", 1);
@@ -107,7 +107,7 @@ digest_calc_ha1 (const char *alg,
   MD5Update (&md5, ":", 1);
   MD5Update (&md5, password, strlen (password));
   MD5Final (ha1, &md5);
-  if (0 == strcasecmp (alg, "md5-sess")) 
+  if (0 == strcasecmp (alg, "md5-sess"))
     {
       MD5Init (&md5);
       MD5Update (&md5, ha1, sizeof (ha1));
@@ -122,8 +122,8 @@ digest_calc_ha1 (const char *alg,
 
 
 /**
- * Calculate request-digest/response-digest as per RFC2617 spec 
- * 
+ * Calculate request-digest/response-digest as per RFC2617 spec
+ *
  * @param ha1 H(A1)
  * @param nonce nonce from server
  * @param noncecount 8 hex digits
@@ -149,11 +149,11 @@ digest_calc_response (const char *ha1,
   unsigned char ha2[MD5_DIGEST_SIZE];
   unsigned char resphash[MD5_DIGEST_SIZE];
   char ha2hex[HASH_MD5_HEX_LEN + 1];
-  
+
   MD5Init (&md5);
   MD5Update (&md5, method, strlen(method));
   MD5Update (&md5, ":", 1);
-  MD5Update (&md5, uri, strlen(uri)); 
+  MD5Update (&md5, uri, strlen(uri));
 #if 0
   if (0 == strcasecmp(qop, "auth-int"))
     {
@@ -163,15 +163,15 @@ digest_calc_response (const char *ha1,
       if (NULL != hentity)
 	MD5Update (&md5, hentity, strlen(hentity));
     }
-#endif  
+#endif
   MD5Final (ha2, &md5);
   cvthex (ha2, MD5_DIGEST_SIZE, ha2hex);
-  MD5Init (&md5);  
-  /* calculate response */  
+  MD5Init (&md5);
+  /* calculate response */
   MD5Update (&md5, ha1, HASH_MD5_HEX_LEN);
   MD5Update (&md5, ":", 1);
   MD5Update (&md5, nonce, strlen(nonce));
-  MD5Update (&md5, ":", 1);  
+  MD5Update (&md5, ":", 1);
   if ('\0' != *qop)
     {
       MD5Update (&md5, noncecount, strlen(noncecount));
@@ -180,7 +180,7 @@ digest_calc_response (const char *ha1,
       MD5Update (&md5, ":", 1);
       MD5Update (&md5, qop, strlen(qop));
       MD5Update (&md5, ":", 1);
-    }  
+    }
   MD5Update (&md5, ha2hex, HASH_MD5_HEX_LEN);
   MD5Final (resphash, &md5);
   cvthex (resphash, sizeof (resphash), response);
@@ -225,7 +225,7 @@ lookup_sub_value (char *dest,
 	return 0;
       q1 = eq + 1;
       while (' ' == *q1)
-	q1++;      
+	q1++;
       if ('\"' != *q1)
 	{
 	  q2 = strchr (q1, ',');
@@ -238,7 +238,7 @@ lookup_sub_value (char *dest,
 	  if (NULL == q2)
 	    return 0; /* end quote not found */
 	  qn = q2 + 1;
-	}      
+	}
       if ( (0 == strncasecmp (ptr,
 			      key,
 			      keylen)) &&
@@ -261,7 +261,7 @@ lookup_sub_value (char *dest,
 	      if (size > (q2 - q1) + 1)
 		size = (q2 - q1) + 1;
 	      size--;
-	      memcpy (dest, 
+	      memcpy (dest,
 		      q1,
 		      size);
 	      dest[size] = '\0';
@@ -316,13 +316,13 @@ check_nonce_nc (struct MHD_Connection *connection,
    * nonce counter is less than the current nonce counter by 1,
    * then only increase the nonce counter by one.
    */
-  
+
   (void) pthread_mutex_lock (&connection->daemon->nnc_lock);
   if (0 == nc)
     {
-      strcpy(connection->daemon->nnc[off].nonce, 
+      strcpy(connection->daemon->nnc[off].nonce,
 	     nonce);
-      connection->daemon->nnc[off].nc = 0;  
+      connection->daemon->nnc[off].nc = 0;
       (void) pthread_mutex_unlock (&connection->daemon->nnc_lock);
       return MHD_YES;
     }
@@ -331,7 +331,7 @@ check_nonce_nc (struct MHD_Connection *connection,
     {
       (void) pthread_mutex_unlock (&connection->daemon->nnc_lock);
 #if HAVE_MESSAGES
-      MHD_DLOG (connection->daemon, 
+      MHD_DLOG (connection->daemon,
 		"Stale nonce received.  If this happens a lot, you should probably increase the size of the nonce array.\n");
 #endif
       return MHD_NO;
@@ -356,9 +356,9 @@ MHD_digest_auth_get_username(struct MHD_Connection *connection)
   size_t len;
   char user[MAX_USERNAME_LENGTH];
   const char *header;
-  
+
   if (NULL == (header = MHD_lookup_connection_value (connection,
-						     MHD_HEADER_KIND, 
+						     MHD_HEADER_KIND,
 						     MHD_HTTP_HEADER_AUTHORIZATION)))
     return NULL;
   if (0 != strncmp (header, _BASE, strlen (_BASE)))
@@ -366,7 +366,7 @@ MHD_digest_auth_get_username(struct MHD_Connection *connection)
   header += strlen (_BASE);
   if (0 == (len = lookup_sub_value (user,
 				    sizeof (user),
-				    header, 
+				    header,
 				    "username")))
     return NULL;
   return strdup (user);
@@ -404,7 +404,7 @@ calculate_nonce (uint32_t nonce_time,
   timestamp[0] = (nonce_time & 0xff000000) >> 0x18;
   timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10;
   timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08;
-  timestamp[3] = (nonce_time & 0x000000ff);    
+  timestamp[3] = (nonce_time & 0x000000ff);
   MD5Update (&md5, timestamp, 4);
   MD5Update (&md5, ":", 1);
   MD5Update (&md5, method, strlen(method));
@@ -415,8 +415,8 @@ calculate_nonce (uint32_t nonce_time,
   MD5Update (&md5, uri, strlen(uri));
   MD5Update (&md5, ":", 1);
   MD5Update (&md5, realm, strlen(realm));
-  MD5Final (tmpnonce, &md5);  
-  cvthex (tmpnonce, sizeof (tmpnonce), nonce);  
+  MD5Final (tmpnonce, &md5);
+  cvthex (tmpnonce, sizeof (tmpnonce), nonce);
   cvthex (timestamp, 4, timestamphex);
   strncat (nonce, timestamphex, 8);
 }
@@ -429,7 +429,7 @@ calculate_nonce (uint32_t nonce_time,
  * @param connection the connection
  * @param key the key
  * @param value the value, can be NULL
- * @return MHD_YES if the key-value pair is in the headers, 
+ * @return MHD_YES if the key-value pair is in the headers,
  *         MHD_NO if not
  */
 static int
@@ -445,14 +445,14 @@ test_header (struct MHD_Connection *connection,
 	continue;
       if (0 != strcmp (key, pos->header))
 	continue;
-      if ( (NULL == value) && 
+      if ( (NULL == value) &&
 	   (NULL == pos->value) )
 	return MHD_YES;
-      if ( (NULL == value) || 
+      if ( (NULL == value) ||
 	   (NULL == pos->value) ||
 	   (0 != strcmp (value, pos->value)) )
 	continue;
-      return MHD_YES;      
+      return MHD_YES;
     }
   return MHD_NO;
 }
@@ -487,8 +487,8 @@ check_argument_match (struct MHD_Connection *connection,
 	  ('\0' != argp[0]) )
     {
       equals = strchr (argp, '=');
-      if (NULL == equals) 
-	{	  
+      if (NULL == equals)
+	{
 	  /* add with 'value' NULL */
 	  connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
 						 connection,
@@ -517,7 +517,7 @@ check_argument_match (struct MHD_Connection *connection,
       num_headers++;
       argp = amper;
     }
-  
+
   /* also check that the number of headers matches */
   for (pos = connection->headers_received; NULL != pos; pos = pos->next)
     {
@@ -525,7 +525,7 @@ check_argument_match (struct MHD_Connection *connection,
 	continue;
       num_headers--;
     }
-  if (0 != num_headers)  
+  if (0 != num_headers)
     return MHD_NO;
   return MHD_YES;
 }
@@ -570,10 +570,10 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
 
   header = MHD_lookup_connection_value (connection,
 					MHD_HEADER_KIND,
-					MHD_HTTP_HEADER_AUTHORIZATION);  
-  if (NULL == header) 
+					MHD_HTTP_HEADER_AUTHORIZATION);
+  if (NULL == header)
     return MHD_NO;
-  if (0 != strncmp(header, _BASE, strlen(_BASE))) 
+  if (0 != strncmp(header, _BASE, strlen(_BASE)))
     return MHD_NO;
   header += strlen (_BASE);
   left = strlen (header);
@@ -585,7 +585,7 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
 			    sizeof (un),
 			    header, "username");
     if ( (0 == len) ||
-	 (0 != strcmp(username, un)) ) 
+	 (0 != strcmp(username, un)) )
       return MHD_NO;
     left -= strlen ("username") + len;
   }
@@ -593,32 +593,42 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
   {
     char r[MAX_REALM_LENGTH];
 
-    len = lookup_sub_value(r, 
+    len = lookup_sub_value(r,
 			   sizeof (r),
-			   header, "realm");  
-    if ( (0 == len) || 
+			   header, "realm");
+    if ( (0 == len) ||
 	 (0 != strcmp(realm, r)) )
       return MHD_NO;
     left -= strlen ("realm") + len;
   }
 
-  if (0 == (len = lookup_sub_value (nonce, 
+  if (0 == (len = lookup_sub_value (nonce,
 				    sizeof (nonce),
 				    header, "nonce")))
     return MHD_NO;
   left -= strlen ("nonce") + len;
-
+  if (left > 32 * 1024)
   {
-    char uri[left];  
-  
-    if (0 == lookup_sub_value(uri,
-			      sizeof (uri),
-			      header, "uri")) 
+    /* we do not permit URIs longer than 32k, as we want to
+       make sure to not blow our stack (or per-connection
+       heap memory limit).  Besides, 32k is already insanely
+       large, but of course in theory the
+       #MHD_OPTION_CONNECTION_MEMORY_LIMIT might be very large
+       and would thus permit sending a >32k authorization
+       header value. */
+    return MHD_NO;
+  }
+  {
+    char uri[left];
+
+    if (0 == lookup_sub_value (uri,
+                               sizeof (uri),
+                               header, "uri"))
       return MHD_NO;
-      
-    /* 8 = 4 hexadecimal numbers for the timestamp */  
-    nonce_time = strtoul(nonce + len - 8, (char **)NULL, 16);  
-    t = (uint32_t) MHD_monotonic_time();    
+
+    /* 8 = 4 hexadecimal numbers for the timestamp */
+    nonce_time = strtoul (nonce + len - 8, (char **)NULL, 16);
+    t = (uint32_t) MHD_monotonic_time();
     /*
      * First level vetting for the nonce validity if the timestamp
      * attached to the nonce exceeds `nonce_timeout' then the nonce is
@@ -632,7 +642,7 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
 		      strlen (connection->url)))
     {
 #if HAVE_MESSAGES
-      MHD_DLOG (connection->daemon, 
+      MHD_DLOG (connection->daemon,
 		"Authentication failed, URI does not match.\n");
 #endif
       return MHD_NO;
@@ -646,10 +656,10 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
 	args++;
       if (MHD_YES !=
 	  check_argument_match (connection,
-				args) ) 
+				args) )
       {
 #if HAVE_MESSAGES
-	MHD_DLOG (connection->daemon, 
+	MHD_DLOG (connection->daemon,
 		  "Authentication failed, arguments do not match.\n");
 #endif
 	return MHD_NO;
@@ -671,31 +681,31 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
      * not, the nonce fabrication process going to be
      * very hard to achieve.
      */
-    
+
     if (0 != strcmp (nonce, noncehashexp))
       return MHD_INVALID_NONCE;
     if ( (0 == lookup_sub_value (cnonce,
-				 sizeof (cnonce), 
+				 sizeof (cnonce),
 				 header, "cnonce")) ||
 	 (0 == lookup_sub_value (qop, sizeof (qop), header, "qop")) ||
-	 ( (0 != strcmp (qop, "auth")) && 
+	 ( (0 != strcmp (qop, "auth")) &&
 	   (0 != strcmp (qop, "")) ) ||
 	 (0 == lookup_sub_value (nc, sizeof (nc), header, "nc"))  ||
 	 (0 == lookup_sub_value (response, sizeof (response), header, "response")) )
     {
 #if HAVE_MESSAGES
-      MHD_DLOG (connection->daemon, 
+      MHD_DLOG (connection->daemon,
 		"Authentication failed, invalid format.\n");
 #endif
       return MHD_NO;
     }
     nci = strtoul (nc, &end, 16);
     if ( ('\0' != *end) ||
-	 ( (LONG_MAX == nci) && 
+	 ( (LONG_MAX == nci) &&
 	   (ERANGE == errno) ) )
     {
 #if HAVE_MESSAGES
-      MHD_DLOG (connection->daemon, 
+      MHD_DLOG (connection->daemon,
 		"Authentication failed, invalid format.\n");
 #endif
       return MHD_NO; /* invalid nonce format */
@@ -705,10 +715,10 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
      * and not a replay attack attempt. Also adds the nonce
      * to the nonce-nc map if it does not exist there.
      */
-    
+
     if (MHD_YES != check_nonce_nc (connection, nonce, nci))
       return MHD_NO;
-    
+
     digest_calc_ha1("md5",
 		    username,
 		    realm,
@@ -724,9 +734,9 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
 			  connection->method,
 			  uri,
 			  hentity,
-			  respexp);  
-    return (0 == strcmp(response, respexp)) 
-      ? MHD_YES 
+			  respexp);
+    return (0 == strcmp(response, respexp))
+      ? MHD_YES
       : MHD_NO;
   }
 }
@@ -757,7 +767,7 @@ MHD_queue_auth_fail_response (struct MHD_Connection *connection,
   size_t hlen;
   char nonce[HASH_MD5_HEX_LEN + 9];
 
-  /* Generating the server nonce */  
+  /* Generating the server nonce */
   calculate_nonce ((uint32_t) MHD_monotonic_time(),
 		   connection->method,
 		   connection->daemon->digest_auth_random,
@@ -768,20 +778,20 @@ MHD_queue_auth_fail_response (struct MHD_Connection *connection,
   if (MHD_YES != check_nonce_nc (connection, nonce, 0))
     {
 #if HAVE_MESSAGES
-      MHD_DLOG (connection->daemon, 
+      MHD_DLOG (connection->daemon,
 		"Could not register nonce (is the nonce array size zero?).\n");
 #endif
-      return MHD_NO;  
+      return MHD_NO;
     }
   /* Building the authentication header */
   hlen = snprintf (NULL,
 		   0,
 		   "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
-		   realm, 
+		   realm,
 		   nonce,
 		   opaque,
-		   signal_stale 
-		   ? ",stale=\"true\"" 
+		   signal_stale
+		   ? ",stale=\"true\""
 		   : "");
   {
     char header[hlen + 1];
@@ -789,20 +799,20 @@ MHD_queue_auth_fail_response (struct MHD_Connection *connection,
     snprintf (header,
 	      sizeof(header),
 	      "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
-	      realm, 
+	      realm,
 	      nonce,
 	      opaque,
-	      signal_stale 
-	      ? ",stale=\"true\"" 
+	      signal_stale
+	      ? ",stale=\"true\""
 	      : "");
     ret = MHD_add_response_header(response,
-				  MHD_HTTP_HEADER_WWW_AUTHENTICATE, 
+				  MHD_HTTP_HEADER_WWW_AUTHENTICATE,
 				  header);
   }
-  if (MHD_YES == ret) 
-    ret = MHD_queue_response(connection, 
-			     MHD_HTTP_UNAUTHORIZED, 
-			     response);  
+  if (MHD_YES == ret)
+    ret = MHD_queue_response(connection,
+			     MHD_HTTP_UNAUTHORIZED,
+			     response);
   return ret;
 }