libmicrohttpd

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

commit 1e11e972c5089c43061d382cb39a573d3adedcce
parent 2fed504cefb1990ab26ee4f105006c74b68c79ca
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed,  1 Sep 2010 19:20:01 +0000

fixing authentication header parser

Diffstat:
Msrc/daemon/digestauth.c | 334+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
1 file changed, 207 insertions(+), 127 deletions(-)

diff --git a/src/daemon/digestauth.c b/src/daemon/digestauth.c @@ -28,9 +28,32 @@ #define HASH_MD5_HEX_LEN (2 * MD5_DIGEST_SIZE) +/** + * Beginning string for any valid Digest authentication header. + */ #define _BASE "Digest " /** + * Maximum length of a username for digest authentication. + */ +#define MAX_USERNAME_LENGTH 128 + +/** + * Maximum length of a realm for digest authentication. + */ +#define MAX_REALM_LENGTH 256 + +/** + * Maximum length of a nonce in digest authentication. + */ +#define MAX_NONCE_LENGTH 128 + +/** + * Maximum length of the response in digest authentication. + */ +#define MAX_AUTH_RESPONSE_LENGTH 128 + +/** * convert bin to hex * * @param bin binary data @@ -59,6 +82,12 @@ cvthex(const unsigned char *bin, * calculate H(A1) as per RFC2617 spec and store the * result in 'sessionkey'. * + * @param alg FIXME: document + * @param username FIXME: document + * @param realm FIXME: document + * @param password FIXME: document + * @param nonce FIXME: document + * @param cnonce FIXME: document * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes */ static void @@ -156,49 +185,81 @@ digest_calc_response(const char *ha1, /** - * Lookup subvalue off of the HTTP Authorization header + * Lookup subvalue off of the HTTP Authorization header. + * + * A description of the input format for 'data' is at + * http://en.wikipedia.org/wiki/Digest_access_authentication * - * @param dest A pointer to char * to store the result - * @param size The size of dst - * @param data A pointer to char * of the Authorization header - * @param key A pointer to char * of the key in the header + * + * @param dest where to store the result (possibly truncated if + * the buffer is not big enough). + * @param size size of dest + * @param data pointer to the Authorization header + * @param key key to look up in data * @return size of the located value, 0 if otherwise */ static int lookup_sub_value(char *dest, - size_t size, - const char *data, - const char *key) + size_t size, + const char *data, + const char *key) { size_t keylen = strlen(key); const char *ptr = data; - char field[size]; - char fmt[24 + keylen + 1]; - int items_read; - - ptr += strstr(ptr, key) - ptr; - - if (*(ptr + keylen) != ' ' && *(ptr + keylen) != '=') - { - ++ptr; - ptr += strstr(ptr, key) - ptr; - } - if (!ptr) + const char *eq; + const char *q1; + const char *q2; + + if (0 == size) return 0; - - snprintf(fmt, - sizeof (fmt), - "%s%%*[ =\"]%%%u[^, \"]", - key, - (unsigned int) size - 1); - - items_read = sscanf(ptr, fmt, field); - - if (items_read == 1) + while ('\0' != *ptr) { - strcpy(dest, field); - return strlen(dest); - } + if (NULL == (eq = strstr (ptr, "="))) + return 0; + q1 = strstr (eq, "\""); + if (q1 == NULL) + { + q1 = eq + 1; + q2 = strstr (q1, ","); + } + else + { + q2 = strstr (q1 + 1, "\""); + if (NULL == q2) + return 0; /* end quote not found */ + } + if (0 == strncasecmp (ptr, + key, + keylen)) + { + if (q2 == NULL) + { + strncpy (dest, + q1 + 1, + size - 1); + dest[size-1] = '\0'; + return strlen (dest); + } + else + { + if (size >= q2 - q1) + size = (q2 - q1) - 1; + memcpy (dest, + q1 + 1, + size); + dest[size] = '\0'; + return size; + } + } + if (NULL == q2) + return 0; + ptr = strstr (q2, ","); + if (NULL == ptr) + return 0; + ptr++; + while (' ' == *ptr) + ptr++; + } return 0; } @@ -214,7 +275,7 @@ char * MHD_digest_auth_get_username(struct MHD_Connection *connection) { size_t len; - char user[50]; + char user[MAX_USERNAME_LENGTH]; const char *header; header = MHD_lookup_connection_value(connection, @@ -234,6 +295,17 @@ MHD_digest_auth_get_username(struct MHD_Connection *connection) return strdup(user); } + +/** + * FIXME: document + * + * @param nonce_time FIXME: document + * @param method FIXME: document + * @param rnd FIXME: document + * @param uri FIXME: document + * @param realm FIXME: document + * @param nonce FIXME: document + */ static void calculate_nonce (uint32_t nonce_time, const char *method, @@ -287,24 +359,21 @@ MHD_digest_auth_check(struct MHD_Connection *connection, const char *password, unsigned int nonce_timeout) { - int auth; size_t len; const char *header; - const char *rnd; - char ret[60]; - char nonce[50]; - char cnonce[50]; - char uri[100]; -/*char qop[15]; // Uncomment when supporting "auth-int" */ - char qop[] = "auth"; /* "auth-int" is not supported */ - char nc[10]; - char response[35]; - char *hentity = NULL; /* "auth-int" is not supported */ + char nonce[MAX_NONCE_LENGTH]; + char cnonce[MAX_NONCE_LENGTH]; + /* char qop[15]; // Uncomment when supporting "auth-int" */ + const char * qop = "auth"; /* "auth-int" is not supported */ + char nc[20]; + char response[MAX_AUTH_RESPONSE_LENGTH]; + const char *hentity = NULL; /* "auth-int" is not supported */ char ha1[HASH_MD5_HEX_LEN + 1]; char respexp[HASH_MD5_HEX_LEN + 1]; char noncehashexp[HASH_MD5_HEX_LEN + 9]; uint32_t nonce_time; uint32_t t; + size_t left; /* number of characters left in 'header' for 'uri' */ header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, @@ -313,86 +382,100 @@ MHD_digest_auth_check(struct MHD_Connection *connection, return MHD_NO; if (strncmp(header, _BASE, strlen(_BASE)) != 0) return MHD_NO; + header += strlen (_BASE); + left = strlen (header); - rnd = connection->daemon->digest_auth_random; - - len = lookup_sub_value(ret, - sizeof (ret), - header, "username"); - if ( (!len) || - (strcmp(username, ret) != 0) ) - return MHD_NO; - len = lookup_sub_value(ret, - sizeof (ret), - header, "realm"); - if ( (!len) || - (strcmp(realm, ret) != 0) ) - return MHD_NO; - if ( (0 == lookup_sub_value(uri, - sizeof (uri), - header, "uri")) || - (0 == (len = lookup_sub_value(nonce, - sizeof (nonce), - header, "nonce"))) ) - return MHD_NO; - - /* 8 = 4 hexadecimal numbers for the timestamp */ - nonce_time = strtoul(nonce + len - 8, 0, 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 - * invalid. - */ - if (t > nonce_time + nonce_timeout) - return MHD_INVALID_NONCE; - calculate_nonce (nonce_time, - connection->method, - rnd, - uri, - realm, - noncehashexp); - /* - * Second level vetting for the nonce validity - * if the timestamp attached to the nonce is valid - * and possibility fabricated (in case of an attack) - * the attacker must also know the password to be - * able to generate a "sane" nonce, which if he does - * 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), - header, "cnonce")) || - /* (0 == lookup_sub_value(qop, sizeof (qop), header, "qop")) || // Uncomment when supporting "auth-int" */ - (0 == lookup_sub_value(nc, sizeof (nc), header, "nc")) || - (0 == lookup_sub_value(response, sizeof (response), header, "response")) ) + { + char un[MAX_USERNAME_LENGTH]; + len = lookup_sub_value(un, + sizeof (un), + header, "username"); + if ( (!len) || + (strcmp(username, un) != 0) ) + return MHD_NO; + left -= strlen ("username") + len; + } + + { + char r[MAX_REALM_LENGTH]; + len = lookup_sub_value(r, + sizeof (r), + header, "realm"); + if ( (!len) || + (strcmp(realm, r) != 0) ) + return MHD_NO; + left -= strlen ("realm") + len; + } + + if (0 == (len = lookup_sub_value(nonce, + sizeof (nonce), + header, "nonce"))) return MHD_NO; + left -= strlen ("nonce") + len; - digest_calc_ha1("md5", - username, - realm, - password, - nonce, - cnonce, - ha1); - digest_calc_response(ha1, - nonce, - nc, - cnonce, - qop, - connection->method, - uri, - hentity, - respexp); - - auth = strcmp(response, respexp) == 0 ? MHD_YES : MHD_NO; + { + char uri[left]; - return auth; + 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, 0, 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 + * invalid. + */ + if (t > nonce_time + nonce_timeout) + return MHD_INVALID_NONCE; + calculate_nonce (nonce_time, + connection->method, + connection->daemon->digest_auth_random, + uri, + realm, + noncehashexp); + /* + * Second level vetting for the nonce validity + * if the timestamp attached to the nonce is valid + * and possibility fabricated (in case of an attack) + * the attacker must also know the password to be + * able to generate a "sane" nonce, which if he does + * 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), + header, "cnonce")) || + /* (0 == lookup_sub_value(qop, sizeof (qop), header, "qop")) || // Uncomment when supporting "auth-int" */ + (0 == lookup_sub_value(nc, sizeof (nc), header, "nc")) || + (0 == lookup_sub_value(response, sizeof (response), header, "response")) ) + return MHD_NO; + + digest_calc_ha1("md5", + username, + realm, + password, + nonce, + cnonce, + ha1); + digest_calc_response(ha1, + nonce, + nc, + cnonce, + qop, + connection->method, + uri, + hentity, + respexp); + return strcmp(response, respexp) == 0 ? MHD_YES : MHD_NO; + } } @@ -414,20 +497,17 @@ MHD_queue_auth_fail_response(struct MHD_Connection *connection, { int ret; size_t hlen; - const char *rnd; char nonce[HASH_MD5_HEX_LEN + 9]; struct MHD_Response *response; response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO); if (NULL == response) return MHD_NO; - - rnd = connection->daemon->digest_auth_random; /* Generating the server nonce */ calculate_nonce ((uint32_t) time(NULL), connection->method, - rnd, + connection->daemon->digest_auth_random, connection->url, realm, nonce); @@ -439,7 +519,7 @@ MHD_queue_auth_fail_response(struct MHD_Connection *connection, realm, nonce, opaque, - signal_stale ? ",stale=true" : ""); + signal_stale ? ",stale=\"true\"" : ""); { char header[hlen + 1]; snprintf(header, @@ -448,7 +528,7 @@ MHD_queue_auth_fail_response(struct MHD_Connection *connection, realm, nonce, opaque, - signal_stale ? ",stale=true" : ""); + signal_stale ? ",stale=\"true\"" : ""); ret = MHD_add_response_header(response, MHD_HTTP_HEADER_WWW_AUTHENTICATE, header);