source: EcnlProtoTool/trunk/curl-7.57.0/lib/vauth/digest.c@ 331

Last change on this file since 331 was 331, checked in by coas-nagasima, 6 years ago

prototoolに関連するプロジェクトをnewlibからmuslを使うよう変更・更新
ntshellをnewlibの下位の実装から、muslのsyscallの実装に変更・更新
以下のOSSをアップデート
・mruby-1.3.0
・musl-1.1.18
・onigmo-6.1.3
・tcc-0.9.27
以下のOSSを追加
・openssl-1.1.0e
・curl-7.57.0
・zlib-1.2.11
以下のmrbgemsを追加
・iij/mruby-digest
・iij/mruby-env
・iij/mruby-errno
・iij/mruby-iijson
・iij/mruby-ipaddr
・iij/mruby-mock
・iij/mruby-require
・iij/mruby-tls-openssl

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc
File size: 30.6 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
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 https://curl.haxx.se/docs/copyright.html.
13 *
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.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * RFC2831 DIGEST-MD5 authentication
22 * RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication
23 *
24 ***************************************************************************/
25
26#include "curl_setup.h"
27
28#if !defined(CURL_DISABLE_CRYPTO_AUTH)
29
30#include <curl/curl.h>
31
32#include "vauth/vauth.h"
33#include "vauth/digest.h"
34#include "urldata.h"
35#include "curl_base64.h"
36#include "curl_hmac.h"
37#include "curl_md5.h"
38#include "curl_sha256.h"
39#include "vtls/vtls.h"
40#include "warnless.h"
41#include "strtok.h"
42#include "strcase.h"
43#include "non-ascii.h" /* included for Curl_convert_... prototypes */
44#include "curl_printf.h"
45#include "rand.h"
46
47/* The last #include files should be: */
48#include "curl_memory.h"
49#include "memdebug.h"
50
51#if !defined(USE_WINDOWS_SSPI)
52#define DIGEST_QOP_VALUE_AUTH (1 << 0)
53#define DIGEST_QOP_VALUE_AUTH_INT (1 << 1)
54#define DIGEST_QOP_VALUE_AUTH_CONF (1 << 2)
55
56#define DIGEST_QOP_VALUE_STRING_AUTH "auth"
57#define DIGEST_QOP_VALUE_STRING_AUTH_INT "auth-int"
58#define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf"
59
60/* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
61 It converts digest text to ASCII so the MD5 will be correct for
62 what ultimately goes over the network.
63*/
64#define CURL_OUTPUT_DIGEST_CONV(a, b) \
65 result = Curl_convert_to_network(a, (char *)b, strlen((const char *)b)); \
66 if(result) { \
67 free(b); \
68 return result; \
69 }
70#endif /* !USE_WINDOWS_SSPI */
71
72bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
73 const char **endptr)
74{
75 int c;
76 bool starts_with_quote = FALSE;
77 bool escape = FALSE;
78
79 for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--);)
80 *value++ = *str++;
81 *value = 0;
82
83 if('=' != *str++)
84 /* eek, no match */
85 return FALSE;
86
87 if('\"' == *str) {
88 /* This starts with a quote so it must end with one as well! */
89 str++;
90 starts_with_quote = TRUE;
91 }
92
93 for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
94 switch(*str) {
95 case '\\':
96 if(!escape) {
97 /* possibly the start of an escaped quote */
98 escape = TRUE;
99 *content++ = '\\'; /* Even though this is an escape character, we still
100 store it as-is in the target buffer */
101 continue;
102 }
103 break;
104
105 case ',':
106 if(!starts_with_quote) {
107 /* This signals the end of the content if we didn't get a starting
108 quote and then we do "sloppy" parsing */
109 c = 0; /* the end */
110 continue;
111 }
112 break;
113
114 case '\r':
115 case '\n':
116 /* end of string */
117 c = 0;
118 continue;
119
120 case '\"':
121 if(!escape && starts_with_quote) {
122 /* end of string */
123 c = 0;
124 continue;
125 }
126 break;
127 }
128
129 escape = FALSE;
130 *content++ = *str;
131 }
132
133 *content = 0;
134 *endptr = str;
135
136 return TRUE;
137}
138
139#if !defined(USE_WINDOWS_SSPI)
140/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
141static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
142 unsigned char *dest) /* 33 bytes */
143{
144 int i;
145 for(i = 0; i < 16; i++)
146 snprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
147}
148
149/* Convert sha256 chunk to RFC7616 -suitable ascii string*/
150static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */
151 unsigned char *dest) /* 65 bytes */
152{
153 int i;
154 for(i = 0; i < 32; i++)
155 snprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
156}
157
158/* Perform quoted-string escaping as described in RFC2616 and its errata */
159static char *auth_digest_string_quoted(const char *source)
160{
161 char *dest, *d;
162 const char *s = source;
163 size_t n = 1; /* null terminator */
164
165 /* Calculate size needed */
166 while(*s) {
167 ++n;
168 if(*s == '"' || *s == '\\') {
169 ++n;
170 }
171 ++s;
172 }
173
174 dest = malloc(n);
175 if(dest) {
176 s = source;
177 d = dest;
178 while(*s) {
179 if(*s == '"' || *s == '\\') {
180 *d++ = '\\';
181 }
182 *d++ = *s++;
183 }
184 *d = 0;
185 }
186
187 return dest;
188}
189
190/* Retrieves the value for a corresponding key from the challenge string
191 * returns TRUE if the key could be found, FALSE if it does not exists
192 */
193static bool auth_digest_get_key_value(const char *chlg,
194 const char *key,
195 char *value,
196 size_t max_val_len,
197 char end_char)
198{
199 char *find_pos;
200 size_t i;
201
202 find_pos = strstr(chlg, key);
203 if(!find_pos)
204 return FALSE;
205
206 find_pos += strlen(key);
207
208 for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i)
209 value[i] = *find_pos++;
210 value[i] = '\0';
211
212 return TRUE;
213}
214
215static CURLcode auth_digest_get_qop_values(const char *options, int *value)
216{
217 char *tmp;
218 char *token;
219 char *tok_buf = NULL;
220
221 /* Initialise the output */
222 *value = 0;
223
224 /* Tokenise the list of qop values. Use a temporary clone of the buffer since
225 strtok_r() ruins it. */
226 tmp = strdup(options);
227 if(!tmp)
228 return CURLE_OUT_OF_MEMORY;
229
230 token = strtok_r(tmp, ",", &tok_buf);
231 while(token != NULL) {
232 if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH))
233 *value |= DIGEST_QOP_VALUE_AUTH;
234 else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT))
235 *value |= DIGEST_QOP_VALUE_AUTH_INT;
236 else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF))
237 *value |= DIGEST_QOP_VALUE_AUTH_CONF;
238
239 token = strtok_r(NULL, ",", &tok_buf);
240 }
241
242 free(tmp);
243
244 return CURLE_OK;
245}
246
247/*
248 * auth_decode_digest_md5_message()
249 *
250 * This is used internally to decode an already encoded DIGEST-MD5 challenge
251 * message into the separate attributes.
252 *
253 * Parameters:
254 *
255 * chlg64 [in] - The base64 encoded challenge message.
256 * nonce [in/out] - The buffer where the nonce will be stored.
257 * nlen [in] - The length of the nonce buffer.
258 * realm [in/out] - The buffer where the realm will be stored.
259 * rlen [in] - The length of the realm buffer.
260 * alg [in/out] - The buffer where the algorithm will be stored.
261 * alen [in] - The length of the algorithm buffer.
262 * qop [in/out] - The buffer where the qop-options will be stored.
263 * qlen [in] - The length of the qop buffer.
264 *
265 * Returns CURLE_OK on success.
266 */
267static CURLcode auth_decode_digest_md5_message(const char *chlg64,
268 char *nonce, size_t nlen,
269 char *realm, size_t rlen,
270 char *alg, size_t alen,
271 char *qop, size_t qlen)
272{
273 CURLcode result = CURLE_OK;
274 unsigned char *chlg = NULL;
275 size_t chlglen = 0;
276 size_t chlg64len = strlen(chlg64);
277
278 /* Decode the base-64 encoded challenge message */
279 if(chlg64len && *chlg64 != '=') {
280 result = Curl_base64_decode(chlg64, &chlg, &chlglen);
281 if(result)
282 return result;
283 }
284
285 /* Ensure we have a valid challenge message */
286 if(!chlg)
287 return CURLE_BAD_CONTENT_ENCODING;
288
289 /* Retrieve nonce string from the challenge */
290 if(!auth_digest_get_key_value((char *) chlg, "nonce=\"", nonce, nlen,
291 '\"')) {
292 free(chlg);
293 return CURLE_BAD_CONTENT_ENCODING;
294 }
295
296 /* Retrieve realm string from the challenge */
297 if(!auth_digest_get_key_value((char *) chlg, "realm=\"", realm, rlen,
298 '\"')) {
299 /* Challenge does not have a realm, set empty string [RFC2831] page 6 */
300 strcpy(realm, "");
301 }
302
303 /* Retrieve algorithm string from the challenge */
304 if(!auth_digest_get_key_value((char *) chlg, "algorithm=", alg, alen, ',')) {
305 free(chlg);
306 return CURLE_BAD_CONTENT_ENCODING;
307 }
308
309 /* Retrieve qop-options string from the challenge */
310 if(!auth_digest_get_key_value((char *) chlg, "qop=\"", qop, qlen, '\"')) {
311 free(chlg);
312 return CURLE_BAD_CONTENT_ENCODING;
313 }
314
315 free(chlg);
316
317 return CURLE_OK;
318}
319
320/*
321 * Curl_auth_is_digest_supported()
322 *
323 * This is used to evaluate if DIGEST is supported.
324 *
325 * Parameters: None
326 *
327 * Returns TRUE as DIGEST as handled by libcurl.
328 */
329bool Curl_auth_is_digest_supported(void)
330{
331 return TRUE;
332}
333
334/*
335 * Curl_auth_create_digest_md5_message()
336 *
337 * This is used to generate an already encoded DIGEST-MD5 response message
338 * ready for sending to the recipient.
339 *
340 * Parameters:
341 *
342 * data [in] - The session handle.
343 * chlg64 [in] - The base64 encoded challenge message.
344 * userp [in] - The user name.
345 * passdwp [in] - The user's password.
346 * service [in] - The service type such as http, smtp, pop or imap.
347 * outptr [in/out] - The address where a pointer to newly allocated memory
348 * holding the result will be stored upon completion.
349 * outlen [out] - The length of the output message.
350 *
351 * Returns CURLE_OK on success.
352 */
353CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
354 const char *chlg64,
355 const char *userp,
356 const char *passwdp,
357 const char *service,
358 char **outptr, size_t *outlen)
359{
360 CURLcode result = CURLE_OK;
361 size_t i;
362 MD5_context *ctxt;
363 char *response = NULL;
364 unsigned char digest[MD5_DIGEST_LEN];
365 char HA1_hex[2 * MD5_DIGEST_LEN + 1];
366 char HA2_hex[2 * MD5_DIGEST_LEN + 1];
367 char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
368 char nonce[64];
369 char realm[128];
370 char algorithm[64];
371 char qop_options[64];
372 int qop_values;
373 char cnonce[33];
374 char nonceCount[] = "00000001";
375 char method[] = "AUTHENTICATE";
376 char qop[] = DIGEST_QOP_VALUE_STRING_AUTH;
377 char *spn = NULL;
378
379 /* Decode the challenge message */
380 result = auth_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
381 realm, sizeof(realm),
382 algorithm, sizeof(algorithm),
383 qop_options, sizeof(qop_options));
384 if(result)
385 return result;
386
387 /* We only support md5 sessions */
388 if(strcmp(algorithm, "md5-sess") != 0)
389 return CURLE_BAD_CONTENT_ENCODING;
390
391 /* Get the qop-values from the qop-options */
392 result = auth_digest_get_qop_values(qop_options, &qop_values);
393 if(result)
394 return result;
395
396 /* We only support auth quality-of-protection */
397 if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
398 return CURLE_BAD_CONTENT_ENCODING;
399
400 /* Generate 32 random hex chars, 32 bytes + 1 zero termination */
401 result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce));
402 if(result)
403 return result;
404
405 /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
406 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
407 if(!ctxt)
408 return CURLE_OUT_OF_MEMORY;
409
410 Curl_MD5_update(ctxt, (const unsigned char *) userp,
411 curlx_uztoui(strlen(userp)));
412 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
413 Curl_MD5_update(ctxt, (const unsigned char *) realm,
414 curlx_uztoui(strlen(realm)));
415 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
416 Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
417 curlx_uztoui(strlen(passwdp)));
418 Curl_MD5_final(ctxt, digest);
419
420 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
421 if(!ctxt)
422 return CURLE_OUT_OF_MEMORY;
423
424 Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
425 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
426 Curl_MD5_update(ctxt, (const unsigned char *) nonce,
427 curlx_uztoui(strlen(nonce)));
428 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
429 Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
430 curlx_uztoui(strlen(cnonce)));
431 Curl_MD5_final(ctxt, digest);
432
433 /* Convert calculated 16 octet hex into 32 bytes string */
434 for(i = 0; i < MD5_DIGEST_LEN; i++)
435 snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
436
437 /* Generate our SPN */
438 spn = Curl_auth_build_spn(service, realm, NULL);
439 if(!spn)
440 return CURLE_OUT_OF_MEMORY;
441
442 /* Calculate H(A2) */
443 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
444 if(!ctxt) {
445 free(spn);
446
447 return CURLE_OUT_OF_MEMORY;
448 }
449
450 Curl_MD5_update(ctxt, (const unsigned char *) method,
451 curlx_uztoui(strlen(method)));
452 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
453 Curl_MD5_update(ctxt, (const unsigned char *) spn,
454 curlx_uztoui(strlen(spn)));
455 Curl_MD5_final(ctxt, digest);
456
457 for(i = 0; i < MD5_DIGEST_LEN; i++)
458 snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
459
460 /* Now calculate the response hash */
461 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
462 if(!ctxt) {
463 free(spn);
464
465 return CURLE_OUT_OF_MEMORY;
466 }
467
468 Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
469 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
470 Curl_MD5_update(ctxt, (const unsigned char *) nonce,
471 curlx_uztoui(strlen(nonce)));
472 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
473
474 Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
475 curlx_uztoui(strlen(nonceCount)));
476 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
477 Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
478 curlx_uztoui(strlen(cnonce)));
479 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
480 Curl_MD5_update(ctxt, (const unsigned char *) qop,
481 curlx_uztoui(strlen(qop)));
482 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
483
484 Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
485 Curl_MD5_final(ctxt, digest);
486
487 for(i = 0; i < MD5_DIGEST_LEN; i++)
488 snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
489
490 /* Generate the response */
491 response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
492 "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
493 "qop=%s",
494 userp, realm, nonce,
495 cnonce, nonceCount, spn, resp_hash_hex, qop);
496 free(spn);
497 if(!response)
498 return CURLE_OUT_OF_MEMORY;
499
500 /* Base64 encode the response */
501 result = Curl_base64_encode(data, response, 0, outptr, outlen);
502
503 free(response);
504
505 return result;
506}
507
508/*
509 * Curl_auth_decode_digest_http_message()
510 *
511 * This is used to decode a HTTP DIGEST challenge message into the separate
512 * attributes.
513 *
514 * Parameters:
515 *
516 * chlg [in] - The challenge message.
517 * digest [in/out] - The digest data struct being used and modified.
518 *
519 * Returns CURLE_OK on success.
520 */
521CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
522 struct digestdata *digest)
523{
524 bool before = FALSE; /* got a nonce before */
525 bool foundAuth = FALSE;
526 bool foundAuthInt = FALSE;
527 char *token = NULL;
528 char *tmp = NULL;
529
530 /* If we already have received a nonce, keep that in mind */
531 if(digest->nonce)
532 before = TRUE;
533
534 /* Clean up any former leftovers and initialise to defaults */
535 Curl_auth_digest_cleanup(digest);
536
537 for(;;) {
538 char value[DIGEST_MAX_VALUE_LENGTH];
539 char content[DIGEST_MAX_CONTENT_LENGTH];
540
541 /* Pass all additional spaces here */
542 while(*chlg && ISSPACE(*chlg))
543 chlg++;
544
545 /* Extract a value=content pair */
546 if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
547 if(strcasecompare(value, "nonce")) {
548 free(digest->nonce);
549 digest->nonce = strdup(content);
550 if(!digest->nonce)
551 return CURLE_OUT_OF_MEMORY;
552 }
553 else if(strcasecompare(value, "stale")) {
554 if(strcasecompare(content, "true")) {
555 digest->stale = TRUE;
556 digest->nc = 1; /* we make a new nonce now */
557 }
558 }
559 else if(strcasecompare(value, "realm")) {
560 free(digest->realm);
561 digest->realm = strdup(content);
562 if(!digest->realm)
563 return CURLE_OUT_OF_MEMORY;
564 }
565 else if(strcasecompare(value, "opaque")) {
566 free(digest->opaque);
567 digest->opaque = strdup(content);
568 if(!digest->opaque)
569 return CURLE_OUT_OF_MEMORY;
570 }
571 else if(strcasecompare(value, "qop")) {
572 char *tok_buf = NULL;
573 /* Tokenize the list and choose auth if possible, use a temporary
574 clone of the buffer since strtok_r() ruins it */
575 tmp = strdup(content);
576 if(!tmp)
577 return CURLE_OUT_OF_MEMORY;
578
579 token = strtok_r(tmp, ",", &tok_buf);
580 while(token != NULL) {
581 if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
582 foundAuth = TRUE;
583 }
584 else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
585 foundAuthInt = TRUE;
586 }
587 token = strtok_r(NULL, ",", &tok_buf);
588 }
589
590 free(tmp);
591
592 /* Select only auth or auth-int. Otherwise, ignore */
593 if(foundAuth) {
594 free(digest->qop);
595 digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
596 if(!digest->qop)
597 return CURLE_OUT_OF_MEMORY;
598 }
599 else if(foundAuthInt) {
600 free(digest->qop);
601 digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
602 if(!digest->qop)
603 return CURLE_OUT_OF_MEMORY;
604 }
605 }
606 else if(strcasecompare(value, "algorithm")) {
607 free(digest->algorithm);
608 digest->algorithm = strdup(content);
609 if(!digest->algorithm)
610 return CURLE_OUT_OF_MEMORY;
611
612 if(strcasecompare(content, "MD5-sess"))
613 digest->algo = CURLDIGESTALGO_MD5SESS;
614 else if(strcasecompare(content, "MD5"))
615 digest->algo = CURLDIGESTALGO_MD5;
616 else if(strcasecompare(content, "SHA-256"))
617 digest->algo = CURLDIGESTALGO_SHA256;
618 else if(strcasecompare(content, "SHA-256-SESS"))
619 digest->algo = CURLDIGESTALGO_SHA256SESS;
620 else if(strcasecompare(content, "SHA-512-256"))
621 digest->algo = CURLDIGESTALGO_SHA512_256;
622 else if(strcasecompare(content, "SHA-512-256-SESS"))
623 digest->algo = CURLDIGESTALGO_SHA512_256SESS;
624 else
625 return CURLE_BAD_CONTENT_ENCODING;
626 }
627 else if(strcasecompare(value, "userhash")) {
628 if(strcasecompare(content, "true")) {
629 digest->userhash = TRUE;
630 }
631 }
632 else {
633 /* Unknown specifier, ignore it! */
634 }
635 }
636 else
637 break; /* We're done here */
638
639 /* Pass all additional spaces here */
640 while(*chlg && ISSPACE(*chlg))
641 chlg++;
642
643 /* Allow the list to be comma-separated */
644 if(',' == *chlg)
645 chlg++;
646 }
647
648 /* We had a nonce since before, and we got another one now without
649 'stale=true'. This means we provided bad credentials in the previous
650 request */
651 if(before && !digest->stale)
652 return CURLE_BAD_CONTENT_ENCODING;
653
654 /* We got this header without a nonce, that's a bad Digest line! */
655 if(!digest->nonce)
656 return CURLE_BAD_CONTENT_ENCODING;
657
658 return CURLE_OK;
659}
660
661/*
662 * _Curl_auth_create_digest_http_message()
663 *
664 * This is used to generate a HTTP DIGEST response message ready for sending
665 * to the recipient.
666 *
667 * Parameters:
668 *
669 * data [in] - The session handle.
670 * userp [in] - The user name.
671 * passdwp [in] - The user's password.
672 * request [in] - The HTTP request.
673 * uripath [in] - The path of the HTTP uri.
674 * digest [in/out] - The digest data struct being used and modified.
675 * outptr [in/out] - The address where a pointer to newly allocated memory
676 * holding the result will be stored upon completion.
677 * outlen [out] - The length of the output message.
678 *
679 * Returns CURLE_OK on success.
680 */
681static CURLcode _Curl_auth_create_digest_http_message(
682 struct Curl_easy *data,
683 const char *userp,
684 const char *passwdp,
685 const unsigned char *request,
686 const unsigned char *uripath,
687 struct digestdata *digest,
688 char **outptr, size_t *outlen,
689 void (*convert_to_ascii)(unsigned char *, unsigned char *),
690 void (*hash)(unsigned char *, const unsigned char *))
691{
692 CURLcode result;
693 unsigned char hashbuf[32]; /* 32 bytes/256 bits */
694 unsigned char request_digest[65];
695 unsigned char *hashthis;
696 unsigned char ha1[65]; /* 64 digits and 1 zero byte */
697 unsigned char ha2[65]; /* 64 digits and 1 zero byte */
698 char userh[65];
699 char cnoncebuf[33];
700 char *cnonce = NULL;
701 size_t cnonce_sz = 0;
702 char *userp_quoted;
703 char *response = NULL;
704 char *tmp = NULL;
705
706 if(!digest->nc)
707 digest->nc = 1;
708
709 if(!digest->cnonce) {
710 result = Curl_rand_hex(data, (unsigned char *)cnoncebuf,
711 sizeof(cnoncebuf));
712 if(result)
713 return result;
714
715 result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
716 &cnonce, &cnonce_sz);
717 if(result)
718 return result;
719
720 digest->cnonce = cnonce;
721 }
722
723 if(digest->userhash) {
724 hashthis = (unsigned char *) aprintf("%s:%s", userp, digest->realm);
725 if(!hashthis)
726 return CURLE_OUT_OF_MEMORY;
727
728 CURL_OUTPUT_DIGEST_CONV(data, hashthis);
729 hash(hashbuf, hashthis);
730 free(hashthis);
731 convert_to_ascii(hashbuf, (unsigned char *)userh);
732 }
733
734 /*
735 If the algorithm is "MD5" or unspecified (which then defaults to MD5):
736
737 A1 = unq(username-value) ":" unq(realm-value) ":" passwd
738
739 If the algorithm is "MD5-sess" then:
740
741 A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":"
742 unq(nonce-value) ":" unq(cnonce-value)
743 */
744
745 hashthis = (unsigned char *)
746 aprintf("%s:%s:%s", digest->userhash ? userh : userp,
747 digest->realm, passwdp);
748 if(!hashthis)
749 return CURLE_OUT_OF_MEMORY;
750
751 CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
752 hash(hashbuf, hashthis);
753 free(hashthis);
754 convert_to_ascii(hashbuf, ha1);
755
756 if(digest->algo == CURLDIGESTALGO_MD5SESS ||
757 digest->algo == CURLDIGESTALGO_SHA256SESS ||
758 digest->algo == CURLDIGESTALGO_SHA512_256SESS) {
759 /* nonce and cnonce are OUTSIDE the hash */
760 tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
761 if(!tmp)
762 return CURLE_OUT_OF_MEMORY;
763
764 CURL_OUTPUT_DIGEST_CONV(data, tmp); /* Convert on non-ASCII machines */
765 hash(hashbuf, (unsigned char *) tmp);
766 free(tmp);
767 convert_to_ascii(hashbuf, ha1);
768 }
769
770 /*
771 If the "qop" directive's value is "auth" or is unspecified, then A2 is:
772
773 A2 = Method ":" digest-uri-value
774
775 If the "qop" value is "auth-int", then A2 is:
776
777 A2 = Method ":" digest-uri-value ":" H(entity-body)
778
779 (The "Method" value is the HTTP request method as specified in section
780 5.1.1 of RFC 2616)
781 */
782
783 hashthis = (unsigned char *) aprintf("%s:%s", request, uripath);
784
785 if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
786 /* We don't support auth-int for PUT or POST at the moment.
787 TODO: replace hash of empty string with entity-body for PUT/POST */
788 char hashed[65];
789 unsigned char *hashthis2;
790
791 hash(hashbuf, (const unsigned char *)"");
792 convert_to_ascii(hashbuf, (unsigned char *)hashed);
793
794 hashthis2 = (unsigned char *)aprintf("%s:%s", hashthis, hashed);
795 free(hashthis);
796 hashthis = hashthis2;
797 }
798
799 if(!hashthis)
800 return CURLE_OUT_OF_MEMORY;
801
802 CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
803 hash(hashbuf, hashthis);
804 free(hashthis);
805 convert_to_ascii(hashbuf, ha2);
806
807 if(digest->qop) {
808 hashthis = (unsigned char *) aprintf("%s:%s:%08x:%s:%s:%s",
809 ha1,
810 digest->nonce,
811 digest->nc,
812 digest->cnonce,
813 digest->qop,
814 ha2);
815 }
816 else {
817 hashthis = (unsigned char *) aprintf("%s:%s:%s",
818 ha1,
819 digest->nonce,
820 ha2);
821 }
822
823 if(!hashthis)
824 return CURLE_OUT_OF_MEMORY;
825
826 CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
827 hash(hashbuf, hashthis);
828 free(hashthis);
829 convert_to_ascii(hashbuf, request_digest);
830
831 /* For test case 64 (snooped from a Mozilla 1.3a request)
832
833 Authorization: Digest username="testuser", realm="testrealm", \
834 nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
835
836 Digest parameters are all quoted strings. Username which is provided by
837 the user will need double quotes and backslashes within it escaped. For
838 the other fields, this shouldn't be an issue. realm, nonce, and opaque
839 are copied as is from the server, escapes and all. cnonce is generated
840 with web-safe characters. uri is already percent encoded. nc is 8 hex
841 characters. algorithm and qop with standard values only contain web-safe
842 characters.
843 */
844 userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
845 if(!userp_quoted)
846 return CURLE_OUT_OF_MEMORY;
847
848 if(digest->qop) {
849 response = aprintf("username=\"%s\", "
850 "realm=\"%s\", "
851 "nonce=\"%s\", "
852 "uri=\"%s\", "
853 "cnonce=\"%s\", "
854 "nc=%08x, "
855 "qop=%s, "
856 "response=\"%s\"",
857 userp_quoted,
858 digest->realm,
859 digest->nonce,
860 uripath,
861 digest->cnonce,
862 digest->nc,
863 digest->qop,
864 request_digest);
865
866 if(strcasecompare(digest->qop, "auth"))
867 digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0
868 padded which tells to the server how many times you are
869 using the same nonce in the qop=auth mode */
870 }
871 else {
872 response = aprintf("username=\"%s\", "
873 "realm=\"%s\", "
874 "nonce=\"%s\", "
875 "uri=\"%s\", "
876 "response=\"%s\"",
877 userp_quoted,
878 digest->realm,
879 digest->nonce,
880 uripath,
881 request_digest);
882 }
883 free(userp_quoted);
884 if(!response)
885 return CURLE_OUT_OF_MEMORY;
886
887 /* Add the optional fields */
888 if(digest->opaque) {
889 /* Append the opaque */
890 tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
891 free(response);
892 if(!tmp)
893 return CURLE_OUT_OF_MEMORY;
894
895 response = tmp;
896 }
897
898 if(digest->algorithm) {
899 /* Append the algorithm */
900 tmp = aprintf("%s, algorithm=\"%s\"", response, digest->algorithm);
901 free(response);
902 if(!tmp)
903 return CURLE_OUT_OF_MEMORY;
904
905 response = tmp;
906 }
907
908 if(digest->userhash) {
909 /* Append the userhash */
910 tmp = aprintf("%s, userhash=true", response);
911 free(response);
912 if(!tmp)
913 return CURLE_OUT_OF_MEMORY;
914
915 response = tmp;
916 }
917
918 /* Return the output */
919 *outptr = response;
920 *outlen = strlen(response);
921
922 return CURLE_OK;
923}
924
925/*
926 * Curl_auth_create_digest_http_message()
927 *
928 * This is used to generate a HTTP DIGEST response message ready for sending
929 * to the recipient.
930 *
931 * Parameters:
932 *
933 * data [in] - The session handle.
934 * userp [in] - The user name.
935 * passdwp [in] - The user's password.
936 * request [in] - The HTTP request.
937 * uripath [in] - The path of the HTTP uri.
938 * digest [in/out] - The digest data struct being used and modified.
939 * outptr [in/out] - The address where a pointer to newly allocated memory
940 * holding the result will be stored upon completion.
941 * outlen [out] - The length of the output message.
942 *
943 * Returns CURLE_OK on success.
944 */
945CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
946 const char *userp,
947 const char *passwdp,
948 const unsigned char *request,
949 const unsigned char *uripath,
950 struct digestdata *digest,
951 char **outptr, size_t *outlen)
952{
953 switch(digest->algo) {
954 case CURLDIGESTALGO_MD5:
955 case CURLDIGESTALGO_MD5SESS:
956 return _Curl_auth_create_digest_http_message(data, userp, passwdp,
957 request, uripath, digest,
958 outptr, outlen,
959 auth_digest_md5_to_ascii,
960 Curl_md5it);
961
962 case CURLDIGESTALGO_SHA256:
963 case CURLDIGESTALGO_SHA256SESS:
964 case CURLDIGESTALGO_SHA512_256:
965 case CURLDIGESTALGO_SHA512_256SESS:
966 return _Curl_auth_create_digest_http_message(data, userp, passwdp,
967 request, uripath, digest,
968 outptr, outlen,
969 auth_digest_sha256_to_ascii,
970 Curl_sha256it);
971
972 default:
973 return CURLE_UNSUPPORTED_PROTOCOL;
974 }
975}
976
977/*
978 * Curl_auth_digest_cleanup()
979 *
980 * This is used to clean up the digest specific data.
981 *
982 * Parameters:
983 *
984 * digest [in/out] - The digest data struct being cleaned up.
985 *
986 */
987void Curl_auth_digest_cleanup(struct digestdata *digest)
988{
989 Curl_safefree(digest->nonce);
990 Curl_safefree(digest->cnonce);
991 Curl_safefree(digest->realm);
992 Curl_safefree(digest->opaque);
993 Curl_safefree(digest->qop);
994 Curl_safefree(digest->algorithm);
995
996 digest->nc = 0;
997 digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
998 digest->stale = FALSE; /* default means normal, not stale */
999 digest->userhash = FALSE;
1000}
1001#endif /* !USE_WINDOWS_SSPI */
1002
1003#endif /* CURL_DISABLE_CRYPTO_AUTH */
Note: See TracBrowser for help on using the repository browser.