use digest authentication instead of basic authentication in the connection request. this is a requirement from the standard.*** the code is an open source already developed by a PIVA consultant (Oussama Ghorbel)

This commit is contained in:
zribiahmed 2013-02-16 11:25:08 +00:00
parent 78d60a9c3d
commit 577dbad71c
8 changed files with 833 additions and 235 deletions

144
src/b64.c
View file

@ -1,144 +0,0 @@
/*
* zstream - Micro URL I/O stream library
* Copyright (C) 2010 Steven Barth <steven@midlink.org>
* Copyright (C) 2010 John Crispin <blogic@openwrt.org>
*
* 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 St, Fifth Floor, Boston, MA 02110, USA
*
*/
#ifndef HTTP_ZSTREAM
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
static const unsigned char b64encode_tbl[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char* zstream_b64encode(const void *in, size_t *len) {
size_t lenout, pad, i;
const uint8_t *data = (const uint8_t*)in;
lenout = *len / 3;
lenout *= 4;
pad = *len % 3;
if (*len == 0) {
return strdup("");
} else if (pad) {
lenout += 4;
}
char *out = malloc(lenout + 1);
if (!out) {
return NULL;
}
uint8_t *o = (uint8_t*)out;
for (i = 0; i < *len; i += 3) {
uint32_t cv = (data[i] << 16) | (data[i+1] << 8) | data[i+2];
*(o+3) = b64encode_tbl[ cv & 0x3f];
*(o+2) = b64encode_tbl[(cv >> 6) & 0x3f];
*(o+1) = b64encode_tbl[(cv >> 12) & 0x3f];
*o = b64encode_tbl[(cv >> 18) & 0x3f];
o += 4;
}
if (pad) {
uint32_t cv = data[*len-pad] << 16;
*(o-1) = '=';
*(o-2) = '=';
if (pad == 2) {
cv |= data[*len-pad+1] << 8;
*(o-2) = b64encode_tbl[(cv >> 6) & 0x3f];
}
*(o-3) = b64encode_tbl[(cv >> 12) & 0x3f];
*(o-4) = b64encode_tbl[(cv >> 18) & 0x3f];
}
out[lenout] = 0;
*len = lenout;
return out;
}
static const unsigned char b64decode_tbl[] = {
0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff,
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01,
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b,
0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33
};
void* zstream_b64decode(const char *in, size_t *len) {
size_t lenout, i;
if (*len == 0) {
return strdup("");
} else if (*len % 4) {
errno = EINVAL;
return NULL;
}
lenout = *len / 4 * 3;
unsigned char *out = malloc(lenout);
if (!out) {
return NULL;
}
unsigned char *o = out;
for (i = 0; i < *len; i += 4) {
uint32_t cv = 0;
uint8_t j;
for (j = 0; j < 4; j++) {
unsigned char c = in[i + j] - 43;
if (c > 79 || (c = b64decode_tbl[c]) == 0xff) {
free(out);
errno = EINVAL;
return NULL;
}
cv |= c;
if (j != 3) {
cv <<= 6;
}
}
*(o+2) = (unsigned char)(cv & 0xff);
*(o+1) = (unsigned char)((cv >> 8) & 0xff);
*o = (unsigned char)((cv >> 16) & 0xff);
o += 3;
}
if (in[*len-1] == '=') {
lenout--;
}
if (in[*len-2] == '=') {
lenout--;
}
*len = lenout;
return out;
}
#endif /* HTTP_ZSTREAM */

View file

@ -2,15 +2,16 @@ bin_PROGRAMS = cwmpd
CWMP_VERSION = 2.2
cwmpd_SOURCES = \
../b64.c \
../backupSession.c \
../config.c \
../cwmp.c \
../digestauth.c \
../event.c \
../external.c \
../http.c \
../jshn.c \
../log.c \
../md5.c \
../netlink.c \
../time.c \
../ubus.c \

468
src/digestauth.c Normal file
View file

@ -0,0 +1,468 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* HTTP digest auth functions: originally imported from libmicrohttpd
*
* Copyright (C) 2013 Oussama Ghorbel <oussama.ghorbel@pivasoftware.com>
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include "md5.h"
#include "digestauth.h"
#undef DIGEST_DEBUG
#define HASH_MD5_HEX_LEN (2 * MD5_DIGEST_SIZE)
/**
* Maximum length of a username for digest authentication.
*/
#define MAX_USERNAME_LENGTH 128
/**
* Maximum length of a realm for digest authentication.
*/
#define MAX_REALM_LENGTH 256
/**
* Maximum length of the response in digest authentication.
*/
#define MAX_AUTH_RESPONSE_LENGTH 128
#define MAX_NONCE_LENGTH 129
#define NONCE_PRIV_KEY "h5ffku7rlxp6tjf2xamnfqjev5ul"
static time_t MHD_monotonic_time(void)
{
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
return ts.tv_sec;
return time(NULL );
}
/**
* convert bin to hex
*
* @param bin binary data
* @param len number of bytes in bin
* @param hex pointer to len*2+1 bytes
*/
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 the server nonce so that it mitigates replay attacks
* The current format of the nonce is ...
* H(timestamp ":" method ":" random ":" uri ":" realm) + Hex(timestamp)
*
* @param nonce_time The amount of time in seconds for a nonce to be invalid
* @param method HTTP method
* @param rnd A pointer to a character array for the random seed
* @param rnd_size The size of the random seed array
* @param uri HTTP URI (in MHD, without the arguments ("?k=v")
* @param realm A string of characters that describes the realm of auth.
* @param nonce A pointer to a character array for the nonce to put in
*/
static void calculate_nonce(uint32_t nonce_time, const char *method,
const char *rnd, unsigned int rnd_size, const char *uri,
const char *realm, char *nonce)
{
struct MD5Context md5;
unsigned char timestamp[4];
unsigned char tmpnonce[MD5_DIGEST_SIZE];
char timestamphex[sizeof(timestamp) * 2 + 1];
MD5Init(&md5);
timestamp[0] = (nonce_time & 0xff000000) >> 0x18;
timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10;
timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08;
timestamp[3] = (nonce_time & 0x000000ff);
MD5Update(&md5, timestamp, 4);
MD5Update(&md5, ":", 1);
MD5Update(&md5, method, strlen(method));
MD5Update(&md5, ":", 1);
if (rnd_size > 0)
MD5Update(&md5, rnd, rnd_size);
MD5Update(&md5, ":", 1);
MD5Update(&md5, uri, strlen(uri));
MD5Update(&md5, ":", 1);
MD5Update(&md5, realm, strlen(realm));
MD5Final(tmpnonce, &md5);
cvthex(tmpnonce, sizeof(tmpnonce), nonce);
cvthex(timestamp, 4, timestamphex);
strncat(nonce, timestamphex, 8);
}
/**
* Lookup subvalue off of the HTTP Authorization header.
*
* A description of the input format for 'data' is at
* http://en.wikipedia.org/wiki/Digest_access_authentication
*
*
* @param dest where to store the result (possibly truncated if
* the buffer is not big enough).
* @param size size of dest
* @param data pointer to the Authorization header
* @param key key to look up in data
* @return size of the located value, 0 if otherwise
*/
static int lookup_sub_value(char *dest, size_t size, const char *data,
const char *key)
{
size_t keylen;
size_t len;
const char *ptr;
const char *eq;
const char *q1;
const char *q2;
const char *qn;
if (0 == size)
return 0;
keylen = strlen(key);
ptr = data;
while ('\0' != *ptr)
{
if (NULL == (eq = strchr(ptr, '=')))
return 0;
q1 = eq + 1;
while (' ' == *q1)
q1++;
if ('\"' != *q1)
{
q2 = strchr(q1, ',');
qn = q2;
}
else
{
q1++;
q2 = strchr(q1, '\"');
if (NULL == q2)
return 0; /* end quote not found */
qn = q2 + 1;
}
if ((0 == strncasecmp(ptr, key, keylen)) && (eq == &ptr[keylen]))
{
if (NULL == q2)
{
len = strlen(q1) + 1;
if (size > len)
size = len;
size--;
strncpy(dest, q1, size);
dest[size] = '\0';
return size;
}
else
{
if (size > (q2 - q1) + 1)
size = (q2 - q1) + 1;
size--;
memcpy(dest, q1, size);
dest[size] = '\0';
return size;
}
}
if (NULL == qn)
return 0;
ptr = strchr(qn, ',');
if (NULL == ptr)
return 0;
ptr++;
while (' ' == *ptr)
ptr++;
}
return 0;
}
/**
* calculate H(A1) as per RFC2617 spec and store the
* result in 'sessionkey'.
*
* @param alg The hash algorithm used, can be "md5" or "md5-sess"
* @param username A `char *' pointer to the username value
* @param realm A `char *' pointer to the realm value
* @param password A `char *' pointer to the password value
* @param nonce A `char *' pointer to the nonce value
* @param cnonce A `char *' pointer to the cnonce value
* @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes
*/
static void digest_calc_ha1(const char *alg, const char *username,
const char *realm, const char *password, const char *nonce,
const char *cnonce, char *sessionkey)
{
struct MD5Context md5;
unsigned char ha1[MD5_DIGEST_SIZE];
MD5Init(&md5);
MD5Update(&md5, username, strlen(username));
MD5Update(&md5, ":", 1);
MD5Update(&md5, realm, strlen(realm));
MD5Update(&md5, ":", 1);
MD5Update(&md5, password, strlen(password));
MD5Final(ha1, &md5);
if (0 == strcasecmp(alg, "md5-sess"))
{
MD5Init(&md5);
MD5Update(&md5, ha1, sizeof(ha1));
MD5Update(&md5, ":", 1);
MD5Update(&md5, nonce, strlen(nonce));
MD5Update(&md5, ":", 1);
MD5Update(&md5, cnonce, strlen(cnonce));
MD5Final(ha1, &md5);
}
cvthex(ha1, sizeof(ha1), sessionkey);
}
/**
* Calculate request-digest/response-digest as per RFC2617 spec
*
* @param ha1 H(A1)
* @param nonce nonce from server
* @param noncecount 8 hex digits
* @param cnonce client nonce
* @param qop qop-value: "", "auth" or "auth-int"
* @param method method from request
* @param uri requested URL
* @param hentity H(entity body) if qop="auth-int"
* @param response request-digest or response-digest
*/
static void digest_calc_response(const char *ha1, const char *nonce,
const char *noncecount, const char *cnonce, const char *qop,
const char *method, const char *uri, const char *hentity,
char *response)
{
struct MD5Context md5;
unsigned char ha2[MD5_DIGEST_SIZE];
unsigned char resphash[MD5_DIGEST_SIZE];
char ha2hex[HASH_MD5_HEX_LEN + 1];
MD5Init(&md5);
MD5Update(&md5, method, strlen(method));
MD5Update(&md5, ":", 1);
MD5Update(&md5, uri, strlen(uri));
MD5Final(ha2, &md5);
cvthex(ha2, MD5_DIGEST_SIZE, ha2hex);
MD5Init(&md5);
/* calculate response */
MD5Update(&md5, ha1, HASH_MD5_HEX_LEN);
MD5Update(&md5, ":", 1);
MD5Update(&md5, nonce, strlen(nonce));
MD5Update(&md5, ":", 1);
if ('\0' != *qop)
{
MD5Update(&md5, noncecount, strlen(noncecount));
MD5Update(&md5, ":", 1);
MD5Update(&md5, cnonce, strlen(cnonce));
MD5Update(&md5, ":", 1);
MD5Update(&md5, qop, strlen(qop));
MD5Update(&md5, ":", 1);
}
MD5Update(&md5, ha2hex, HASH_MD5_HEX_LEN);
MD5Final(resphash, &md5);
cvthex(resphash, sizeof(resphash), response);
}
/**
* make response to request authentication from the client
*
* @param fp
* @param http_method
* @param url
* @param realm the realm presented to the client
* @param opaque string to user for opaque value
* @return MHD_YES on success, MHD_NO otherwise
*/
int http_digest_auth_fail_response(FILE *fp, const char *http_method,
const char *url, const char *realm, const char *opaque)
{
size_t hlen;
char nonce[HASH_MD5_HEX_LEN + 9];
int signal_stale = 0;
/* Generating the server nonce */
calculate_nonce((uint32_t) MHD_monotonic_time(), http_method,
NONCE_PRIV_KEY, strlen(NONCE_PRIV_KEY), url, realm, nonce);
/* Building the authentication header */
hlen = snprintf(NULL, 0,
"Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
realm, nonce, opaque, signal_stale ? ",stale=\"true\"" : "");
{
char header[hlen + 1];
snprintf(header, sizeof(header),
"Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
realm, nonce, opaque, signal_stale ? ",stale=\"true\"" : "");
#ifdef DIGEST_DEBUG
printf("%s: header: %s\n", __FUNCTION__, header);
#endif
fputs("WWW-Authenticate: ", fp);
fputs(header, fp);
return MHD_YES;
}
return MHD_NO;
}
/**
* Authenticates the authorization header sent by the client
*
* @param http_method
* @param url
* @param header: pointer to the position just after the string "Authorization: Digest "
* @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 http_digest_auth_check(const char *http_method, const char *url,
const char *header, const char *realm, const char *username,
const char *password, unsigned int nonce_timeout)
{
size_t len;
char *end;
char nonce[MAX_NONCE_LENGTH];
char cnonce[MAX_NONCE_LENGTH];
char qop[15]; /* auth,auth-int */
char nc[20];
char response[MAX_AUTH_RESPONSE_LENGTH];
const char *hentity = NULL; /* "auth-int" is not supported */
char ha1[HASH_MD5_HEX_LEN + 1];
char respexp[HASH_MD5_HEX_LEN + 1];
char noncehashexp[HASH_MD5_HEX_LEN + 9];
uint32_t nonce_time;
uint32_t t;
size_t left; /* number of characters left in 'header' for 'uri' */
unsigned long int nci;
#ifdef DIGEST_DEBUG
printf("%s: header: %s\n", __FUNCTION__, header);
#endif
left = strlen(header);
{
char un[MAX_USERNAME_LENGTH];
len = lookup_sub_value(un, sizeof(un), header, "username");
if ((0 == len) || (0 != strcmp(username, un)))
return MHD_NO;
left -= strlen("username") + len;
}
{
char r[MAX_REALM_LENGTH];
len = lookup_sub_value(r, sizeof(r), header, "realm");
if ((0 == len) || (0 != strcmp(realm, r)))
return MHD_NO;
left -= strlen("realm") + len;
}
if (0 == (len = lookup_sub_value(nonce, sizeof(nonce), header, "nonce")))
return MHD_NO;
left -= strlen("nonce") + len;
{
char uri[left];
if (0 == lookup_sub_value(uri, sizeof(uri), header, "uri"))
return MHD_NO;
/* 8 = 4 hexadecimal numbers for the timestamp */
nonce_time = strtoul(nonce + len - 8, (char **) NULL, 16);
t = (uint32_t) MHD_monotonic_time();
/*
* 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)
|| (nonce_time + nonce_timeout < nonce_time))
return MHD_INVALID_NONCE;
if (0 != strncmp(uri, url, strlen(url)))
{
#ifdef DIGEST_DEBUG
printf("Authentication failed: URI does not match.\n");
#endif
return MHD_NO;
}
calculate_nonce(nonce_time, http_method, NONCE_PRIV_KEY,
strlen(NONCE_PRIV_KEY), url, realm, noncehashexp);
/*
* Second level vetting for the nonce validity
* if the timestamp attached to the nonce is valid
* and possibly fabricated (in case of an attack)
* the attacker must also know the random seed to be
* able to generate a "sane" nonce, which if he does
* not, the nonce fabrication process going to be
* very hard to achieve.
*/
if (0 != strcmp(nonce, noncehashexp))
return MHD_INVALID_NONCE;
if ((0 == lookup_sub_value(cnonce, sizeof(cnonce), header, "cnonce"))
|| (0 == lookup_sub_value(qop, sizeof(qop), header, "qop"))
|| ((0 != strcmp(qop, "auth")) && (0 != strcmp(qop, "")))
|| (0 == lookup_sub_value(nc, sizeof(nc), header, "nc"))
|| (0
== lookup_sub_value(response, sizeof(response), header,
"response")))
{
#ifdef DIGEST_DEBUG
printf("Authentication failed, invalid format.\n");
#endif
return MHD_NO;
}
nci = strtoul(nc, &end, 16);
if (('\0' != *end) || ((LONG_MAX == nci) && (ERANGE == errno)))
{
#ifdef DIGEST_DEBUG
printf("Authentication failed, invalid format.\n");
#endif
return MHD_NO; /* invalid nonce format */
}
/*
* Checking if that combination of nonce and nc is sound
* and not a replay attack attempt. Also adds the nonce
* to the nonce-nc map if it does not exist there.
*/
digest_calc_ha1("md5", username, realm, password, nonce, cnonce, ha1);
digest_calc_response(ha1, nonce, nc, cnonce, qop, http_method, uri,
hentity, respexp);
return (0 == strcmp(response, respexp)) ? MHD_YES : MHD_NO;
}
}

View file

@ -32,12 +32,13 @@
#include <zstream/http.h>
#endif
#ifndef HTTP_ZSTREAM
#include "b64.h"
#endif
#include "http.h"
#include "digestauth.h"
#define REALM "authenticate@cwmp"
#define OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4"
static struct http_client http_c;
static struct http_server http_s;
@ -300,70 +301,20 @@ static void http_new_client(struct uloop_fd *ufd, unsigned events)
fp = fdopen(client, "r+");
while (fgets(buffer, sizeof(buffer), fp)) {
if (!strncasecmp(buffer, "Authorization: Basic ", strlen("Authorization: Basic "))) {
const char *c1, *c2, *min, *val;
char *username = NULL;
char *password = NULL;
char *acs_auth_basic = NULL;
char *auth_basic_check = NULL;
int len;
username = cwmp_main.conf.cpe_userid;
password = cwmp_main.conf.cpe_passwd;
if (!strncasecmp(buffer, "Authorization: Digest ", strlen("Authorization: Digest "))) {
char *username = cwmp_main.conf.cpe_userid;
char *password = cwmp_main.conf.cpe_passwd;
if (!username || !password) {
// if we dont have username or password configured proceed with connecting to ACS
FREE(username);
FREE(password);
auth_status = 1;
goto http_end_child;
}
c1 = strrchr(buffer, '\r');
c2 = strrchr(buffer, '\n');
if (!c1)
c1 = c2;
if (!c2)
c2 = c1;
if (!c1 || !c2)
continue;
min = (c1 < c2) ? c1 : c2;
val = strrchr(buffer, ' ');
if (!val)
continue;
val += sizeof(char);
ssize_t size = min - val;
acs_auth_basic = (char *) zstream_b64decode(val, &size);
if (!acs_auth_basic)
continue;
if (asprintf(&auth_basic_check, "%s:%s", username, password) == -1) {
FREE(username);
FREE(password);
free(acs_auth_basic);
goto error_child;
}
if (size == strlen(auth_basic_check)) {
len = size;
} else {
auth_status = 0;
goto free_resources;
}
if (!memcmp(acs_auth_basic, auth_basic_check, len * sizeof(char)))
if (http_digest_auth_check("GET", "/", buffer + strlen("Authorization: Digest "), REALM, username, password, 300) == MHD_YES)
auth_status = 1;
else
auth_status = 0;
free_resources:
free(acs_auth_basic);
free(auth_basic_check);
}
if (buffer[0] == '\r' || buffer[0] == '\n') {
@ -387,7 +338,7 @@ http_end_child:
status = EACCES;
fputs("HTTP/1.1 401 Unauthorized\r\n", fp);
fputs("Connection: close\r\n", fp);
fputs("WWW-Authenticate: Basic realm=\"default\"\r\n", fp);
http_digest_auth_fail_response(fp, "GET", "/", REALM, OPAQUE);
}
fputs("\r\n", fp);
goto done_child;

View file

@ -1,31 +0,0 @@
/*
* zstream - Micro URL I/O stream library
* Copyright (C) 2010 Steven Barth <steven@midlink.org>
* Copyright (C) 2010 John Crispin <blogic@openwrt.org>
*
* 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 St, Fifth Floor, Boston, MA 02110, USA
*
*/
#ifndef _FREECWMP_B64_H__
#define _FREECWMP_B64_H__
#ifndef HTTP_ZSTREAM
char* zstream_b64encode(const void *in, size_t *len);
void* zstream_b64decode(const char *in, size_t *len);
#endif /* HTTP_ZSTREAM */
#endif

38
src/inc/digestauth.h Normal file
View file

@ -0,0 +1,38 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* HTTP digest auth functions: originally imported from libmicrohttpd
*
* Copyright (C) 2013 Oussama Ghorbel <oussama.ghorbel@pivasoftware.com>
*
*/
#ifndef DIGESTAUTH_H_
#define DIGESTAUTH_H_
/**
* MHD-internal return code for "YES".
*/
#define MHD_YES 1
/**
* MHD-internal return code for "NO".
*/
#define MHD_NO 0
/**
* MHD digest auth internal code for an invalid nonce.
*/
#define MHD_INVALID_NONCE -1
int http_digest_auth_fail_response(FILE *fp, const char *http_method,
const char *url, const char *realm, const char *opaque);
int http_digest_auth_check(const char *http_method, const char *url,
const char *header, const char *realm, const char *username,
const char *password, unsigned int nonce_timeout);
#endif /* DIGESTAUTH_H_ */

52
src/inc/md5.h Normal file
View file

@ -0,0 +1,52 @@
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
#ifndef MD5_H
#define MD5_H
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#ifdef WORDS_BIGENDIAN
#define HIGHFIRST
#endif
#define MD5_DIGEST_SIZE 16
struct MD5Context
{
uint32_t buf[4];
uint32_t bits[2];
unsigned char in[64];
};
void
MD5Init(struct MD5Context *ctx);
void
MD5Update(struct MD5Context *ctx,
const void *buf,
unsigned len);
void MD5Final(unsigned char digest[MD5_DIGEST_SIZE],
struct MD5Context *ctx);
#endif /* !MD5_H */

263
src/md5.c Normal file
View file

@ -0,0 +1,263 @@
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
#include "md5.h"
#ifndef HIGHFIRST
#define byteReverse(buf, len) /* Nothing */
#else
/*
* Note: this code is harmless on little-endian machines.
*/
static void
byteReverse(unsigned char *buf,
unsigned longs)
{
uint32_t t;
do {
t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
((unsigned) buf[1] << 8 | buf[0]);
*(uint32_t *) buf = t;
buf += 4;
} while (--longs);
}
#endif
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
static void
MD5Transform(uint32_t buf[4],
uint32_t in[16])
{
uint32_t a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void
MD5Init(struct MD5Context *ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
void
MD5Update(struct MD5Context *ctx,
const void *data,
unsigned len)
{
const unsigned char *buf = data;
uint32_t t;
/* Update bitcount */
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
ctx->bits[1]++; /* Carry from low to high */
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
/* Handle any leading odd-sized chunks */
if (t) {
unsigned char *p = (unsigned char *) ctx->in + t;
t = 64 - t;
if (len < t) {
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += t;
len -= t;
}
/* Process data in 64-byte chunks */
while (len >= 64) {
memcpy(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += 64;
len -= 64;
}
/* Handle any remaining bytes of data. */
memcpy(ctx->in, buf, len);
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void
MD5Final(unsigned char digest[16],
struct MD5Context *ctx)
{
unsigned count;
unsigned char *p;
/* Compute number of bytes mod 64 */
count = (ctx->bits[0] >> 3) & 0x3F;
/* Set the first char of padding to 0x80. This is safe since there is
always at least one byte free */
p = ctx->in + count;
*p++ = 0x80;
/* Bytes of padding needed to make 64 bytes */
count = 64 - 1 - count;
/* Pad out to 56 mod 64 */
if (count < 8)
{
/* Two lots of padding: Pad the first block to 64 bytes */
memset(p, 0, count);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
/* Now fill the next block with 56 bytes */
memset(ctx->in, 0, 56);
}
else
{
/* Pad block to 56 bytes */
memset(p, 0, count - 8);
}
byteReverse(ctx->in, 14);
/* Append length in bits and transform */
((uint32_t *) ctx->in)[14] = ctx->bits[0];
((uint32_t *) ctx->in)[15] = ctx->bits[1];
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
memset(ctx, 0, sizeof(struct MD5Context)); /* In case it's sensitive */
}
/* end of md5.c */