diff -U2 -arB libmicrohttpd/configure.ac libmicrohttpd.new/configure.ac --- libmicrohttpd/configure.ac 2010-08-19 00:50:26.000000000 +0000 +++ libmicrohttpd.new/configure.ac 2010-08-19 22:53:43.000000000 +0000 @@ -292,4 +292,29 @@ 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-dauth], + [disable HTTP Digest Auth support (default is NO)]), + [disable_dauth="yes"], + [disable_dauth="no"]) + +if test "x$disable_dauth" != "xyes" +then + if test "$gcrypt" = "true" + then + enable_dauth="yes" + MHD_LIBDEPS="$LIBGCRYPT_LIBS" + else + disable_dauth="yes (lacking libgcrypt)" + enable_dauth="no (lacking libgcrypt)" + fi +else + enable_dauth="no" +fi +AC_MSG_RESULT($disable_dauth) + +AM_CONDITIONAL(ENABLE_DAUTH, [test "x$disable_dauth" != "xyes" -a "$gcrypt" = "true"]) + MHD_LIB_LDFLAGS="-export-dynamic -no-undefined" @@ -366,4 +391,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-20 13:11:35.000000000 +0000 +++ libmicrohttpd.new/src/daemon/digestauth.c 2010-08-20 12:21:41.000000000 +0000 @@ -0,0 +1,612 @@ +/* + 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 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=" + +/* convert bin to hex */ +static void +cvthex(const char *bin, size_t len, char *hex) +{ + size_t i; + unsigned int j; + + 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; + + hex[i * 2 + 1] = j <= 9 ? (j + '0') : (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; + size_t keylen; + size_t 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; +} + +/** + * Get the username from the authorization header sent by the client + * + * @param connection The MHD connection structure + * @return NULL if no username could be found, a pointer + * to the username if found + */ +char * +MHD_digest_auth_get_username( + struct MHD_Connection *connection + ) +{ + size_t len; + size_t userlen; + char *buffer; + char *username; + const char *user; + const char *header; + + header = MHD_lookup_connection_value(connection, + MHD_HEADER_KIND, MHD_HTTP_HEADER_AUTHORIZATION); + + if (header == NULL) return NULL; + if (strncmp(header, _BASE, strlen(_BASE)) != 0) return NULL; + + len = strlen(header) - strlen(_BASE) + 1; + buffer = malloc(len); + + if (buffer == NULL) return NULL; + + strncpy(buffer, header + strlen(_BASE), len); + + user = lookup_sub_value(buffer, len, "username"); + + if (user) { + userlen = strlen(user) + 1; + username = malloc(userlen); + + if (username != NULL) { + strncpy(username, user, userlen); + free(buffer); + return username; + } + } + + free(buffer); + return NULL; +} + +/** + * Authenticates the authorization header sent by the client + * + * @param connection The MHD connection structure + * @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 nonce_timeout The amount of time for a nonce to be + * invalid in seconds + * @return MHD_YES if authenticated, MHD_NO if not, + * MHD_INVALID_NONCE if nonce is invalid + */ +int +MHD_digest_auth_check( + struct MHD_Connection *connection, + const char *realm, + const char *username, + const char *password, + unsigned int nonce_timeout + ) +{ + int auth; + size_t 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[5]; + 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, + MHD_HTTP_HEADER_AUTHORIZATION); + + if (header == NULL) return MHD_NO; + if (strncmp(header, _BASE, strlen(_BASE)) != 0) return MHD_NO; + + len = strlen(header) - strlen(_BASE) + 1; + + buffer = malloc(len); + + if (buffer == NULL) return MHD_NO; + + strncpy(buffer, header + strlen(_BASE), len); + + ret = lookup_sub_value(buffer, len, "username"); + + if (ret == NULL || strcmp(username, ret) != 0) { + free(buffer); + return MHD_NO; + } + + ret = lookup_sub_value(buffer, len, "realm"); + + if (ret == NULL || strcmp(realm, ret) != 0) { + free(buffer); + return MHD_NO; + } + + if ((uri = lookup_sub_value(buffer, len, "uri")) == NULL) { + free(buffer); + return MHD_NO; + } + + if ((nonce = lookup_sub_value(buffer, len, "nonce")) == NULL) { + free(buffer); + return MHD_NO; + } + + /* + * 8 = 4 hexadecimal numbers for the timestamp + */ + + 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 MHD_INVALID_NONCE; + } + + gerror = gcry_md_open(&sha1, GCRY_MD_SHA1, GCRY_MD_FLAG_SECURE); + + if (gerror) { + free(buffer); + return MHD_NO; + } + + 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, connection->method, strlen(connection->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 MHD_INVALID_NONCE; + } + + if ((cnonce = lookup_sub_value(buffer, len, "cnonce")) == NULL) { + free(buffer); + return MHD_NO; + } + + if ((qop = lookup_sub_value(buffer, len, "qop")) == NULL) { + free(buffer); + return MHD_NO; + } + + if ((nc = lookup_sub_value(buffer, len, "nc")) == NULL) { + free(buffer); + return MHD_NO; + } + + if ((response = lookup_sub_value(buffer, len, "response")) == NULL) { + free(buffer); + return MHD_NO; + } + + auth = digest_calc_ha1( + "md5", + username, + realm, + password, + nonce, + cnonce, + ha1 + ); + + if (auth) { + free(buffer); + return MHD_NO; + } + + auth = digest_calc_response( + ha1, + nonce, + nc, + cnonce, + qop, + connection->method, + uri, + hentity, + respexp + ); + + if (auth) { + free(buffer); + return MHD_NO; + } + + auth = strcmp(response, respexp) == 0 ? MHD_YES : MHD_NO; + + free(buffer); + + return auth; +} + +/** + * Queues a response to request authentication from the client + * + * @param connection The MHD connection structure + * @param realm The realm presented to the client + * @param password The password used in authentication + * @param nonce_timeout The amount of time for a nonce to be + * invalid in seconds + * @param signal_stale MHD_YES if the nonce is invalid to add + * 'stale=true' to the authentication header + * @return MHD_YES on success, MHD_NO otherwise + */ +int +MHD_queue_auth_fail_response( + struct MHD_Connection *connection, + const char *realm, + const char *password, + unsigned int nonce_timeout, + int signal_stale + ) +{ + int ret; + size_t hlen; + char *header; + char *tmpnonce; + char timestamp[5]; + char timestamphex[9]; + char nonce[HASH_SHA1_HEX_LEN + 9]; + time_t t; + struct MHD_Response *response; + gcry_error_t gerror; + gcry_md_hd_t sha1; + + 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, connection->method, strlen(connection->method)); + gcry_md_write(sha1, ":", 1); + gcry_md_write(sha1, password, strlen(password)); + gcry_md_write(sha1, ":", 1); + gcry_md_write(sha1, connection->url, strlen(connection->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 (signal_stale) + hlen += strlen(_STALE) + 1; + + header = malloc(hlen); + + if (header == NULL) return MHD_NO; + + snprintf(header, hlen, + "%s%s\"%s\",%s,%s\"%s\",%s", + _BASE, _REALM, realm, _QOP, + _NONCE, nonce, _OPAQUE); + + /* Add "stale=true" to the authentication header if nonce is invalid */ + if (signal_stale) { + strncat(header, ",", 1); + strncat(header, _STALE, strlen(_STALE)); + } + + /* + * Sending response with authentication header + */ + + ret = MHD_add_response_header(response, + MHD_HTTP_HEADER_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-20 13:11:35.000000000 +0000 +++ libmicrohttpd.new/src/daemon/digestauth.h 2010-08-20 11:13:43.000000000 +0000 @@ -0,0 +1,87 @@ +/* + 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" + +#define MHD_INVALID_NONCE -1 + +/** + * Get the username from the authorization header sent by the client + * + * @param connection The MHD connection structure + * @return NULL if no username could be found, a pointer + * to the username if found + */ +char * +MHD_digest_auth_get_username( + struct MHD_Connection *connection + ); + +/** + * Authenticates the authorization header sent by the client + * + * @param connection The MHD connection structure + * @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 nonce_timeout The amount of time for a nonce to be + * invalid in seconds + * @return MHD_YES if authenticated, MHD_NO if not, + * MHD_INVALID_NONCE if nonce is invalid + */ +int +MHD_digest_auth_check( + struct MHD_Connection *connection, + const char *realm, + const char *username, + const char *password, + unsigned int nonce_timeout + ); + +/** + * Queues a response to request authentication from the client + * + * @param connection The MHD connection structure + * @param realm The realm presented to the client + * @param password The password used in authentication + * @param nonce_timeout The amount of time for a nonce to be + * invalid in seconds + * @param signal_stale MHD_YES if the nonce is invalid to add + * 'stale=true' to the authentication header + * @return MHD_YES on success, MHD_NO otherwise + */ +int +MHD_queue_auth_fail_response( + struct MHD_Connection *connection, + const char *realm, + const char *password, + unsigned int nonce_timeout, + int signal_stale + ); + +#endif /* DIGESTAUTH_H */ diff -U2 -arB libmicrohttpd/src/daemon/Makefile.am libmicrohttpd.new/src/daemon/Makefile.am --- libmicrohttpd/src/daemon/Makefile.am 2010-08-19 00:50:26.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/examples/digest_auth_example.c libmicrohttpd.new/src/examples/digest_auth_example.c --- libmicrohttpd/src/examples/digest_auth_example.c 2010-08-20 13:11:48.000000000 +0000 +++ libmicrohttpd.new/src/examples/digest_auth_example.c 2010-08-20 12:12:57.000000000 +0000 @@ -0,0 +1,100 @@ +/* + 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) +{ + struct MHD_Response *response; + char *username; + const char *password = "testpass"; + const char *realm = "address@hidden"; + int ret; + + username = MHD_digest_auth_get_username(connection); + + if (username == NULL) { + ret = MHD_queue_auth_fail_response(connection, realm, + password, 300, MHD_NO); + + return ret; + } + + ret = MHD_digest_auth_check(connection, realm, + username, password, 300); + + free(username); + + if (ret == MHD_INVALID_NONCE) { + ret = MHD_queue_auth_fail_response(connection, realm, + password, 300, MHD_YES); + + return ret; + } + + if (ret == MHD_NO) { + ret = MHD_queue_auth_fail_response(connection, realm, + password, 300, MHD_NO); + + return ret; + } + + response = MHD_create_response_from_data(strlen(PAGE), PAGE, + MHD_NO, MHD_NO); + + ret = MHD_queue_response(connection, MHD_HTTP_OK, response); + + MHD_destroy_response(response); + 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-19 00:50: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/include/microhttpd.h libmicrohttpd.new/src/include/microhttpd.h --- libmicrohttpd/src/include/microhttpd.h 2010-07-30 05:50:05.000000000 +0000 +++ libmicrohttpd.new/src/include/microhttpd.h 2010-08-20 11:33:39.000000000 +0000 @@ -95,4 +95,9 @@ /** + * MHD digest auth internal code for an invalid nonce. + */ +#define MHD_INVALID_NONCE -1 + +/** * Constant used to indicate unknown size (use when * creating a response).