ENH: typo
[cmake.git] / Utilities / cmcurl / http_digest.c
blob5e43d74106bbb56d4924c9790c3d1e7913fcbb83
1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2007, 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.
21 * $Id: http_digest.c,v 1.2 2007-03-15 19:22:13 andy Exp $
22 ***************************************************************************/
23 #include "setup.h"
25 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
26 /* -- WIN32 approved -- */
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <ctype.h>
33 #include "urldata.h"
34 #include "sendf.h"
35 #include "strequal.h"
36 #include "base64.h"
37 #include "md5.h"
38 #include "http_digest.h"
39 #include "strtok.h"
40 #include "url.h" /* for Curl_safefree() */
41 #include "memory.h"
42 #include "easyif.h" /* included for Curl_convert_... prototypes */
44 #define _MPRINTF_REPLACE /* use our functions only */
45 #include <curl/mprintf.h>
47 /* The last #include file should be: */
48 #include "memdebug.h"
50 /* Test example headers:
52 WWW-Authenticate: Digest realm="testrealm", nonce="1053604598"
53 Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598"
57 CURLdigest Curl_input_digest(struct connectdata *conn,
58 bool proxy,
59 char *header) /* rest of the *-authenticate:
60 header */
62 bool more = TRUE;
63 char *token = NULL;
64 char *tmp = NULL;
65 bool foundAuth = FALSE;
66 bool foundAuthInt = FALSE;
67 struct SessionHandle *data=conn->data;
68 bool before = FALSE; /* got a nonce before */
69 struct digestdata *d;
71 if(proxy) {
72 d = &data->state.proxydigest;
74 else {
75 d = &data->state.digest;
78 /* skip initial whitespaces */
79 while(*header && ISSPACE(*header))
80 header++;
82 if(checkprefix("Digest", header)) {
83 header += strlen("Digest");
85 /* If we already have received a nonce, keep that in mind */
86 if(d->nonce)
87 before = TRUE;
89 /* clear off any former leftovers and init to defaults */
90 Curl_digest_cleanup_one(d);
92 while(more) {
93 char value[32];
94 char content[128];
95 size_t totlen=0;
97 while(*header && ISSPACE(*header))
98 header++;
100 /* how big can these strings be? */
101 if((2 == sscanf(header, "%31[^=]=\"%127[^\"]\"",
102 value, content)) ||
103 /* try the same scan but without quotes around the content but don't
104 include the possibly trailing comma */
105 (2 == sscanf(header, "%31[^=]=%127[^,]",
106 value, content)) ) {
107 if(strequal(value, "nonce")) {
108 d->nonce = strdup(content);
109 if(!d->nonce)
110 return CURLDIGEST_NOMEM;
112 else if(strequal(value, "stale")) {
113 if(strequal(content, "true")) {
114 d->stale = TRUE;
115 d->nc = 1; /* we make a new nonce now */
118 else if(strequal(value, "realm")) {
119 d->realm = strdup(content);
120 if(!d->realm)
121 return CURLDIGEST_NOMEM;
123 else if(strequal(value, "opaque")) {
124 d->opaque = strdup(content);
125 if(!d->opaque)
126 return CURLDIGEST_NOMEM;
128 else if(strequal(value, "qop")) {
129 char *tok_buf;
130 /* tokenize the list and choose auth if possible, use a temporary
131 clone of the buffer since strtok_r() ruins it */
132 tmp = strdup(content);
133 if(!tmp)
134 return CURLDIGEST_NOMEM;
135 token = strtok_r(tmp, ",", &tok_buf);
136 while (token != NULL) {
137 if (strequal(token, "auth")) {
138 foundAuth = TRUE;
140 else if (strequal(token, "auth-int")) {
141 foundAuthInt = TRUE;
143 token = strtok_r(NULL, ",", &tok_buf);
145 free(tmp);
146 /*select only auth o auth-int. Otherwise, ignore*/
147 if (foundAuth) {
148 d->qop = strdup("auth");
149 if(!d->qop)
150 return CURLDIGEST_NOMEM;
152 else if (foundAuthInt) {
153 d->qop = strdup("auth-int");
154 if(!d->qop)
155 return CURLDIGEST_NOMEM;
158 else if(strequal(value, "algorithm")) {
159 d->algorithm = strdup(content);
160 if(!d->algorithm)
161 return CURLDIGEST_NOMEM;
162 if(strequal(content, "MD5-sess"))
163 d->algo = CURLDIGESTALGO_MD5SESS;
164 else if(strequal(content, "MD5"))
165 d->algo = CURLDIGESTALGO_MD5;
166 else
167 return CURLDIGEST_BADALGO;
169 else {
170 /* unknown specifier, ignore it! */
172 totlen = strlen(value)+strlen(content)+1;
174 if(header[strlen(value)+1] == '\"')
175 /* the contents were within quotes, then add 2 for them to the
176 length */
177 totlen += 2;
179 else
180 break; /* we're done here */
182 header += totlen;
183 if(',' == *header)
184 /* allow the list to be comma-separated */
185 header++;
187 /* We had a nonce since before, and we got another one now without
188 'stale=true'. This means we provided bad credentials in the previous
189 request */
190 if(before && !d->stale)
191 return CURLDIGEST_BAD;
193 /* We got this header without a nonce, that's a bad Digest line! */
194 if(!d->nonce)
195 return CURLDIGEST_BAD;
197 else
198 /* else not a digest, get out */
199 return CURLDIGEST_NONE;
201 return CURLDIGEST_FINE;
204 /* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
205 static void md5_to_ascii(unsigned char *source, /* 16 bytes */
206 unsigned char *dest) /* 33 bytes */
208 int i;
209 for(i=0; i<16; i++)
210 snprintf((char *)&dest[i*2], 3, "%02x", source[i]);
213 CURLcode Curl_output_digest(struct connectdata *conn,
214 bool proxy,
215 unsigned char *request,
216 unsigned char *uripath)
218 /* We have a Digest setup for this, use it! Now, to get all the details for
219 this sorted out, I must urge you dear friend to read up on the RFC2617
220 section 3.2.2, */
221 unsigned char md5buf[16]; /* 16 bytes/128 bits */
222 unsigned char request_digest[33];
223 unsigned char *md5this;
224 unsigned char *ha1;
225 unsigned char ha2[33];/* 32 digits and 1 zero byte */
226 char cnoncebuf[7];
227 char *cnonce;
228 char *tmp = NULL;
229 struct timeval now;
231 char **allocuserpwd;
232 char *userp;
233 char *passwdp;
234 struct auth *authp;
236 struct SessionHandle *data = conn->data;
237 struct digestdata *d;
238 #ifdef CURL_DOES_CONVERSIONS
239 CURLcode rc;
240 /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
241 It converts digest text to ASCII so the MD5 will be correct for
242 what ultimately goes over the network.
244 #define CURL_OUTPUT_DIGEST_CONV(a, b) \
245 rc = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \
246 if (rc != CURLE_OK) { \
247 free(b); \
248 return rc; \
250 #else
251 #define CURL_OUTPUT_DIGEST_CONV(a, b)
252 #endif /* CURL_DOES_CONVERSIONS */
254 if(proxy) {
255 d = &data->state.proxydigest;
256 allocuserpwd = &conn->allocptr.proxyuserpwd;
257 userp = conn->proxyuser;
258 passwdp = conn->proxypasswd;
259 authp = &data->state.authproxy;
261 else {
262 d = &data->state.digest;
263 allocuserpwd = &conn->allocptr.userpwd;
264 userp = conn->user;
265 passwdp = conn->passwd;
266 authp = &data->state.authhost;
269 /* not set means empty */
270 if(!userp)
271 userp=(char *)"";
273 if(!passwdp)
274 passwdp=(char *)"";
276 if(!d->nonce) {
277 authp->done = FALSE;
278 return CURLE_OK;
280 authp->done = TRUE;
282 if(!d->nc)
283 d->nc = 1;
285 if(!d->cnonce) {
286 /* Generate a cnonce */
287 now = Curl_tvnow();
288 snprintf(cnoncebuf, sizeof(cnoncebuf), "%06ld", now.tv_sec);
289 if(Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), &cnonce))
290 d->cnonce = cnonce;
291 else
292 return CURLE_OUT_OF_MEMORY;
296 if the algorithm is "MD5" or unspecified (which then defaults to MD5):
298 A1 = unq(username-value) ":" unq(realm-value) ":" passwd
300 if the algorithm is "MD5-sess" then:
302 A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
303 ":" unq(nonce-value) ":" unq(cnonce-value)
306 md5this = (unsigned char *)
307 aprintf("%s:%s:%s", userp, d->realm, passwdp);
308 if(!md5this)
309 return CURLE_OUT_OF_MEMORY;
311 CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
312 Curl_md5it(md5buf, md5this);
313 free(md5this); /* free this again */
315 ha1 = (unsigned char *)malloc(33); /* 32 digits and 1 zero byte */
316 if(!ha1)
317 return CURLE_OUT_OF_MEMORY;
319 md5_to_ascii(md5buf, ha1);
321 if(d->algo == CURLDIGESTALGO_MD5SESS) {
322 /* nonce and cnonce are OUTSIDE the hash */
323 tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce);
324 if(!tmp)
325 return CURLE_OUT_OF_MEMORY;
326 CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */
327 Curl_md5it(md5buf, (unsigned char *)tmp);
328 free(tmp); /* free this again */
329 md5_to_ascii(md5buf, ha1);
333 If the "qop" directive's value is "auth" or is unspecified, then A2 is:
335 A2 = Method ":" digest-uri-value
337 If the "qop" value is "auth-int", then A2 is:
339 A2 = Method ":" digest-uri-value ":" H(entity-body)
341 (The "Method" value is the HTTP request method as specified in section
342 5.1.1 of RFC 2616)
345 md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
346 if(!md5this) {
347 free(ha1);
348 return CURLE_OUT_OF_MEMORY;
351 if (d->qop && strequal(d->qop, "auth-int")) {
352 /* We don't support auth-int at the moment. I can't see a easy way to get
353 entity-body here */
354 /* TODO: Append H(entity-body)*/
356 CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
357 Curl_md5it(md5buf, md5this);
358 free(md5this); /* free this again */
359 md5_to_ascii(md5buf, ha2);
361 if (d->qop) {
362 md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s",
363 ha1,
364 d->nonce,
365 d->nc,
366 d->cnonce,
367 d->qop,
368 ha2);
370 else {
371 md5this = (unsigned char *)aprintf("%s:%s:%s",
372 ha1,
373 d->nonce,
374 ha2);
376 free(ha1);
377 if(!md5this)
378 return CURLE_OUT_OF_MEMORY;
380 CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
381 Curl_md5it(md5buf, md5this);
382 free(md5this); /* free this again */
383 md5_to_ascii(md5buf, request_digest);
385 /* for test case 64 (snooped from a Mozilla 1.3a request)
387 Authorization: Digest username="testuser", realm="testrealm", \
388 nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
391 Curl_safefree(*allocuserpwd);
393 if (d->qop) {
394 *allocuserpwd =
395 aprintf( "%sAuthorization: Digest "
396 "username=\"%s\", "
397 "realm=\"%s\", "
398 "nonce=\"%s\", "
399 "uri=\"%s\", "
400 "cnonce=\"%s\", "
401 "nc=%08x, "
402 "qop=\"%s\", "
403 "response=\"%s\"",
404 proxy?"Proxy-":"",
405 userp,
406 d->realm,
407 d->nonce,
408 uripath, /* this is the PATH part of the URL */
409 d->cnonce,
410 d->nc,
411 d->qop,
412 request_digest);
414 if(strequal(d->qop, "auth"))
415 d->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded
416 which tells to the server how many times you are using the
417 same nonce in the qop=auth mode. */
419 else {
420 *allocuserpwd =
421 aprintf( "%sAuthorization: Digest "
422 "username=\"%s\", "
423 "realm=\"%s\", "
424 "nonce=\"%s\", "
425 "uri=\"%s\", "
426 "response=\"%s\"",
427 proxy?"Proxy-":"",
428 userp,
429 d->realm,
430 d->nonce,
431 uripath, /* this is the PATH part of the URL */
432 request_digest);
434 if(!*allocuserpwd)
435 return CURLE_OUT_OF_MEMORY;
437 /* Add optional fields */
438 if(d->opaque) {
439 /* append opaque */
440 tmp = aprintf("%s, opaque=\"%s\"", *allocuserpwd, d->opaque);
441 if(!tmp)
442 return CURLE_OUT_OF_MEMORY;
443 free(*allocuserpwd);
444 *allocuserpwd = tmp;
447 if(d->algorithm) {
448 /* append algorithm */
449 tmp = aprintf("%s, algorithm=\"%s\"", *allocuserpwd, d->algorithm);
450 if(!tmp)
451 return CURLE_OUT_OF_MEMORY;
452 free(*allocuserpwd);
453 *allocuserpwd = tmp;
456 /* append CRLF to the userpwd header */
457 tmp = (char*) realloc(*allocuserpwd, strlen(*allocuserpwd) + 3 + 1);
458 if(!tmp)
459 return CURLE_OUT_OF_MEMORY;
460 strcat(tmp, "\r\n");
461 *allocuserpwd = tmp;
463 return CURLE_OK;
466 void Curl_digest_cleanup_one(struct digestdata *d)
468 if(d->nonce)
469 free(d->nonce);
470 d->nonce = NULL;
472 if(d->cnonce)
473 free(d->cnonce);
474 d->cnonce = NULL;
476 if(d->realm)
477 free(d->realm);
478 d->realm = NULL;
480 if(d->opaque)
481 free(d->opaque);
482 d->opaque = NULL;
484 if(d->qop)
485 free(d->qop);
486 d->qop = NULL;
488 if(d->algorithm)
489 free(d->algorithm);
490 d->algorithm = NULL;
492 d->nc = 0;
493 d->algo = CURLDIGESTALGO_MD5; /* default algorithm */
494 d->stale = FALSE; /* default means normal, not stale */
498 void Curl_digest_cleanup(struct SessionHandle *data)
500 Curl_digest_cleanup_one(&data->state.digest);
501 Curl_digest_cleanup_one(&data->state.proxydigest);
504 #endif