Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Utilities / cmcurl-7.19.0 / lib / http_digest.c
blob21b3060bf16f1dd7daeb9f8b8cbb94f34d03efc6
1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2008, 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.1.1.1 2008-09-23 16:32:05 hoffman 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 "curl_base64.h"
37 #include "curl_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 const 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[256];
94 char content[1024];
95 size_t totlen=0;
97 while(*header && ISSPACE(*header))
98 header++;
100 /* how big can these strings be? */
101 if((2 == sscanf(header, "%255[^=]=\"%1023[^\"]\"",
102 value, content)) ||
103 /* try the same scan but without quotes around the content but don't
104 include the possibly trailing comma, newline or carriage return */
105 (2 == sscanf(header, "%255[^=]=%1023[^\r\n,]",
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 /* pass all additional spaces here */
184 while(*header && ISSPACE(*header))
185 header++;
186 if(',' == *header)
187 /* allow the list to be comma-separated */
188 header++;
190 /* We had a nonce since before, and we got another one now without
191 'stale=true'. This means we provided bad credentials in the previous
192 request */
193 if(before && !d->stale)
194 return CURLDIGEST_BAD;
196 /* We got this header without a nonce, that's a bad Digest line! */
197 if(!d->nonce)
198 return CURLDIGEST_BAD;
200 else
201 /* else not a digest, get out */
202 return CURLDIGEST_NONE;
204 return CURLDIGEST_FINE;
207 /* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
208 static void md5_to_ascii(unsigned char *source, /* 16 bytes */
209 unsigned char *dest) /* 33 bytes */
211 int i;
212 for(i=0; i<16; i++)
213 snprintf((char *)&dest[i*2], 3, "%02x", source[i]);
216 CURLcode Curl_output_digest(struct connectdata *conn,
217 bool proxy,
218 const unsigned char *request,
219 const unsigned char *uripath)
221 /* We have a Digest setup for this, use it! Now, to get all the details for
222 this sorted out, I must urge you dear friend to read up on the RFC2617
223 section 3.2.2, */
224 unsigned char md5buf[16]; /* 16 bytes/128 bits */
225 unsigned char request_digest[33];
226 unsigned char *md5this;
227 unsigned char *ha1;
228 unsigned char ha2[33];/* 32 digits and 1 zero byte */
229 char cnoncebuf[7];
230 char *cnonce;
231 char *tmp = NULL;
232 struct timeval now;
234 char **allocuserpwd;
235 char *userp;
236 char *passwdp;
237 struct auth *authp;
239 struct SessionHandle *data = conn->data;
240 struct digestdata *d;
241 #ifdef CURL_DOES_CONVERSIONS
242 CURLcode rc;
243 /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
244 It converts digest text to ASCII so the MD5 will be correct for
245 what ultimately goes over the network.
247 #define CURL_OUTPUT_DIGEST_CONV(a, b) \
248 rc = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \
249 if(rc != CURLE_OK) { \
250 free(b); \
251 return rc; \
253 #else
254 #define CURL_OUTPUT_DIGEST_CONV(a, b)
255 #endif /* CURL_DOES_CONVERSIONS */
257 if(proxy) {
258 d = &data->state.proxydigest;
259 allocuserpwd = &conn->allocptr.proxyuserpwd;
260 userp = conn->proxyuser;
261 passwdp = conn->proxypasswd;
262 authp = &data->state.authproxy;
264 else {
265 d = &data->state.digest;
266 allocuserpwd = &conn->allocptr.userpwd;
267 userp = conn->user;
268 passwdp = conn->passwd;
269 authp = &data->state.authhost;
272 if(*allocuserpwd) {
273 Curl_safefree(*allocuserpwd);
274 *allocuserpwd = NULL;
277 /* not set means empty */
278 if(!userp)
279 userp=(char *)"";
281 if(!passwdp)
282 passwdp=(char *)"";
284 if(!d->nonce) {
285 authp->done = FALSE;
286 return CURLE_OK;
288 authp->done = TRUE;
290 if(!d->nc)
291 d->nc = 1;
293 if(!d->cnonce) {
294 /* Generate a cnonce */
295 now = Curl_tvnow();
296 snprintf(cnoncebuf, sizeof(cnoncebuf), "%06ld", now.tv_sec);
297 if(Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), &cnonce))
298 d->cnonce = cnonce;
299 else
300 return CURLE_OUT_OF_MEMORY;
304 if the algorithm is "MD5" or unspecified (which then defaults to MD5):
306 A1 = unq(username-value) ":" unq(realm-value) ":" passwd
308 if the algorithm is "MD5-sess" then:
310 A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
311 ":" unq(nonce-value) ":" unq(cnonce-value)
314 md5this = (unsigned char *)
315 aprintf("%s:%s:%s", userp, d->realm, passwdp);
316 if(!md5this)
317 return CURLE_OUT_OF_MEMORY;
319 CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
320 Curl_md5it(md5buf, md5this);
321 free(md5this); /* free this again */
323 ha1 = (unsigned char *)malloc(33); /* 32 digits and 1 zero byte */
324 if(!ha1)
325 return CURLE_OUT_OF_MEMORY;
327 md5_to_ascii(md5buf, ha1);
329 if(d->algo == CURLDIGESTALGO_MD5SESS) {
330 /* nonce and cnonce are OUTSIDE the hash */
331 tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce);
332 if(!tmp)
333 return CURLE_OUT_OF_MEMORY;
334 CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */
335 Curl_md5it(md5buf, (unsigned char *)tmp);
336 free(tmp); /* free this again */
337 md5_to_ascii(md5buf, ha1);
341 If the "qop" directive's value is "auth" or is unspecified, then A2 is:
343 A2 = Method ":" digest-uri-value
345 If the "qop" value is "auth-int", then A2 is:
347 A2 = Method ":" digest-uri-value ":" H(entity-body)
349 (The "Method" value is the HTTP request method as specified in section
350 5.1.1 of RFC 2616)
353 md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
354 if(!md5this) {
355 free(ha1);
356 return CURLE_OUT_OF_MEMORY;
359 if(d->qop && strequal(d->qop, "auth-int")) {
360 /* We don't support auth-int at the moment. I can't see a easy way to get
361 entity-body here */
362 /* TODO: Append H(entity-body)*/
364 CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
365 Curl_md5it(md5buf, md5this);
366 free(md5this); /* free this again */
367 md5_to_ascii(md5buf, ha2);
369 if(d->qop) {
370 md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s",
371 ha1,
372 d->nonce,
373 d->nc,
374 d->cnonce,
375 d->qop,
376 ha2);
378 else {
379 md5this = (unsigned char *)aprintf("%s:%s:%s",
380 ha1,
381 d->nonce,
382 ha2);
384 free(ha1);
385 if(!md5this)
386 return CURLE_OUT_OF_MEMORY;
388 CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
389 Curl_md5it(md5buf, md5this);
390 free(md5this); /* free this again */
391 md5_to_ascii(md5buf, request_digest);
393 /* for test case 64 (snooped from a Mozilla 1.3a request)
395 Authorization: Digest username="testuser", realm="testrealm", \
396 nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
399 if(d->qop) {
400 *allocuserpwd =
401 aprintf( "%sAuthorization: Digest "
402 "username=\"%s\", "
403 "realm=\"%s\", "
404 "nonce=\"%s\", "
405 "uri=\"%s\", "
406 "cnonce=\"%s\", "
407 "nc=%08x, "
408 "qop=\"%s\", "
409 "response=\"%s\"",
410 proxy?"Proxy-":"",
411 userp,
412 d->realm,
413 d->nonce,
414 uripath, /* this is the PATH part of the URL */
415 d->cnonce,
416 d->nc,
417 d->qop,
418 request_digest);
420 if(strequal(d->qop, "auth"))
421 d->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded
422 which tells to the server how many times you are using the
423 same nonce in the qop=auth mode. */
425 else {
426 *allocuserpwd =
427 aprintf( "%sAuthorization: Digest "
428 "username=\"%s\", "
429 "realm=\"%s\", "
430 "nonce=\"%s\", "
431 "uri=\"%s\", "
432 "response=\"%s\"",
433 proxy?"Proxy-":"",
434 userp,
435 d->realm,
436 d->nonce,
437 uripath, /* this is the PATH part of the URL */
438 request_digest);
440 if(!*allocuserpwd)
441 return CURLE_OUT_OF_MEMORY;
443 /* Add optional fields */
444 if(d->opaque) {
445 /* append opaque */
446 tmp = aprintf("%s, opaque=\"%s\"", *allocuserpwd, d->opaque);
447 if(!tmp)
448 return CURLE_OUT_OF_MEMORY;
449 free(*allocuserpwd);
450 *allocuserpwd = tmp;
453 if(d->algorithm) {
454 /* append algorithm */
455 tmp = aprintf("%s, algorithm=\"%s\"", *allocuserpwd, d->algorithm);
456 if(!tmp)
457 return CURLE_OUT_OF_MEMORY;
458 free(*allocuserpwd);
459 *allocuserpwd = tmp;
462 /* append CRLF to the userpwd header */
463 tmp = (char*) realloc(*allocuserpwd, strlen(*allocuserpwd) + 3 + 1);
464 if(!tmp)
465 return CURLE_OUT_OF_MEMORY;
466 strcat(tmp, "\r\n");
467 *allocuserpwd = tmp;
469 return CURLE_OK;
472 void Curl_digest_cleanup_one(struct digestdata *d)
474 if(d->nonce)
475 free(d->nonce);
476 d->nonce = NULL;
478 if(d->cnonce)
479 free(d->cnonce);
480 d->cnonce = NULL;
482 if(d->realm)
483 free(d->realm);
484 d->realm = NULL;
486 if(d->opaque)
487 free(d->opaque);
488 d->opaque = NULL;
490 if(d->qop)
491 free(d->qop);
492 d->qop = NULL;
494 if(d->algorithm)
495 free(d->algorithm);
496 d->algorithm = NULL;
498 d->nc = 0;
499 d->algo = CURLDIGESTALGO_MD5; /* default algorithm */
500 d->stale = FALSE; /* default means normal, not stale */
504 void Curl_digest_cleanup(struct SessionHandle *data)
506 Curl_digest_cleanup_one(&data->state.digest);
507 Curl_digest_cleanup_one(&data->state.proxydigest);
510 #endif