gnunet-svn
[Top][All Lists]
Advanced

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

[libmicrohttpd] branch master updated (a76f989d -> 7899565c)


From: gnunet
Subject: [libmicrohttpd] branch master updated (a76f989d -> 7899565c)
Date: Sun, 15 May 2022 20:02:56 +0200

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

karlson2k pushed a change to branch master
in repository libmicrohttpd.

    from a76f989d test_parse_cookie: split into two tests
     new d08fb47c Rewritten cookie parsing.
     new 7899565c Made cookie parsing optional feature

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 configure.ac                |  15 ++
 src/include/microhttpd.h    |  13 +-
 src/microhttpd/connection.c | 433 +++++++++++++++++++++++++++++++++++---------
 src/microhttpd/daemon.c     |   6 +
 src/testcurl/Makefile.am    |   8 +-
 5 files changed, 383 insertions(+), 92 deletions(-)

diff --git a/configure.ac b/configure.ac
index 30041fd8..652f5b10 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2879,6 +2879,19 @@ AS_VAR_IF([[enable_httpupgrade]],[["yes"]],
 AM_CONDITIONAL([ENABLE_UPGRADE], [[test "x$enable_httpupgrade" = "xyes"]])
 AC_MSG_RESULT([[$enable_httpupgrade]])
 
+# optional: HTTP cookie parsing support. Enabled by default
+AC_MSG_CHECKING([[whether to support HTTP cookie parsing]])
+AC_ARG_ENABLE([[cookie]],
+    AS_HELP_STRING([[--disable-cookie]],
+      [disable HTTP cookie parsing support]),
+    [AS_VAR_IF([[enable_cookie]],[["no"]],[],[[enable_cookie='yes']])],
+    [[enable_cookie='yes']])
+AS_VAR_IF([[enable_cookie]],[["yes"]],
+  [
+   AC_DEFINE([[COOKIE_SUPPORT]],[[1]],[Define to 1 if libmicrohttpd is 
compiled with HTTP cookie parsing support.]) ])
+AM_CONDITIONAL([ENABLE_COOKIE], [[test "x$enable_cookie" = "xyes"]])
+AC_MSG_RESULT([[$enable_cookie]])
+
 AC_CACHE_CHECK([[for calloc()]], [[mhd_cv_have_func_calloc]],
   [
    AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@@ -3749,6 +3762,7 @@ AC_MSG_NOTICE([GNU libmicrohttpd ${PACKAGE_VERSION} 
Configuration Summary:
   Basic auth.:       ${enable_bauth}
   Digest auth.:      ${enable_dauth}
   HTTP "Upgrade":    ${enable_httpupgrade}
+  Cookie parsing:    ${enable_cookie}
   Postproc:          ${enable_postprocessor}
   Build docs:        ${enable_doc}
   Build examples:    ${enable_examples}
@@ -3766,5 +3780,6 @@ AS_IF([test "x$enable_https" = "xyes"],
 AS_IF([test "x$enable_bauth" != "xyes" || \
    test "x$enable_dauth" != "xyes" || \
    test "x$enable_httpupgrade" != "xyes" || \
+   test "x$enable_cookie" != "xyes" || \
    test "x$enable_postprocessor" != "xyes"],
    [AC_MSG_NOTICE([WARNING: This will be a custom build with missing symbols. 
Do NOT use this build in a distribution. Building with these kinds of configure 
options is only for custom builds for embedded systems.])])
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index 3e1dbc96..84561dec 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -96,7 +96,7 @@ extern "C"
  * they are parsed as decimal numbers.
  * Example: 0x01093001 = 1.9.30-1.
  */
-#define MHD_VERSION 0x00097513
+#define MHD_VERSION 0x00097514
 
 /* If generic headers don't work on your platform, include headers
    which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t',
@@ -4964,7 +4964,16 @@ enum MHD_FEATURE
    * Get whether option #MHD_OPTION_HTTPS_CERT_CALLBACK2 is
    * supported.
    */
-  MHD_FEATURE_HTTPS_CERT_CALLBACK2 = 23
+  MHD_FEATURE_HTTPS_CERT_CALLBACK2 = 23,
+
+  /**
+   * Get whether option automatic parsing of HTTP Cookie header
+   * is enabled.
+   * If disabled, no MHD_COOKIE_KIND will be generated by MHD.
+   * MHD versions before 0x00097514 always support cookie parsing.
+   * @note Available since #MHD_VERSION 0x00097514
+   */
+  MHD_FEATURE_HTTPS_COOKIE_PARSING = 24
 } _MHD_FIXED_ENUM;
 
 
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index 5d795b47..e563fb71 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -2752,21 +2752,322 @@ connection_add_header (struct MHD_Connection 
*connection,
 }
 
 
+#ifdef COOKIE_SUPPORT
+
 /**
  * Cookie parsing result
  */
 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 cookie header (see RFC 2109).
+ * 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 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 +3075,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 +3086,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,86 +3098,23 @@ 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);
 }
 
 
+#endif /* COOKIE_SUPPORT */
+
 /**
  * Detect HTTP version
  *
@@ -3620,7 +3855,11 @@ parse_connection_headers (struct MHD_Connection 
*connection)
   const char *enc;
   size_t val_len;
 
-  if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (connection))
+#ifdef COOKIE_SUPPORT
+  enum _MHD_ParseCookie cookie_res;
+
+  cookie_res = parse_cookie_header (connection);
+  if (MHD_PARSE_COOKIE_NO_MEMORY == cookie_res)
   {
 #ifdef HAVE_MESSAGES
     MHD_DLOG (connection->daemon,
@@ -3631,6 +3870,24 @@ 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 */
+  }
+#endif /* COOKIE_SUPPORT */
   if ( (1 <= connection->daemon->strict_for_client) &&
        (MHD_IS_HTTP_VER_1_1_COMPAT (connection->http_ver)) &&
        (MHD_NO ==
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index e96ef4a2..b179297b 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -8289,6 +8289,12 @@ MHD_is_feature_supported (enum MHD_FEATURE feature)
 #else
     return MHD_NO;
 #endif
+  case MHD_FEATURE_HTTPS_COOKIE_PARSING:
+#if defined(COOKIE_SUPPORT)
+    return MHD_YES;
+#else
+    return MHD_NO;
+#endif
 
   }
   return MHD_NO;
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am
index 58158b18..4b0c3d1c 100644
--- a/src/testcurl/Makefile.am
+++ b/src/testcurl/Makefile.am
@@ -102,8 +102,6 @@ check_PROGRAMS = \
   test_add_conn_nolisten \
   test_process_headers \
   test_process_arguments \
-  test_parse_cookies \
-  test_parse_cookies_invalid \
   test_toolarge_method \
   test_toolarge_url \
   test_toolarge_request_header_name \
@@ -142,6 +140,12 @@ check_PROGRAMS = \
   test_callback \
   $(EMPTY_ITEM)
 
+if ENABLE_COOKIE
+check_PROGRAMS += \
+  test_parse_cookies \
+  test_parse_cookies_invalid
+endif
+
 if HEAVY_TESTS
 check_PROGRAMS += \
   perf_get

-- 
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]