VirtualBox

source: vbox/trunk/src/libs/curl-7.83.1/lib/vauth/digest.c@ 98341

最後變更 在這個檔案從98341是 95312,由 vboxsync 提交於 3 年 前

libs/{curl,libxml2}: OSE export fixes, bugref:8515

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette