initial draft for digest authentication (based on patch R3)

This commit is contained in:
Christian Grothoff
2010-08-22 14:49:48 +00:00
parent 53d412023b
commit 7b221e3a6a
8 changed files with 840 additions and 18 deletions
+1
View File
@@ -3,6 +3,7 @@ Christian Grothoff <christian@grothoff.org> (maintainer)
Nils Durner <durner@gnunet.org> (W32 port)
Sagie Amir (TLS/SSL support using GNUtls)
Richard Alimi <rich@velvetsea.net> (performance)
Amr Ali <amr.ali.cc@gmail.com> (digest authentication)
Code contributions also came from:
Chris GauthierDickey <chrisg@cs.du.edu>
+3
View File
@@ -1,3 +1,6 @@
Sun Aug 22 16:49:13 CEST 2010
Initial draft for digest authentication. -AA
Thu Aug 19 14:15:01 CEST 2010
Changed code to enable error messages and HTTPS by default;
added option to disable post processor API (use
+48 -14
View File
@@ -221,31 +221,37 @@ AC_FUNC_FSEEKO
# optional: have error messages ?
AC_MSG_CHECKING(whether to enable error messages)
AC_ARG_ENABLE(messages,
AC_ARG_ENABLE([messages],
[AS_HELP_STRING([--disable-messages],
[disable MHD error messages])],
[disable_messages=$enableval],
[disable MHD error messages (default is NO)])],
[disable_messages="yes"],
[disable_messages="no"])
AC_MSG_RESULT($disable_messages)
if test "$disable_messages" = "no"
then
AC_DEFINE([HAVE_MESSAGES],[1],[Include error messages])
enable_messages="yes"
else
AC_DEFINE([HAVE_MESSAGES],[0],[Disable error messages])
enable_messages="no"
fi
# optional: have postprocessor?
AC_MSG_CHECKING(whether to enable postprocessor)
AC_ARG_ENABLE(postprocessor,
AC_ARG_ENABLE([postprocessor],
[AS_HELP_STRING([--disable-postprocessor],
[disable MHD PostProcessor functionality])],
[disable_postprocessor=$enableval],
[disable MHD PostProcessor functionality (default is NO)])],
[disable_postprocessor="yes"],
[disable_postprocessor="no"])
AC_MSG_RESULT($disable_postprocessor)
if test "$disable_postprocessor" = "no"
then
AC_DEFINE([HAVE_POSTPROCESSOR],[1],[Include postprocessor])
enable_postprocessor="yes"
else
enable_postprocessor="no"
fi
AM_CONDITIONAL([HAVE_POSTPROCESSOR],test x$disable_postprocessor = xno)
AM_CONDITIONAL([HAVE_POSTPROCESSOR],test x$disable_postprocessor != xyes)
# optional: have zzuf, socat?
@@ -300,8 +306,8 @@ AC_DEFINE_UNQUOTED([HAVE_GNUTLS], $gnutls, [We have gnutls])
AC_MSG_CHECKING(whether to enable HTTPS support)
AC_ARG_ENABLE([https],
[AS_HELP_STRING([--disable-https],
[disable HTTPS support (default is enabled)])],
[disable_https=$enableval],
[disable HTTPS support (default is NO)])],
[disable_https="yes"],
[disable_https="no"])
if test "$disable_https" = "no"
then
@@ -309,17 +315,44 @@ then
then
AC_DEFINE([HTTPS_SUPPORT],[1],[include HTTPS support])
MHD_LIBDEPS="$LIBGCRYPT_LIBS"
enable_https="yes"
else
AC_DEFINE([HTTPS_SUPPORT],[0],[no libgcrypt or libgnutls])
enable_https="no (lacking libgcrypt or libgnutls)"
fi
else
AC_DEFINE([HTTPS_SUPPORT],[0],[disable HTTPS support])
enable_https="no (disabled)"
fi
AC_MSG_RESULT($disable_https)
AM_CONDITIONAL(ENABLE_HTTPS, test "$disable_https" = "no")
# 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"
# TODO insert a proper check here
@@ -393,12 +426,13 @@ AC_MSG_NOTICE([Configuration Summary:
libgcrypt: ${MSG_GCRYPT}
libcurl (testing): ${MSG_CURL}
Target directory: ${prefix}
Messages disabled: ${disable_messages}
Postproc disabled: ${disable_postprocessor}
HTTPS disabled: ${disable_https}
Messages: ${enable_messages}
HTTP Digest Auth: ${enable_dauth}
Postproc: ${enable_postprocessor}
HTTPS support: ${enable_https}
])
if test "$disable_https" = "no"
if test "x$enable_https" = "xyes"
then
AC_MSG_NOTICE([HTTPS subsystem configuration:
License : LGPL only
+9 -4
View File
@@ -13,9 +13,6 @@ EXTRA_DIST = EXPORT.sym
lib_LTLIBRARIES = \
libmicrohttpd.la
if HAVE_POSTPROCESSOR
SUPPORT_POSTPROCESSOR = postprocessor.c
endif
libmicrohttpd_la_SOURCES = \
connection.c connection.h \
@@ -23,7 +20,6 @@ libmicrohttpd_la_SOURCES = \
daemon.c \
internal.c internal.h \
memorypool.c memorypool.h \
$(SUPPORT_POSTPROCESSOR) \
response.c response.h
libmicrohttpd_la_LDFLAGS = \
$(MHD_LIB_LDFLAGS) \
@@ -33,6 +29,15 @@ if USE_COVERAGE
AM_CFLAGS = --coverage
endif
if HAVE_POSTPROCESSOR
libmicrohttpd_la_SOURCES += \
postprocessor.c
endif
if ENABLE_DAUTH
libmicrohttpd_la_SOURCES += \
digestauth.c
endif
if ENABLE_HTTPS
libmicrohttpd_la_SOURCES += \
+601
View File
@@ -0,0 +1,601 @@
/*
This file is part of libmicrohttpd
(C) 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 <stdlib.h>
#include <gcrypt.h>
#include <string.h>
#include <time.h>
#include "internal.h"
#include "connection.h"
#include "microhttpd.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 unsigned 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;
unsigned 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;
unsigned char *ha2;
unsigned 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 *nonce;
const char *cnonce;
const char *uri;
const char *qop;
const char *nc;
const char *response;
unsigned 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 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,
const char *opaque,
int signal_stale)
{
int ret;
size_t hlen;
unsigned char *tmpnonce;
char *header;
unsigned 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;
}
/* end of digestauth.c */
+9
View File
@@ -28,6 +28,10 @@ if ENABLE_HTTPS
noinst_PROGRAMS += https_fileserver_example
endif
if ENABLE_DAUTH
noinst_PROGRAMS += digest_auth_example
endif
minimal_example_SOURCES = \
minimal_example.c
minimal_example_LDADD = \
@@ -43,6 +47,11 @@ authorization_example_SOURCES = \
authorization_example_LDADD = \
$(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
refuse_post_example_LDADD = \
+105
View File
@@ -0,0 +1,105 @@
/*
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 <microhttpd.h>
#include <stdlib.h>
#define PAGE "<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>"
#define OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4"
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 = "test@example.com";
int ret;
username = MHD_digest_auth_get_username(connection);
if (username == NULL) {
ret = MHD_queue_auth_fail_response(connection, realm,
password,
OPAQUE,
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,
OPAQUE, MHD_YES);
return ret;
}
if (ret == MHD_NO) {
ret = MHD_queue_auth_fail_response(connection, realm,
password, OPAQUE, 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;
}
+64
View File
@@ -93,6 +93,11 @@ extern "C"
*/
#define MHD_NO 0
/**
* 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).
@@ -1215,6 +1220,65 @@ MHD_post_process (struct MHD_PostProcessor *pp,
int MHD_destroy_post_processor (struct MHD_PostProcessor *pp);
/* ********************* Digest Authentication functions *************** */
/**
* Constant to indicate that the nonce of the provided
* authentication code was wrong.
*/
#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 opaque string to user for opaque value
* @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,
const char *opaque,
int signal_stale);
/* ********************** generic query functions ********************** */