gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libmicrohttpd] 01/02: Rewritten cookie parsing.


From: gnunet
Subject: [libmicrohttpd] 01/02: Rewritten cookie parsing.
Date: Sun, 15 May 2022 20:02:57 +0200

This is an automated email from the git hooks/post-receive script.

karlson2k pushed a commit to branch master
in repository libmicrohttpd.

commit d08fb47c0a53a658fdd97fd1a4e265aade576418
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
AuthorDate: Sun May 15 20:59:22 2022 +0300

    Rewritten cookie parsing.
    
    Updated to match the current RFC, fixed some edge cases.
---
 src/microhttpd/connection.c | 426 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 338 insertions(+), 88 deletions(-)

diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index 5d795b47..47d63d1f 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -2758,15 +2758,314 @@ connection_add_header (struct MHD_Connection 
*connection,
 enum _MHD_ParseCookie
 {
   MHD_PARSE_COOKIE_OK = MHD_YES,      /**< Success or no cookies in headers */
+  MHD_PARSE_COOKIE_OK_LAX = 2,        /**< Cookies parsed, but workarounds 
used */
   MHD_PARSE_COOKIE_MALFORMED = -1,    /**< Invalid cookie header */
   MHD_PARSE_COOKIE_NO_MEMORY = MHD_NO /**< Not enough memory in the pool */
 };
 
+
+/**
+ * Parse the cookies string (see RFC 6265).
+ *
+ * Parsing may fail if the string is not formed strictly as defined by RFC 
6265.
+ *
+ * @param str the string to parse, without leading whitespaces
+ * @param str_len the size of the @a str, not including mandatory
+ *                zero-termination
+ * @param connection the connection to add parsed cookies
+ * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise
+ */
+static enum _MHD_ParseCookie
+parse_cookies_string_strict (char *str,
+                             size_t str_len,
+                             struct MHD_Connection *connection)
+{
+  size_t i;
+
+  i = 0;
+  while (i < str_len)
+  {
+    size_t name_start;
+    size_t name_len;
+    size_t value_start;
+    size_t value_len;
+    bool val_quoted;
+    /* 'i' must point to the first char of cookie-name */
+    name_start = i;
+    /* Find the end of the cookie-name */
+    do
+    {
+      const char l = str[i];
+      if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) 
||
+          (';' == l) || (0 == l))
+        break;
+    } while (str_len > ++i);
+    if ((str_len == i) || ('=' != str[i]) || (name_start == i))
+      return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */
+    name_len = i - name_start;
+    /* 'i' must point to the '=' char */
+    mhd_assert ('=' == str[i]);
+    i++;
+    /* 'i' must point to the first char of cookie-value */
+    if (str_len == i)
+    {
+      value_start = 0;
+      value_len = 0;
+    }
+    else
+    {
+      bool valid_cookie;
+      val_quoted = ('"' == str[i]);
+      if (val_quoted)
+        i++;
+      value_start = i;
+      /* Find the end of the cookie-value */
+      while (str_len > i)
+      {
+        const char l = str[i];
+        if ((';' == l) || ('"' == l) || (' ' == l) || ('\t' == l)
+            || (',' == l) || ('\\' == l) || (0 == l))
+          break;
+        i++;
+      }
+      value_len = i - value_start;
+      if (val_quoted)
+      {
+        if ((str_len == i) || ('"' != str[i]))
+          return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no 
closing quote */
+        i++;
+      }
+      if (str_len == i)
+        valid_cookie = true;
+      else if (';' == str[i])
+        valid_cookie = true;
+      else if ((' ' == str[i]) || ('\t' == str[i]))
+      { /* Optional whitespace at the end of the string? */
+        while (str_len > ++i)
+        {
+          if ((' ' != str[i]) && ('\t' != str[i]))
+            break;
+        }
+        if (str_len == i)
+          valid_cookie = true;
+        else
+          valid_cookie = false;
+      }
+      else
+        valid_cookie = false;
+
+      if (! valid_cookie)
+        return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie 
value */
+    }
+    mhd_assert (0 != name_len);
+    str[name_start + name_len] = 0; /* Zero-terminate the name */
+    if (0 != value_len)
+    {
+      mhd_assert (0 == str[i] || ';' == str[i]);
+      mhd_assert (! val_quoted || ';' == str[i]);
+      str[value_start + value_len] = 0; /* Zero-terminate the value */
+      if (MHD_NO ==
+          MHD_set_connection_value_n_nocheck_ (connection,
+                                               MHD_COOKIE_KIND,
+                                               str + name_start,
+                                               name_len,
+                                               str + value_start,
+                                               value_len))
+        return MHD_PARSE_COOKIE_NO_MEMORY;
+    }
+    else
+    {
+      if (MHD_NO ==
+          MHD_set_connection_value_n_nocheck_ (connection,
+                                               MHD_COOKIE_KIND,
+                                               str + name_start,
+                                               name_len,
+                                               "",
+                                               0))
+        return MHD_PARSE_COOKIE_NO_MEMORY;
+    }
+    if (str_len > i)
+    {
+      mhd_assert (0 == str[i] || ';' == str[i]);
+      mhd_assert (! val_quoted || ';' == str[i]);
+      mhd_assert (';' != str[i] || val_quoted || 0 == value_len);
+      i++;
+      if (str_len == i)
+        return MHD_PARSE_COOKIE_MALFORMED;  /* No cookie name after semicolon 
*/
+      if (' ' != str[i])
+        return MHD_PARSE_COOKIE_MALFORMED;  /* No space after semicolon */
+      i++;
+    }
+  }
+  return MHD_PARSE_COOKIE_OK;
+}
+
+
+/**
+ * Parse the cookies string (see RFC 6265).
+ *
+ * Try to parse the cookies string even if it is not strictly formed
+ * as specified by RFC 6265.
+ *
+ * @param str the string to parse, without leading whitespaces
+ * @param str_len the size of the @a str, not including mandatory
+ *                zero-termination
+ * @param connection the connection to add parsed cookies
+ * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise
+ */
+static enum _MHD_ParseCookie
+parse_cookies_string_lenient (char *str,
+                              size_t str_len,
+                              struct MHD_Connection *connection)
+{
+  size_t i;
+  bool non_strict;
+
+  non_strict = false;
+  i = 0;
+  while (i < str_len)
+  {
+    size_t name_start;
+    size_t name_len;
+    size_t value_start;
+    size_t value_len;
+    bool val_quoted;
+    /* Skip any whitespaces and empty cookies */
+    while (' ' == str[i] || '\t' == str[i] || ';' == str[i])
+    {
+      non_strict = true;
+      i++;
+      if (i == str_len)
+        return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
+    }
+    /* 'i' must point to the first char of cookie-name */
+    name_start = i;
+    /* Find the end of the cookie-name */
+    do
+    {
+      const char l = str[i];
+      if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) 
||
+          (';' == l) || (0 == l))
+        break;
+    } while (str_len > ++i);
+    name_len = i - name_start;
+    /* Skip any whitespaces */
+    while (str_len > i && (' ' == str[i] || '\t' == str[i]))
+    {
+      non_strict = true;
+      i++;
+    }
+    if ((str_len == i) || ('=' != str[i]) || (0 == name_len))
+      return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */
+    /* 'i' must point to the '=' char */
+    mhd_assert ('=' == str[i]);
+    i++;
+    /* Skip any whitespaces */
+    while (str_len > i && (' ' == str[i] || '\t' == str[i]))
+    {
+      non_strict = true;
+      i++;
+    }
+    /* 'i' must point to the first char of cookie-value */
+    if (str_len == i)
+    {
+      value_start = 0;
+      value_len = 0;
+    }
+    else
+    {
+      bool valid_cookie;
+      val_quoted = ('"' == str[i]);
+      if (val_quoted)
+        i++;
+      value_start = i;
+      /* Find the end of the cookie-value */
+      while (str_len > i)
+      {
+        const char l = str[i];
+        if ((';' == l) || ('"' == l) || (',' == l) || (';' == l) ||
+            ('\\' == l) || (0 == l))
+          break;
+        if ((' ' == l) || ('\t' == l))
+        {
+          if (! val_quoted)
+            break;
+          non_strict = true;
+        }
+        i++;
+      }
+      value_len = i - value_start;
+      if (val_quoted)
+      {
+        if ((str_len == i) || ('"' != str[i]))
+          return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no 
closing quote */
+        i++;
+      }
+      /* Skip any whitespaces */
+      while (str_len > i && (' ' == str[i] || '\t' == str[i]))
+      {
+        non_strict = true;
+        i++;
+      }
+      if (str_len == i)
+        valid_cookie = true;
+      else if (';' == str[i])
+        valid_cookie = true;
+      else
+        valid_cookie = false;
+
+      if (! valid_cookie)
+        return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie 
value */
+    }
+    mhd_assert (0 != name_len);
+    str[name_start + name_len] = 0; /* Zero-terminate the name */
+    if (0 != value_len)
+    {
+      mhd_assert (value_start + value_len <= str_len);
+      str[value_start + value_len] = 0; /* Zero-terminate the value */
+      if (MHD_NO ==
+          MHD_set_connection_value_n_nocheck_ (connection,
+                                               MHD_COOKIE_KIND,
+                                               str + name_start,
+                                               name_len,
+                                               str + value_start,
+                                               value_len))
+        return MHD_PARSE_COOKIE_NO_MEMORY;
+    }
+    else
+    {
+      if (MHD_NO ==
+          MHD_set_connection_value_n_nocheck_ (connection,
+                                               MHD_COOKIE_KIND,
+                                               str + name_start,
+                                               name_len,
+                                               "",
+                                               0))
+        return MHD_PARSE_COOKIE_NO_MEMORY;
+    }
+    if (str_len > i)
+    {
+      mhd_assert (0 == str[i] || ';' == str[i]);
+      mhd_assert (! val_quoted || ';' == str[i]);
+      mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len);
+      i++;
+      if (str_len == i)
+        non_strict = true;  /* No cookie name after semicolon */
+      else if (' ' != str[i])
+        non_strict = true;  /* No space after semicolon */
+      else
+        i++;
+    }
+  }
+  return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
+}
+
+
 /**
- * Parse the cookie header (see RFC 2109).
+ * Parse the cookie header (see RFC 6265).
  *
  * @param connection connection to parse header of
- * @return #MHD_YES for success, #MHD_NO for failure (malformed, out of memory)
+ * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise
  */
 static enum _MHD_ParseCookie
 parse_cookie_header (struct MHD_Connection *connection)
@@ -2774,14 +3073,8 @@ parse_cookie_header (struct MHD_Connection *connection)
   const char *hdr;
   size_t hdr_len;
   char *cpy;
-  char *pos;
-  char *sce;
-  char *semicolon;
-  char *equals;
-  char *ekill;
-  char *end;
-  char old;
-  int quotes;
+  bool strict_parsing;
+  size_t i;
 
   if (MHD_NO == MHD_lookup_connection_value_n (connection,
                                                MHD_HEADER_KIND,
@@ -2791,6 +3084,9 @@ parse_cookie_header (struct MHD_Connection *connection)
                                                &hdr,
                                                &hdr_len))
     return MHD_PARSE_COOKIE_OK;
+  if (0 == hdr_len)
+    return MHD_PARSE_COOKIE_OK;
+
   cpy = connection_alloc_memory (connection,
                                  hdr_len + 1);
   if (NULL == cpy)
@@ -2800,83 +3096,18 @@ parse_cookie_header (struct MHD_Connection *connection)
           hdr,
           hdr_len);
   cpy[hdr_len] = '\0';
-  pos = cpy;
-  while (NULL != pos)
-  {
-    while (' ' == *pos)
-      pos++;                    /* skip spaces */
-
-    sce = pos;
-    while ( ((*sce) != '\0') &&
-            ((*sce) != ',') &&
-            ((*sce) != ';') &&
-            ((*sce) != '=') )
-      sce++;
-    /* remove tailing whitespace (if any) from key */
-    ekill = sce - 1;
-    while ( (*ekill == ' ') &&
-            (ekill >= pos) )
-      *(ekill--) = '\0';
-    old = *sce;
-    *sce = '\0';
-    if (old != '=')
-    {
-      /* value part omitted, use empty string... */
-      mhd_assert (ekill >= pos);
-      if (MHD_NO ==
-          MHD_set_connection_value_n_nocheck_ (connection,
-                                               MHD_COOKIE_KIND,
-                                               pos,
-                                               (size_t) (ekill - pos + 1),
-                                               "",
-                                               0))
-        return MHD_PARSE_COOKIE_NO_MEMORY;
-      if (old == '\0')
-        break;
-      pos = sce + 1;
-      continue;
-    }
-    equals = sce + 1;
-    quotes = 0;
-    semicolon = equals;
-    while ( ('\0' != semicolon[0]) &&
-            ( (0 != quotes) ||
-              ( (';' != semicolon[0]) &&
-                (',' != semicolon[0]) ) ) )
-    {
-      if ('"' == semicolon[0])
-        quotes = (quotes + 1) & 1;
-      semicolon++;
-    }
-    end = semicolon;
-    if ('\0' == semicolon[0])
-      semicolon = NULL;
-    if (NULL != semicolon)
-    {
-      semicolon[0] = '\0';
-      semicolon++;
-    }
-    /* remove quotes */
-    if ( ('"' == equals[0]) &&
-         ('"' == end[-1]) )
-    {
-      equals++;
-      end--;
-      *end = '\0';
-    }
-    mhd_assert (ekill >= pos);
-    mhd_assert (end >= equals);
-    if (MHD_NO ==
-        MHD_set_connection_value_n_nocheck_ (connection,
-                                             MHD_COOKIE_KIND,
-                                             pos,
-                                             (size_t) (ekill - pos + 1),
-                                             equals,
-                                             (size_t) (end - equals)))
-      return MHD_PARSE_COOKIE_NO_MEMORY;
-    pos = semicolon;
-  }
-  return MHD_PARSE_COOKIE_OK;
+
+  strict_parsing = false; /* TODO: make it configurable */
+  i = 0;
+  /* Skip all initial whitespaces */
+  while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i]))
+    i++;
+
+  /* 'i' points to the first non-whitespace char or to the end of the string */
+  if (strict_parsing)
+    return parse_cookies_string_strict (cpy + i, hdr_len - i, connection);
+
+  return parse_cookies_string_lenient (cpy + i, hdr_len - i, connection);
 }
 
 
@@ -3619,8 +3850,10 @@ parse_connection_headers (struct MHD_Connection 
*connection)
   const char *clen;
   const char *enc;
   size_t val_len;
+  enum _MHD_ParseCookie cookie_res;
 
-  if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (connection))
+  cookie_res = parse_cookie_header (connection);
+  if (MHD_PARSE_COOKIE_NO_MEMORY == cookie_res)
   {
 #ifdef HAVE_MESSAGES
     MHD_DLOG (connection->daemon,
@@ -3631,6 +3864,23 @@ parse_connection_headers (struct MHD_Connection 
*connection)
                                     REQUEST_TOO_BIG);
     return;
   }
+  else if (MHD_PARSE_COOKIE_OK_LAX == cookie_res)
+  {
+#ifdef HAVE_MESSAGES
+    MHD_DLOG (connection->daemon,
+              _ ("The Cookie header has been parsed, but is not fully "
+                 "compliant with the standard.\n"));
+#endif
+    (void) 0; /* Mute compiler warning */
+  }
+  else if (MHD_PARSE_COOKIE_MALFORMED == cookie_res)
+  {
+#ifdef HAVE_MESSAGES
+    MHD_DLOG (connection->daemon,
+              _ ("The Cookie header has malformed data.\n"));
+#endif
+    (void) 0; /* Mute compiler warning */
+  }
   if ( (1 <= connection->daemon->strict_for_client) &&
        (MHD_IS_HTTP_VER_1_1_COMPAT (connection->http_ver)) &&
        (MHD_NO ==

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]