libmicrohttpd

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

commit a5af7f737931d4ab4f64977d9a029a326f178f37
parent 6ccaa5da57dbfb74e6131ce0ff0165522ef501d6
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Fri, 17 Jun 2022 20:03:57 +0300

digest_auth_check_all(): simplified, improved readability

Added check for too long nonce (the size is know in advance, no need to
try other check if nonce size is wrong);
Used caseless match more 'qop' value (as required by RFC), too long
'qop' values reject early, added special result value for wrong qop;
Reject early with too long 'nc' value;
Reject early with wrong response size (the size is know in advance, no
need to make CPU-intensive hash calculations if size if incorrect);
Added special return value is any parameter is too large to be processed

Diffstat:
Msrc/include/microhttpd.h | 24+++++++++++++++++-------
Msrc/microhttpd/digestauth.c | 536+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/microhttpd/mhd_str.h | 9+++++++++
3 files changed, 275 insertions(+), 294 deletions(-)

diff --git 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 0x00097517 +#define MHD_VERSION 0x00097518 /* If generic headers don't work on your platform, include headers which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t', @@ -4376,7 +4376,7 @@ enum MHD_DigestAuthAlgorithm * * All error values are zero or negative. * - * @note Available since #MHD_VERSION 0x00097513 + * @note Available since #MHD_VERSION 0x00097518 */ enum MHD_DigestAuthResult { @@ -4410,25 +4410,35 @@ enum MHD_DigestAuthResult */ MHD_DAUTH_WRONG_URI = -4, + /** + * Wrong 'qop'. + */ + MHD_DAUTH_WRONG_QOP = -5, + + /** + * Too large (>64 KiB) Authorization parameter value. + */ + MHD_DAUTH_TOO_LARGE = -15, + /* The different form of naming is intentionally used for the results below, * as they are more important */ /** * The 'nonce' is too old. Suggest the client to retry with the same * username and password to get the fresh 'nonce'. - * The validity of the 'nonce' may not be checked. + * The validity of the 'nonce' may be not checked. */ - MHD_DAUTH_NONCE_STALE = -16, + MHD_DAUTH_NONCE_STALE = -17, /** * The 'nonce' is wrong. May indicate an attack attempt. */ - MHD_DAUTH_NONCE_WRONG = -32, + MHD_DAUTH_NONCE_WRONG = -33, /** * The 'response' is wrong. May indicate an attack attempt. */ - MHD_DAUTH_RESPONSE_WRONG = -33, + MHD_DAUTH_RESPONSE_WRONG = -34, }; @@ -4443,7 +4453,7 @@ enum MHD_DigestAuthResult * @param algo the digest algorithms allowed for verification * @return #MHD_DAUTH_OK if authenticated, * the error code otherwise - * @note Available since #MHD_VERSION 0x00097513 + * @note Available since #MHD_VERSION 0x00097518 * @ingroup authentication */ _MHD_EXTERN enum MHD_DigestAuthResult diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c @@ -134,7 +134,7 @@ /** * Maximum length of the response in digest authentication. */ -#define MAX_AUTH_RESPONSE_LENGTH 256 +#define MAX_AUTH_RESPONSE_LENGTH (MAX_DIGEST * 2) /** * The token for MD5 algorithm. @@ -1305,19 +1305,56 @@ check_argument_match (struct MHD_Connection *connection, */ #define _MHD_STATIC_UNQ_BUFFER_SIZE 128 + /** - * The result of parameter unquoting + * Get the pointer to buffer with required size + * @param tmp1 the first buffer with fixed size + * @param ptmp2 the pointer to pointer to malloc'ed buffer + * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2 + * @param required_size the required size in buffer + * @return the pointer to the buffer or NULL if failed to allocate buffer with + * requested size */ +static char * +get_buffer_for_size (char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], + char **ptmp2, + size_t *ptmp2_size, + size_t required_size) +{ + mhd_assert ((0 == *ptmp2_size) || (NULL != *ptmp2)); + mhd_assert ((NULL != *ptmp2) || (0 == *ptmp2_size)); + mhd_assert ((0 == *ptmp2_size) || \ + (_MHD_STATIC_UNQ_BUFFER_SIZE < *ptmp2_size)); + + if (required_size <= _MHD_STATIC_UNQ_BUFFER_SIZE) + return tmp1; + + if (required_size <= *ptmp2_size) + return *ptmp2; + + if (required_size > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) + return NULL; + if (NULL != *ptmp2) + free (*ptmp2); + *ptmp2 = (char *) malloc (required_size); + if (NULL == *ptmp2) + *ptmp2_size = 0; + else + *ptmp2_size = required_size; + return *ptmp2; +} + + +/** + * The result of parameter unquoting + */ enum _MHD_GetUnqResult { - _MHD_UNQ_NON_EMPTY = 0, /**< The sting is not empty */ - _MHD_UNQ_NO_STRING = MHD_DAUTH_WRONG_HEADER, /**< No string (no such parameter) */ - _MHD_UNQ_EMPTY = 1, /**< The string is empty */ - _MHD_UNQ_TOO_LARGE = 2, /**< The string is too large to unqoute */ - _MHD_UNQ_OUT_OF_MEM = 3 /**< Out of memory error */ + _MHD_UNQ_OK = 0, /**< Got unquoted string */ + _MHD_UNQ_TOO_LARGE = -7, /**< The string is too large to unqoute */ + _MHD_UNQ_OUT_OF_MEM = 3 /**< Out of memory error */ }; - /** * Get Digest authorisation parameter as unquoted string. * @param param the parameter to process @@ -1328,60 +1365,79 @@ enum _MHD_GetUnqResult * @return enum code indicating result of the process */ static enum _MHD_GetUnqResult -get_unqouted_param (const struct MHD_RqDAuthParam *param, +get_unquoted_param (const struct MHD_RqDAuthParam *param, char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], char **ptmp2, size_t *ptmp2_size, - struct _MHD_cstr_w_len *unquoted) + struct _MHD_str_w_len *unquoted) { char *str; size_t len; - mhd_assert ((0 == *ptmp2_size) || (NULL != *ptmp2)); - mhd_assert ((NULL != *ptmp2) || (0 == *ptmp2_size)); - mhd_assert ((0 == *ptmp2_size) || \ - (_MHD_STATIC_UNQ_BUFFER_SIZE < *ptmp2_size)); + mhd_assert (NULL != param->value.str); + mhd_assert (0 != param->value.len); - if (NULL == param->value.str) - { - const struct _MHD_cstr_w_len res = {NULL, 0}; - mhd_assert (! param->quoted); - mhd_assert (0 == param->value.len); - memcpy (unquoted, &res, sizeof(res)); - return _MHD_UNQ_NO_STRING; - } if (! param->quoted) { - memcpy (unquoted, &param->value, sizeof(param->value)); - return (0 == param->value.len) ? _MHD_UNQ_EMPTY : _MHD_UNQ_NON_EMPTY; + unquoted->str = param->value.str; + unquoted->len = param->value.len; + return _MHD_UNQ_OK; } /* The value is present and is quoted, needs to be copied and unquoted */ - mhd_assert (0 != param->value.len); - if (param->value.len <= _MHD_STATIC_UNQ_BUFFER_SIZE) - str = tmp1; - else if (param->value.len <= *ptmp2_size) - str = *ptmp2; - else - { - if (param->value.len > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) - return _MHD_UNQ_TOO_LARGE; - if (NULL != *ptmp2) - free (*ptmp2); - *ptmp2 = (char *) malloc (param->value.len); - if (NULL == *ptmp2) - return _MHD_UNQ_OUT_OF_MEM; - *ptmp2_size = param->value.len; - str = *ptmp2; - } + str = get_buffer_for_size (tmp1, ptmp2, ptmp2_size, param->value.len); + if (NULL == str) + return (param->value.len > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) ? + _MHD_UNQ_TOO_LARGE : _MHD_UNQ_OUT_OF_MEM; len = MHD_str_unquote (param->value.str, param->value.len, str); - if (1) + unquoted->str = str; + unquoted->len = len; + mhd_assert (0 != unquoted->len); + mhd_assert (unquoted->len < param->value.len); + return _MHD_UNQ_OK; +} + + +/** + * Get copy of Digest authorisation parameter as unquoted string. + * @param param the parameter to process + * @param tmp1 the small buffer in stack + * @param ptmp2 the pointer to pointer to malloc'ed buffer + * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2 + * @param[out] unquoted the pointer to store the result, NOT zero terminated, + * but with enough space to zero-terminate + * @return enum code indicating result of the process + */ +static enum _MHD_GetUnqResult +get_unquoted_param_copy (const struct MHD_RqDAuthParam *param, + char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], + char **ptmp2, + size_t *ptmp2_size, + struct _MHD_mstr_w_len *unquoted) +{ + mhd_assert (NULL != param->value.str); + mhd_assert (0 != param->value.len); + + /* The value is present and is quoted, needs to be copied and unquoted */ + /* Allocate buffer with one more additional byte for zero-termination */ + unquoted->str = + get_buffer_for_size (tmp1, ptmp2, ptmp2_size, param->value.len + 1); + + if (NULL == unquoted->str) + return (param->value.len + 1 > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) ? + _MHD_UNQ_TOO_LARGE : _MHD_UNQ_OUT_OF_MEM; + + if (! param->quoted) { - const struct _MHD_cstr_w_len res = {str, len}; - memcpy (unquoted, &res, sizeof(res)); + memcpy (unquoted->str, param->value.str, param->value.len); + unquoted->len = param->value.len; + return _MHD_UNQ_OK; } + + unquoted->len = + MHD_str_unquote (param->value.str, param->value.len, unquoted->str); mhd_assert (0 != unquoted->len); mhd_assert (unquoted->len < param->value.len); - return _MHD_UNQ_NON_EMPTY; + return _MHD_UNQ_OK; } @@ -1424,18 +1480,21 @@ is_param_equal (const struct MHD_RqDAuthParam *param, * (must contain "da->digest_size" bytes or be NULL) * @param nonce_timeout The amount of time for a nonce to be * invalid in seconds + * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer, + * to be free if not NULL upon return * @return #MHD_DAUTH_OK if authenticated, * error code otherwise. * @ingroup authentication */ static enum MHD_DigestAuthResult -digest_auth_check_all (struct MHD_Connection *connection, - struct DigestAlgorithm *da, - const char *realm, - const char *username, - const char *password, - const uint8_t *digest, - unsigned int nonce_timeout) +digest_auth_check_all_inner (struct MHD_Connection *connection, + struct DigestAlgorithm *da, + const char *realm, + const char *username, + const char *password, + const uint8_t *digest, + unsigned int nonce_timeout, + char **pbuf) { struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); char cnonce[MAX_CLIENT_NONCE_LENGTH]; @@ -1452,91 +1511,63 @@ digest_auth_check_all (struct MHD_Connection *connection, char *qmark; const struct MHD_RqDAuth *params; char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE]; /**< Temporal buffer in stack for unqouting */ - char *tmp2; /**< Temporal malloc'ed buffer for unqouting */ + char **const ptmp2 = pbuf; /**< Temporal malloc'ed buffer for unqouting */ size_t tmp2_size; /**< The size of @a tmp2 buffer */ - struct _MHD_cstr_w_len unquoted; + struct _MHD_str_w_len unquoted; + struct _MHD_mstr_w_len unq_copy; enum _MHD_GetUnqResult unq_res; enum MHD_DigestAuthResult ret; -#ifdef HAVE_MESSAGES - bool err_logged; -#endif /* HAVE_MESSAGES */ size_t username_len; size_t realm_len; - tmp2 = NULL; tmp2_size = 0; -#ifdef HAVE_MESSAGES - err_logged = false; -#endif /* HAVE_MESSAGES */ do /* Only to avoid "goto" */ { params = get_rq_dauth_params (connection); if (NULL == params) - { - ret = MHD_DAUTH_WRONG_HEADER; - break; - } + return MHD_DAUTH_WRONG_HEADER; /* Check 'username' */ if (NULL == params->username.value.str) - { - ret = MHD_DAUTH_WRONG_HEADER; - break; - } + return MHD_DAUTH_WRONG_HEADER; + username_len = strlen (username); if (! is_param_equal (&params->username, username, username_len)) - { - ret = MHD_DAUTH_WRONG_USERNAME; - break; - } + return MHD_DAUTH_WRONG_USERNAME; /* 'username' valid */ /* Check 'realm' */ if (NULL == params->realm.value.str) - { - ret = MHD_DAUTH_WRONG_HEADER; - break; - } + return MHD_DAUTH_WRONG_HEADER; realm_len = strlen (realm); if (! is_param_equal (&params->realm, realm, realm_len)) - { - ret = MHD_DAUTH_WRONG_REALM; - break; - } + return MHD_DAUTH_WRONG_REALM; /* 'realm' valid */ /* Check 'nonce' */ - unq_res = get_unqouted_param (&params->nonce, tmp1, &tmp2, &tmp2_size, + if (NULL == params->nonce.value.str) + return MHD_DAUTH_WRONG_HEADER; + else if (0 == params->nonce.value.len) + return MHD_DAUTH_NONCE_WRONG; + else if (NONCE_STD_LEN (digest_size) * 2 < params->nonce.value.len) + return MHD_DAUTH_NONCE_WRONG; + + unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size, &unquoted); - if (_MHD_UNQ_NON_EMPTY != unq_res) - { - if (_MHD_UNQ_NO_STRING == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_EMPTY == unq_res) - ret = MHD_DAUTH_NONCE_WRONG; - else if (_MHD_UNQ_TOO_LARGE == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_OUT_OF_MEM == unq_res) - ret = MHD_DAUTH_ERROR; - else - { - mhd_assert (0); /* Must not happen */ - ret = MHD_DAUTH_ERROR; - } - break; - } - /* TODO: check correct 'nonce' length */ + if (_MHD_UNQ_OK != unq_res) + return MHD_DAUTH_ERROR; + if (NONCE_STD_LEN (digest_size) != unquoted.len) + return MHD_DAUTH_NONCE_WRONG; + if (! get_nonce_timestamp (unquoted.str, unquoted.len, &nonce_time)) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, _ ("Authentication failed, invalid timestamp format.\n")); - err_logged = true; #endif - ret = MHD_DAUTH_NONCE_WRONG; - break; + return MHD_DAUTH_NONCE_WRONG; } t = MHD_monotonic_msec_counter (); /* @@ -1545,11 +1576,7 @@ digest_auth_check_all (struct MHD_Connection *connection, * invalid. */ if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000)) - { - /* too old */ - ret = MHD_DAUTH_NONCE_STALE; - break; - } + return MHD_DAUTH_NONCE_STALE; /* too old */ calculate_nonce (nonce_time, connection->method, @@ -1571,107 +1598,66 @@ digest_auth_check_all (struct MHD_Connection *connection, */ if ((0 != strncmp (noncehashexp, unquoted.str, unquoted.len)) || (0 != noncehashexp[unquoted.len])) - { - ret = MHD_DAUTH_NONCE_WRONG; - break; - } + return MHD_DAUTH_NONCE_WRONG; /* 'nonce' valid */ /* Get 'cnonce' */ - unq_res = get_unqouted_param (&params->cnonce, tmp1, &tmp2, &tmp2_size, + if (NULL == params->cnonce.value.str) + return MHD_DAUTH_WRONG_HEADER; + else if (0 == params->cnonce.value.len) + return MHD_DAUTH_WRONG_HEADER; + unq_res = get_unquoted_param (&params->cnonce, tmp1, ptmp2, &tmp2_size, &unquoted); - if (_MHD_UNQ_NON_EMPTY != unq_res) - { - if (_MHD_UNQ_NO_STRING == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_EMPTY == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_TOO_LARGE == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_OUT_OF_MEM == unq_res) - ret = MHD_DAUTH_ERROR; - else - { - mhd_assert (0); /* Must not happen */ - ret = MHD_DAUTH_ERROR; - } - break; - } + if (_MHD_UNQ_OK != unq_res) + return (_MHD_UNQ_TOO_LARGE == unq_res) ? + MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR; + if (sizeof(cnonce) <= unquoted.len) - { - /* TODO: handle large client nonces */ - ret = MHD_DAUTH_ERROR; - break; - } + return MHD_DAUTH_ERROR; /* TODO: handle large client nonces */ + /* TODO: avoid memcpy() */ memcpy (cnonce, unquoted.str, unquoted.len); cnonce[unquoted.len] = 0; /* Got 'cnonce' */ /* Get 'qop' */ - unq_res = get_unqouted_param (&params->qop, tmp1, &tmp2, &tmp2_size, + if (NULL == params->qop.value.str) + return MHD_DAUTH_WRONG_HEADER; + else if (0 == params->qop.value.len) + return MHD_DAUTH_WRONG_QOP; + else if (MHD_STATICSTR_LEN_ ("auth-int") * 2 < params->qop.value.len) + return MHD_DAUTH_WRONG_QOP; + unq_res = get_unquoted_param (&params->qop, tmp1, ptmp2, &tmp2_size, &unquoted); - if (_MHD_UNQ_NON_EMPTY != unq_res) - { - if (_MHD_UNQ_NO_STRING == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_EMPTY == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_TOO_LARGE == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_OUT_OF_MEM == unq_res) - ret = MHD_DAUTH_ERROR; - else - { - mhd_assert (0); /* Must not happen */ - ret = MHD_DAUTH_ERROR; - } - break; - } + if (_MHD_UNQ_OK != unq_res) + return MHD_DAUTH_ERROR; + if (sizeof(qop) <= unquoted.len) - { - /* TODO: handle large client qop */ - ret = MHD_DAUTH_ERROR; - break; - } + return MHD_DAUTH_ERROR; /* TODO: handle large client qop */ /* TODO: avoid memcpy() */ memcpy (qop, unquoted.str, unquoted.len); qop[unquoted.len] = 0; - /* TODO: use caseless match, use dedicated return code */ - if ( (0 != strcmp (qop, "auth")) && + /* TODO: Really support empty value, support "auth-int" */ + if ( ((MHD_STATICSTR_LEN_ ("auth") != unquoted.len) || + (! MHD_str_equal_caseless_bin_n_ (qop, "auth", unquoted.len))) && (0 != strcmp (qop,"")) ) - { - ret = MHD_DAUTH_WRONG_HEADER; - break; - } + return MHD_DAUTH_WRONG_QOP; /* Got 'qop' */ /* Get 'nc' */ - unq_res = get_unqouted_param (&params->nc, tmp1, &tmp2, &tmp2_size, + if (NULL == params->nc.value.str) + return MHD_DAUTH_WRONG_HEADER; + else if (0 == params->nc.value.len) + return MHD_DAUTH_WRONG_HEADER; + else if (4 * 8 < params->nc.value.len) /* Four time more than needed */ + return MHD_DAUTH_NONCE_WRONG; + unq_res = get_unquoted_param (&params->nc, tmp1, ptmp2, &tmp2_size, &unquoted); - if (_MHD_UNQ_NON_EMPTY != unq_res) - { - if (_MHD_UNQ_NO_STRING == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_EMPTY == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_TOO_LARGE == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_OUT_OF_MEM == unq_res) - ret = MHD_DAUTH_ERROR; - else - { - mhd_assert (0); /* Must not happen */ - ret = MHD_DAUTH_ERROR; - } - break; - } + if (_MHD_UNQ_OK != unq_res) + return MHD_DAUTH_ERROR; + if (sizeof(nc) <= unquoted.len) - { - /* TODO: handle large client nc */ - ret = MHD_DAUTH_ERROR; - break; - } + return MHD_DAUTH_ERROR; /* TODO: avoid memcpy() */ memcpy (nc, unquoted.str, unquoted.len); nc[unquoted.len] = 0; @@ -1682,49 +1668,37 @@ digest_auth_check_all (struct MHD_Connection *connection, #ifdef HAVE_MESSAGES MHD_DLOG (daemon, _ ("Authentication failed, invalid nc format.\n")); - err_logged = true; #endif - ret = MHD_DAUTH_WRONG_HEADER; /* invalid nonce format */ - break; + return MHD_DAUTH_WRONG_HEADER; /* invalid nonce format */ } if (0 == nci) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, _ ("Authentication failed, invalid 'nc' value.\n")); - err_logged = true; #endif - ret = MHD_DAUTH_WRONG_HEADER; /* invalid nc value */ - break; + return MHD_DAUTH_WRONG_HEADER; /* invalid nc value */ } /* Got 'nc' */ /* Get 'response' */ - unq_res = get_unqouted_param (&params->response, tmp1, &tmp2, &tmp2_size, + if (NULL == params->response.value.str) + return MHD_DAUTH_WRONG_HEADER; + else if (0 == params->response.value.len) + return MHD_DAUTH_RESPONSE_WRONG; + else if (digest_size * 4 < params->response.value.len) + return MHD_DAUTH_RESPONSE_WRONG; + unq_res = get_unquoted_param (&params->response, tmp1, ptmp2, &tmp2_size, &unquoted); - if (_MHD_UNQ_NON_EMPTY != unq_res) - { - if (_MHD_UNQ_NO_STRING == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_EMPTY == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_TOO_LARGE == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_OUT_OF_MEM == unq_res) - ret = MHD_DAUTH_ERROR; - else - { - mhd_assert (0); /* Must not happen */ - ret = MHD_DAUTH_ERROR; - } - break; - } + if (_MHD_UNQ_OK != unq_res) + return MHD_DAUTH_ERROR; + if (digest_size * 2 != unquoted.len) + return MHD_DAUTH_RESPONSE_WRONG; + + mhd_assert (sizeof(response) > unquoted.len); + if (sizeof(response) <= unquoted.len) - { - /* TODO: handle large client response */ - ret = MHD_DAUTH_ERROR; - break; - } + return MHD_DAUTH_ERROR; /* TODO: avoid memcpy() */ memcpy (response, unquoted.str, unquoted.len); response[unquoted.len] = 0; @@ -1749,10 +1723,8 @@ digest_auth_check_all (struct MHD_Connection *connection, MHD_DLOG (daemon, _ ("Stale nonce received. If this happens a lot, you should " "probably increase the size of the nonce array.\n")); - err_logged = true; #endif - ret = MHD_DAUTH_NONCE_STALE; - break; + return MHD_DAUTH_NONCE_STALE; } else if (MHD_CHECK_NONCENC_WRONG == nonce_nc_check) { @@ -1760,34 +1732,23 @@ digest_auth_check_all (struct MHD_Connection *connection, MHD_DLOG (daemon, _ ("Received nonce that technically valid, but was not " "generated by MHD. This may indicate an attack attempt.\n")); - err_logged = true; #endif - ret = MHD_DAUTH_NONCE_WRONG; - break; + return MHD_DAUTH_NONCE_WRONG; } mhd_assert (MHD_CHECK_NONCENC_OK == nonce_nc_check); } /* Get 'uri' */ - unq_res = get_unqouted_param (&params->uri, tmp1, &tmp2, &tmp2_size, - &unquoted); - if (_MHD_UNQ_NON_EMPTY != unq_res) - { - if (_MHD_UNQ_NO_STRING == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_EMPTY == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_TOO_LARGE == unq_res) - ret = MHD_DAUTH_WRONG_HEADER; - else if (_MHD_UNQ_OUT_OF_MEM == unq_res) - ret = MHD_DAUTH_ERROR; - else - { - mhd_assert (0); /* Must not happen */ - ret = MHD_DAUTH_ERROR; - } - break; - } + if (NULL == params->uri.value.str) + return MHD_DAUTH_WRONG_HEADER; + else if (0 == params->uri.value.len) + return MHD_DAUTH_WRONG_URI; + unq_res = get_unquoted_param_copy (&params->uri, tmp1, ptmp2, &tmp2_size, + &unq_copy); + if (_MHD_UNQ_OK != unq_res) + return (_MHD_UNQ_TOO_LARGE == unq_res) ? + MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR; + if (NULL != digest) { /* This will initialize da->digest_hex (ha1) */ @@ -1821,45 +1782,16 @@ digest_auth_check_all (struct MHD_Connection *connection, cnonce, qop, connection->method, - unquoted.str, - unquoted.len, + unq_copy.str, + unq_copy.len, hentity, da); if (1) { char *uri; size_t uri_len; - uri_len = unquoted.len; - /* TODO: simplify string copy, avoid potential double copy */ - if ( ((tmp1 != unquoted.str) && (tmp2 != unquoted.str)) || - ((tmp1 == unquoted.str) && (sizeof(tmp1) >= unquoted.len)) || - ((tmp2 == unquoted.str) && (tmp2_size >= unquoted.len))) - { - char *buf; - mhd_assert ((tmp1 != unquoted.str) || \ - (sizeof(tmp1) == unquoted.len)); - mhd_assert ((tmp2 != unquoted.str) || \ - (tmp2_size == unquoted.len)); - buf = malloc (unquoted.len + 1); - if (NULL == buf) - { - ret = MHD_DAUTH_ERROR; - break; - } - memcpy (buf, unquoted.str, unquoted.len); - if (NULL != tmp2) - free (tmp2); - tmp2 = buf; - tmp2_size = unquoted.len + 1; - uri = tmp2; - } - else if (tmp1 == unquoted.str) - uri = tmp1; - else - { - mhd_assert (tmp2 == unquoted.str); - uri = tmp2; - } + uri = unq_copy.str; + uri_len = unq_copy.len; uri[uri_len] = 0; qmark = memchr (uri, @@ -1878,10 +1810,8 @@ digest_auth_check_all (struct MHD_Connection *connection, #ifdef HAVE_MESSAGES MHD_DLOG (daemon, _ ("Authentication failed, URI does not match.\n")); - err_logged = true; #endif - ret = MHD_DAUTH_WRONG_URI; - break; + return MHD_DAUTH_WRONG_URI; } if (1) @@ -1899,10 +1829,8 @@ digest_auth_check_all (struct MHD_Connection *connection, #ifdef HAVE_MESSAGES MHD_DLOG (daemon, _ ("Authentication failed, arguments do not match.\n")); - err_logged = true; #endif - ret = MHD_DAUTH_WRONG_URI; - break; + return MHD_DAUTH_WRONG_URI; } } @@ -1912,19 +1840,53 @@ digest_auth_check_all (struct MHD_Connection *connection, : MHD_DAUTH_RESPONSE_WRONG; } } while (0); - if (NULL != tmp2) - free (tmp2); - - if ((MHD_DAUTH_OK != ret) && ! err_logged) - { - (void) 0; /* TODO: add logging */ - } return ret; } /** + * Authenticates the authorization header sent by the client + * + * @param connection The MHD connection structure + * @param[in,out] da digest algorithm to use for checking (written to as + * part of the calculations, but the values left in the struct + * are not actually expected to be useful for the caller) + * @param realm The realm presented to the client + * @param username The username needs to be authenticated + * @param password The password used in the authentication + * @param digest An optional binary hash + * of the precalculated hash value "username:realm:password" + * (must contain "da->digest_size" bytes or be NULL) + * @param nonce_timeout The amount of time for a nonce to be + * invalid in seconds + * @return #MHD_DAUTH_OK if authenticated, + * error code otherwise. + * @ingroup authentication + */ +static enum MHD_DigestAuthResult +digest_auth_check_all (struct MHD_Connection *connection, + struct DigestAlgorithm *da, + const char *realm, + const char *username, + const char *password, + const uint8_t *digest, + unsigned int nonce_timeout) +{ + enum MHD_DigestAuthResult res; + char *buf; + + buf = NULL; + res = digest_auth_check_all_inner (connection, da, realm, username, password, + digest, nonce_timeout, &buf); + if (NULL != buf) + free (buf); + + return res; +} + + +/** * Authenticates the authorization header sent by the client. * Uses #MHD_DIGEST_ALG_MD5 (for now, for backwards-compatibility). * Note that this MAY change to #MHD_DIGEST_ALG_AUTO in the future. diff --git a/src/microhttpd/mhd_str.h b/src/microhttpd/mhd_str.h @@ -73,6 +73,15 @@ struct _MHD_str_w_len }; /** + * Modifiable string with length + */ +struct _MHD_mstr_w_len +{ + char *str; + size_t len; +}; + +/** * Static string initialiser for struct _MHD_str_w_len */ #define _MHD_S_STR_W_LEN(str) { str, MHD_STATICSTR_LEN_(str) }