From 334d78cd18a7310144383929bdcef34ffbf6159b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 22 May 2003 16:09:54 +0000 Subject: [PATCH] Initial Digest support. At least partly working. --- lib/Makefile.am | 3 +- lib/http.c | 29 +++-- lib/http_digest.c | 210 ++++++++++++++++++++++++++++++ lib/http_digest.h | 43 +++++++ lib/md5.c | 320 ++++++++++++++++++++++++++++++++++++++++++++++ lib/md5.h | 4 + lib/transfer.c | 76 +++++++---- lib/urldata.h | 10 +- 8 files changed, 659 insertions(+), 36 deletions(-) create mode 100644 lib/http_digest.c create mode 100644 lib/http_digest.h create mode 100644 lib/md5.c create mode 100644 lib/md5.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 62c5c55d9..b17bac198 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -67,7 +67,8 @@ getpass.c netrc.c telnet.h getinfo.c getinfo.h transfer.c strequal.c \ strequal.h easy.c security.h security.c krb4.c krb4.h memdebug.c \ memdebug.h inet_ntoa_r.h http_chunks.c http_chunks.h strtok.c strtok.h \ connect.c connect.h llist.c llist.h hash.c hash.h multi.c \ -content_encoding.c content_encoding.h share.c share.h +content_encoding.c content_encoding.h share.c share.h http_digest.c \ +md5.c md5.h http_digest.h noinst_HEADERS = setup.h transfer.h diff --git a/lib/http.c b/lib/http.c index a41d0b048..a7ad947e1 100644 --- a/lib/http.c +++ b/lib/http.c @@ -89,6 +89,7 @@ #include "cookie.h" #include "strequal.h" #include "ssluse.h" +#include "http_digest.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -627,6 +628,7 @@ CURLcode Curl_http(struct connectdata *conn) char *host = conn->name; const char *te = ""; /* tranfer-encoding */ char *ptr; + unsigned char *request; if(!conn->proto.http) { /* Only allocate this struct if we don't already have it! */ @@ -647,6 +649,12 @@ CURLcode Curl_http(struct connectdata *conn) data->set.upload) { data->set.httpreq = HTTPREQ_PUT; } + + request = data->set.customrequest?data->set.customrequest: + (data->set.no_body?"HEAD": + ((HTTPREQ_POST == data->set.httpreq) || + (HTTPREQ_POST_FORM == data->set.httpreq))?"POST": + (HTTPREQ_PUT == data->set.httpreq)?"PUT":"GET"); /* The User-Agent string has been built in url.c already, because it might have been used in the proxy connect, but if we have got a header with @@ -657,7 +665,12 @@ CURLcode Curl_http(struct connectdata *conn) conn->allocptr.uagent=NULL; } - if((conn->bits.user_passwd) && !checkheaders(data, "Authorization:")) { + if(data->state.digest.nonce) { + result = Curl_output_digest(conn, request, (unsigned char *)ppath); + if(result) + return result; + } + else if((conn->bits.user_passwd) && !checkheaders(data, "Authorization:")) { char *authorization; /* To prevent the user+password to get sent to other than the original @@ -902,7 +915,7 @@ CURLcode Curl_http(struct connectdata *conn) /* add the main request stuff */ add_bufferf(req_buffer, "%s " /* GET/HEAD/POST/PUT */ - "%s HTTP/%s\r\n" /* path */ + "%s HTTP/%s\r\n" /* path + HTTP version */ "%s" /* proxyuserpwd */ "%s" /* userpwd */ "%s" /* range */ @@ -915,16 +928,12 @@ CURLcode Curl_http(struct connectdata *conn) "%s" /* referer */ "%s",/* transfer-encoding */ - data->set.customrequest?data->set.customrequest: - (data->set.no_body?"HEAD": - ((HTTPREQ_POST == data->set.httpreq) || - (HTTPREQ_POST_FORM == data->set.httpreq))?"POST": - (HTTPREQ_PUT == data->set.httpreq)?"PUT":"GET"), - ppath, httpstring, + request, + ppath, + httpstring, (conn->bits.proxy_user_passwd && conn->allocptr.proxyuserpwd)?conn->allocptr.proxyuserpwd:"", - (conn->bits.user_passwd && conn->allocptr.userpwd)? - conn->allocptr.userpwd:"", + conn->allocptr.userpwd?conn->allocptr.userpwd:"", (conn->bits.use_range && conn->allocptr.rangeline)? conn->allocptr.rangeline:"", (data->set.useragent && *data->set.useragent && conn->allocptr.uagent)? diff --git a/lib/http_digest.c b/lib/http_digest.c new file mode 100644 index 000000000..82ae1d2a8 --- /dev/null +++ b/lib/http_digest.c @@ -0,0 +1,210 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2003, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ +#include "setup.h" + +#ifndef CURL_DISABLE_HTTP +/* -- WIN32 approved -- */ +#include +#include +#include +#include +#include + +#include "urldata.h" +#include "sendf.h" +#include "strequal.h" + +#include "md5.h" +#include "http_digest.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +/* Test example header: + +WWW-Authenticate: Digest realm="testrealm", nonce="1053604598" + +*/ + +CURLdigest Curl_input_digest(struct connectdata *conn, + char *header) /* rest of the www-authenticate: + header */ +{ + bool more = TRUE; + struct SessionHandle *data=conn->data; + + /* skip initial whitespaces */ + while(*header && isspace((int)*header)) + header++; + + if(checkprefix("Digest", header)) { + header += strlen("Digest"); + + data->state.digest.algo = CURLDIGESTALGO_MD5; /* default algorithm */ + + while(more) { + char value[32]; + char content[128]; + int totlen=0; + + while(*header && isspace((int)*header)) + header++; + + /* how big can these strings be? */ + if(2 == sscanf(header, "%31[^=]=\"%127[^\"]\"", + value, content)) { + if(strequal(value, "nonce")) { + data->state.digest.nonce = strdup(content); + } + else if(strequal(value, "cnonce")) { + data->state.digest.cnonce = strdup(content); + } + else if(strequal(value, "realm")) { + data->state.digest.realm = strdup(content); + } + else if(strequal(value, "algorithm")) { + if(strequal(content, "MD5-sess")) + data->state.digest.algo = CURLDIGESTALGO_MD5SESS; + /* else, remain using the default md5 */ + } + else { + /* unknown specifier, ignore it! */ + } + totlen = strlen(value)+strlen(content)+3; + } + else + break; /* we're done here */ + + header += totlen; + if(',' == *header) + /* allow the list to be comma-separated */ + header++; + } + } + else + /* else not a digest, get out */ + return CURLDIGEST_NONE; + + return CURLDIGEST_FINE; +} + +/* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/ +static void md5_to_ascii(unsigned char *source, /* 16 bytes */ + unsigned char *dest) /* 33 bytes */ +{ + int i; + for(i=0; i<16; i++) + sprintf((char *)&dest[i*2], "%02x", source[i]); +} + +CURLcode Curl_output_digest(struct connectdata *conn, + unsigned char *request, + unsigned char *uripath) +{ + /* We have a Digest setup for this, use it! + Now, to get all the details for this sorted out, I must urge you dear friend + to read up on the RFC2617 section 3.2.2, */ + unsigned char md5buf[16]; /* 16 bytes/128 bits */ + unsigned char ha1[33]; /* 32 digits and 1 zero byte */ + unsigned char ha2[33]; + unsigned char request_digest[33]; + char *md5this; + + struct SessionHandle *data = conn->data; + + /* + if the algorithm is "MD5" or unspecified (which then defaults to MD5): + + A1 = unq(username-value) ":" unq(realm-value) ":" passwd + + if the algorithm is "MD5-sess" then: + + A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd ) + ":" unq(nonce-value) ":" unq(cnonce-value) + */ + if(data->state.digest.algo == CURLDIGESTALGO_MD5SESS) { + md5this = aprintf("%s:%s:%s:%s:%s", + data->state.user, + data->state.digest.realm, + data->state.passwd, + data->state.digest.nonce, + data->state.digest.cnonce); + } + else { + md5this = aprintf("%s:%s:%s", + data->state.user, + data->state.digest.realm, + data->state.passwd); + } + Curl_md5it(md5buf, md5this); + free(md5this); /* free this again */ + md5_to_ascii(md5buf, ha1); + + /* + A2 = Method ":" digest-uri-value + + (The "Method" value is the HTTP request method as specified in section + 5.1.1 of RFC 2616) + */ + + md5this = aprintf("%s:%s", request, uripath); + Curl_md5it(md5buf, md5this); + free(md5this); /* free this again */ + md5_to_ascii(md5buf, ha2); + + md5this = aprintf("%s:%s:%s", + ha1, + data->state.digest.nonce, + ha2); + Curl_md5it(md5buf, md5this); + free(md5this); /* free this again */ + md5_to_ascii(md5buf, request_digest); + + /* for test case 64 (snooped from a Mozilla 1.3a request) + + Authorization: Digest username="testuser", realm="testrealm", \ + nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" + */ + + conn->allocptr.userpwd = + aprintf( "Authorization: Digest " + "username=\"%s\", " + "realm=\"%s\", " + "nonce=\"%s\", " + "uri=\"%s\", " + "response=\"%s\"\r\n", + data->state.user, + data->state.digest.realm, + data->state.digest.nonce, + uripath, /* this is the PATH part of the URL */ + request_digest ); + + return CURLE_OK; +} + +#endif diff --git a/lib/http_digest.h b/lib/http_digest.h new file mode 100644 index 000000000..f4204c40d --- /dev/null +++ b/lib/http_digest.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2003, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +typedef enum { + CURLDIGEST_NONE, /* not a digest */ + CURLDIGEST_BAD, /* a digest, but one we don't like */ + CURLDIGEST_FINE, /* a digest we act on */ + + CURLDIGEST_LAST /* last entry in this enum, don't use */ +} CURLdigest; + +enum { + CURLDIGESTALGO_MD5, + CURLDIGESTALGO_MD5SESS +}; + +/* this is for digest header input */ +CURLdigest Curl_input_digest(struct connectdata *conn, char *header); + +/* this is for creating digest header output */ +CURLcode Curl_output_digest(struct connectdata *conn, + unsigned char *request, + unsigned char *uripath); diff --git a/lib/md5.c b/lib/md5.c new file mode 100644 index 000000000..f6abde9e5 --- /dev/null +++ b/lib/md5.c @@ -0,0 +1,320 @@ +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +#include + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +typedef unsigned long int UINT4; + +/* MD5 context. */ +struct md5_ctx { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +}; + +static void MD5Init(struct md5_ctx *); +static void MD5Update(struct md5_ctx *, unsigned char *, unsigned int); +static void MD5Final(unsigned char [16], struct md5_ctx *); + +/* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform(UINT4 [4], unsigned char [64]); +static void Encode(unsigned char *, UINT4 *, unsigned int); +static void Decode(UINT4 *, unsigned char *, unsigned int); + +#define MD5_memcpy(dst,src,len) memcpy(dst,src,len) +#define MD5_memset(dst,val,len) memset(dst,val,len) + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +static void MD5Init (context) +struct md5_ctx *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. +*/ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +static void MD5Update (context, input, inputLen) +struct md5_ctx *context; /* context */ +unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) { + MD5_memcpy((void *)&context->buffer[index], (void *)input, partLen); + MD5Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform(context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy((void *)&context->buffer[index], (void *)&input[i], + inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +static void MD5Final (digest, context) +unsigned char digest[16]; /* message digest */ +struct md5_ctx *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. */ + MD5_memset ((void *)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. */ +static void MD5Transform (state, block) +UINT4 state[4]; +unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. */ + MD5_memset ((void *)x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode (unsigned char *output, + UINT4 *input, + unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. +*/ +static void Decode (UINT4 *output, + unsigned char *input, + unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +void Curl_md5it(unsigned char *outbuffer, /* 16 bytes */ + unsigned char *input) +{ + struct md5_ctx ctx; + MD5Init(&ctx); + MD5Update(&ctx, input, strlen((char *)input)); + MD5Final(outbuffer, &ctx); +} diff --git a/lib/md5.h b/lib/md5.h new file mode 100644 index 000000000..df7587e8c --- /dev/null +++ b/lib/md5.h @@ -0,0 +1,4 @@ + +void Curl_md5it(unsigned char *output, + unsigned char *input); + diff --git a/lib/transfer.c b/lib/transfer.c index 9befa5585..c8c127348 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -95,6 +95,7 @@ #include "url.h" #include "getinfo.h" #include "ssluse.h" +#include "http_digest.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -701,32 +702,52 @@ CURLcode Curl_readwrite(struct connectdata *conn, if(data->set.get_filetime) data->info.filetime = k->timeofdoc; } - else if ((k->httpcode >= 300 && k->httpcode < 400) && - (data->set.http_follow_location) && - checkprefix("Location:", k->p)) { - /* this is the URL that the server advices us to get instead */ - char *ptr; - char *start=k->p; - char backup; - - start += 9; /* pass "Location:" */ - - /* Skip spaces and tabs. We do this to support multiple - white spaces after the "Location:" keyword. */ - while(*start && isspace((int)*start )) - start++; - ptr = start; /* start scanning here */ - - /* scan through the string to find the end */ - while(*ptr && !isspace((int)*ptr)) - ptr++; - backup = *ptr; /* store the ending letter */ - if(ptr != start) { - *ptr = '\0'; /* zero terminate */ - conn->newurl = strdup(start); /* clone string */ - *ptr = backup; /* restore ending letter */ + else if(checkprefix("WWW-Authenticate:", k->p) && + (401 == k->httpcode) && + 1 /* TODO: replace with a check for Digest authentication + activated */) { + CURLdigest dig = Curl_input_digest(conn, k->p+ + strlen("WWW-Authenticate:")); + if(CURLDIGEST_FINE == dig) { + /* We act on it. Store our new url, which happens to be + the same one we already use! */ + conn->newurl = strdup(data->change.url); /* clone string */ } } + else if ((k->httpcode >= 300 && k->httpcode < 400) && + checkprefix("Location:", k->p)) { + if(data->set.http_follow_location) { + /* this is the URL that the server advices us to get instead */ + char *ptr; + char *start=k->p; + char backup; + + start += 9; /* pass "Location:" */ + + /* Skip spaces and tabs. We do this to support multiple + white spaces after the "Location:" keyword. */ + while(*start && isspace((int)*start )) + start++; + ptr = start; /* start scanning here */ + + /* scan through the string to find the end */ + while(*ptr && !isspace((int)*ptr)) + ptr++; + backup = *ptr; /* store the ending letter */ + if(ptr != start) { + *ptr = '\0'; /* zero terminate */ + conn->newurl = strdup(start); /* clone string */ + *ptr = backup; /* restore ending letter */ + } + } +#if 0 /* for consideration */ + else { + /* This is a Location: but we have not been instructed to + follow it */ + infof(data, "We ignore this location header as instructed\n"); + } +#endif + } /* * End of header-checks. Write them to the client. @@ -1554,8 +1575,15 @@ CURLcode Curl_follow(struct SessionHandle *data, * differently based on exactly what return code there was. * Discussed on the curl mailing list and posted about on the 26th * of January 2001. + * + * News from 7.10.6: we can also get here on a 401, in case we need to + * do Digest authentication. */ switch(data->info.httpcode) { + case 401: + /* Act on a digest authentication, we keep on moving and do the + Authorization: Digest header in the HTTP request code snippet */ + break; case 300: /* Multiple Choices */ case 306: /* Not used */ case 307: /* Temporary Redirect */ diff --git a/lib/urldata.h b/lib/urldata.h index efd73b2cf..31a755355 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -152,6 +152,13 @@ struct curl_ssl_session { struct ssl_config_data ssl_config; /* setup for this session */ }; +/* Struct used for Digest challenge-response authentication */ +struct digestdata { + char *nonce; + char *cnonce; + char *realm; + int algo; +}; /**************************************************************************** * HTTP unique setup @@ -327,7 +334,6 @@ struct Curl_transfer_keeper { and we're uploading the last chunk */ }; - /* * The connectdata struct contains all fields and variables that should be * unique for an entire connection. @@ -619,6 +625,8 @@ struct UrlState { #endif bool allow_port; /* Is set.use_port allowed to take effect or not. This is always set TRUE when curl_easy_perform() is called. */ + + struct digestdata digest; };