libmicrohttpd

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

commit b54d68ff219a354920cf650bbb5418c5c2af5b66
parent 24fe454f1044082ea0f9f457fd7570387d79e50a
Author: Christian Grothoff <christian@grothoff.org>
Date:   Thu, 19 Jan 2012 16:40:57 +0000

properly fixing #2059, keeping the check that the uri from the nonce generation is exactly the same as the primary uri we got from the HTTP request

Diffstat:
MChangeLog | 4++++
Msrc/daemon/connection.c | 11+++++++++--
Msrc/daemon/digestauth.c | 165+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/include/microhttpd.h | 2+-
Msrc/testcurl/Makefile.am | 8+++++++-
5 files changed, 175 insertions(+), 15 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,7 @@ +Thu Jan 19 13:31:27 CET 2012 + Fixing digest authentication for GET requests with URI arguments + (#2059). -CG + Sat Jan 7 17:30:48 CET 2012 Digest authentication expects nonce count in base 16, not base 10 (#2061). -tclaveirole diff --git a/src/daemon/connection.c b/src/daemon/connection.c @@ -1066,6 +1066,12 @@ connection_add_header (struct MHD_Connection *connection, } /** + * Parse and unescape the arguments given by the client as part + * of the HTTP request URI. + * + * @param kind header kind to use for adding to the connection + * @param connection connection to add headers to + * @param args argument URI string (after "?" in URI) * @return MHD_NO on failure (out of memory), MHD_YES for success */ static int @@ -1111,6 +1117,7 @@ parse_arguments (enum MHD_ValueKind kind, return MHD_YES; } + /** * Parse the cookie header (see RFC 2109). * @@ -1238,7 +1245,7 @@ parse_initial_message_line (struct MHD_Connection *connection, char *line) connection->daemon->uri_log_callback (connection->daemon-> uri_log_callback_cls, uri); args = strstr (uri, "?"); - if (args != NULL) + if (NULL != args) { args[0] = '\0'; args++; @@ -1248,7 +1255,7 @@ parse_initial_message_line (struct MHD_Connection *connection, char *line) connection, uri); connection->url = uri; - if (httpVersion == NULL) + if (NULL == httpVersion) connection->version = ""; else connection->version = httpVersion; diff --git a/src/daemon/digestauth.c b/src/daemon/digestauth.c @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - (C) 2010 Daniel Pittman and Christian Grothoff + (C) 2010, 2011, 2012 Daniel Pittman and Christian Grothoff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -387,7 +387,7 @@ MHD_digest_auth_get_username(struct MHD_Connection *connection) * @param method HTTP method * @param rnd A pointer to a character array for the random seed * @param rnd_size The size of the random seed array - * @param uri HTTP URI + * @param uri HTTP URI (in MHD, without the arguments ("?k=v") * @param realm A string of characters that describes the realm of auth. * @param nonce A pointer to a character array for the nonce to put in */ @@ -428,6 +428,114 @@ calculate_nonce (uint32_t nonce_time, /** + * Test if the given key-value pair is in the headers for the + * given connection. + * + * @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, + * MHD_NO if not + */ +static int +test_header (struct MHD_Connection *connection, + const char *key, + const char *value) +{ + struct MHD_HTTP_Header *pos; + + for (pos = connection->headers_received; NULL != pos; pos = pos->next) + { + if (MHD_GET_ARGUMENT_KIND != pos->kind) + continue; + if (0 != strcmp (key, pos->header)) + continue; + if ( (NULL == value) && (NULL == pos->value)) + return MHD_YES; + if ( (NULL == value) || (NULL == pos->value)) + continue; + if (0 != strcmp (value, pos->value)) + continue; + return MHD_YES; + } + return MHD_NO; +} + + +/** + * Check that the arguments given by the client as part + * of the authentication header match the arguments we + * got as part of the HTTP request URI. + * + * @param connection connections with headers to compare against + * @param args argument URI string (after "?" in URI) + * @return MHD_YES if the arguments match, + * MHD_NO if not + */ +static int +check_argument_match (struct MHD_Connection *connection, + const char *args) +{ + struct MHD_HTTP_Header *pos; + size_t slen = strlen (args) + 1; + char argb[slen]; + char *argp; + char *equals; + char *amper; + unsigned int num_headers; + + num_headers = 0; + memcpy (argb, args, slen); + argp = argb; + while ( (argp != NULL) && + (argp[0] != '\0') ) + { + equals = strstr (argp, "="); + if (equals == NULL) + { + /* add with 'value' NULL */ + connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls, + connection, + argp); + if (MHD_YES != test_header (connection, argp, NULL)) + return MHD_NO; + num_headers++; + break; + } + equals[0] = '\0'; + equals++; + amper = strstr (equals, "&"); + if (amper != NULL) + { + amper[0] = '\0'; + amper++; + } + connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls, + connection, + argp); + connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls, + connection, + equals); + if (! test_header (connection, argp, equals)) + return MHD_NO; + num_headers++; + argp = amper; + } + + /* also check that the number of headers matches */ + for (pos = connection->headers_received; NULL != pos; pos = pos->next) + { + if (MHD_GET_ARGUMENT_KIND != pos->kind) + continue; + num_headers--; + } + if (0 != num_headers) + return MHD_NO; + return MHD_YES; +} + + +/** * Authenticates the authorization header sent by the client * * @param connection The MHD connection structure @@ -513,16 +621,40 @@ MHD_digest_auth_check(struct MHD_Connection *connection, nonce_time = strtoul(nonce + len - 8, (char **)NULL, 16); t = (uint32_t) time(NULL); /* - * First level vetting for the nonce validity - * if the timestamp attached to the nonce - * exceeds `nonce_timeout' then the nonce is + * First level vetting for the nonce validity if the timestamp + * attached to the nonce exceeds `nonce_timeout' then the nonce is * invalid. */ if ( (t > nonce_time + nonce_timeout) || - (0 != strncmp (uri, - connection->url, - strlen (connection->url))) ) - return MHD_INVALID_NONCE; + (nonce_time + nonce_timeout < nonce_time) ) + return MHD_INVALID_NONCE; + if (0 != strncmp (uri, + connection->url, + strlen (connection->url))) + { +#if HAVE_MESSAGES + MHD_DLOG (connection->daemon, + "Authentication failed, URI does not match.\n"); +#endif + return MHD_NO; + } + { + const char *args = strstr (uri, "?"); + if (args == NULL) + args = ""; + else + args++; + if (MHD_YES != + check_argument_match (connection, + args) ) + { +#if HAVE_MESSAGES + MHD_DLOG (connection->daemon, + "Authentication failed, arguments do not match.\n"); +#endif + return MHD_NO; + } + } calculate_nonce (nonce_time, connection->method, connection->daemon->digest_auth_random, @@ -550,12 +682,23 @@ MHD_digest_auth_check(struct MHD_Connection *connection, (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, + "Authentication failed, invalid format.\n"); +#endif return MHD_NO; + } nci = strtoul (nc, &end, 16); if ( ('\0' != *end) || ( (LONG_MAX == nci) && (errno == ERANGE) ) ) - return MHD_NO; /* invalid nonce */ - + { +#if HAVE_MESSAGES + MHD_DLOG (connection->daemon, + "Authentication failed, invalid format.\n"); +#endif + return MHD_NO; /* invalid nonce format */ + } /* * Checking if that combination of nonce and nc is sound * and not a replay attack attempt. Also adds the nonce diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -106,7 +106,7 @@ extern "C" /** * Current version of the library. */ -#define MHD_VERSION 0x00091101 +#define MHD_VERSION 0x00091102 /** * MHD-internal return code for "YES". diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am @@ -63,7 +63,7 @@ noinst_PROGRAMS = \ if ENABLE_DAUTH check_PROGRAMS += \ - daemontest_digestauth + daemontest_digestauth daemontest_digestauth_with_arguments endif TESTS = $(check_PROGRAMS) @@ -115,6 +115,12 @@ daemontest_digestauth_LDADD = \ $(top_builddir)/src/daemon/libmicrohttpd.la \ @LIBCURL@ +daemontest_digestauth_with_arguments_SOURCES = \ + daemontest_digestauth_with_arguments.c +daemontest_digestauth_with_arguments_LDADD = \ + $(top_builddir)/src/daemon/libmicrohttpd.la \ + @LIBCURL@ + daemontest_get_sendfile_SOURCES = \ daemontest_get_sendfile.c daemontest_get_sendfile_LDADD = \