libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 4245c6e9c371a8434b13a37edbc4e6dc239813da
parent ba6b6cad9a70cef22f62d5b76cb1180b2aca4e94
Author: Christian Grothoff <christian@grothoff.org>
Date:   Fri, 29 Nov 2013 19:18:51 +0000

eliminate theoretical stack overflow

Diffstat:
MChangeLog | 7+++++++
Msrc/microhttpd/digestauth.c | 178++++++++++++++++++++++++++++++++++++++++++-------------------------------------
2 files changed, 101 insertions(+), 84 deletions(-)

diff --git a/ChangeLog b/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 diff --git a/src/microhttpd/digestauth.c b/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; }