#include "setup.h"
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include "urldata.h"
#include "sendf.h"
#include "rawstr.h"
#include "curl_base64.h"
#include "curl_md5.h"
#include "http_digest.h"
#include "strtok.h"
#include "url.h"
#include "curl_memory.h"
#include "easyif.h"
#define _MPRINTF_REPLACE
#include <curl/mprintf.h>
#include "memdebug.h"
#define MAX_VALUE_LENGTH 256
#define MAX_CONTENT_LENGTH 1024
static int get_pair(const char *str, char *value, char *content,
const char **endptr)
{
int c;
bool starts_with_quote = FALSE;
bool escape = FALSE;
for(c=MAX_VALUE_LENGTH-1; (*str && (*str != '=') && c--); )
*value++ = *str++;
*value=0;
if('=' != *str++)
return 1;
if('\"' == *str) {
str++;
starts_with_quote = TRUE;
}
for(c=MAX_CONTENT_LENGTH-1; *str && c--; str++) {
switch(*str) {
case '\\':
if(!escape) {
escape = TRUE;
*content++ = '\\';
continue;
}
break;
case ',':
if(!starts_with_quote) {
c=0;
continue;
}
break;
case '\r':
case '\n':
c=0;
continue;
case '\"':
if(!escape && starts_with_quote) {
c=0;
continue;
}
break;
}
escape = FALSE;
*content++ = *str;
}
*content=0;
*endptr = str;
return 0;
}
CURLdigest Curl_input_digest(struct connectdata *conn,
bool proxy,
const char *header)
{
bool more = TRUE;
char *token = NULL;
char *tmp = NULL;
bool foundAuth = FALSE;
bool foundAuthInt = FALSE;
struct SessionHandle *data=conn->data;
bool before = FALSE;
struct digestdata *d;
if(proxy) {
d = &data->state.proxydigest;
}
else {
d = &data->state.digest;
}
while(*header && ISSPACE(*header))
header++;
if(checkprefix("Digest", header)) {
header += strlen("Digest");
if(d->nonce)
before = TRUE;
Curl_digest_cleanup_one(d);
while(more) {
char value[MAX_VALUE_LENGTH];
char content[MAX_CONTENT_LENGTH];
while(*header && ISSPACE(*header))
header++;
if(!get_pair(header, value, content, &header)) {
if(Curl_raw_equal(value, "nonce")) {
d->nonce = strdup(content);
if(!d->nonce)
return CURLDIGEST_NOMEM;
}
else if(Curl_raw_equal(value, "stale")) {
if(Curl_raw_equal(content, "true")) {
d->stale = TRUE;
d->nc = 1;
}
}
else if(Curl_raw_equal(value, "realm")) {
d->realm = strdup(content);
if(!d->realm)
return CURLDIGEST_NOMEM;
}
else if(Curl_raw_equal(value, "opaque")) {
d->opaque = strdup(content);
if(!d->opaque)
return CURLDIGEST_NOMEM;
}
else if(Curl_raw_equal(value, "qop")) {
char *tok_buf;
tmp = strdup(content);
if(!tmp)
return CURLDIGEST_NOMEM;
token = strtok_r(tmp, ",", &tok_buf);
while(token != NULL) {
if(Curl_raw_equal(token, "auth")) {
foundAuth = TRUE;
}
else if(Curl_raw_equal(token, "auth-int")) {
foundAuthInt = TRUE;
}
token = strtok_r(NULL, ",", &tok_buf);
}
free(tmp);
if(foundAuth) {
d->qop = strdup("auth");
if(!d->qop)
return CURLDIGEST_NOMEM;
}
else if(foundAuthInt) {
d->qop = strdup("auth-int");
if(!d->qop)
return CURLDIGEST_NOMEM;
}
}
else if(Curl_raw_equal(value, "algorithm")) {
d->algorithm = strdup(content);
if(!d->algorithm)
return CURLDIGEST_NOMEM;
if(Curl_raw_equal(content, "MD5-sess"))
d->algo = CURLDIGESTALGO_MD5SESS;
else if(Curl_raw_equal(content, "MD5"))
d->algo = CURLDIGESTALGO_MD5;
else
return CURLDIGEST_BADALGO;
}
else {
}
}
else
break;
while(*header && ISSPACE(*header))
header++;
if(',' == *header)
header++;
}
if(before && !d->stale)
return CURLDIGEST_BAD;
if(!d->nonce)
return CURLDIGEST_BAD;
}
else
return CURLDIGEST_NONE;
return CURLDIGEST_FINE;
}
static void md5_to_ascii(unsigned char *source,
unsigned char *dest)
{
int i;
for(i=0; i<16; i++)
snprintf((char *)&dest[i*2], 3, "%02x", source[i]);
}
CURLcode Curl_output_digest(struct connectdata *conn,
bool proxy,
const unsigned char *request,
const unsigned char *uripath)
{
unsigned char md5buf[16];
unsigned char request_digest[33];
unsigned char *md5this;
unsigned char *ha1;
unsigned char ha2[33];
char cnoncebuf[7];
char *cnonce;
char *tmp = NULL;
struct timeval now;
char **allocuserpwd;
const char *userp;
const char *passwdp;
struct auth *authp;
struct SessionHandle *data = conn->data;
struct digestdata *d;
#ifdef CURL_DOES_CONVERSIONS
CURLcode rc;
#define CURL_OUTPUT_DIGEST_CONV(a, b) \
rc = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \
if(rc != CURLE_OK) { \
free(b); \
return rc; \
}
#else
#define CURL_OUTPUT_DIGEST_CONV(a, b)
#endif
if(proxy) {
d = &data->state.proxydigest;
allocuserpwd = &conn->allocptr.proxyuserpwd;
userp = conn->proxyuser;
passwdp = conn->proxypasswd;
authp = &data->state.authproxy;
}
else {
d = &data->state.digest;
allocuserpwd = &conn->allocptr.userpwd;
userp = conn->user;
passwdp = conn->passwd;
authp = &data->state.authhost;
}
if(*allocuserpwd) {
Curl_safefree(*allocuserpwd);
*allocuserpwd = NULL;
}
if(!userp)
userp="";
if(!passwdp)
passwdp="";
if(!d->nonce) {
authp->done = FALSE;
return CURLE_OK;
}
authp->done = TRUE;
if(!d->nc)
d->nc = 1;
if(!d->cnonce) {
now = Curl_tvnow();
snprintf(cnoncebuf, sizeof(cnoncebuf), "%06ld", (long)now.tv_sec);
if(Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), &cnonce))
d->cnonce = cnonce;
else
return CURLE_OUT_OF_MEMORY;
}
md5this = (unsigned char *)
aprintf("%s:%s:%s", userp, d->realm, passwdp);
if(!md5this)
return CURLE_OUT_OF_MEMORY;
CURL_OUTPUT_DIGEST_CONV(data, md5this);
Curl_md5it(md5buf, md5this);
free(md5this);
ha1 = malloc(33);
if(!ha1)
return CURLE_OUT_OF_MEMORY;
md5_to_ascii(md5buf, ha1);
if(d->algo == CURLDIGESTALGO_MD5SESS) {
tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
CURL_OUTPUT_DIGEST_CONV(data, tmp);
Curl_md5it(md5buf, (unsigned char *)tmp);
free(tmp);
md5_to_ascii(md5buf, ha1);
}
if(authp->iestyle && ((tmp = strchr((char *)uripath, '?')) != NULL)) {
md5this = (unsigned char *)aprintf("%s:%.*s", request,
(int)(tmp - (char *)uripath), uripath);
}
else
md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
if(!md5this) {
free(ha1);
return CURLE_OUT_OF_MEMORY;
}
if(d->qop && Curl_raw_equal(d->qop, "auth-int")) {
}
CURL_OUTPUT_DIGEST_CONV(data, md5this);
Curl_md5it(md5buf, md5this);
free(md5this);
md5_to_ascii(md5buf, ha2);
if(d->qop) {
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);
if(!md5this)
return CURLE_OUT_OF_MEMORY;
CURL_OUTPUT_DIGEST_CONV(data, md5this);
Curl_md5it(md5buf, md5this);
free(md5this);
md5_to_ascii(md5buf, request_digest);
if(d->qop) {
*allocuserpwd =
aprintf( "%sAuthorization: Digest "
"username=\"%s\", "
"realm=\"%s\", "
"nonce=\"%s\", "
"uri=\"%s\", "
"cnonce=\"%s\", "
"nc=%08x, "
"qop=\"%s\", "
"response=\"%s\"",
proxy?"Proxy-":"",
userp,
d->realm,
d->nonce,
uripath,
d->cnonce,
d->nc,
d->qop,
request_digest);
if(Curl_raw_equal(d->qop, "auth"))
d->nc++;
}
else {
*allocuserpwd =
aprintf( "%sAuthorization: Digest "
"username=\"%s\", "
"realm=\"%s\", "
"nonce=\"%s\", "
"uri=\"%s\", "
"response=\"%s\"",
proxy?"Proxy-":"",
userp,
d->realm,
d->nonce,
uripath,
request_digest);
}
if(!*allocuserpwd)
return CURLE_OUT_OF_MEMORY;
if(d->opaque) {
tmp = aprintf("%s, opaque=\"%s\"", *allocuserpwd, d->opaque);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
free(*allocuserpwd);
*allocuserpwd = tmp;
}
if(d->algorithm) {
tmp = aprintf("%s, algorithm=\"%s\"", *allocuserpwd, d->algorithm);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
free(*allocuserpwd);
*allocuserpwd = tmp;
}
tmp = realloc(*allocuserpwd, strlen(*allocuserpwd) + 3);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
strcat(tmp, "\r\n");
*allocuserpwd = tmp;
return CURLE_OK;
}
void Curl_digest_cleanup_one(struct digestdata *d)
{
if(d->nonce)
free(d->nonce);
d->nonce = NULL;
if(d->cnonce)
free(d->cnonce);
d->cnonce = NULL;
if(d->realm)
free(d->realm);
d->realm = NULL;
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;
d->stale = FALSE;
}
void Curl_digest_cleanup(struct SessionHandle *data)
{
Curl_digest_cleanup_one(&data->state.digest);
Curl_digest_cleanup_one(&data->state.proxydigest);
}
#endif