1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2004, 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;
60 bool before = FALSE; /* got a nonce before */
61 struct digestdata *d = &data->state.digest;
63 /* skip initial whitespaces */
64 while(*header && isspace((int)*header))
67 if(checkprefix("Digest", header)) {
68 header += strlen("Digest");
70 /* If we already have received a nonce, keep that in mind */
74 /* clear off any former leftovers and init to defaults */
75 Curl_digest_cleanup(data);
82 while(*header && isspace((int)*header))
85 /* how big can these strings be? */
86 if((2 == sscanf(header, "%31[^=]=\"%127[^\"]\"",
88 /* try the same scan but without quotes around the content but don't
89 include the possibly trailing comma */
90 (2 == sscanf(header, "%31[^=]=%127[^,]",
92 if(strequal(value, "nonce")) {
93 d->nonce = strdup(content);
95 else if(strequal(value, "stale")) {
96 if(strequal(content, "true"))
99 else if(strequal(value, "cnonce")) {
100 d->cnonce = strdup(content);
102 else if(strequal(value, "realm")) {
103 d->realm = strdup(content);
105 else if(strequal(value, "algorithm")) {
106 if(strequal(content, "MD5-sess"))
107 d->algo = CURLDIGESTALGO_MD5SESS;
108 else if(strequal(content, "MD5"))
109 d->algo = CURLDIGESTALGO_MD5;
111 return CURLDIGEST_BADALGO;
114 /* unknown specifier, ignore it! */
116 totlen = strlen(value)+strlen(content)+3;
119 break; /* we're done here */
123 /* allow the list to be comma-separated */
126 /* We had a nonce since before, and we got another one now without
127 'stale=true'. This means we provided bad credentials in the previous
130 if(before && !d->stale)
131 return CURLDIGEST_BAD;
133 /* We got this header without a nonce, that's a bad Digest line! */
135 return CURLDIGEST_BAD;
138 /* else not a digest, get out */
139 return CURLDIGEST_NONE;
141 return CURLDIGEST_FINE;
144 /* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
145 static void md5_to_ascii(unsigned char *source, /* 16 bytes */
146 unsigned char *dest) /* 33 bytes */
150 sprintf((char *)&dest[i*2], "%02x", source[i]);
153 CURLcode Curl_output_digest(struct connectdata *conn,
154 unsigned char *request,
155 unsigned char *uripath)
157 /* We have a Digest setup for this, use it! Now, to get all the details for
158 this sorted out, I must urge you dear friend to read up on the RFC2617
160 unsigned char md5buf[16]; /* 16 bytes/128 bits */
161 unsigned char ha1[33]; /* 32 digits and 1 zero byte */
162 unsigned char ha2[33];
163 unsigned char request_digest[33];
164 unsigned char *md5this;
166 struct SessionHandle *data = conn->data;
167 struct digestdata *d = &data->state.digest;
170 if the algorithm is "MD5" or unspecified (which then defaults to MD5):
172 A1 = unq(username-value) ":" unq(realm-value) ":" passwd
174 if the algorithm is "MD5-sess" then:
176 A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
177 ":" unq(nonce-value) ":" unq(cnonce-value)
179 if(d->algo == CURLDIGESTALGO_MD5SESS) {
180 md5this = (unsigned char *)
181 aprintf("%s:%s:%s:%s:%s",
189 md5this = (unsigned char *)
195 Curl_md5it(md5buf, md5this);
196 free(md5this); /* free this again */
197 md5_to_ascii(md5buf, ha1);
200 A2 = Method ":" digest-uri-value
202 (The "Method" value is the HTTP request method as specified in section
206 md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
207 Curl_md5it(md5buf, md5this);
208 free(md5this); /* free this again */
209 md5_to_ascii(md5buf, ha2);
211 md5this = (unsigned char *)aprintf("%s:%s:%s", ha1, d->nonce,
213 Curl_md5it(md5buf, md5this);
214 free(md5this); /* free this again */
215 md5_to_ascii(md5buf, request_digest);
217 /* for test case 64 (snooped from a Mozilla 1.3a request)
219 Authorization: Digest username="testuser", realm="testrealm", \
220 nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
223 Curl_safefree(conn->allocptr.userpwd);
224 conn->allocptr.userpwd =
225 aprintf( "Authorization: Digest "
230 "response=\"%s\"\r\n",
234 uripath, /* this is the PATH part of the URL */
240 void Curl_digest_cleanup(struct SessionHandle *data)
242 struct digestdata *d = &data->state.digest;
256 d->algo = CURLDIGESTALGO_MD5; /* default algorithm */
258 d->stale = FALSE; /* default means normal, not stale */