1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2012 - 2014, 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 * RFC2195 CRAM-MD5 authentication
22 * RFC2617 Basic and Digest Access Authentication
23 * RFC2831 DIGEST-MD5 authentication
24 * RFC4422 Simple Authentication and Security Layer (SASL)
25 * RFC4616 PLAIN authentication
26 * RFC6749 OAuth 2.0 Authorization Framework
27 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
29 ***************************************************************************/
31 #include "curl_setup.h"
33 #include <curl/curl.h>
36 #include "curl_base64.h"
38 #include "vtls/vtls.h"
39 #include "curl_hmac.h"
40 #include "curl_sasl.h"
42 #include "curl_memory.h"
45 #include "non-ascii.h" /* included for Curl_convert_... prototypes */
47 #define _MPRINTF_REPLACE /* use our functions only */
48 #include <curl/mprintf.h>
50 /* The last #include file should be: */
53 #if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(USE_WINDOWS_SSPI)
54 #define DIGEST_QOP_VALUE_AUTH (1 << 0)
55 #define DIGEST_QOP_VALUE_AUTH_INT (1 << 1)
56 #define DIGEST_QOP_VALUE_AUTH_CONF (1 << 2)
58 #define DIGEST_QOP_VALUE_STRING_AUTH "auth"
59 #define DIGEST_QOP_VALUE_STRING_AUTH_INT "auth-int"
60 #define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf"
62 #define DIGEST_MAX_VALUE_LENGTH 256
63 #define DIGEST_MAX_CONTENT_LENGTH 1024
65 /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
66 It converts digest text to ASCII so the MD5 will be correct for
67 what ultimately goes over the network.
69 #define CURL_OUTPUT_DIGEST_CONV(a, b) \
70 result = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \
77 * Return 0 on success and then the buffers are filled in fine.
79 * Non-zero means failure to parse.
81 static int sasl_digest_get_pair(const char *str, char *value, char *content,
85 bool starts_with_quote = FALSE;
88 for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--); )
97 /* this starts with a quote so it must end with one as well! */
99 starts_with_quote = TRUE;
102 for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
106 /* possibly the start of an escaped quote */
108 *content++ = '\\'; /* even though this is an escape character, we still
109 store it as-is in the target buffer */
114 if(!starts_with_quote) {
115 /* this signals the end of the content if we didn't get a starting
116 quote and then we do "sloppy" parsing */
127 if(!escape && starts_with_quote) {
141 return 0; /* all is fine! */
144 /* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
145 static void sasl_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
146 unsigned char *dest) /* 33 bytes */
149 for(i = 0; i < 16; i++)
150 snprintf((char *)&dest[i*2], 3, "%02x", source[i]);
153 /* Perform quoted-string escaping as described in RFC2616 and its errata */
154 static char *sasl_digest_string_quoted(const char *source)
157 const char *s = source;
158 size_t n = 1; /* null terminator */
160 /* Calculate size needed */
163 if(*s == '"' || *s == '\\') {
174 if(*s == '"' || *s == '\\') {
185 /* Retrieves the value for a corresponding key from the challenge string
186 * returns TRUE if the key could be found, FALSE if it does not exists
188 static bool sasl_digest_get_key_value(const char *chlg,
197 find_pos = strstr(chlg, key);
201 find_pos += strlen(key);
203 for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i)
204 value[i] = *find_pos++;
210 static CURLcode sasl_digest_get_qop_values(const char *options, int *value)
216 /* Initialise the output */
219 /* Tokenise the list of qop values. Use a temporary clone of the buffer since
220 strtok_r() ruins it. */
221 tmp = strdup(options);
223 return CURLE_OUT_OF_MEMORY;
225 token = strtok_r(tmp, ",", &tok_buf);
226 while(token != NULL) {
227 if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH))
228 *value |= DIGEST_QOP_VALUE_AUTH;
229 else if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH_INT))
230 *value |= DIGEST_QOP_VALUE_AUTH_INT;
231 else if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF))
232 *value |= DIGEST_QOP_VALUE_AUTH_CONF;
234 token = strtok_r(NULL, ",", &tok_buf);
241 #endif /* !CURL_DISABLE_CRYPTO_AUTH && !USE_WINDOWS_SSPI */
243 #if !defined(USE_WINDOWS_SSPI)
245 * Curl_sasl_build_spn()
247 * This is used to build a SPN string in the format service/host.
251 * serivce [in] - The service type such as www, smtp, pop or imap.
252 * host [in] - The host name or realm.
254 * Returns a pointer to the newly allocated SPN.
256 char *Curl_sasl_build_spn(const char *service, const char *host)
258 /* Generate and return our SPN */
259 return aprintf("%s/%s", service, host);
264 * Curl_sasl_create_plain_message()
266 * This is used to generate an already encoded PLAIN message ready
267 * for sending to the recipient.
271 * data [in] - The session handle.
272 * userp [in] - The user name.
273 * passdwp [in] - The user's password.
274 * outptr [in/out] - The address where a pointer to newly allocated memory
275 * holding the result will be stored upon completion.
276 * outlen [out] - The length of the output message.
278 * Returns CURLE_OK on success.
280 CURLcode Curl_sasl_create_plain_message(struct SessionHandle *data,
283 char **outptr, size_t *outlen)
290 ulen = strlen(userp);
291 plen = strlen(passwdp);
293 plainauth = malloc(2 * ulen + plen + 2);
297 return CURLE_OUT_OF_MEMORY;
300 /* Calculate the reply */
301 memcpy(plainauth, userp, ulen);
302 plainauth[ulen] = '\0';
303 memcpy(plainauth + ulen + 1, userp, ulen);
304 plainauth[2 * ulen + 1] = '\0';
305 memcpy(plainauth + 2 * ulen + 2, passwdp, plen);
307 /* Base64 encode the reply */
308 result = Curl_base64_encode(data, plainauth, 2 * ulen + plen + 2, outptr,
310 Curl_safefree(plainauth);
315 * Curl_sasl_create_login_message()
317 * This is used to generate an already encoded LOGIN message containing the
318 * user name or password ready for sending to the recipient.
322 * data [in] - The session handle.
323 * valuep [in] - The user name or user's password.
324 * outptr [in/out] - The address where a pointer to newly allocated memory
325 * holding the result will be stored upon completion.
326 * outlen [out] - The length of the output message.
328 * Returns CURLE_OK on success.
330 CURLcode Curl_sasl_create_login_message(struct SessionHandle *data,
331 const char *valuep, char **outptr,
334 size_t vlen = strlen(valuep);
337 /* Calculate an empty reply */
338 *outptr = strdup("=");
340 *outlen = (size_t) 1;
345 return CURLE_OUT_OF_MEMORY;
348 /* Base64 encode the value */
349 return Curl_base64_encode(data, valuep, vlen, outptr, outlen);
352 #ifndef CURL_DISABLE_CRYPTO_AUTH
354 * Curl_sasl_decode_cram_md5_message()
356 * This is used to decode an already encoded CRAM-MD5 challenge message.
360 * chlg64 [in] - The base64 encoded challenge message.
361 * outptr [in/out] - The address where a pointer to newly allocated memory
362 * holding the result will be stored upon completion.
363 * outlen [out] - The length of the output message.
365 * Returns CURLE_OK on success.
367 CURLcode Curl_sasl_decode_cram_md5_message(const char *chlg64, char **outptr,
370 CURLcode result = CURLE_OK;
371 size_t chlg64len = strlen(chlg64);
376 /* Decode the challenge if necessary */
377 if(chlg64len && *chlg64 != '=')
378 result = Curl_base64_decode(chlg64, (unsigned char **) outptr, outlen);
384 * Curl_sasl_create_cram_md5_message()
386 * This is used to generate an already encoded CRAM-MD5 response message ready
387 * for sending to the recipient.
391 * data [in] - The session handle.
392 * chlg [in] - The challenge.
393 * userp [in] - The user name.
394 * passdwp [in] - The user's password.
395 * outptr [in/out] - The address where a pointer to newly allocated memory
396 * holding the result will be stored upon completion.
397 * outlen [out] - The length of the output message.
399 * Returns CURLE_OK on success.
401 CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data,
405 char **outptr, size_t *outlen)
407 CURLcode result = CURLE_OK;
410 unsigned char digest[MD5_DIGEST_LEN];
414 chlglen = strlen(chlg);
416 /* Compute the digest using the password as the key */
417 ctxt = Curl_HMAC_init(Curl_HMAC_MD5,
418 (const unsigned char *) passwdp,
419 curlx_uztoui(strlen(passwdp)));
421 return CURLE_OUT_OF_MEMORY;
423 /* Update the digest with the given challenge */
425 Curl_HMAC_update(ctxt, (const unsigned char *) chlg,
426 curlx_uztoui(chlglen));
428 /* Finalise the digest */
429 Curl_HMAC_final(ctxt, digest);
431 /* Generate the response */
433 "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
434 userp, digest[0], digest[1], digest[2], digest[3], digest[4],
435 digest[5], digest[6], digest[7], digest[8], digest[9], digest[10],
436 digest[11], digest[12], digest[13], digest[14], digest[15]);
438 return CURLE_OUT_OF_MEMORY;
440 /* Base64 encode the response */
441 result = Curl_base64_encode(data, response, 0, outptr, outlen);
443 Curl_safefree(response);
448 #ifndef USE_WINDOWS_SSPI
450 * sasl_decode_digest_md5_message()
452 * This is used internally to decode an already encoded DIGEST-MD5 challenge
453 * message into the seperate attributes.
457 * chlg64 [in] - The base64 encoded challenge message.
458 * nonce [in/out] - The buffer where the nonce will be stored.
459 * nlen [in] - The length of the nonce buffer.
460 * realm [in/out] - The buffer where the realm will be stored.
461 * rlen [in] - The length of the realm buffer.
462 * alg [in/out] - The buffer where the algorithm will be stored.
463 * alen [in] - The length of the algorithm buffer.
464 * qop [in/out] - The buffer where the qop-options will be stored.
465 * qlen [in] - The length of the qop buffer.
467 * Returns CURLE_OK on success.
469 static CURLcode sasl_decode_digest_md5_message(const char *chlg64,
470 char *nonce, size_t nlen,
471 char *realm, size_t rlen,
472 char *alg, size_t alen,
473 char *qop, size_t qlen)
475 CURLcode result = CURLE_OK;
476 unsigned char *chlg = NULL;
478 size_t chlg64len = strlen(chlg64);
480 /* Decode the base-64 encoded challenge message */
481 if(chlg64len && *chlg64 != '=') {
482 result = Curl_base64_decode(chlg64, &chlg, &chlglen);
487 /* Ensure we have a valid challenge message */
489 return CURLE_BAD_CONTENT_ENCODING;
491 /* Retrieve nonce string from the challenge */
492 if(!sasl_digest_get_key_value((char *)chlg, "nonce=\"", nonce, nlen, '\"')) {
494 return CURLE_BAD_CONTENT_ENCODING;
497 /* Retrieve realm string from the challenge */
498 if(!sasl_digest_get_key_value((char *)chlg, "realm=\"", realm, rlen, '\"')) {
499 /* Challenge does not have a realm, set empty string [RFC2831] page 6 */
503 /* Retrieve algorithm string from the challenge */
504 if(!sasl_digest_get_key_value((char *)chlg, "algorithm=", alg, alen, ',')) {
506 return CURLE_BAD_CONTENT_ENCODING;
509 /* Retrieve qop-options string from the challenge */
510 if(!sasl_digest_get_key_value((char *)chlg, "qop=\"", qop, qlen, '\"')) {
512 return CURLE_BAD_CONTENT_ENCODING;
521 * Curl_sasl_create_digest_md5_message()
523 * This is used to generate an already encoded DIGEST-MD5 response message
524 * ready for sending to the recipient.
528 * data [in] - The session handle.
529 * chlg64 [in] - The base64 encoded challenge message.
530 * userp [in] - The user name.
531 * passdwp [in] - The user's password.
532 * service [in] - The service type such as www, smtp, pop or imap.
533 * outptr [in/out] - The address where a pointer to newly allocated memory
534 * holding the result will be stored upon completion.
535 * outlen [out] - The length of the output message.
537 * Returns CURLE_OK on success.
539 CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,
544 char **outptr, size_t *outlen)
546 CURLcode result = CURLE_OK;
549 char *response = NULL;
550 unsigned char digest[MD5_DIGEST_LEN];
551 char HA1_hex[2 * MD5_DIGEST_LEN + 1];
552 char HA2_hex[2 * MD5_DIGEST_LEN + 1];
553 char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
557 char qop_options[64];
560 unsigned int entropy[4];
561 char nonceCount[] = "00000001";
562 char method[] = "AUTHENTICATE";
563 char qop[] = DIGEST_QOP_VALUE_STRING_AUTH;
566 /* Decode the challange message */
567 result = sasl_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
568 realm, sizeof(realm),
569 algorithm, sizeof(algorithm),
570 qop_options, sizeof(qop_options));
574 /* We only support md5 sessions */
575 if(strcmp(algorithm, "md5-sess") != 0)
576 return CURLE_BAD_CONTENT_ENCODING;
578 /* Get the qop-values from the qop-options */
579 result = sasl_digest_get_qop_values(qop_options, &qop_values);
583 /* We only support auth quality-of-protection */
584 if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
585 return CURLE_BAD_CONTENT_ENCODING;
587 /* Generate 16 bytes of random data */
588 entropy[0] = Curl_rand(data);
589 entropy[1] = Curl_rand(data);
590 entropy[2] = Curl_rand(data);
591 entropy[3] = Curl_rand(data);
593 /* Convert the random data into a 32 byte hex string */
594 snprintf(cnonce, sizeof(cnonce), "%08x%08x%08x%08x",
595 entropy[0], entropy[1], entropy[2], entropy[3]);
597 /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
598 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
600 return CURLE_OUT_OF_MEMORY;
602 Curl_MD5_update(ctxt, (const unsigned char *) userp,
603 curlx_uztoui(strlen(userp)));
604 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
605 Curl_MD5_update(ctxt, (const unsigned char *) realm,
606 curlx_uztoui(strlen(realm)));
607 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
608 Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
609 curlx_uztoui(strlen(passwdp)));
610 Curl_MD5_final(ctxt, digest);
612 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
614 return CURLE_OUT_OF_MEMORY;
616 Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
617 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
618 Curl_MD5_update(ctxt, (const unsigned char *) nonce,
619 curlx_uztoui(strlen(nonce)));
620 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
621 Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
622 curlx_uztoui(strlen(cnonce)));
623 Curl_MD5_final(ctxt, digest);
625 /* Convert calculated 16 octet hex into 32 bytes string */
626 for(i = 0; i < MD5_DIGEST_LEN; i++)
627 snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
629 /* Generate our SPN */
630 spn = Curl_sasl_build_spn(service, realm);
632 return CURLE_OUT_OF_MEMORY;
634 /* Calculate H(A2) */
635 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
639 return CURLE_OUT_OF_MEMORY;
642 Curl_MD5_update(ctxt, (const unsigned char *) method,
643 curlx_uztoui(strlen(method)));
644 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
645 Curl_MD5_update(ctxt, (const unsigned char *) spn,
646 curlx_uztoui(strlen(spn)));
647 Curl_MD5_final(ctxt, digest);
649 for(i = 0; i < MD5_DIGEST_LEN; i++)
650 snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
652 /* Now calculate the response hash */
653 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
657 return CURLE_OUT_OF_MEMORY;
660 Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
661 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
662 Curl_MD5_update(ctxt, (const unsigned char *) nonce,
663 curlx_uztoui(strlen(nonce)));
664 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
666 Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
667 curlx_uztoui(strlen(nonceCount)));
668 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
669 Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
670 curlx_uztoui(strlen(cnonce)));
671 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
672 Curl_MD5_update(ctxt, (const unsigned char *) qop,
673 curlx_uztoui(strlen(qop)));
674 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
676 Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
677 Curl_MD5_final(ctxt, digest);
679 for(i = 0; i < MD5_DIGEST_LEN; i++)
680 snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
682 /* Generate the response */
683 response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
684 "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
687 cnonce, nonceCount, spn, resp_hash_hex, qop);
690 return CURLE_OUT_OF_MEMORY;
692 /* Base64 encode the response */
693 result = Curl_base64_encode(data, response, 0, outptr, outlen);
695 Curl_safefree(response);
701 * Curl_sasl_decode_digest_http_message()
703 * This is used to decode a HTTP DIGEST challenge message into the seperate
708 * chlg [in] - The challenge message.
709 * digest [in/out] - The digest data struct being used and modified.
711 * Returns CURLE_OK on success.
713 CURLcode Curl_sasl_decode_digest_http_message(const char *chlg,
714 struct digestdata *digest)
716 bool before = FALSE; /* got a nonce before */
717 bool foundAuth = FALSE;
718 bool foundAuthInt = FALSE;
722 /* If we already have received a nonce, keep that in mind */
726 /* Clean up any former leftovers and initialise to defaults */
727 Curl_sasl_digest_cleanup(digest);
730 char value[DIGEST_MAX_VALUE_LENGTH];
731 char content[DIGEST_MAX_CONTENT_LENGTH];
733 /* Pass all additional spaces here */
734 while(*chlg && ISSPACE(*chlg))
737 /* Extract a value=content pair */
738 if(!sasl_digest_get_pair(chlg, value, content, &chlg)) {
739 if(Curl_raw_equal(value, "nonce")) {
740 digest->nonce = strdup(content);
742 return CURLE_OUT_OF_MEMORY;
744 else if(Curl_raw_equal(value, "stale")) {
745 if(Curl_raw_equal(content, "true")) {
746 digest->stale = TRUE;
747 digest->nc = 1; /* we make a new nonce now */
750 else if(Curl_raw_equal(value, "realm")) {
751 digest->realm = strdup(content);
753 return CURLE_OUT_OF_MEMORY;
755 else if(Curl_raw_equal(value, "opaque")) {
756 digest->opaque = strdup(content);
758 return CURLE_OUT_OF_MEMORY;
760 else if(Curl_raw_equal(value, "qop")) {
762 /* Tokenize the list and choose auth if possible, use a temporary
763 clone of the buffer since strtok_r() ruins it */
764 tmp = strdup(content);
766 return CURLE_OUT_OF_MEMORY;
768 token = strtok_r(tmp, ",", &tok_buf);
769 while(token != NULL) {
770 if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
773 else if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
776 token = strtok_r(NULL, ",", &tok_buf);
781 /* Select only auth or auth-int. Otherwise, ignore */
783 digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
785 return CURLE_OUT_OF_MEMORY;
787 else if(foundAuthInt) {
788 digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
790 return CURLE_OUT_OF_MEMORY;
793 else if(Curl_raw_equal(value, "algorithm")) {
794 digest->algorithm = strdup(content);
795 if(!digest->algorithm)
796 return CURLE_OUT_OF_MEMORY;
798 if(Curl_raw_equal(content, "MD5-sess"))
799 digest->algo = CURLDIGESTALGO_MD5SESS;
800 else if(Curl_raw_equal(content, "MD5"))
801 digest->algo = CURLDIGESTALGO_MD5;
803 return CURLE_BAD_CONTENT_ENCODING;
806 /* unknown specifier, ignore it! */
810 break; /* we're done here */
812 /* Pass all additional spaces here */
813 while(*chlg && ISSPACE(*chlg))
816 /* Allow the list to be comma-separated */
821 /* We had a nonce since before, and we got another one now without
822 'stale=true'. This means we provided bad credentials in the previous
824 if(before && !digest->stale)
825 return CURLE_BAD_CONTENT_ENCODING;
827 /* We got this header without a nonce, that's a bad Digest line! */
829 return CURLE_BAD_CONTENT_ENCODING;
835 * Curl_sasl_create_digest_http_message()
837 * This is used to generate a HTTP DIGEST response message ready for sending
842 * data [in] - The session handle.
843 * userp [in] - The user name.
844 * passdwp [in] - The user's password.
845 * request [in] - The HTTP request.
846 * uripath [in] - The path of the HTTP uri.
847 * digest [in/out] - The digest data struct being used and modified.
848 * outptr [in/out] - The address where a pointer to newly allocated memory
849 * holding the result will be stored upon completion.
850 * outlen [out] - The length of the output message.
852 * Returns CURLE_OK on success.
854 CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data,
857 const unsigned char *request,
858 const unsigned char *uripath,
859 struct digestdata *digest,
860 char **outptr, size_t *outlen)
863 unsigned char md5buf[16]; /* 16 bytes/128 bits */
864 unsigned char request_digest[33];
865 unsigned char *md5this;
866 unsigned char ha1[33];/* 32 digits and 1 zero byte */
867 unsigned char ha2[33];/* 32 digits and 1 zero byte */
870 size_t cnonce_sz = 0;
872 char *response = NULL;
878 if(!digest->cnonce) {
879 snprintf(cnoncebuf, sizeof(cnoncebuf), "%08x%08x%08x%08x",
880 Curl_rand(data), Curl_rand(data),
881 Curl_rand(data), Curl_rand(data));
883 result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
884 &cnonce, &cnonce_sz);
888 digest->cnonce = cnonce;
892 if the algorithm is "MD5" or unspecified (which then defaults to MD5):
894 A1 = unq(username-value) ":" unq(realm-value) ":" passwd
896 if the algorithm is "MD5-sess" then:
898 A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
899 ":" unq(nonce-value) ":" unq(cnonce-value)
902 md5this = (unsigned char *)
903 aprintf("%s:%s:%s", userp, digest->realm, passwdp);
905 return CURLE_OUT_OF_MEMORY;
907 CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
908 Curl_md5it(md5buf, md5this);
909 Curl_safefree(md5this);
910 sasl_digest_md5_to_ascii(md5buf, ha1);
912 if(digest->algo == CURLDIGESTALGO_MD5SESS) {
913 /* nonce and cnonce are OUTSIDE the hash */
914 tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
916 return CURLE_OUT_OF_MEMORY;
918 CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */
919 Curl_md5it(md5buf, (unsigned char *)tmp);
921 sasl_digest_md5_to_ascii(md5buf, ha1);
925 If the "qop" directive's value is "auth" or is unspecified, then A2 is:
927 A2 = Method ":" digest-uri-value
929 If the "qop" value is "auth-int", then A2 is:
931 A2 = Method ":" digest-uri-value ":" H(entity-body)
933 (The "Method" value is the HTTP request method as specified in section
937 md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
939 if(digest->qop && Curl_raw_equal(digest->qop, "auth-int")) {
940 /* We don't support auth-int for PUT or POST at the moment.
941 TODO: replace md5 of empty string with entity-body for PUT/POST */
942 unsigned char *md5this2 = (unsigned char *)
943 aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e");
944 Curl_safefree(md5this);
949 return CURLE_OUT_OF_MEMORY;
951 CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
952 Curl_md5it(md5buf, md5this);
953 Curl_safefree(md5this);
954 sasl_digest_md5_to_ascii(md5buf, ha2);
957 md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s",
966 md5this = (unsigned char *)aprintf("%s:%s:%s",
973 return CURLE_OUT_OF_MEMORY;
975 CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
976 Curl_md5it(md5buf, md5this);
977 Curl_safefree(md5this);
978 sasl_digest_md5_to_ascii(md5buf, request_digest);
980 /* for test case 64 (snooped from a Mozilla 1.3a request)
982 Authorization: Digest username="testuser", realm="testrealm", \
983 nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
985 Digest parameters are all quoted strings. Username which is provided by
986 the user will need double quotes and backslashes within it escaped. For
987 the other fields, this shouldn't be an issue. realm, nonce, and opaque
988 are copied as is from the server, escapes and all. cnonce is generated
989 with web-safe characters. uri is already percent encoded. nc is 8 hex
990 characters. algorithm and qop with standard values only contain web-safe
993 userp_quoted = sasl_digest_string_quoted(userp);
995 return CURLE_OUT_OF_MEMORY;
998 response = aprintf("username=\"%s\", "
1015 if(Curl_raw_equal(digest->qop, "auth"))
1016 digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0
1017 padded which tells to the server how many times you are
1018 using the same nonce in the qop=auth mode */
1021 response = aprintf("username=\"%s\", "
1032 Curl_safefree(userp_quoted);
1034 return CURLE_OUT_OF_MEMORY;
1036 /* Add the optional fields */
1037 if(digest->opaque) {
1038 /* Append the opaque */
1039 tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
1042 return CURLE_OUT_OF_MEMORY;
1047 if(digest->algorithm) {
1048 /* Append the algorithm */
1049 tmp = aprintf("%s, algorithm=\"%s\"", response, digest->algorithm);
1052 return CURLE_OUT_OF_MEMORY;
1057 /* Return the output */
1059 *outlen = strlen(response);
1065 * Curl_sasl_digest_cleanup()
1067 * This is used to clean up the digest specific data.
1071 * digest [in/out] - The digest data struct being cleaned up.
1074 void Curl_sasl_digest_cleanup(struct digestdata *digest)
1076 Curl_safefree(digest->nonce);
1077 Curl_safefree(digest->cnonce);
1078 Curl_safefree(digest->realm);
1079 Curl_safefree(digest->opaque);
1080 Curl_safefree(digest->qop);
1081 Curl_safefree(digest->algorithm);
1084 digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
1085 digest->stale = FALSE; /* default means normal, not stale */
1087 #endif /* !USE_WINDOWS_SSPI */
1089 #endif /* CURL_DISABLE_CRYPTO_AUTH */
1091 #if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI)
1093 * Curl_sasl_ntlm_cleanup()
1095 * This is used to clean up the ntlm specific data.
1099 * ntlm [in/out] - The ntlm data struct being cleaned up.
1102 void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm)
1104 /* Free the target info */
1105 Curl_safefree(ntlm->target_info);
1107 /* Reset any variables */
1108 ntlm->target_info_len = 0;
1110 #endif /* USE_NTLM && !USE_WINDOWS_SSPI*/
1113 * Curl_sasl_create_xoauth2_message()
1115 * This is used to generate an already encoded OAuth 2.0 message ready for
1116 * sending to the recipient.
1120 * data [in] - The session handle.
1121 * user [in] - The user name.
1122 * bearer [in] - The bearer token.
1123 * outptr [in/out] - The address where a pointer to newly allocated memory
1124 * holding the result will be stored upon completion.
1125 * outlen [out] - The length of the output message.
1127 * Returns CURLE_OK on success.
1129 CURLcode Curl_sasl_create_xoauth2_message(struct SessionHandle *data,
1132 char **outptr, size_t *outlen)
1134 CURLcode result = CURLE_OK;
1135 char *xoauth = NULL;
1137 /* Generate the message */
1138 xoauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer);
1140 return CURLE_OUT_OF_MEMORY;
1142 /* Base64 encode the reply */
1143 result = Curl_base64_encode(data, xoauth, strlen(xoauth), outptr, outlen);
1145 Curl_safefree(xoauth);
1151 * Curl_sasl_cleanup()
1153 * This is used to cleanup any libraries or curl modules used by the sasl
1158 * conn [in] - The connection data.
1159 * authused [in] - The authentication mechanism used.
1161 void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
1163 #if defined(USE_KERBEROS5)
1164 /* Cleanup the gssapi structure */
1165 if(authused == SASL_MECH_GSSAPI) {
1166 Curl_sasl_gssapi_cleanup(&conn->krb5);
1170 #if defined(USE_NTLM)
1171 /* Cleanup the ntlm structure */
1172 if(authused == SASL_MECH_NTLM) {
1173 Curl_sasl_ntlm_cleanup(&conn->ntlm);
1177 #if !defined(USE_KERBEROS5) && !defined(USE_NTLM)
1178 /* Reserved for future use */