1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2017, 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 https://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 * RFC2831 DIGEST-MD5 authentication
22 * RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication
24 ***************************************************************************/
26 #include "curl_setup.h"
28 #if !defined(CURL_DISABLE_CRYPTO_AUTH)
30 #include <curl/curl.h>
32 #include "vauth/vauth.h"
33 #include "vauth/digest.h"
35 #include "curl_base64.h"
36 #include "curl_hmac.h"
38 #include "curl_sha256.h"
39 #include "vtls/vtls.h"
43 #include "non-ascii.h" /* included for Curl_convert_... prototypes */
44 #include "curl_printf.h"
47 /* The last #include files should be: */
48 #include "curl_memory.h"
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)
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"
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.
64 #define CURL_OUTPUT_DIGEST_CONV(a, b) \
65 result = Curl_convert_to_network(a, (char *)b, strlen((const char *)b)); \
70 #endif /* !USE_WINDOWS_SSPI */
72 bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
76 bool starts_with_quote = FALSE;
79 for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--);)
88 /* This starts with a quote so it must end with one as well! */
90 starts_with_quote = TRUE;
93 for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
97 /* possibly the start of an escaped quote */
99 *content++ = '\\'; /* Even though this is an escape character, we still
100 store it as-is in the target buffer */
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 */
121 if(!escape && starts_with_quote) {
139 #if !defined(USE_WINDOWS_SSPI)
140 /* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
141 static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
142 unsigned char *dest) /* 33 bytes */
145 for(i = 0; i < 16; i++)
146 snprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
149 /* Convert sha256 chunk to RFC7616 -suitable ascii string*/
150 static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */
151 unsigned char *dest) /* 65 bytes */
154 for(i = 0; i < 32; i++)
155 snprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
158 /* Perform quoted-string escaping as described in RFC2616 and its errata */
159 static char *auth_digest_string_quoted(const char *source)
162 const char *s = source;
163 size_t n = 1; /* null terminator */
165 /* Calculate size needed */
168 if(*s == '"' || *s == '\\') {
179 if(*s == '"' || *s == '\\') {
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
193 static bool auth_digest_get_key_value(const char *chlg,
202 find_pos = strstr(chlg, key);
206 find_pos += strlen(key);
208 for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i)
209 value[i] = *find_pos++;
215 static CURLcode auth_digest_get_qop_values(const char *options, int *value)
219 char *tok_buf = NULL;
221 /* Initialise the output */
224 /* Tokenise the list of qop values. Use a temporary clone of the buffer since
225 strtok_r() ruins it. */
226 tmp = strdup(options);
228 return CURLE_OUT_OF_MEMORY;
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;
239 token = strtok_r(NULL, ",", &tok_buf);
248 * auth_decode_digest_md5_message()
250 * This is used internally to decode an already encoded DIGEST-MD5 challenge
251 * message into the separate attributes.
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.
265 * Returns CURLE_OK on success.
267 static 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)
273 CURLcode result = CURLE_OK;
274 unsigned char *chlg = NULL;
276 size_t chlg64len = strlen(chlg64);
278 /* Decode the base-64 encoded challenge message */
279 if(chlg64len && *chlg64 != '=') {
280 result = Curl_base64_decode(chlg64, &chlg, &chlglen);
285 /* Ensure we have a valid challenge message */
287 return CURLE_BAD_CONTENT_ENCODING;
289 /* Retrieve nonce string from the challenge */
290 if(!auth_digest_get_key_value((char *) chlg, "nonce=\"", nonce, nlen,
293 return CURLE_BAD_CONTENT_ENCODING;
296 /* Retrieve realm string from the challenge */
297 if(!auth_digest_get_key_value((char *) chlg, "realm=\"", realm, rlen,
299 /* Challenge does not have a realm, set empty string [RFC2831] page 6 */
303 /* Retrieve algorithm string from the challenge */
304 if(!auth_digest_get_key_value((char *) chlg, "algorithm=", alg, alen, ',')) {
306 return CURLE_BAD_CONTENT_ENCODING;
309 /* Retrieve qop-options string from the challenge */
310 if(!auth_digest_get_key_value((char *) chlg, "qop=\"", qop, qlen, '\"')) {
312 return CURLE_BAD_CONTENT_ENCODING;
321 * Curl_auth_is_digest_supported()
323 * This is used to evaluate if DIGEST is supported.
327 * Returns TRUE as DIGEST as handled by libcurl.
329 bool Curl_auth_is_digest_supported(void)
335 * Curl_auth_create_digest_md5_message()
337 * This is used to generate an already encoded DIGEST-MD5 response message
338 * ready for sending to the recipient.
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.
351 * Returns CURLE_OK on success.
353 CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
358 char **outptr, size_t *outlen)
360 CURLcode result = CURLE_OK;
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];
371 char qop_options[64];
374 char nonceCount[] = "00000001";
375 char method[] = "AUTHENTICATE";
376 char qop[] = DIGEST_QOP_VALUE_STRING_AUTH;
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));
387 /* We only support md5 sessions */
388 if(strcmp(algorithm, "md5-sess") != 0)
389 return CURLE_BAD_CONTENT_ENCODING;
391 /* Get the qop-values from the qop-options */
392 result = auth_digest_get_qop_values(qop_options, &qop_values);
396 /* We only support auth quality-of-protection */
397 if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
398 return CURLE_BAD_CONTENT_ENCODING;
400 /* Generate 32 random hex chars, 32 bytes + 1 zero termination */
401 result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce));
405 /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
406 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
408 return CURLE_OUT_OF_MEMORY;
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);
420 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
422 return CURLE_OUT_OF_MEMORY;
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);
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]);
437 /* Generate our SPN */
438 spn = Curl_auth_build_spn(service, realm, NULL);
440 return CURLE_OUT_OF_MEMORY;
442 /* Calculate H(A2) */
443 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
447 return CURLE_OUT_OF_MEMORY;
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);
457 for(i = 0; i < MD5_DIGEST_LEN; i++)
458 snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
460 /* Now calculate the response hash */
461 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
465 return CURLE_OUT_OF_MEMORY;
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);
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);
484 Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
485 Curl_MD5_final(ctxt, digest);
487 for(i = 0; i < MD5_DIGEST_LEN; i++)
488 snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
490 /* Generate the response */
491 response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
492 "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
495 cnonce, nonceCount, spn, resp_hash_hex, qop);
498 return CURLE_OUT_OF_MEMORY;
500 /* Base64 encode the response */
501 result = Curl_base64_encode(data, response, 0, outptr, outlen);
509 * Curl_auth_decode_digest_http_message()
511 * This is used to decode a HTTP DIGEST challenge message into the separate
516 * chlg [in] - The challenge message.
517 * digest [in/out] - The digest data struct being used and modified.
519 * Returns CURLE_OK on success.
521 CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
522 struct digestdata *digest)
524 bool before = FALSE; /* got a nonce before */
525 bool foundAuth = FALSE;
526 bool foundAuthInt = FALSE;
530 /* If we already have received a nonce, keep that in mind */
534 /* Clean up any former leftovers and initialise to defaults */
535 Curl_auth_digest_cleanup(digest);
538 char value[DIGEST_MAX_VALUE_LENGTH];
539 char content[DIGEST_MAX_CONTENT_LENGTH];
541 /* Pass all additional spaces here */
542 while(*chlg && ISSPACE(*chlg))
545 /* Extract a value=content pair */
546 if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
547 if(strcasecompare(value, "nonce")) {
549 digest->nonce = strdup(content);
551 return CURLE_OUT_OF_MEMORY;
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 */
559 else if(strcasecompare(value, "realm")) {
561 digest->realm = strdup(content);
563 return CURLE_OUT_OF_MEMORY;
565 else if(strcasecompare(value, "opaque")) {
566 free(digest->opaque);
567 digest->opaque = strdup(content);
569 return CURLE_OUT_OF_MEMORY;
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);
577 return CURLE_OUT_OF_MEMORY;
579 token = strtok_r(tmp, ",", &tok_buf);
580 while(token != NULL) {
581 if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
584 else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
587 token = strtok_r(NULL, ",", &tok_buf);
592 /* Select only auth or auth-int. Otherwise, ignore */
595 digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
597 return CURLE_OUT_OF_MEMORY;
599 else if(foundAuthInt) {
601 digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
603 return CURLE_OUT_OF_MEMORY;
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;
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;
625 return CURLE_BAD_CONTENT_ENCODING;
627 else if(strcasecompare(value, "userhash")) {
628 if(strcasecompare(content, "true")) {
629 digest->userhash = TRUE;
633 /* Unknown specifier, ignore it! */
637 break; /* We're done here */
639 /* Pass all additional spaces here */
640 while(*chlg && ISSPACE(*chlg))
643 /* Allow the list to be comma-separated */
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
651 if(before && !digest->stale)
652 return CURLE_BAD_CONTENT_ENCODING;
654 /* We got this header without a nonce, that's a bad Digest line! */
656 return CURLE_BAD_CONTENT_ENCODING;
662 * _Curl_auth_create_digest_http_message()
664 * This is used to generate a HTTP DIGEST response message ready for sending
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.
679 * Returns CURLE_OK on success.
681 static CURLcode _Curl_auth_create_digest_http_message(
682 struct Curl_easy *data,
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 *))
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 */
701 size_t cnonce_sz = 0;
703 char *response = NULL;
709 if(!digest->cnonce) {
710 result = Curl_rand_hex(data, (unsigned char *)cnoncebuf,
715 result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
716 &cnonce, &cnonce_sz);
720 digest->cnonce = cnonce;
723 if(digest->userhash) {
724 hashthis = (unsigned char *) aprintf("%s:%s", userp, digest->realm);
726 return CURLE_OUT_OF_MEMORY;
728 CURL_OUTPUT_DIGEST_CONV(data, hashthis);
729 hash(hashbuf, hashthis);
731 convert_to_ascii(hashbuf, (unsigned char *)userh);
735 If the algorithm is "MD5" or unspecified (which then defaults to MD5):
737 A1 = unq(username-value) ":" unq(realm-value) ":" passwd
739 If the algorithm is "MD5-sess" then:
741 A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":"
742 unq(nonce-value) ":" unq(cnonce-value)
745 hashthis = (unsigned char *)
746 aprintf("%s:%s:%s", digest->userhash ? userh : userp,
747 digest->realm, passwdp);
749 return CURLE_OUT_OF_MEMORY;
751 CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
752 hash(hashbuf, hashthis);
754 convert_to_ascii(hashbuf, ha1);
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);
762 return CURLE_OUT_OF_MEMORY;
764 CURL_OUTPUT_DIGEST_CONV(data, tmp); /* Convert on non-ASCII machines */
765 hash(hashbuf, (unsigned char *) tmp);
767 convert_to_ascii(hashbuf, ha1);
771 If the "qop" directive's value is "auth" or is unspecified, then A2 is:
773 A2 = Method ":" digest-uri-value
775 If the "qop" value is "auth-int", then A2 is:
777 A2 = Method ":" digest-uri-value ":" H(entity-body)
779 (The "Method" value is the HTTP request method as specified in section
783 hashthis = (unsigned char *) aprintf("%s:%s", request, uripath);
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 */
789 unsigned char *hashthis2;
791 hash(hashbuf, (const unsigned char *)"");
792 convert_to_ascii(hashbuf, (unsigned char *)hashed);
794 hashthis2 = (unsigned char *)aprintf("%s:%s", hashthis, hashed);
796 hashthis = hashthis2;
800 return CURLE_OUT_OF_MEMORY;
802 CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
803 hash(hashbuf, hashthis);
805 convert_to_ascii(hashbuf, ha2);
808 hashthis = (unsigned char *) aprintf("%s:%s:%08x:%s:%s:%s",
817 hashthis = (unsigned char *) aprintf("%s:%s:%s",
824 return CURLE_OUT_OF_MEMORY;
826 CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
827 hash(hashbuf, hashthis);
829 convert_to_ascii(hashbuf, request_digest);
831 /* For test case 64 (snooped from a Mozilla 1.3a request)
833 Authorization: Digest username="testuser", realm="testrealm", \
834 nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
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
844 userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
846 return CURLE_OUT_OF_MEMORY;
849 response = aprintf("username=\"%s\", "
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 */
872 response = aprintf("username=\"%s\", "
885 return CURLE_OUT_OF_MEMORY;
887 /* Add the optional fields */
889 /* Append the opaque */
890 tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
893 return CURLE_OUT_OF_MEMORY;
898 if(digest->algorithm) {
899 /* Append the algorithm */
900 tmp = aprintf("%s, algorithm=\"%s\"", response, digest->algorithm);
903 return CURLE_OUT_OF_MEMORY;
908 if(digest->userhash) {
909 /* Append the userhash */
910 tmp = aprintf("%s, userhash=true", response);
913 return CURLE_OUT_OF_MEMORY;
918 /* Return the output */
920 *outlen = strlen(response);
926 * Curl_auth_create_digest_http_message()
928 * This is used to generate a HTTP DIGEST response message ready for sending
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.
943 * Returns CURLE_OK on success.
945 CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
948 const unsigned char *request,
949 const unsigned char *uripath,
950 struct digestdata *digest,
951 char **outptr, size_t *outlen)
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,
959 auth_digest_md5_to_ascii,
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,
969 auth_digest_sha256_to_ascii,
973 return CURLE_UNSUPPORTED_PROTOCOL;
978 * Curl_auth_digest_cleanup()
980 * This is used to clean up the digest specific data.
984 * digest [in/out] - The digest data struct being cleaned up.
987 void Curl_auth_digest_cleanup(struct digestdata *digest)
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);
997 digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
998 digest->stale = FALSE; /* default means normal, not stale */
999 digest->userhash = FALSE;
1001 #endif /* !USE_WINDOWS_SSPI */
1003 #endif /* CURL_DISABLE_CRYPTO_AUTH */