mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
initial draft for digest authentication (based on patch R3)
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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 += \
|
||||
|
||||
@@ -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 */
|
||||
@@ -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 = \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 ********************** */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user