From 577dbad71cb7e5904fc0d62dbeff8e9dd29b286a Mon Sep 17 00:00:00 2001 From: zribiahmed Date: Sat, 16 Feb 2013 11:25:08 +0000 Subject: [PATCH] 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) --- src/b64.c | 144 ------------- src/bin/Makefile.am | 3 +- src/digestauth.c | 468 +++++++++++++++++++++++++++++++++++++++++++ src/http.c | 69 +------ src/inc/b64.h | 31 --- src/inc/digestauth.h | 38 ++++ src/inc/md5.h | 52 +++++ src/md5.c | 263 ++++++++++++++++++++++++ 8 files changed, 833 insertions(+), 235 deletions(-) delete mode 100644 src/b64.c create mode 100644 src/digestauth.c delete mode 100644 src/inc/b64.h create mode 100644 src/inc/digestauth.h create mode 100644 src/inc/md5.h create mode 100644 src/md5.c diff --git a/src/b64.c b/src/b64.c deleted file mode 100644 index dc411c6..0000000 --- a/src/b64.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * zstream - Micro URL I/O stream library - * Copyright (C) 2010 Steven Barth - * Copyright (C) 2010 John Crispin - * - * 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 -#include -#include -#include - -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 */ - diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index c8934c7..da36c89 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -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 \ diff --git a/src/digestauth.c b/src/digestauth.c new file mode 100644 index 0000000..b19fd0c --- /dev/null +++ b/src/digestauth.c @@ -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 + * + */ + +#include +#include +#include +#include +#include +#include +#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; + } +} diff --git a/src/http.c b/src/http.c index 7eae93f..e4e26d7 100644 --- a/src/http.c +++ b/src/http.c @@ -32,12 +32,13 @@ #include #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; diff --git a/src/inc/b64.h b/src/inc/b64.h deleted file mode 100644 index 7bc2c45..0000000 --- a/src/inc/b64.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * zstream - Micro URL I/O stream library - * Copyright (C) 2010 Steven Barth - * Copyright (C) 2010 John Crispin - * - * 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 - diff --git a/src/inc/digestauth.h b/src/inc/digestauth.h new file mode 100644 index 0000000..d6a1483 --- /dev/null +++ b/src/inc/digestauth.h @@ -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 + * + */ + + +#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_ */ diff --git a/src/inc/md5.h b/src/inc/md5.h new file mode 100644 index 0000000..dd68ba6 --- /dev/null +++ b/src/inc/md5.h @@ -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 +#include +#include +#include + + +#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 */ diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 0000000..4671b90 --- /dev/null +++ b/src/md5.c @@ -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<>(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 */