mirror of
https://dev.iopsys.eu/bbf/icwmp.git
synced 2026-03-11 03:28:31 +01:00
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:
parent
78d60a9c3d
commit
577dbad71c
8 changed files with 833 additions and 235 deletions
144
src/b64.c
144
src/b64.c
|
|
@ -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 */
|
||||
|
||||
|
|
@ -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
468
src/digestauth.c
Normal 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;
|
||||
}
|
||||
}
|
||||
69
src/http.c
69
src/http.c
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
38
src/inc/digestauth.h
Normal 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
52
src/inc/md5.h
Normal 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
263
src/md5.c
Normal 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 */
|
||||
Loading…
Add table
Reference in a new issue