Luca Altea's major HTTP Digest update

This commit is contained in:
Daniel Stenberg 2004-04-29 08:18:32 +00:00
parent 699ebe2f0b
commit b34c40dcf5
2 changed files with 195 additions and 55 deletions

View File

@ -1,8 +1,8 @@
/*************************************************************************** /***************************************************************************
* _ _ ____ _ * _ _ ____ _
* Project ___| | | | _ \| | * Project ___| | | | _ \| |
* / __| | | | |_) | | * / __| | | | |_) | |
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
@ -10,7 +10,7 @@
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html. * are also available at http://curl.haxx.se/docs/copyright.html.
* *
* You may opt to use, copy, modify, merge, publish, distribute and/or sell * 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 * copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file. * furnished to do so, under the terms of the COPYING file.
@ -33,9 +33,10 @@
#include "urldata.h" #include "urldata.h"
#include "sendf.h" #include "sendf.h"
#include "strequal.h" #include "strequal.h"
#include "base64.h"
#include "md5.h" #include "md5.h"
#include "http_digest.h" #include "http_digest.h"
#include "strtok.h"
#include "url.h" /* for Curl_safefree() */ #include "url.h" /* for Curl_safefree() */
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
@ -57,6 +58,10 @@ CURLdigest Curl_input_digest(struct connectdata *conn,
header */ header */
{ {
bool more = TRUE; bool more = TRUE;
char *token = NULL;
char *tmp = NULL;
bool foundAuth = FALSE;
bool foundAuthInt = FALSE;
struct SessionHandle *data=conn->data; struct SessionHandle *data=conn->data;
bool before = FALSE; /* got a nonce before */ bool before = FALSE; /* got a nonce before */
struct digestdata *d = &data->state.digest; struct digestdata *d = &data->state.digest;
@ -82,7 +87,7 @@ CURLdigest Curl_input_digest(struct connectdata *conn,
while(*header && isspace((int)*header)) while(*header && isspace((int)*header))
header++; header++;
/* how big can these strings be? */ /* how big can these strings be? */
if((2 == sscanf(header, "%31[^=]=\"%127[^\"]\"", if((2 == sscanf(header, "%31[^=]=\"%127[^\"]\"",
value, content)) || value, content)) ||
@ -94,16 +99,43 @@ CURLdigest Curl_input_digest(struct connectdata *conn,
d->nonce = strdup(content); d->nonce = strdup(content);
} }
else if(strequal(value, "stale")) { else if(strequal(value, "stale")) {
if(strequal(content, "true")) if(strequal(content, "true")) {
d->stale = TRUE; d->stale = TRUE;
} d->nc = 1; /* we make a new nonce now */
else if(strequal(value, "cnonce")) { }
d->cnonce = strdup(content);
} }
else if(strequal(value, "realm")) { else if(strequal(value, "realm")) {
d->realm = strdup(content); d->realm = strdup(content);
} }
else if(strequal(value, "opaque")) {
d->opaque = strdup(content);
}
else if(strequal(value, "qop")) {
char *tok_buf;
/* tokenize the list and choose auth if possible, use a temporary
clone of the buffer since strtok_r() ruins it */
tmp = strdup(content);
token = strtok_r(tmp, ",", &tok_buf);
while (token != NULL) {
if (strequal(token, "auth")) {
foundAuth = TRUE;
}
else if (strequal(token, "auth-int")) {
foundAuthInt = TRUE;
}
token = strtok (NULL, ",");
}
free(tmp);
/*select only auth o auth-int. Otherwise, ignore*/
if (foundAuth) {
d->qop = strdup("auth");
}
else if (foundAuthInt) {
d->qop = strdup("auth-int");
}
}
else if(strequal(value, "algorithm")) { else if(strequal(value, "algorithm")) {
d->algorithm = strdup(content);
if(strequal(content, "MD5-sess")) if(strequal(content, "MD5-sess"))
d->algo = CURLDIGESTALGO_MD5SESS; d->algo = CURLDIGESTALGO_MD5SESS;
else if(strequal(content, "MD5")) else if(strequal(content, "MD5"))
@ -116,18 +148,17 @@ CURLdigest Curl_input_digest(struct connectdata *conn,
} }
totlen = strlen(value)+strlen(content)+3; totlen = strlen(value)+strlen(content)+3;
} }
else else
break; /* we're done here */ break; /* we're done here */
header += totlen; header += totlen;
if(',' == *header) if(',' == *header)
/* allow the list to be comma-separated */ /* allow the list to be comma-separated */
header++; header++;
} }
/* We had a nonce since before, and we got another one now without /* We had a nonce since before, and we got another one now without
'stale=true'. This means we provided bad credentials in the previous 'stale=true'. This means we provided bad credentials in the previous
request */ request */
if(before && !d->stale) if(before && !d->stale)
return CURLDIGEST_BAD; return CURLDIGEST_BAD;
@ -135,7 +166,7 @@ CURLdigest Curl_input_digest(struct connectdata *conn,
if(!d->nonce) if(!d->nonce)
return CURLDIGEST_BAD; return CURLDIGEST_BAD;
} }
else else
/* else not a digest, get out */ /* else not a digest, get out */
return CURLDIGEST_NONE; return CURLDIGEST_NONE;
@ -159,17 +190,34 @@ CURLcode Curl_output_digest(struct connectdata *conn,
this sorted out, I must urge you dear friend to read up on the RFC2617 this sorted out, I must urge you dear friend to read up on the RFC2617
section 3.2.2, */ section 3.2.2, */
unsigned char md5buf[16]; /* 16 bytes/128 bits */ 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]; unsigned char request_digest[33];
unsigned char *md5this; unsigned char *md5this;
unsigned char *ha1;
unsigned char ha2[33];/* 32 digits and 1 zero byte */
char cnoncebuf[7];
char *cnonce;
char *tmp = NULL;
struct timeval now;
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
struct digestdata *d = &data->state.digest; struct digestdata *d = &data->state.digest;
ha1 = (unsigned char *)malloc(33); /* 32 digits and 1 zero byte */
if(!d->nc)
d->nc = 1;
if(!d->cnonce) {
/* Generate a cnonce */
now = Curl_tvnow();
snprintf(cnoncebuf, sizeof(cnoncebuf), "%06d", now.tv_sec);
Curl_base64_encode(cnoncebuf, strlen(cnoncebuf), &cnonce);
d->cnonce = cnonce;
}
/* /*
if the algorithm is "MD5" or unspecified (which then defaults to MD5): if the algorithm is "MD5" or unspecified (which then defaults to MD5):
A1 = unq(username-value) ":" unq(realm-value) ":" passwd A1 = unq(username-value) ":" unq(realm-value) ":" passwd
if the algorithm is "MD5-sess" then: if the algorithm is "MD5-sess" then:
@ -177,40 +225,61 @@ CURLcode Curl_output_digest(struct connectdata *conn,
A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd ) A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
":" unq(nonce-value) ":" unq(cnonce-value) ":" unq(nonce-value) ":" unq(cnonce-value)
*/ */
if(d->algo == CURLDIGESTALGO_MD5SESS) {
md5this = (unsigned char *) md5this = (unsigned char *)
aprintf("%s:%s:%s:%s:%s", aprintf("%s:%s:%s", conn->user, d->realm, conn->passwd);
conn->user,
d->realm,
conn->passwd,
d->nonce,
d->cnonce);
}
else {
md5this = (unsigned char *)
aprintf("%s:%s:%s",
conn->user,
d->realm,
conn->passwd);
}
Curl_md5it(md5buf, md5this); Curl_md5it(md5buf, md5this);
free(md5this); /* free this again */ free(md5this); /* free this again */
md5_to_ascii(md5buf, ha1); md5_to_ascii(md5buf, ha1);
if(d->algo == CURLDIGESTALGO_MD5SESS) {
/* nonce and cnonce are OUTSIDE the hash */
tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce);
free(ha1);
ha1 = (unsigned char *)tmp;
}
/* /*
A2 = Method ":" digest-uri-value If the "qop" directive's value is "auth" or is unspecified, then A2 is:
A2 = Method ":" digest-uri-value
If the "qop" value is "auth-int", then A2 is:
A2 = Method ":" digest-uri-value ":" H(entity-body)
(The "Method" value is the HTTP request method as specified in section (The "Method" value is the HTTP request method as specified in section
5.1.1 of RFC 2616) 5.1.1 of RFC 2616)
*/ */
md5this = (unsigned char *)aprintf("%s:%s", request, uripath); md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
if (d->qop && strequal(d->qop, "auth-int")) {
/* We don't support auth-int at the moment. I can't see a easy way to get
entity-body here */
/* TODO: Append H(entity-body)*/
}
Curl_md5it(md5buf, md5this); Curl_md5it(md5buf, md5this);
free(md5this); /* free this again */ free(md5this); /* free this again */
md5_to_ascii(md5buf, ha2); md5_to_ascii(md5buf, ha2);
md5this = (unsigned char *)aprintf("%s:%s:%s", ha1, d->nonce, if (d->qop) {
ha2); md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s",
ha1,
d->nonce,
d->nc,
d->cnonce,
d->qop,
ha2);
}
else {
md5this = (unsigned char *)aprintf("%s:%s:%s",
ha1,
d->nonce,
ha2);
}
free(ha1);
Curl_md5it(md5buf, md5this); Curl_md5it(md5buf, md5this);
free(md5this); /* free this again */ free(md5this); /* free this again */
md5_to_ascii(md5buf, request_digest); md5_to_ascii(md5buf, request_digest);
@ -222,18 +291,73 @@ CURLcode Curl_output_digest(struct connectdata *conn,
*/ */
Curl_safefree(conn->allocptr.userpwd); Curl_safefree(conn->allocptr.userpwd);
conn->allocptr.userpwd =
aprintf( "Authorization: Digest " if (d->qop) {
"username=\"%s\", " conn->allocptr.userpwd =
"realm=\"%s\", " aprintf( "Authorization: Digest "
"nonce=\"%s\", " "username=\"%s\", "
"uri=\"%s\", " "realm=\"%s\", "
"response=\"%s\"\r\n", "nonce=\"%s\", "
conn->user, "uri=\"%s\", "
d->realm, "cnonce=\"%s\", "
d->nonce, "nc=\"%08x\", "
uripath, /* this is the PATH part of the URL */ "qop=\"%s\", "
request_digest ); "response=\"%s\"",
conn->user,
d->realm,
d->nonce,
uripath, /* this is the PATH part of the URL */
d->cnonce,
d->nc,
d->qop,
request_digest);
if(strequal(d->qop, "auth"))
d->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded
which tells to the server how many times you are using the
same nonce in the qop=auth mode. */
}
else {
conn->allocptr.userpwd =
aprintf( "Authorization: Digest "
"username=\"%s\", "
"realm=\"%s\", "
"nonce=\"%s\", "
"uri=\"%s\", "
"response=\"%s\"",
conn->user,
d->realm,
d->nonce,
uripath, /* this is the PATH part of the URL */
request_digest);
}
/* Add optional fields */
if(d->opaque) {
/* append opaque */
tmp = aprintf(", opaque=\"%s\"", d->opaque);
conn->allocptr.userpwd = (char*)
realloc(conn->allocptr.userpwd,
strlen(conn->allocptr.userpwd) + strlen(tmp) + 1);
strcat(conn->allocptr.userpwd, tmp);
free(tmp);
}
if(d->algorithm) {
/* append algorithm */
tmp = aprintf(", algorithm=\"%s\"", d->algorithm);
conn->allocptr.userpwd = (char*)
realloc(conn->allocptr.userpwd,
strlen(conn->allocptr.userpwd) + strlen(tmp) + 1);
strcat(conn->allocptr.userpwd, tmp);
free(tmp);
}
/* append CRLF to the userpwd header */
conn->allocptr.userpwd = (char*)
realloc(conn->allocptr.userpwd,
strlen(conn->allocptr.userpwd) + 3 + 1);
strcat(conn->allocptr.userpwd, "\r\n");
return CURLE_OK; return CURLE_OK;
} }
@ -254,8 +378,20 @@ void Curl_digest_cleanup(struct SessionHandle *data)
free(d->realm); free(d->realm);
d->realm = NULL; d->realm = NULL;
d->algo = CURLDIGESTALGO_MD5; /* default algorithm */ if(d->opaque)
free(d->opaque);
d->opaque = NULL;
if(d->qop)
free(d->qop);
d->qop = NULL;
if(d->algorithm)
free(d->algorithm);
d->algorithm = NULL;
d->nc = 0;
d->algo = CURLDIGESTALGO_MD5; /* default algorithm */
d->stale = FALSE; /* default means normal, not stale */ d->stale = FALSE; /* default means normal, not stale */
} }

View File

@ -171,6 +171,10 @@ struct digestdata {
char *realm; char *realm;
int algo; int algo;
bool stale; /* set true for re-negotiation */ bool stale; /* set true for re-negotiation */
char *opaque;
char *qop;
char *algorithm;
int nc; /* nounce count */
}; };
typedef enum { typedef enum {