1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2003, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
22 ***************************************************************************/
25 #ifndef CURL_DISABLE_HTTP
26 /* -- WIN32 approved -- */
38 #include "http_digest.h"
40 #define _MPRINTF_REPLACE /* use our functions only */
41 #include <curl/mprintf.h>
43 /* The last #include file should be: */
48 /* Test example header:
50 WWW-Authenticate: Digest realm="testrealm", nonce="1053604598"
54 CURLdigest Curl_input_digest(struct connectdata *conn,
55 char *header) /* rest of the www-authenticate:
59 struct SessionHandle *data=conn->data;
61 /* skip initial whitespaces */
62 while(*header && isspace((int)*header))
65 if(checkprefix("Digest", header)) {
66 header += strlen("Digest");
68 data->state.digest.algo = CURLDIGESTALGO_MD5; /* default algorithm */
75 while(*header && isspace((int)*header))
78 /* how big can these strings be? */
79 if(2 == sscanf(header, "%31[^=]=\"%127[^\"]\"",
81 if(strequal(value, "nonce")) {
82 data->state.digest.nonce = strdup(content);
84 else if(strequal(value, "cnonce")) {
85 data->state.digest.cnonce = strdup(content);
87 else if(strequal(value, "realm")) {
88 data->state.digest.realm = strdup(content);
90 else if(strequal(value, "algorithm")) {
91 if(strequal(content, "MD5-sess"))
92 data->state.digest.algo = CURLDIGESTALGO_MD5SESS;
93 /* else, remain using the default md5 */
96 /* unknown specifier, ignore it! */
98 totlen = strlen(value)+strlen(content)+3;
101 break; /* we're done here */
105 /* allow the list to be comma-separated */
110 /* else not a digest, get out */
111 return CURLDIGEST_NONE;
113 return CURLDIGEST_FINE;
116 /* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
117 static void md5_to_ascii(unsigned char *source, /* 16 bytes */
118 unsigned char *dest) /* 33 bytes */
122 sprintf((char *)&dest[i*2], "%02x", source[i]);
125 CURLcode Curl_output_digest(struct connectdata *conn,
126 unsigned char *request,
127 unsigned char *uripath)
129 /* We have a Digest setup for this, use it!
130 Now, to get all the details for this sorted out, I must urge you dear friend
131 to read up on the RFC2617 section 3.2.2, */
132 unsigned char md5buf[16]; /* 16 bytes/128 bits */
133 unsigned char ha1[33]; /* 32 digits and 1 zero byte */
134 unsigned char ha2[33];
135 unsigned char request_digest[33];
138 struct SessionHandle *data = conn->data;
141 if the algorithm is "MD5" or unspecified (which then defaults to MD5):
143 A1 = unq(username-value) ":" unq(realm-value) ":" passwd
145 if the algorithm is "MD5-sess" then:
147 A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
148 ":" unq(nonce-value) ":" unq(cnonce-value)
150 if(data->state.digest.algo == CURLDIGESTALGO_MD5SESS) {
151 md5this = aprintf("%s:%s:%s:%s:%s",
153 data->state.digest.realm,
155 data->state.digest.nonce,
156 data->state.digest.cnonce);
159 md5this = aprintf("%s:%s:%s",
161 data->state.digest.realm,
164 Curl_md5it(md5buf, md5this);
165 free(md5this); /* free this again */
166 md5_to_ascii(md5buf, ha1);
169 A2 = Method ":" digest-uri-value
171 (The "Method" value is the HTTP request method as specified in section
175 md5this = aprintf("%s:%s", request, uripath);
176 Curl_md5it(md5buf, md5this);
177 free(md5this); /* free this again */
178 md5_to_ascii(md5buf, ha2);
180 md5this = aprintf("%s:%s:%s",
182 data->state.digest.nonce,
184 Curl_md5it(md5buf, md5this);
185 free(md5this); /* free this again */
186 md5_to_ascii(md5buf, request_digest);
188 /* for test case 64 (snooped from a Mozilla 1.3a request)
190 Authorization: Digest username="testuser", realm="testrealm", \
191 nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
194 conn->allocptr.userpwd =
195 aprintf( "Authorization: Digest "
200 "response=\"%s\"\r\n",
202 data->state.digest.realm,
203 data->state.digest.nonce,
204 uripath, /* this is the PATH part of the URL */