diff -U2 -arB libmicrohttpd/configure libmicrohttpd.new/configure --- libmicrohttpd/configure 2010-08-18 23:46:53.000000000 +0000 +++ libmicrohttpd.new/configure 2010-08-18 23:41:44.000000000 +0000 @@ -761,4 +761,6 @@ USE_COVERAGE_FALSE USE_COVERAGE_TRUE +ENABLE_DAUTH_FALSE +ENABLE_DAUTH_TRUE ENABLE_HTTPS_FALSE ENABLE_HTTPS_TRUE @@ -911,4 +913,5 @@ with_gnutls enable_https +enable_dauth enable_coverage ' @@ -1552,4 +1555,5 @@ --enable-messages enable MHD error messages --enable-https enable HTTPS support (default is yes) + --disable-digest-auth disable HTTP Digest Auth support (default is no) --enable-coverage compile the library with code coverage support (default is NO) @@ -5107,11 +5111,11 @@ lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:5109: $ac_compile\"" >&5) + (eval echo "\"\$as_me:5113: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:5112: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:5116: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:5115: output\"" >&5) + (eval echo "\"\$as_me:5119: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then @@ -6305,5 +6309,5 @@ *-*-irix6*) # Find out which ABI we are using. - echo '#line 6307 "configure"' > conftest.$ac_ext + echo '#line 6311 "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 @@ -7833,9 +7837,9 @@ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7835: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7839: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7839: \$? = $ac_status" >&5 + echo "$as_me:7843: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized @@ -8172,9 +8176,9 @@ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:8174: $lt_compile\"" >&5) + (eval echo "\"\$as_me:8178: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:8178: \$? = $ac_status" >&5 + echo "$as_me:8182: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized @@ -8277,9 +8281,9 @@ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:8279: $lt_compile\"" >&5) + (eval echo "\"\$as_me:8283: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:8283: \$? = $ac_status" >&5 + echo "$as_me:8287: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then @@ -8332,9 +8336,9 @@ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:8334: $lt_compile\"" >&5) + (eval echo "\"\$as_me:8338: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:8338: \$? = $ac_status" >&5 + echo "$as_me:8342: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then @@ -10716,5 +10720,5 @@ lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10718 "configure" +#line 10722 "configure" #include "confdefs.h" @@ -10812,5 +10816,5 @@ lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10814 "configure" +#line 10818 "configure" #include "confdefs.h" @@ -12955,4 +12959,37 @@ +# optional: HTTP Digest Auth support. Enabled by default +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to disable HTTP Digest Auth support" >&5 +$as_echo_n "checking whether to disable HTTP Digest Auth support... " >&6; } +# Check whether --enable-dauth was given. +if test "${enable_dauth+set}" = set; then : + enableval=$enable_dauth; disable_dauth=$enableval +else + disable_dauth="no" +fi + +if test "$disable_dauth" = "no" +then + if test "$gcrypt" = "true" + then + enable_dauth="yes" + MHD_LIBDEPS="$LIBGCRYPT_LIBS" + else + enable_dauth="no" + disable_dauth="yes (lacking libgcrypt)" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $disable_dauth" >&5 +$as_echo "$disable_dauth" >&6; } + + if test "$disable_dauth" = "no" -a "$gcrypt" = "true"; then + ENABLE_DAUTH_TRUE= + ENABLE_DAUTH_FALSE='#' +else + ENABLE_DAUTH_TRUE='#' + ENABLE_DAUTH_FALSE= +fi + + MHD_LIB_LDFLAGS="-export-dynamic -no-undefined" @@ -13184,4 +13221,8 @@ Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${ENABLE_DAUTH_TRUE}" && test -z "${ENABLE_DAUTH_FALSE}"; then + as_fn_error "conditional \"ENABLE_DAUTH\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${USE_COVERAGE_TRUE}" && test -z "${USE_COVERAGE_FALSE}"; then as_fn_error "conditional \"USE_COVERAGE\" was never defined. @@ -15469,4 +15510,5 @@ libcurl (testing): ${MSG_CURL} HTTPS support: ${enable_https} + HTTP Digest Auth: ${enable_dauth} " >&5 $as_echo "$as_me: Configuration Summary: @@ -15477,4 +15519,5 @@ libcurl (testing): ${MSG_CURL} HTTPS support: ${enable_https} + HTTP Digest Auth: ${enable_dauth} " >&6;} diff -U2 -arB libmicrohttpd/configure.ac libmicrohttpd.new/configure.ac --- libmicrohttpd/configure.ac 2010-08-18 23:46:53.000000000 +0000 +++ libmicrohttpd.new/configure.ac 2010-08-18 23:41:44.000000000 +0000 @@ -292,4 +292,26 @@ AM_CONDITIONAL(ENABLE_HTTPS, test "$enable_https" = "yes") +# optional: HTTP Digest Auth support. Enabled by default +AC_MSG_CHECKING(whether to disable HTTP Digest Auth support) +AC_ARG_ENABLE([dauth], + [AS_HELP_STRING([--disable-digest-auth], + [disable HTTP Digest Auth support (default is no)])], + [disable_dauth=$enableval], + [disable_dauth="no"]) +if test "$disable_dauth" = "no" +then + if test "$gcrypt" = "true" + then + enable_dauth="yes" + MHD_LIBDEPS="$LIBGCRYPT_LIBS" + else + enable_dauth="no" + disable_dauth="yes (lacking libgcrypt)" + fi +fi +AC_MSG_RESULT($disable_dauth) + +AM_CONDITIONAL(ENABLE_DAUTH, test "$disable_dauth" = "no" -a "$gcrypt" = "true") + MHD_LIB_LDFLAGS="-export-dynamic -no-undefined" @@ -366,4 +388,5 @@ libcurl (testing): ${MSG_CURL} HTTPS support: ${enable_https} + HTTP Digest Auth: ${enable_dauth} ]) Only in libmicrohttpd/: .git diff -U2 -arB libmicrohttpd/src/daemon/digestauth.c libmicrohttpd.new/src/daemon/digestauth.c --- libmicrohttpd/src/daemon/digestauth.c 2010-08-19 00:15:04.000000000 +0000 +++ libmicrohttpd.new/src/daemon/digestauth.c 2010-08-18 23:41:44.000000000 +0000 @@ -0,0 +1,598 @@ +/* + This file is part of libmicrohttpd + (C) 2007, 2009, 2010 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 + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/** + * @file digestauth.c + * @brief Implements HTTP/1.1 Digest Auth according to RFC2617 + * @author Amr Ali + */ + +#include +#include +#include +#include +#include "internal.h" +#include "connection.h" +#include "microhttpd.h" +#include "digestauth.h" + +#define INVALID_NONCE 2 + +#define HASH_MD5_LEN 16 +#define HASH_SHA1_LEN 20 +#define HASH_MD5_HEX_LEN 32 +#define HASH_SHA1_HEX_LEN 40 + +#define _OPAQUE "opaque=\"11733b200778ce33060f31c9af70a870ba96ddd4\"" +#define _QOP "qop=\"auth\"" +#define _STALE "stale=true" +#define _BASE "Digest " +#define _REALM "realm=" +#define _NONCE "nonce=" +#define _QUOTE "\"" +#define _COM "," + +/* convert bin to hex */ +static void +cvthex(char *bin, int len, char *hex) +{ + unsigned short i; + unsigned int j; + + for (i = 0; i < len; ++i) { + j = (bin[i] >> 4) & 0x0f; + + if (j <= 9) + hex[i * 2] = (j + '0'); + else + hex[i * 2] = (j + 'a' - 10); + + j = bin[i] & 0x0f; + + if (j <= 9) + hex[i * 2 + 1] = (j + '0'); + else + hex[i * 2 + 1] = (j + 'a' - 10); + } + + hex[len * 2] = '\0'; +} + +/* calculate H(A1) as per RFC2617 spec */ +static int +digest_calc_ha1( + const char *alg, + const char *username, + const char *realm, + const char *password, + const char *nonce, + const char *cnonce, + char *sessionkey + ) +{ + gcry_md_hd_t md5; + gcry_error_t gerror; + char *ha1; + + gerror = gcry_md_open(&md5, GCRY_MD_MD5, GCRY_MD_FLAG_SECURE); + + if (gerror) return gerror; + + gcry_md_write(md5, username, strlen(username)); + gcry_md_write(md5, ":", 1); + gcry_md_write(md5, realm, strlen(realm)); + gcry_md_write(md5, ":", 1); + gcry_md_write(md5, password, strlen(password)); + gcry_md_final(md5); + + ha1 = gcry_md_read(md5, GCRY_MD_MD5); + + if (strcasecmp(alg, "md5-sess") == 0) { + gcry_md_reset(md5); + gcry_md_write(md5, ha1, HASH_MD5_LEN); + gcry_md_write(md5, ":", 1); + gcry_md_write(md5, nonce, strlen(nonce)); + gcry_md_write(md5, ":", 1); + gcry_md_write(md5, cnonce, strlen(cnonce)); + gcry_md_final(md5); + + ha1 = gcry_md_read(md5, GCRY_MD_MD5); + } + + cvthex(ha1, HASH_MD5_LEN, sessionkey); + gcry_md_close(md5); + + return 0; +} + +/* calculate request-digest/response-digest as per RFC2617 spec */ +static int +digest_calc_response( + const char *ha1, /* H(A1) */ + const char *nonce, /* nonce from server */ + const char *noncecount, /* 8 hex digits */ + const char *cnonce, /* client nonce */ + const char *qop, /* qop-value: "", "auth", "auth-int" */ + const char *method, /* method from the request */ + const char *uri, /* requested URL */ + const char *hentity, /* H(entity body) if qop="auth-int" */ + char *response /* request-digest or response-digest */ + ) +{ + gcry_md_hd_t md5; + gcry_error_t gerror; + char *ha2; + char *resphash; + char ha2hex[HASH_MD5_HEX_LEN + 1]; + + gerror = gcry_md_open(&md5, GCRY_MD_MD5, GCRY_MD_FLAG_SECURE); + + if (gerror) return gerror; + + // calculate H(A2) + gcry_md_write(md5, method, strlen(method)); + gcry_md_write(md5, ":", 1); + gcry_md_write(md5, uri, strlen(uri)); + + if (strcasecmp(qop, "auth-int") == 0) { + gcry_md_write(md5, ":", 1); + gcry_md_write(md5, hentity, strlen(hentity)); + } + + gcry_md_final(md5); + + ha2 = gcry_md_read(md5, GCRY_MD_MD5); + + cvthex(ha2, HASH_MD5_LEN, ha2hex); + gcry_md_reset(md5); + + // calculate response + gcry_md_write(md5, ha1, HASH_MD5_HEX_LEN); + gcry_md_write(md5, ":", 1); + gcry_md_write(md5, nonce, strlen(nonce)); + gcry_md_write(md5, ":", 1); + + if (*qop) { + gcry_md_write(md5, noncecount, strlen(noncecount)); + gcry_md_write(md5, ":", 1); + gcry_md_write(md5, cnonce, strlen(cnonce)); + gcry_md_write(md5, ":", 1); + gcry_md_write(md5, qop, strlen(qop)); + gcry_md_write(md5, ":", 1); + } + + gcry_md_write(md5, ha2hex, HASH_MD5_HEX_LEN); + gcry_md_final(md5); + + resphash = gcry_md_read(md5, GCRY_MD_MD5); + + cvthex(resphash, HASH_MD5_LEN, response); + gcry_md_close(md5); + + return 0; +} + +static const char * +lookup_sub_value(char *data, size_t len, const char *key) +{ + char *tmp = data; + char *value = NULL; + int keylen; + int i; + + keylen = strlen(key); + + for (i = 0; i < len; ++i) { + if (strncmp(tmp, key, keylen) == 0 && + strncmp(tmp + keylen, "=", 1) == 0) { + tmp += keylen; + break; + } else { + tmp++; + } + + if ((i + 1) == len) return NULL; + } + + while (1) { + tmp++; + + if (*tmp == '"' && *(tmp + 1) == ',') { + *tmp = '\0'; + break; + } + + if (*tmp == '"' && *(tmp + 1) == '\0') { + *tmp = '\0'; + break; + } + + if (*tmp == ',' || *tmp == '\0') { + *tmp = '\0'; + break; + } + + if (*tmp == '"') continue; + + if (value == NULL) + value = tmp; + } + + return value; +} + +static int +is_authenticated(struct MHD_Connection *connection, const char *method, + const char *username, const char *password, + const char *realm, + int nonce_timeout) +{ + int auth; + int len; + char *buffer; + const char *header; + const char *ret; + const char *value; + const char *nonce; + const char *cnonce; + const char *uri; + const char *qop; + const char *nc; + const char *response; + const char *opaque; + char *tmpnonce; + char *hentity = NULL; /* "auth-int" is not supported */ + char timestamp[4]; + char ha1[HASH_MD5_HEX_LEN + 1]; + char respexp[HASH_MD5_HEX_LEN + 1]; + char noncehashexp[HASH_SHA1_HEX_LEN + 9]; + unsigned int nonce_time; + time_t t; + gcry_error_t gerror; + gcry_md_hd_t sha1; + + header = MHD_lookup_connection_value( + connection, MHD_HEADER_KIND, "Authorization"); + + if (header == NULL) return 0; + if (strncmp(header, _BASE, strlen(_BASE)) != 0) return 0; + + len = strlen(header) - strlen(_BASE); + + buffer = malloc(len); + + if (buffer == NULL) return 0; + + strncpy(buffer, header + strlen(_BASE), len); + + ret = lookup_sub_value(buffer, len, "username"); + + if (ret != NULL) { + if (strcmp(username, ret) != 0) { + free(buffer); + return 0; + } + } else { + free(buffer); + return 0; + } + + ret = lookup_sub_value(buffer, len, "realm"); + + if (ret != NULL) { + if (strcmp(realm, ret) != 0) { + free(buffer); + return 0; + } + } else { + free(buffer); + return 0; + } + + if ((uri = lookup_sub_value(buffer, len, "uri")) == NULL) { + free(buffer); + return 0; + } + + if ((nonce = lookup_sub_value(buffer, len, "nonce")) == NULL) { + free(buffer); + return 0; + } + + nonce_time = strtoul(nonce + strlen(nonce) - 8, 0, 16); + + time(&t); + + /* + * 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) { + free(buffer); + return INVALID_NONCE; + } + + gerror = gcry_md_open(&sha1, GCRY_MD_SHA1, GCRY_MD_FLAG_SECURE); + + if (gerror) { + free(buffer); + return 0; + } + + timestamp[0] = (nonce_time & 0xff000000) >> 0x18; + timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10; + timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08; + timestamp[3] = nonce_time & 0x000000ff; + timestamp[4] = '\0'; + + gcry_md_write(sha1, timestamp, 4); + gcry_md_write(sha1, ":", 1); + gcry_md_write(sha1, method, strlen(method)); + gcry_md_write(sha1, ":", 1); + gcry_md_write(sha1, password, strlen(password)); + gcry_md_write(sha1, ":", 1); + gcry_md_write(sha1, uri, strlen(uri)); + gcry_md_write(sha1, ":", 1); + gcry_md_write(sha1, realm, strlen(realm)); + gcry_md_final(sha1); + + tmpnonce = gcry_md_read(sha1, GCRY_MD_SHA1); + + cvthex(tmpnonce, HASH_SHA1_LEN, noncehashexp); + gcry_md_close(sha1); + + strncat(noncehashexp, nonce + strlen(nonce) - 8, 8); + + /* + * 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 (strncmp(nonce, noncehashexp, strlen(nonce)) != 0) { + free(buffer); + return INVALID_NONCE; + } + + if ((cnonce = lookup_sub_value(buffer, len, "cnonce")) == NULL) { + free(buffer); + return 0; + } + + if ((qop = lookup_sub_value(buffer, len, "qop")) == NULL) { + free(buffer); + return 0; + } + + if ((nc = lookup_sub_value(buffer, len, "nc")) == NULL) { + free(buffer); + return 0; + } + + if ((response = lookup_sub_value(buffer, len, "response")) == NULL) { + free(buffer); + return 0; + } + + auth = digest_calc_ha1( + "md5", + username, + realm, + password, + nonce, + cnonce, + ha1 + ); + + if (auth) { + free(buffer); + return 0; + } + + auth = digest_calc_response( + ha1, + nonce, + nc, + cnonce, + qop, + method, + uri, + hentity, + respexp + ); + + if (auth) { + free(buffer); + return 0; + } + + if (strcmp(response, respexp) == 0) + auth = 1; + else + auth = 0; + + free(buffer); + + return auth; +} + +/** + * Authenticate a client with HTTP Digest Auth according to RFC2617 + * + * @param connection The MHD connection structure + * @param data Data to be sent if authentication succeeds + * @param size Size of the data + * @param method The request method + * @param url The URL requested + * @param username The username needs to be authenticated + * @param password The password used in authentication + * @param realm The realm presented to the client + * @param nonce_timeout The amount of time for a nonce to be + * invalid in seconds + * @param must_free libmicrohttpd should free data when done + * @param must_copy libmicrohttpd must make a copy of data + * right away, the data maybe released anytime after + * this call returns + * @return MHD_YES on success, MHD_NO if authentication + * fails for any reason. + */ +int +MHD_digest_auth( + struct MHD_Connection *connection, + void *data, + size_t size, + const char *method, + const char *url, + const char *username, + const char *password, + const char *realm, + int nonce_timeout, + int must_free, + int must_copy + ) +{ + int ret; + int auth; + int hlen; + char *header; + char *tmpnonce; + char timestamp[4]; + char timestamphex[8]; + char nonce[HASH_SHA1_HEX_LEN + 9]; + time_t t; + struct MHD_Response *response; + gcry_error_t gerror; + gcry_md_hd_t sha1; + + auth = is_authenticated(connection, method, username, password, realm, nonce_timeout); + + if (auth == 1) { + response = MHD_create_response_from_data(size, data, must_free, must_copy); + + if (!response) return MHD_NO; + + auth = MHD_queue_response(connection, MHD_HTTP_OK, response); + + MHD_destroy_response(response); + + return auth; + } + + response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO); + + if (!response) return MHD_NO; + + /* + * Generating the server nonce + */ + + gerror = gcry_md_open(&sha1, GCRY_MD_SHA1, GCRY_MD_FLAG_SECURE); + + if (gerror) return MHD_NO; + + time(&t); + + timestamp[0] = (t & 0xff000000) >> 0x18; + timestamp[1] = (t & 0x00ff0000) >> 0x10; + timestamp[2] = (t & 0x0000ff00) >> 0x08; + timestamp[3] = t & 0x000000ff; + timestamp[4] = '\0'; + + gcry_md_write(sha1, timestamp, 4); + gcry_md_write(sha1, ":", 1); + gcry_md_write(sha1, method, strlen(method)); + gcry_md_write(sha1, ":", 1); + gcry_md_write(sha1, password, strlen(password)); + gcry_md_write(sha1, ":", 1); + gcry_md_write(sha1, url, strlen(url)); + gcry_md_write(sha1, ":", 1); + gcry_md_write(sha1, realm, strlen(realm)); + gcry_md_final(sha1); + + tmpnonce = gcry_md_read(sha1, GCRY_MD_SHA1); + + cvthex(timestamp, 4, timestamphex); + cvthex(tmpnonce, HASH_SHA1_LEN, nonce); + strncat(nonce, timestamphex, 8); + gcry_md_close(sha1); + + /* + * Building the authentication header + */ + + /* 4(single quotes) + 3(commas) + NULL = 8 */ + hlen = strlen(_BASE) + strlen(_REALM) + strlen(realm) + + strlen(_QOP) + strlen(_NONCE) + strlen(nonce) + + strlen(_OPAQUE) + 8; + + /* 1(comma for stale=true) */ + if (auth == INVALID_NONCE) + hlen += strlen(_STALE) + 1; + + header = calloc(hlen, sizeof(char)); + + if (header == NULL) return MHD_NO; + + strncpy(header, _BASE, strlen(_BASE)); + strncat(header, _REALM, strlen(_REALM)); + strncat(header, _QUOTE, 1); + strncat(header, realm, strlen(realm)); + strncat(header, _QUOTE, 1); + strncat(header, _COM, 1); + strncat(header, _QOP, strlen(_QOP)); + strncat(header, _COM, 1); + strncat(header, _NONCE, strlen(_NONCE)); + strncat(header, _QUOTE, 1); + strncat(header, nonce, strlen(nonce)); + strncat(header, _QUOTE, 1); + strncat(header, _COM, 1); + strncat(header, _OPAQUE, strlen(_OPAQUE)); + + /* Add "stale=true" to the authentication header if nonce is invalid */ + if (auth == INVALID_NONCE) { + strncat(header, _COM, 1); + strncat(header, _STALE, strlen(_STALE)); + } + + /* + * Sending response with authentication header + */ + + ret = MHD_add_response_header(response, "WWW-Authenticate", header); + + free(header); + + if(!ret) { + MHD_destroy_response(response); + return MHD_NO; + } + + ret = MHD_queue_response(connection, MHD_HTTP_UNAUTHORIZED, response); + + MHD_destroy_response(response); + + return ret; +} diff -U2 -arB libmicrohttpd/src/daemon/digestauth.h libmicrohttpd.new/src/daemon/digestauth.h --- libmicrohttpd/src/daemon/digestauth.h 2010-08-19 00:15:04.000000000 +0000 +++ libmicrohttpd.new/src/daemon/digestauth.h 2010-08-18 23:41:44.000000000 +0000 @@ -0,0 +1,66 @@ +/* + This file is part of libmicrohttpd + (C) 2007, 2009, 2010 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 + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/** + * @file digestauth.h + * @brief Implements HTTP/1.1 Digest Auth according to RFC2617 + * @author Amr Ali + */ + +#ifndef DIGESTAUTH_H +#define DIGESTAUTH_H + +#include "internal.h" + +/** + * Authenticate a client with HTTP Digest Auth according to RFC2617 + * + * @param connection The MHD connection structure + * @param data Data to be sent if authentication succeeds + * @param size Size of the data + * @param method The request method + * @param url the URL requested + * @param username The username needs to be authenticated + * @param password The password used in authentication + * @param realm The realm presented to the client + * @param nonce_timeout The amount of time for a nonce to be + * invalid in seconds + * @param must_free libmicrohttpd should free data when done + * @param must_copy libmicrohttpd must make a copy of data + * right away, the data maybe released anytime after + * this call returns + * @return MHD_YES on success, MHD_NO if authentication + * fails for any reason. + */ +int +MHD_digest_auth( + struct MHD_Connection *connection, + void *data, + size_t size, + const char *method, + const char *url, + const char *username, + const char *password, + const char *realm, + int nonce_timeout, + int must_free, + int must_copy + ); + +#endif /* DIGESTAUTH_H */ diff -U2 -arB libmicrohttpd/src/daemon/Makefile.am libmicrohttpd.new/src/daemon/Makefile.am --- libmicrohttpd/src/daemon/Makefile.am 2010-08-18 23:46:53.000000000 +0000 +++ libmicrohttpd.new/src/daemon/Makefile.am 2010-08-18 23:41:44.000000000 +0000 @@ -25,4 +25,8 @@ endif +if ENABLE_DAUTH +libmicrohttpd_la_SOURCES += \ + digestauth.c digestauth.h +endif if ENABLE_HTTPS diff -U2 -arB libmicrohttpd/src/daemon/Makefile.in libmicrohttpd.new/src/daemon/Makefile.in --- libmicrohttpd/src/daemon/Makefile.in 2010-08-18 23:46:53.000000000 +0000 +++ libmicrohttpd.new/src/daemon/Makefile.in 2010-08-18 23:41:44.000000000 +0000 @@ -35,5 +35,8 @@ build_triplet = @build@ host_triplet = @host@ address@hidden@am__append_1 = \ address@hidden@am__append_1 = \ address@hidden@ digestauth.c digestauth.h + address@hidden@am__append_2 = \ @ENABLE_HTTPS_TRUE@ connection_https.c connection_https.h @@ -81,9 +84,11 @@ reason_phrase.c reason_phrase.h daemon.c internal.c internal.h \ memorypool.c memorypool.h postprocessor.c response.c \ - response.h connection_https.c connection_https.h address@hidden@am__objects_1 = connection_https.lo + response.h digestauth.c digestauth.h connection_https.c \ + connection_https.h address@hidden@am__objects_1 = digestauth.lo address@hidden@am__objects_2 = connection_https.lo am_libmicrohttpd_la_OBJECTS = connection.lo reason_phrase.lo daemon.lo \ internal.lo memorypool.lo postprocessor.lo response.lo \ - $(am__objects_1) + $(am__objects_1) $(am__objects_2) libmicrohttpd_la_OBJECTS = $(am_libmicrohttpd_la_OBJECTS) libmicrohttpd_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ @@ -271,5 +276,5 @@ reason_phrase.h daemon.c internal.c internal.h memorypool.c \ memorypool.h postprocessor.c response.c response.h \ - $(am__append_1) + $(am__append_1) $(am__append_2) libmicrohttpd_la_LDFLAGS = \ $(MHD_LIB_LDFLAGS) \ @@ -393,4 +398,5 @@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ diff -U2 -arB libmicrohttpd/src/examples/digest_auth_example.c libmicrohttpd.new/src/examples/digest_auth_example.c --- libmicrohttpd/src/examples/digest_auth_example.c 2010-08-19 00:15:14.000000000 +0000 +++ libmicrohttpd.new/src/examples/digest_auth_example.c 2010-08-18 23:41:44.000000000 +0000 @@ -0,0 +1,79 @@ +/* + This file is part of libmicrohttpd + (C) 2010 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +/** + * @file digest_auth_example.c + * @brief minimal example for how to use digest auth with libmicrohttpd + * @author Amr Ali + */ + +#include "platform.h" +#include +#include + +#define PAGE "libmicrohttpd demolibmicrohttpd demo" + +static int +ahc_echo (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, size_t *upload_data_size, void **ptr) +{ + static int aptr; + const char *username = "testuser"; + const char *password = "testpass"; + const char *realm = "address@hidden"; + int ret; + + if (0 != strcmp (method, "GET")) + return MHD_NO; /* unexpected method */ + if (&aptr != *ptr) + { + /* do never respond on first call */ + *ptr = &aptr; + return MHD_YES; + } + *ptr = NULL; /* reset when done */ + + ret = MHD_digest_auth(connection, PAGE, strlen(PAGE), + method, url, username, password, realm, 300, MHD_NO, MHD_NO); + + return ret; +} + +int +main (int argc, char *const *argv) +{ + struct MHD_Daemon *d; + + if (argc != 2) + { + printf ("%s PORT\n", argv[0]); + return 1; + } + d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, + atoi (argv[1]), + NULL, NULL, &ahc_echo, PAGE, MHD_OPTION_END); + if (d == NULL) + return 1; + (void) getc (stdin); + MHD_stop_daemon (d); + return 0; +} diff -U2 -arB libmicrohttpd/src/examples/Makefile.am libmicrohttpd.new/src/examples/Makefile.am --- libmicrohttpd/src/examples/Makefile.am 2010-08-18 23:46:53.000000000 +0000 +++ libmicrohttpd.new/src/examples/Makefile.am 2010-08-18 23:41:44.000000000 +0000 @@ -24,4 +24,8 @@ endif +if ENABLE_DAUTH +noinst_PROGRAMS += digest_auth_example +endif + minimal_example_SOURCES = \ minimal_example.c @@ -39,4 +43,9 @@ $(top_builddir)/src/daemon/libmicrohttpd.la +digest_auth_example_SOURCES = \ + digest_auth_example.c +digest_auth_example_LDADD = \ + $(top_builddir)/src/daemon/libmicrohttpd.la + refuse_post_example_SOURCES = \ refuse_post_example.c diff -U2 -arB libmicrohttpd/src/examples/Makefile.in libmicrohttpd.new/src/examples/Makefile.in --- libmicrohttpd/src/examples/Makefile.in 2010-08-18 23:46:53.000000000 +0000 +++ libmicrohttpd.new/src/examples/Makefile.in 2010-08-18 23:41:44.000000000 +0000 @@ -40,6 +40,7 @@ fileserver_example_dirs$(EXEEXT) \ fileserver_example_external_select$(EXEEXT) \ - refuse_post_example$(EXEEXT) $(am__EXEEXT_1) + refuse_post_example$(EXEEXT) $(am__EXEEXT_1) $(am__EXEEXT_2) @address@hidden = https_fileserver_example address@hidden@am__append_2 = digest_auth_example subdir = src/examples DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in @@ -57,4 +58,5 @@ CONFIG_CLEAN_VPATH_FILES = @address@hidden = https_fileserver_example$(EXEEXT) address@hidden@am__EXEEXT_2 = digest_auth_example$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_authorization_example_OBJECTS = authorization_example.$(OBJEXT) @@ -62,4 +64,8 @@ authorization_example_DEPENDENCIES = \ $(top_builddir)/src/daemon/libmicrohttpd.la +am_digest_auth_example_OBJECTS = digest_auth_example.$(OBJEXT) +digest_auth_example_OBJECTS = $(am_digest_auth_example_OBJECTS) +digest_auth_example_DEPENDENCIES = \ + $(top_builddir)/src/daemon/libmicrohttpd.la am_fileserver_example_OBJECTS = fileserver_example.$(OBJEXT) fileserver_example_OBJECTS = $(am_fileserver_example_OBJECTS) @@ -114,5 +120,5 @@ $(LDFLAGS) -o $@ SOURCES = $(authorization_example_SOURCES) \ - $(fileserver_example_SOURCES) \ + $(digest_auth_example_SOURCES) $(fileserver_example_SOURCES) \ $(fileserver_example_dirs_SOURCES) \ $(fileserver_example_external_select_SOURCES) \ @@ -121,5 +127,5 @@ $(querystring_example_SOURCES) $(refuse_post_example_SOURCES) DIST_SOURCES = $(authorization_example_SOURCES) \ - $(fileserver_example_SOURCES) \ + $(digest_auth_example_SOURCES) $(fileserver_example_SOURCES) \ $(fileserver_example_dirs_SOURCES) \ $(fileserver_example_external_select_SOURCES) \ @@ -323,4 +329,10 @@ $(top_builddir)/src/daemon/libmicrohttpd.la +digest_auth_example_SOURCES = \ + digest_auth_example.c + +digest_auth_example_LDADD = \ + $(top_builddir)/src/daemon/libmicrohttpd.la + refuse_post_example_SOURCES = \ refuse_post_example.c @@ -405,4 +417,7 @@ @rm -f authorization_example$(EXEEXT) $(LINK) $(authorization_example_OBJECTS) $(authorization_example_LDADD) $(LIBS) +digest_auth_example$(EXEEXT): $(digest_auth_example_OBJECTS) $(digest_auth_example_DEPENDENCIES) + @rm -f digest_auth_example$(EXEEXT) + $(LINK) $(digest_auth_example_OBJECTS) $(digest_auth_example_LDADD) $(LIBS) fileserver_example$(EXEEXT): $(fileserver_example_OBJECTS) $(fileserver_example_DEPENDENCIES) @rm -f fileserver_example$(EXEEXT) @@ -437,4 +452,5 @@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@