113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci * RFC2831 DIGEST-MD5 authentication
2413498266Sopenharmony_ci * RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication
2513498266Sopenharmony_ci *
2613498266Sopenharmony_ci ***************************************************************************/
2713498266Sopenharmony_ci
2813498266Sopenharmony_ci#include "curl_setup.h"
2913498266Sopenharmony_ci
3013498266Sopenharmony_ci#ifndef CURL_DISABLE_DIGEST_AUTH
3113498266Sopenharmony_ci
3213498266Sopenharmony_ci#include <curl/curl.h>
3313498266Sopenharmony_ci
3413498266Sopenharmony_ci#include "vauth/vauth.h"
3513498266Sopenharmony_ci#include "vauth/digest.h"
3613498266Sopenharmony_ci#include "urldata.h"
3713498266Sopenharmony_ci#include "curl_base64.h"
3813498266Sopenharmony_ci#include "curl_hmac.h"
3913498266Sopenharmony_ci#include "curl_md5.h"
4013498266Sopenharmony_ci#include "curl_sha256.h"
4113498266Sopenharmony_ci#include "vtls/vtls.h"
4213498266Sopenharmony_ci#include "warnless.h"
4313498266Sopenharmony_ci#include "strtok.h"
4413498266Sopenharmony_ci#include "strcase.h"
4513498266Sopenharmony_ci#include "curl_printf.h"
4613498266Sopenharmony_ci#include "rand.h"
4713498266Sopenharmony_ci
4813498266Sopenharmony_ci/* The last #include files should be: */
4913498266Sopenharmony_ci#include "curl_memory.h"
5013498266Sopenharmony_ci#include "memdebug.h"
5113498266Sopenharmony_ci
5213498266Sopenharmony_ci#define SESSION_ALGO 1 /* for algos with this bit set */
5313498266Sopenharmony_ci
5413498266Sopenharmony_ci#define ALGO_MD5 0
5513498266Sopenharmony_ci#define ALGO_MD5SESS (ALGO_MD5 | SESSION_ALGO)
5613498266Sopenharmony_ci#define ALGO_SHA256 2
5713498266Sopenharmony_ci#define ALGO_SHA256SESS (ALGO_SHA256 | SESSION_ALGO)
5813498266Sopenharmony_ci#define ALGO_SHA512_256 4
5913498266Sopenharmony_ci#define ALGO_SHA512_256SESS (ALGO_SHA512_256 | SESSION_ALGO)
6013498266Sopenharmony_ci
6113498266Sopenharmony_ci#if !defined(USE_WINDOWS_SSPI)
6213498266Sopenharmony_ci#define DIGEST_QOP_VALUE_AUTH             (1 << 0)
6313498266Sopenharmony_ci#define DIGEST_QOP_VALUE_AUTH_INT         (1 << 1)
6413498266Sopenharmony_ci#define DIGEST_QOP_VALUE_AUTH_CONF        (1 << 2)
6513498266Sopenharmony_ci
6613498266Sopenharmony_ci#define DIGEST_QOP_VALUE_STRING_AUTH      "auth"
6713498266Sopenharmony_ci#define DIGEST_QOP_VALUE_STRING_AUTH_INT  "auth-int"
6813498266Sopenharmony_ci#define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf"
6913498266Sopenharmony_ci#endif
7013498266Sopenharmony_ci
7113498266Sopenharmony_cibool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
7213498266Sopenharmony_ci                               const char **endptr)
7313498266Sopenharmony_ci{
7413498266Sopenharmony_ci  int c;
7513498266Sopenharmony_ci  bool starts_with_quote = FALSE;
7613498266Sopenharmony_ci  bool escape = FALSE;
7713498266Sopenharmony_ci
7813498266Sopenharmony_ci  for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--);)
7913498266Sopenharmony_ci    *value++ = *str++;
8013498266Sopenharmony_ci  *value = 0;
8113498266Sopenharmony_ci
8213498266Sopenharmony_ci  if('=' != *str++)
8313498266Sopenharmony_ci    /* eek, no match */
8413498266Sopenharmony_ci    return FALSE;
8513498266Sopenharmony_ci
8613498266Sopenharmony_ci  if('\"' == *str) {
8713498266Sopenharmony_ci    /* This starts with a quote so it must end with one as well! */
8813498266Sopenharmony_ci    str++;
8913498266Sopenharmony_ci    starts_with_quote = TRUE;
9013498266Sopenharmony_ci  }
9113498266Sopenharmony_ci
9213498266Sopenharmony_ci  for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
9313498266Sopenharmony_ci    if(!escape) {
9413498266Sopenharmony_ci      switch(*str) {
9513498266Sopenharmony_ci      case '\\':
9613498266Sopenharmony_ci        if(starts_with_quote) {
9713498266Sopenharmony_ci          /* the start of an escaped quote */
9813498266Sopenharmony_ci          escape = TRUE;
9913498266Sopenharmony_ci          continue;
10013498266Sopenharmony_ci        }
10113498266Sopenharmony_ci        break;
10213498266Sopenharmony_ci
10313498266Sopenharmony_ci      case ',':
10413498266Sopenharmony_ci        if(!starts_with_quote) {
10513498266Sopenharmony_ci          /* This signals the end of the content if we didn't get a starting
10613498266Sopenharmony_ci             quote and then we do "sloppy" parsing */
10713498266Sopenharmony_ci          c = 0; /* the end */
10813498266Sopenharmony_ci          continue;
10913498266Sopenharmony_ci        }
11013498266Sopenharmony_ci        break;
11113498266Sopenharmony_ci
11213498266Sopenharmony_ci      case '\r':
11313498266Sopenharmony_ci      case '\n':
11413498266Sopenharmony_ci        /* end of string */
11513498266Sopenharmony_ci        if(starts_with_quote)
11613498266Sopenharmony_ci          return FALSE; /* No closing quote */
11713498266Sopenharmony_ci        c = 0;
11813498266Sopenharmony_ci        continue;
11913498266Sopenharmony_ci
12013498266Sopenharmony_ci      case '\"':
12113498266Sopenharmony_ci        if(starts_with_quote) {
12213498266Sopenharmony_ci          /* end of string */
12313498266Sopenharmony_ci          c = 0;
12413498266Sopenharmony_ci          continue;
12513498266Sopenharmony_ci        }
12613498266Sopenharmony_ci        else
12713498266Sopenharmony_ci          return FALSE;
12813498266Sopenharmony_ci      }
12913498266Sopenharmony_ci    }
13013498266Sopenharmony_ci
13113498266Sopenharmony_ci    escape = FALSE;
13213498266Sopenharmony_ci    *content++ = *str;
13313498266Sopenharmony_ci  }
13413498266Sopenharmony_ci  if(escape)
13513498266Sopenharmony_ci    return FALSE; /* No character after backslash */
13613498266Sopenharmony_ci
13713498266Sopenharmony_ci  *content = 0;
13813498266Sopenharmony_ci  *endptr = str;
13913498266Sopenharmony_ci
14013498266Sopenharmony_ci  return TRUE;
14113498266Sopenharmony_ci}
14213498266Sopenharmony_ci
14313498266Sopenharmony_ci#if !defined(USE_WINDOWS_SSPI)
14413498266Sopenharmony_ci/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string */
14513498266Sopenharmony_cistatic void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
14613498266Sopenharmony_ci                                     unsigned char *dest) /* 33 bytes */
14713498266Sopenharmony_ci{
14813498266Sopenharmony_ci  int i;
14913498266Sopenharmony_ci  for(i = 0; i < 16; i++)
15013498266Sopenharmony_ci    msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
15113498266Sopenharmony_ci}
15213498266Sopenharmony_ci
15313498266Sopenharmony_ci/* Convert sha256 chunk to RFC7616 -suitable ascii string */
15413498266Sopenharmony_cistatic void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */
15513498266Sopenharmony_ci                                     unsigned char *dest) /* 65 bytes */
15613498266Sopenharmony_ci{
15713498266Sopenharmony_ci  int i;
15813498266Sopenharmony_ci  for(i = 0; i < 32; i++)
15913498266Sopenharmony_ci    msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
16013498266Sopenharmony_ci}
16113498266Sopenharmony_ci
16213498266Sopenharmony_ci/* Perform quoted-string escaping as described in RFC2616 and its errata */
16313498266Sopenharmony_cistatic char *auth_digest_string_quoted(const char *source)
16413498266Sopenharmony_ci{
16513498266Sopenharmony_ci  char *dest;
16613498266Sopenharmony_ci  const char *s = source;
16713498266Sopenharmony_ci  size_t n = 1; /* null terminator */
16813498266Sopenharmony_ci
16913498266Sopenharmony_ci  /* Calculate size needed */
17013498266Sopenharmony_ci  while(*s) {
17113498266Sopenharmony_ci    ++n;
17213498266Sopenharmony_ci    if(*s == '"' || *s == '\\') {
17313498266Sopenharmony_ci      ++n;
17413498266Sopenharmony_ci    }
17513498266Sopenharmony_ci    ++s;
17613498266Sopenharmony_ci  }
17713498266Sopenharmony_ci
17813498266Sopenharmony_ci  dest = malloc(n);
17913498266Sopenharmony_ci  if(dest) {
18013498266Sopenharmony_ci    char *d = dest;
18113498266Sopenharmony_ci    s = source;
18213498266Sopenharmony_ci    while(*s) {
18313498266Sopenharmony_ci      if(*s == '"' || *s == '\\') {
18413498266Sopenharmony_ci        *d++ = '\\';
18513498266Sopenharmony_ci      }
18613498266Sopenharmony_ci      *d++ = *s++;
18713498266Sopenharmony_ci    }
18813498266Sopenharmony_ci    *d = '\0';
18913498266Sopenharmony_ci  }
19013498266Sopenharmony_ci
19113498266Sopenharmony_ci  return dest;
19213498266Sopenharmony_ci}
19313498266Sopenharmony_ci
19413498266Sopenharmony_ci/* Retrieves the value for a corresponding key from the challenge string
19513498266Sopenharmony_ci * returns TRUE if the key could be found, FALSE if it does not exists
19613498266Sopenharmony_ci */
19713498266Sopenharmony_cistatic bool auth_digest_get_key_value(const char *chlg,
19813498266Sopenharmony_ci                                      const char *key,
19913498266Sopenharmony_ci                                      char *value,
20013498266Sopenharmony_ci                                      size_t max_val_len,
20113498266Sopenharmony_ci                                      char end_char)
20213498266Sopenharmony_ci{
20313498266Sopenharmony_ci  char *find_pos;
20413498266Sopenharmony_ci  size_t i;
20513498266Sopenharmony_ci
20613498266Sopenharmony_ci  find_pos = strstr(chlg, key);
20713498266Sopenharmony_ci  if(!find_pos)
20813498266Sopenharmony_ci    return FALSE;
20913498266Sopenharmony_ci
21013498266Sopenharmony_ci  find_pos += strlen(key);
21113498266Sopenharmony_ci
21213498266Sopenharmony_ci  for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i)
21313498266Sopenharmony_ci    value[i] = *find_pos++;
21413498266Sopenharmony_ci  value[i] = '\0';
21513498266Sopenharmony_ci
21613498266Sopenharmony_ci  return TRUE;
21713498266Sopenharmony_ci}
21813498266Sopenharmony_ci
21913498266Sopenharmony_cistatic CURLcode auth_digest_get_qop_values(const char *options, int *value)
22013498266Sopenharmony_ci{
22113498266Sopenharmony_ci  char *tmp;
22213498266Sopenharmony_ci  char *token;
22313498266Sopenharmony_ci  char *tok_buf = NULL;
22413498266Sopenharmony_ci
22513498266Sopenharmony_ci  /* Initialise the output */
22613498266Sopenharmony_ci  *value = 0;
22713498266Sopenharmony_ci
22813498266Sopenharmony_ci  /* Tokenise the list of qop values. Use a temporary clone of the buffer since
22913498266Sopenharmony_ci     strtok_r() ruins it. */
23013498266Sopenharmony_ci  tmp = strdup(options);
23113498266Sopenharmony_ci  if(!tmp)
23213498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
23313498266Sopenharmony_ci
23413498266Sopenharmony_ci  token = strtok_r(tmp, ",", &tok_buf);
23513498266Sopenharmony_ci  while(token) {
23613498266Sopenharmony_ci    if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH))
23713498266Sopenharmony_ci      *value |= DIGEST_QOP_VALUE_AUTH;
23813498266Sopenharmony_ci    else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT))
23913498266Sopenharmony_ci      *value |= DIGEST_QOP_VALUE_AUTH_INT;
24013498266Sopenharmony_ci    else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF))
24113498266Sopenharmony_ci      *value |= DIGEST_QOP_VALUE_AUTH_CONF;
24213498266Sopenharmony_ci
24313498266Sopenharmony_ci    token = strtok_r(NULL, ",", &tok_buf);
24413498266Sopenharmony_ci  }
24513498266Sopenharmony_ci
24613498266Sopenharmony_ci  free(tmp);
24713498266Sopenharmony_ci
24813498266Sopenharmony_ci  return CURLE_OK;
24913498266Sopenharmony_ci}
25013498266Sopenharmony_ci
25113498266Sopenharmony_ci/*
25213498266Sopenharmony_ci * auth_decode_digest_md5_message()
25313498266Sopenharmony_ci *
25413498266Sopenharmony_ci * This is used internally to decode an already encoded DIGEST-MD5 challenge
25513498266Sopenharmony_ci * message into the separate attributes.
25613498266Sopenharmony_ci *
25713498266Sopenharmony_ci * Parameters:
25813498266Sopenharmony_ci *
25913498266Sopenharmony_ci * chlgref [in]     - The challenge message.
26013498266Sopenharmony_ci * nonce   [in/out] - The buffer where the nonce will be stored.
26113498266Sopenharmony_ci * nlen    [in]     - The length of the nonce buffer.
26213498266Sopenharmony_ci * realm   [in/out] - The buffer where the realm will be stored.
26313498266Sopenharmony_ci * rlen    [in]     - The length of the realm buffer.
26413498266Sopenharmony_ci * alg     [in/out] - The buffer where the algorithm will be stored.
26513498266Sopenharmony_ci * alen    [in]     - The length of the algorithm buffer.
26613498266Sopenharmony_ci * qop     [in/out] - The buffer where the qop-options will be stored.
26713498266Sopenharmony_ci * qlen    [in]     - The length of the qop buffer.
26813498266Sopenharmony_ci *
26913498266Sopenharmony_ci * Returns CURLE_OK on success.
27013498266Sopenharmony_ci */
27113498266Sopenharmony_cistatic CURLcode auth_decode_digest_md5_message(const struct bufref *chlgref,
27213498266Sopenharmony_ci                                               char *nonce, size_t nlen,
27313498266Sopenharmony_ci                                               char *realm, size_t rlen,
27413498266Sopenharmony_ci                                               char *alg, size_t alen,
27513498266Sopenharmony_ci                                               char *qop, size_t qlen)
27613498266Sopenharmony_ci{
27713498266Sopenharmony_ci  const char *chlg = (const char *) Curl_bufref_ptr(chlgref);
27813498266Sopenharmony_ci
27913498266Sopenharmony_ci  /* Ensure we have a valid challenge message */
28013498266Sopenharmony_ci  if(!Curl_bufref_len(chlgref))
28113498266Sopenharmony_ci    return CURLE_BAD_CONTENT_ENCODING;
28213498266Sopenharmony_ci
28313498266Sopenharmony_ci  /* Retrieve nonce string from the challenge */
28413498266Sopenharmony_ci  if(!auth_digest_get_key_value(chlg, "nonce=\"", nonce, nlen, '\"'))
28513498266Sopenharmony_ci    return CURLE_BAD_CONTENT_ENCODING;
28613498266Sopenharmony_ci
28713498266Sopenharmony_ci  /* Retrieve realm string from the challenge */
28813498266Sopenharmony_ci  if(!auth_digest_get_key_value(chlg, "realm=\"", realm, rlen, '\"')) {
28913498266Sopenharmony_ci    /* Challenge does not have a realm, set empty string [RFC2831] page 6 */
29013498266Sopenharmony_ci    strcpy(realm, "");
29113498266Sopenharmony_ci  }
29213498266Sopenharmony_ci
29313498266Sopenharmony_ci  /* Retrieve algorithm string from the challenge */
29413498266Sopenharmony_ci  if(!auth_digest_get_key_value(chlg, "algorithm=", alg, alen, ','))
29513498266Sopenharmony_ci    return CURLE_BAD_CONTENT_ENCODING;
29613498266Sopenharmony_ci
29713498266Sopenharmony_ci  /* Retrieve qop-options string from the challenge */
29813498266Sopenharmony_ci  if(!auth_digest_get_key_value(chlg, "qop=\"", qop, qlen, '\"'))
29913498266Sopenharmony_ci    return CURLE_BAD_CONTENT_ENCODING;
30013498266Sopenharmony_ci
30113498266Sopenharmony_ci  return CURLE_OK;
30213498266Sopenharmony_ci}
30313498266Sopenharmony_ci
30413498266Sopenharmony_ci/*
30513498266Sopenharmony_ci * Curl_auth_is_digest_supported()
30613498266Sopenharmony_ci *
30713498266Sopenharmony_ci * This is used to evaluate if DIGEST is supported.
30813498266Sopenharmony_ci *
30913498266Sopenharmony_ci * Parameters: None
31013498266Sopenharmony_ci *
31113498266Sopenharmony_ci * Returns TRUE as DIGEST as handled by libcurl.
31213498266Sopenharmony_ci */
31313498266Sopenharmony_cibool Curl_auth_is_digest_supported(void)
31413498266Sopenharmony_ci{
31513498266Sopenharmony_ci  return TRUE;
31613498266Sopenharmony_ci}
31713498266Sopenharmony_ci
31813498266Sopenharmony_ci/*
31913498266Sopenharmony_ci * Curl_auth_create_digest_md5_message()
32013498266Sopenharmony_ci *
32113498266Sopenharmony_ci * This is used to generate an already encoded DIGEST-MD5 response message
32213498266Sopenharmony_ci * ready for sending to the recipient.
32313498266Sopenharmony_ci *
32413498266Sopenharmony_ci * Parameters:
32513498266Sopenharmony_ci *
32613498266Sopenharmony_ci * data    [in]     - The session handle.
32713498266Sopenharmony_ci * chlg    [in]     - The challenge message.
32813498266Sopenharmony_ci * userp   [in]     - The user name.
32913498266Sopenharmony_ci * passwdp [in]     - The user's password.
33013498266Sopenharmony_ci * service [in]     - The service type such as http, smtp, pop or imap.
33113498266Sopenharmony_ci * out     [out]    - The result storage.
33213498266Sopenharmony_ci *
33313498266Sopenharmony_ci * Returns CURLE_OK on success.
33413498266Sopenharmony_ci */
33513498266Sopenharmony_ciCURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
33613498266Sopenharmony_ci                                             const struct bufref *chlg,
33713498266Sopenharmony_ci                                             const char *userp,
33813498266Sopenharmony_ci                                             const char *passwdp,
33913498266Sopenharmony_ci                                             const char *service,
34013498266Sopenharmony_ci                                             struct bufref *out)
34113498266Sopenharmony_ci{
34213498266Sopenharmony_ci  size_t i;
34313498266Sopenharmony_ci  struct MD5_context *ctxt;
34413498266Sopenharmony_ci  char *response = NULL;
34513498266Sopenharmony_ci  unsigned char digest[MD5_DIGEST_LEN];
34613498266Sopenharmony_ci  char HA1_hex[2 * MD5_DIGEST_LEN + 1];
34713498266Sopenharmony_ci  char HA2_hex[2 * MD5_DIGEST_LEN + 1];
34813498266Sopenharmony_ci  char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
34913498266Sopenharmony_ci  char nonce[64];
35013498266Sopenharmony_ci  char realm[128];
35113498266Sopenharmony_ci  char algorithm[64];
35213498266Sopenharmony_ci  char qop_options[64];
35313498266Sopenharmony_ci  int qop_values;
35413498266Sopenharmony_ci  char cnonce[33];
35513498266Sopenharmony_ci  char nonceCount[] = "00000001";
35613498266Sopenharmony_ci  char method[]     = "AUTHENTICATE";
35713498266Sopenharmony_ci  char qop[]        = DIGEST_QOP_VALUE_STRING_AUTH;
35813498266Sopenharmony_ci  char *spn         = NULL;
35913498266Sopenharmony_ci
36013498266Sopenharmony_ci  /* Decode the challenge message */
36113498266Sopenharmony_ci  CURLcode result = auth_decode_digest_md5_message(chlg,
36213498266Sopenharmony_ci                                                   nonce, sizeof(nonce),
36313498266Sopenharmony_ci                                                   realm, sizeof(realm),
36413498266Sopenharmony_ci                                                   algorithm,
36513498266Sopenharmony_ci                                                   sizeof(algorithm),
36613498266Sopenharmony_ci                                                   qop_options,
36713498266Sopenharmony_ci                                                   sizeof(qop_options));
36813498266Sopenharmony_ci  if(result)
36913498266Sopenharmony_ci    return result;
37013498266Sopenharmony_ci
37113498266Sopenharmony_ci  /* We only support md5 sessions */
37213498266Sopenharmony_ci  if(strcmp(algorithm, "md5-sess") != 0)
37313498266Sopenharmony_ci    return CURLE_BAD_CONTENT_ENCODING;
37413498266Sopenharmony_ci
37513498266Sopenharmony_ci  /* Get the qop-values from the qop-options */
37613498266Sopenharmony_ci  result = auth_digest_get_qop_values(qop_options, &qop_values);
37713498266Sopenharmony_ci  if(result)
37813498266Sopenharmony_ci    return result;
37913498266Sopenharmony_ci
38013498266Sopenharmony_ci  /* We only support auth quality-of-protection */
38113498266Sopenharmony_ci  if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
38213498266Sopenharmony_ci    return CURLE_BAD_CONTENT_ENCODING;
38313498266Sopenharmony_ci
38413498266Sopenharmony_ci  /* Generate 32 random hex chars, 32 bytes + 1 null-termination */
38513498266Sopenharmony_ci  result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce));
38613498266Sopenharmony_ci  if(result)
38713498266Sopenharmony_ci    return result;
38813498266Sopenharmony_ci
38913498266Sopenharmony_ci  /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
39013498266Sopenharmony_ci  ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
39113498266Sopenharmony_ci  if(!ctxt)
39213498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
39313498266Sopenharmony_ci
39413498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) userp,
39513498266Sopenharmony_ci                  curlx_uztoui(strlen(userp)));
39613498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
39713498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) realm,
39813498266Sopenharmony_ci                  curlx_uztoui(strlen(realm)));
39913498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
40013498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
40113498266Sopenharmony_ci                  curlx_uztoui(strlen(passwdp)));
40213498266Sopenharmony_ci  Curl_MD5_final(ctxt, digest);
40313498266Sopenharmony_ci
40413498266Sopenharmony_ci  ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
40513498266Sopenharmony_ci  if(!ctxt)
40613498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
40713498266Sopenharmony_ci
40813498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
40913498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
41013498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) nonce,
41113498266Sopenharmony_ci                  curlx_uztoui(strlen(nonce)));
41213498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
41313498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
41413498266Sopenharmony_ci                  curlx_uztoui(strlen(cnonce)));
41513498266Sopenharmony_ci  Curl_MD5_final(ctxt, digest);
41613498266Sopenharmony_ci
41713498266Sopenharmony_ci  /* Convert calculated 16 octet hex into 32 bytes string */
41813498266Sopenharmony_ci  for(i = 0; i < MD5_DIGEST_LEN; i++)
41913498266Sopenharmony_ci    msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
42013498266Sopenharmony_ci
42113498266Sopenharmony_ci  /* Generate our SPN */
42213498266Sopenharmony_ci  spn = Curl_auth_build_spn(service, data->conn->host.name, NULL);
42313498266Sopenharmony_ci  if(!spn)
42413498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
42513498266Sopenharmony_ci
42613498266Sopenharmony_ci  /* Calculate H(A2) */
42713498266Sopenharmony_ci  ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
42813498266Sopenharmony_ci  if(!ctxt) {
42913498266Sopenharmony_ci    free(spn);
43013498266Sopenharmony_ci
43113498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
43213498266Sopenharmony_ci  }
43313498266Sopenharmony_ci
43413498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) method,
43513498266Sopenharmony_ci                  curlx_uztoui(strlen(method)));
43613498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
43713498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) spn,
43813498266Sopenharmony_ci                  curlx_uztoui(strlen(spn)));
43913498266Sopenharmony_ci  Curl_MD5_final(ctxt, digest);
44013498266Sopenharmony_ci
44113498266Sopenharmony_ci  for(i = 0; i < MD5_DIGEST_LEN; i++)
44213498266Sopenharmony_ci    msnprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
44313498266Sopenharmony_ci
44413498266Sopenharmony_ci  /* Now calculate the response hash */
44513498266Sopenharmony_ci  ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
44613498266Sopenharmony_ci  if(!ctxt) {
44713498266Sopenharmony_ci    free(spn);
44813498266Sopenharmony_ci
44913498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
45013498266Sopenharmony_ci  }
45113498266Sopenharmony_ci
45213498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
45313498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
45413498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) nonce,
45513498266Sopenharmony_ci                  curlx_uztoui(strlen(nonce)));
45613498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
45713498266Sopenharmony_ci
45813498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
45913498266Sopenharmony_ci                  curlx_uztoui(strlen(nonceCount)));
46013498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
46113498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
46213498266Sopenharmony_ci                  curlx_uztoui(strlen(cnonce)));
46313498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
46413498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) qop,
46513498266Sopenharmony_ci                  curlx_uztoui(strlen(qop)));
46613498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
46713498266Sopenharmony_ci
46813498266Sopenharmony_ci  Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
46913498266Sopenharmony_ci  Curl_MD5_final(ctxt, digest);
47013498266Sopenharmony_ci
47113498266Sopenharmony_ci  for(i = 0; i < MD5_DIGEST_LEN; i++)
47213498266Sopenharmony_ci    msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
47313498266Sopenharmony_ci
47413498266Sopenharmony_ci  /* Generate the response */
47513498266Sopenharmony_ci  response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
47613498266Sopenharmony_ci                     "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
47713498266Sopenharmony_ci                     "qop=%s",
47813498266Sopenharmony_ci                     userp, realm, nonce,
47913498266Sopenharmony_ci                     cnonce, nonceCount, spn, resp_hash_hex, qop);
48013498266Sopenharmony_ci  free(spn);
48113498266Sopenharmony_ci  if(!response)
48213498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
48313498266Sopenharmony_ci
48413498266Sopenharmony_ci  /* Return the response. */
48513498266Sopenharmony_ci  Curl_bufref_set(out, response, strlen(response), curl_free);
48613498266Sopenharmony_ci  return result;
48713498266Sopenharmony_ci}
48813498266Sopenharmony_ci
48913498266Sopenharmony_ci/*
49013498266Sopenharmony_ci * Curl_auth_decode_digest_http_message()
49113498266Sopenharmony_ci *
49213498266Sopenharmony_ci * This is used to decode an HTTP DIGEST challenge message into the separate
49313498266Sopenharmony_ci * attributes.
49413498266Sopenharmony_ci *
49513498266Sopenharmony_ci * Parameters:
49613498266Sopenharmony_ci *
49713498266Sopenharmony_ci * chlg    [in]     - The challenge message.
49813498266Sopenharmony_ci * digest  [in/out] - The digest data struct being used and modified.
49913498266Sopenharmony_ci *
50013498266Sopenharmony_ci * Returns CURLE_OK on success.
50113498266Sopenharmony_ci */
50213498266Sopenharmony_ciCURLcode Curl_auth_decode_digest_http_message(const char *chlg,
50313498266Sopenharmony_ci                                              struct digestdata *digest)
50413498266Sopenharmony_ci{
50513498266Sopenharmony_ci  bool before = FALSE; /* got a nonce before */
50613498266Sopenharmony_ci  bool foundAuth = FALSE;
50713498266Sopenharmony_ci  bool foundAuthInt = FALSE;
50813498266Sopenharmony_ci  char *token = NULL;
50913498266Sopenharmony_ci  char *tmp = NULL;
51013498266Sopenharmony_ci
51113498266Sopenharmony_ci  /* If we already have received a nonce, keep that in mind */
51213498266Sopenharmony_ci  if(digest->nonce)
51313498266Sopenharmony_ci    before = TRUE;
51413498266Sopenharmony_ci
51513498266Sopenharmony_ci  /* Clean up any former leftovers and initialise to defaults */
51613498266Sopenharmony_ci  Curl_auth_digest_cleanup(digest);
51713498266Sopenharmony_ci
51813498266Sopenharmony_ci  for(;;) {
51913498266Sopenharmony_ci    char value[DIGEST_MAX_VALUE_LENGTH];
52013498266Sopenharmony_ci    char content[DIGEST_MAX_CONTENT_LENGTH];
52113498266Sopenharmony_ci
52213498266Sopenharmony_ci    /* Pass all additional spaces here */
52313498266Sopenharmony_ci    while(*chlg && ISBLANK(*chlg))
52413498266Sopenharmony_ci      chlg++;
52513498266Sopenharmony_ci
52613498266Sopenharmony_ci    /* Extract a value=content pair */
52713498266Sopenharmony_ci    if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
52813498266Sopenharmony_ci      if(strcasecompare(value, "nonce")) {
52913498266Sopenharmony_ci        free(digest->nonce);
53013498266Sopenharmony_ci        digest->nonce = strdup(content);
53113498266Sopenharmony_ci        if(!digest->nonce)
53213498266Sopenharmony_ci          return CURLE_OUT_OF_MEMORY;
53313498266Sopenharmony_ci      }
53413498266Sopenharmony_ci      else if(strcasecompare(value, "stale")) {
53513498266Sopenharmony_ci        if(strcasecompare(content, "true")) {
53613498266Sopenharmony_ci          digest->stale = TRUE;
53713498266Sopenharmony_ci          digest->nc = 1; /* we make a new nonce now */
53813498266Sopenharmony_ci        }
53913498266Sopenharmony_ci      }
54013498266Sopenharmony_ci      else if(strcasecompare(value, "realm")) {
54113498266Sopenharmony_ci        free(digest->realm);
54213498266Sopenharmony_ci        digest->realm = strdup(content);
54313498266Sopenharmony_ci        if(!digest->realm)
54413498266Sopenharmony_ci          return CURLE_OUT_OF_MEMORY;
54513498266Sopenharmony_ci      }
54613498266Sopenharmony_ci      else if(strcasecompare(value, "opaque")) {
54713498266Sopenharmony_ci        free(digest->opaque);
54813498266Sopenharmony_ci        digest->opaque = strdup(content);
54913498266Sopenharmony_ci        if(!digest->opaque)
55013498266Sopenharmony_ci          return CURLE_OUT_OF_MEMORY;
55113498266Sopenharmony_ci      }
55213498266Sopenharmony_ci      else if(strcasecompare(value, "qop")) {
55313498266Sopenharmony_ci        char *tok_buf = NULL;
55413498266Sopenharmony_ci        /* Tokenize the list and choose auth if possible, use a temporary
55513498266Sopenharmony_ci           clone of the buffer since strtok_r() ruins it */
55613498266Sopenharmony_ci        tmp = strdup(content);
55713498266Sopenharmony_ci        if(!tmp)
55813498266Sopenharmony_ci          return CURLE_OUT_OF_MEMORY;
55913498266Sopenharmony_ci
56013498266Sopenharmony_ci        token = strtok_r(tmp, ",", &tok_buf);
56113498266Sopenharmony_ci        while(token) {
56213498266Sopenharmony_ci          /* Pass additional spaces here */
56313498266Sopenharmony_ci          while(*token && ISBLANK(*token))
56413498266Sopenharmony_ci            token++;
56513498266Sopenharmony_ci          if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
56613498266Sopenharmony_ci            foundAuth = TRUE;
56713498266Sopenharmony_ci          }
56813498266Sopenharmony_ci          else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
56913498266Sopenharmony_ci            foundAuthInt = TRUE;
57013498266Sopenharmony_ci          }
57113498266Sopenharmony_ci          token = strtok_r(NULL, ",", &tok_buf);
57213498266Sopenharmony_ci        }
57313498266Sopenharmony_ci
57413498266Sopenharmony_ci        free(tmp);
57513498266Sopenharmony_ci
57613498266Sopenharmony_ci        /* Select only auth or auth-int. Otherwise, ignore */
57713498266Sopenharmony_ci        if(foundAuth) {
57813498266Sopenharmony_ci          free(digest->qop);
57913498266Sopenharmony_ci          digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
58013498266Sopenharmony_ci          if(!digest->qop)
58113498266Sopenharmony_ci            return CURLE_OUT_OF_MEMORY;
58213498266Sopenharmony_ci        }
58313498266Sopenharmony_ci        else if(foundAuthInt) {
58413498266Sopenharmony_ci          free(digest->qop);
58513498266Sopenharmony_ci          digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
58613498266Sopenharmony_ci          if(!digest->qop)
58713498266Sopenharmony_ci            return CURLE_OUT_OF_MEMORY;
58813498266Sopenharmony_ci        }
58913498266Sopenharmony_ci      }
59013498266Sopenharmony_ci      else if(strcasecompare(value, "algorithm")) {
59113498266Sopenharmony_ci        free(digest->algorithm);
59213498266Sopenharmony_ci        digest->algorithm = strdup(content);
59313498266Sopenharmony_ci        if(!digest->algorithm)
59413498266Sopenharmony_ci          return CURLE_OUT_OF_MEMORY;
59513498266Sopenharmony_ci
59613498266Sopenharmony_ci        if(strcasecompare(content, "MD5-sess"))
59713498266Sopenharmony_ci          digest->algo = ALGO_MD5SESS;
59813498266Sopenharmony_ci        else if(strcasecompare(content, "MD5"))
59913498266Sopenharmony_ci          digest->algo = ALGO_MD5;
60013498266Sopenharmony_ci        else if(strcasecompare(content, "SHA-256"))
60113498266Sopenharmony_ci          digest->algo = ALGO_SHA256;
60213498266Sopenharmony_ci        else if(strcasecompare(content, "SHA-256-SESS"))
60313498266Sopenharmony_ci          digest->algo = ALGO_SHA256SESS;
60413498266Sopenharmony_ci        else if(strcasecompare(content, "SHA-512-256"))
60513498266Sopenharmony_ci          digest->algo = ALGO_SHA512_256;
60613498266Sopenharmony_ci        else if(strcasecompare(content, "SHA-512-256-SESS"))
60713498266Sopenharmony_ci          digest->algo = ALGO_SHA512_256SESS;
60813498266Sopenharmony_ci        else
60913498266Sopenharmony_ci          return CURLE_BAD_CONTENT_ENCODING;
61013498266Sopenharmony_ci      }
61113498266Sopenharmony_ci      else if(strcasecompare(value, "userhash")) {
61213498266Sopenharmony_ci        if(strcasecompare(content, "true")) {
61313498266Sopenharmony_ci          digest->userhash = TRUE;
61413498266Sopenharmony_ci        }
61513498266Sopenharmony_ci      }
61613498266Sopenharmony_ci      else {
61713498266Sopenharmony_ci        /* Unknown specifier, ignore it! */
61813498266Sopenharmony_ci      }
61913498266Sopenharmony_ci    }
62013498266Sopenharmony_ci    else
62113498266Sopenharmony_ci      break; /* We're done here */
62213498266Sopenharmony_ci
62313498266Sopenharmony_ci    /* Pass all additional spaces here */
62413498266Sopenharmony_ci    while(*chlg && ISBLANK(*chlg))
62513498266Sopenharmony_ci      chlg++;
62613498266Sopenharmony_ci
62713498266Sopenharmony_ci    /* Allow the list to be comma-separated */
62813498266Sopenharmony_ci    if(',' == *chlg)
62913498266Sopenharmony_ci      chlg++;
63013498266Sopenharmony_ci  }
63113498266Sopenharmony_ci
63213498266Sopenharmony_ci  /* We had a nonce since before, and we got another one now without
63313498266Sopenharmony_ci     'stale=true'. This means we provided bad credentials in the previous
63413498266Sopenharmony_ci     request */
63513498266Sopenharmony_ci  if(before && !digest->stale)
63613498266Sopenharmony_ci    return CURLE_BAD_CONTENT_ENCODING;
63713498266Sopenharmony_ci
63813498266Sopenharmony_ci  /* We got this header without a nonce, that's a bad Digest line! */
63913498266Sopenharmony_ci  if(!digest->nonce)
64013498266Sopenharmony_ci    return CURLE_BAD_CONTENT_ENCODING;
64113498266Sopenharmony_ci
64213498266Sopenharmony_ci  /* "<algo>-sess" protocol versions require "auth" or "auth-int" qop */
64313498266Sopenharmony_ci  if(!digest->qop && (digest->algo & SESSION_ALGO))
64413498266Sopenharmony_ci    return CURLE_BAD_CONTENT_ENCODING;
64513498266Sopenharmony_ci
64613498266Sopenharmony_ci  return CURLE_OK;
64713498266Sopenharmony_ci}
64813498266Sopenharmony_ci
64913498266Sopenharmony_ci/*
65013498266Sopenharmony_ci * auth_create_digest_http_message()
65113498266Sopenharmony_ci *
65213498266Sopenharmony_ci * This is used to generate an HTTP DIGEST response message ready for sending
65313498266Sopenharmony_ci * to the recipient.
65413498266Sopenharmony_ci *
65513498266Sopenharmony_ci * Parameters:
65613498266Sopenharmony_ci *
65713498266Sopenharmony_ci * data    [in]     - The session handle.
65813498266Sopenharmony_ci * userp   [in]     - The user name.
65913498266Sopenharmony_ci * passwdp [in]     - The user's password.
66013498266Sopenharmony_ci * request [in]     - The HTTP request.
66113498266Sopenharmony_ci * uripath [in]     - The path of the HTTP uri.
66213498266Sopenharmony_ci * digest  [in/out] - The digest data struct being used and modified.
66313498266Sopenharmony_ci * outptr  [in/out] - The address where a pointer to newly allocated memory
66413498266Sopenharmony_ci *                    holding the result will be stored upon completion.
66513498266Sopenharmony_ci * outlen  [out]    - The length of the output message.
66613498266Sopenharmony_ci *
66713498266Sopenharmony_ci * Returns CURLE_OK on success.
66813498266Sopenharmony_ci */
66913498266Sopenharmony_cistatic CURLcode auth_create_digest_http_message(
67013498266Sopenharmony_ci                  struct Curl_easy *data,
67113498266Sopenharmony_ci                  const char *userp,
67213498266Sopenharmony_ci                  const char *passwdp,
67313498266Sopenharmony_ci                  const unsigned char *request,
67413498266Sopenharmony_ci                  const unsigned char *uripath,
67513498266Sopenharmony_ci                  struct digestdata *digest,
67613498266Sopenharmony_ci                  char **outptr, size_t *outlen,
67713498266Sopenharmony_ci                  void (*convert_to_ascii)(unsigned char *, unsigned char *),
67813498266Sopenharmony_ci                  CURLcode (*hash)(unsigned char *, const unsigned char *,
67913498266Sopenharmony_ci                                   const size_t))
68013498266Sopenharmony_ci{
68113498266Sopenharmony_ci  CURLcode result;
68213498266Sopenharmony_ci  unsigned char hashbuf[32]; /* 32 bytes/256 bits */
68313498266Sopenharmony_ci  unsigned char request_digest[65];
68413498266Sopenharmony_ci  unsigned char ha1[65];    /* 64 digits and 1 zero byte */
68513498266Sopenharmony_ci  unsigned char ha2[65];    /* 64 digits and 1 zero byte */
68613498266Sopenharmony_ci  char userh[65];
68713498266Sopenharmony_ci  char *cnonce = NULL;
68813498266Sopenharmony_ci  size_t cnonce_sz = 0;
68913498266Sopenharmony_ci  char *userp_quoted;
69013498266Sopenharmony_ci  char *realm_quoted;
69113498266Sopenharmony_ci  char *nonce_quoted;
69213498266Sopenharmony_ci  char *response = NULL;
69313498266Sopenharmony_ci  char *hashthis = NULL;
69413498266Sopenharmony_ci  char *tmp = NULL;
69513498266Sopenharmony_ci
69613498266Sopenharmony_ci  memset(hashbuf, 0, sizeof(hashbuf));
69713498266Sopenharmony_ci  if(!digest->nc)
69813498266Sopenharmony_ci    digest->nc = 1;
69913498266Sopenharmony_ci
70013498266Sopenharmony_ci  if(!digest->cnonce) {
70113498266Sopenharmony_ci    char cnoncebuf[33];
70213498266Sopenharmony_ci    result = Curl_rand_hex(data, (unsigned char *)cnoncebuf,
70313498266Sopenharmony_ci                           sizeof(cnoncebuf));
70413498266Sopenharmony_ci    if(result)
70513498266Sopenharmony_ci      return result;
70613498266Sopenharmony_ci
70713498266Sopenharmony_ci    result = Curl_base64_encode(cnoncebuf, strlen(cnoncebuf),
70813498266Sopenharmony_ci                                &cnonce, &cnonce_sz);
70913498266Sopenharmony_ci    if(result)
71013498266Sopenharmony_ci      return result;
71113498266Sopenharmony_ci
71213498266Sopenharmony_ci    digest->cnonce = cnonce;
71313498266Sopenharmony_ci  }
71413498266Sopenharmony_ci
71513498266Sopenharmony_ci  if(digest->userhash) {
71613498266Sopenharmony_ci    hashthis = aprintf("%s:%s", userp, digest->realm ? digest->realm : "");
71713498266Sopenharmony_ci    if(!hashthis)
71813498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
71913498266Sopenharmony_ci
72013498266Sopenharmony_ci    hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
72113498266Sopenharmony_ci    free(hashthis);
72213498266Sopenharmony_ci    convert_to_ascii(hashbuf, (unsigned char *)userh);
72313498266Sopenharmony_ci  }
72413498266Sopenharmony_ci
72513498266Sopenharmony_ci  /*
72613498266Sopenharmony_ci    If the algorithm is "MD5" or unspecified (which then defaults to MD5):
72713498266Sopenharmony_ci
72813498266Sopenharmony_ci      A1 = unq(username-value) ":" unq(realm-value) ":" passwd
72913498266Sopenharmony_ci
73013498266Sopenharmony_ci    If the algorithm is "MD5-sess" then:
73113498266Sopenharmony_ci
73213498266Sopenharmony_ci      A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":"
73313498266Sopenharmony_ci           unq(nonce-value) ":" unq(cnonce-value)
73413498266Sopenharmony_ci  */
73513498266Sopenharmony_ci
73613498266Sopenharmony_ci  hashthis = aprintf("%s:%s:%s", userp, digest->realm ? digest->realm : "",
73713498266Sopenharmony_ci                     passwdp);
73813498266Sopenharmony_ci  if(!hashthis)
73913498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
74013498266Sopenharmony_ci
74113498266Sopenharmony_ci  hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
74213498266Sopenharmony_ci  free(hashthis);
74313498266Sopenharmony_ci  convert_to_ascii(hashbuf, ha1);
74413498266Sopenharmony_ci
74513498266Sopenharmony_ci  if(digest->algo & SESSION_ALGO) {
74613498266Sopenharmony_ci    /* nonce and cnonce are OUTSIDE the hash */
74713498266Sopenharmony_ci    tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
74813498266Sopenharmony_ci    if(!tmp)
74913498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
75013498266Sopenharmony_ci
75113498266Sopenharmony_ci    hash(hashbuf, (unsigned char *) tmp, strlen(tmp));
75213498266Sopenharmony_ci    free(tmp);
75313498266Sopenharmony_ci    convert_to_ascii(hashbuf, ha1);
75413498266Sopenharmony_ci  }
75513498266Sopenharmony_ci
75613498266Sopenharmony_ci  /*
75713498266Sopenharmony_ci    If the "qop" directive's value is "auth" or is unspecified, then A2 is:
75813498266Sopenharmony_ci
75913498266Sopenharmony_ci      A2 = Method ":" digest-uri-value
76013498266Sopenharmony_ci
76113498266Sopenharmony_ci    If the "qop" value is "auth-int", then A2 is:
76213498266Sopenharmony_ci
76313498266Sopenharmony_ci      A2 = Method ":" digest-uri-value ":" H(entity-body)
76413498266Sopenharmony_ci
76513498266Sopenharmony_ci    (The "Method" value is the HTTP request method as specified in section
76613498266Sopenharmony_ci    5.1.1 of RFC 2616)
76713498266Sopenharmony_ci  */
76813498266Sopenharmony_ci
76913498266Sopenharmony_ci  hashthis = aprintf("%s:%s", request, uripath);
77013498266Sopenharmony_ci  if(!hashthis)
77113498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
77213498266Sopenharmony_ci
77313498266Sopenharmony_ci  if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
77413498266Sopenharmony_ci    /* We don't support auth-int for PUT or POST */
77513498266Sopenharmony_ci    char hashed[65];
77613498266Sopenharmony_ci    char *hashthis2;
77713498266Sopenharmony_ci
77813498266Sopenharmony_ci    hash(hashbuf, (const unsigned char *)"", 0);
77913498266Sopenharmony_ci    convert_to_ascii(hashbuf, (unsigned char *)hashed);
78013498266Sopenharmony_ci
78113498266Sopenharmony_ci    hashthis2 = aprintf("%s:%s", hashthis, hashed);
78213498266Sopenharmony_ci    free(hashthis);
78313498266Sopenharmony_ci    hashthis = hashthis2;
78413498266Sopenharmony_ci  }
78513498266Sopenharmony_ci
78613498266Sopenharmony_ci  if(!hashthis)
78713498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
78813498266Sopenharmony_ci
78913498266Sopenharmony_ci  hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
79013498266Sopenharmony_ci  free(hashthis);
79113498266Sopenharmony_ci  convert_to_ascii(hashbuf, ha2);
79213498266Sopenharmony_ci
79313498266Sopenharmony_ci  if(digest->qop) {
79413498266Sopenharmony_ci    hashthis = aprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc,
79513498266Sopenharmony_ci                       digest->cnonce, digest->qop, ha2);
79613498266Sopenharmony_ci  }
79713498266Sopenharmony_ci  else {
79813498266Sopenharmony_ci    hashthis = aprintf("%s:%s:%s", ha1, digest->nonce, ha2);
79913498266Sopenharmony_ci  }
80013498266Sopenharmony_ci
80113498266Sopenharmony_ci  if(!hashthis)
80213498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
80313498266Sopenharmony_ci
80413498266Sopenharmony_ci  hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
80513498266Sopenharmony_ci  free(hashthis);
80613498266Sopenharmony_ci  convert_to_ascii(hashbuf, request_digest);
80713498266Sopenharmony_ci
80813498266Sopenharmony_ci  /* For test case 64 (snooped from a Mozilla 1.3a request)
80913498266Sopenharmony_ci
81013498266Sopenharmony_ci     Authorization: Digest username="testuser", realm="testrealm", \
81113498266Sopenharmony_ci     nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
81213498266Sopenharmony_ci
81313498266Sopenharmony_ci     Digest parameters are all quoted strings.  Username which is provided by
81413498266Sopenharmony_ci     the user will need double quotes and backslashes within it escaped.
81513498266Sopenharmony_ci     realm, nonce, and opaque will need backslashes as well as they were
81613498266Sopenharmony_ci     de-escaped when copied from request header.  cnonce is generated with
81713498266Sopenharmony_ci     web-safe characters.  uri is already percent encoded.  nc is 8 hex
81813498266Sopenharmony_ci     characters.  algorithm and qop with standard values only contain web-safe
81913498266Sopenharmony_ci     characters.
82013498266Sopenharmony_ci  */
82113498266Sopenharmony_ci  userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
82213498266Sopenharmony_ci  if(!userp_quoted)
82313498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
82413498266Sopenharmony_ci  if(digest->realm)
82513498266Sopenharmony_ci    realm_quoted = auth_digest_string_quoted(digest->realm);
82613498266Sopenharmony_ci  else {
82713498266Sopenharmony_ci    realm_quoted = malloc(1);
82813498266Sopenharmony_ci    if(realm_quoted)
82913498266Sopenharmony_ci      realm_quoted[0] = 0;
83013498266Sopenharmony_ci  }
83113498266Sopenharmony_ci  if(!realm_quoted) {
83213498266Sopenharmony_ci    free(userp_quoted);
83313498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
83413498266Sopenharmony_ci  }
83513498266Sopenharmony_ci  nonce_quoted = auth_digest_string_quoted(digest->nonce);
83613498266Sopenharmony_ci  if(!nonce_quoted) {
83713498266Sopenharmony_ci    free(realm_quoted);
83813498266Sopenharmony_ci    free(userp_quoted);
83913498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
84013498266Sopenharmony_ci  }
84113498266Sopenharmony_ci
84213498266Sopenharmony_ci  if(digest->qop) {
84313498266Sopenharmony_ci    response = aprintf("username=\"%s\", "
84413498266Sopenharmony_ci                       "realm=\"%s\", "
84513498266Sopenharmony_ci                       "nonce=\"%s\", "
84613498266Sopenharmony_ci                       "uri=\"%s\", "
84713498266Sopenharmony_ci                       "cnonce=\"%s\", "
84813498266Sopenharmony_ci                       "nc=%08x, "
84913498266Sopenharmony_ci                       "qop=%s, "
85013498266Sopenharmony_ci                       "response=\"%s\"",
85113498266Sopenharmony_ci                       userp_quoted,
85213498266Sopenharmony_ci                       realm_quoted,
85313498266Sopenharmony_ci                       nonce_quoted,
85413498266Sopenharmony_ci                       uripath,
85513498266Sopenharmony_ci                       digest->cnonce,
85613498266Sopenharmony_ci                       digest->nc,
85713498266Sopenharmony_ci                       digest->qop,
85813498266Sopenharmony_ci                       request_digest);
85913498266Sopenharmony_ci
86013498266Sopenharmony_ci    /* Increment nonce-count to use another nc value for the next request */
86113498266Sopenharmony_ci    digest->nc++;
86213498266Sopenharmony_ci  }
86313498266Sopenharmony_ci  else {
86413498266Sopenharmony_ci    response = aprintf("username=\"%s\", "
86513498266Sopenharmony_ci                       "realm=\"%s\", "
86613498266Sopenharmony_ci                       "nonce=\"%s\", "
86713498266Sopenharmony_ci                       "uri=\"%s\", "
86813498266Sopenharmony_ci                       "response=\"%s\"",
86913498266Sopenharmony_ci                       userp_quoted,
87013498266Sopenharmony_ci                       realm_quoted,
87113498266Sopenharmony_ci                       nonce_quoted,
87213498266Sopenharmony_ci                       uripath,
87313498266Sopenharmony_ci                       request_digest);
87413498266Sopenharmony_ci  }
87513498266Sopenharmony_ci  free(nonce_quoted);
87613498266Sopenharmony_ci  free(realm_quoted);
87713498266Sopenharmony_ci  free(userp_quoted);
87813498266Sopenharmony_ci  if(!response)
87913498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
88013498266Sopenharmony_ci
88113498266Sopenharmony_ci  /* Add the optional fields */
88213498266Sopenharmony_ci  if(digest->opaque) {
88313498266Sopenharmony_ci    char *opaque_quoted;
88413498266Sopenharmony_ci    /* Append the opaque */
88513498266Sopenharmony_ci    opaque_quoted = auth_digest_string_quoted(digest->opaque);
88613498266Sopenharmony_ci    if(!opaque_quoted) {
88713498266Sopenharmony_ci      free(response);
88813498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
88913498266Sopenharmony_ci    }
89013498266Sopenharmony_ci    tmp = aprintf("%s, opaque=\"%s\"", response, opaque_quoted);
89113498266Sopenharmony_ci    free(response);
89213498266Sopenharmony_ci    free(opaque_quoted);
89313498266Sopenharmony_ci    if(!tmp)
89413498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
89513498266Sopenharmony_ci
89613498266Sopenharmony_ci    response = tmp;
89713498266Sopenharmony_ci  }
89813498266Sopenharmony_ci
89913498266Sopenharmony_ci  if(digest->algorithm) {
90013498266Sopenharmony_ci    /* Append the algorithm */
90113498266Sopenharmony_ci    tmp = aprintf("%s, algorithm=%s", response, digest->algorithm);
90213498266Sopenharmony_ci    free(response);
90313498266Sopenharmony_ci    if(!tmp)
90413498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
90513498266Sopenharmony_ci
90613498266Sopenharmony_ci    response = tmp;
90713498266Sopenharmony_ci  }
90813498266Sopenharmony_ci
90913498266Sopenharmony_ci  if(digest->userhash) {
91013498266Sopenharmony_ci    /* Append the userhash */
91113498266Sopenharmony_ci    tmp = aprintf("%s, userhash=true", response);
91213498266Sopenharmony_ci    free(response);
91313498266Sopenharmony_ci    if(!tmp)
91413498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
91513498266Sopenharmony_ci
91613498266Sopenharmony_ci    response = tmp;
91713498266Sopenharmony_ci  }
91813498266Sopenharmony_ci
91913498266Sopenharmony_ci  /* Return the output */
92013498266Sopenharmony_ci  *outptr = response;
92113498266Sopenharmony_ci  *outlen = strlen(response);
92213498266Sopenharmony_ci
92313498266Sopenharmony_ci  return CURLE_OK;
92413498266Sopenharmony_ci}
92513498266Sopenharmony_ci
92613498266Sopenharmony_ci/*
92713498266Sopenharmony_ci * Curl_auth_create_digest_http_message()
92813498266Sopenharmony_ci *
92913498266Sopenharmony_ci * This is used to generate an HTTP DIGEST response message ready for sending
93013498266Sopenharmony_ci * to the recipient.
93113498266Sopenharmony_ci *
93213498266Sopenharmony_ci * Parameters:
93313498266Sopenharmony_ci *
93413498266Sopenharmony_ci * data    [in]     - The session handle.
93513498266Sopenharmony_ci * userp   [in]     - The user name.
93613498266Sopenharmony_ci * passwdp [in]     - The user's password.
93713498266Sopenharmony_ci * request [in]     - The HTTP request.
93813498266Sopenharmony_ci * uripath [in]     - The path of the HTTP uri.
93913498266Sopenharmony_ci * digest  [in/out] - The digest data struct being used and modified.
94013498266Sopenharmony_ci * outptr  [in/out] - The address where a pointer to newly allocated memory
94113498266Sopenharmony_ci *                    holding the result will be stored upon completion.
94213498266Sopenharmony_ci * outlen  [out]    - The length of the output message.
94313498266Sopenharmony_ci *
94413498266Sopenharmony_ci * Returns CURLE_OK on success.
94513498266Sopenharmony_ci */
94613498266Sopenharmony_ciCURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
94713498266Sopenharmony_ci                                              const char *userp,
94813498266Sopenharmony_ci                                              const char *passwdp,
94913498266Sopenharmony_ci                                              const unsigned char *request,
95013498266Sopenharmony_ci                                              const unsigned char *uripath,
95113498266Sopenharmony_ci                                              struct digestdata *digest,
95213498266Sopenharmony_ci                                              char **outptr, size_t *outlen)
95313498266Sopenharmony_ci{
95413498266Sopenharmony_ci  if(digest->algo <= ALGO_MD5SESS)
95513498266Sopenharmony_ci    return auth_create_digest_http_message(data, userp, passwdp,
95613498266Sopenharmony_ci                                           request, uripath, digest,
95713498266Sopenharmony_ci                                           outptr, outlen,
95813498266Sopenharmony_ci                                           auth_digest_md5_to_ascii,
95913498266Sopenharmony_ci                                           Curl_md5it);
96013498266Sopenharmony_ci  DEBUGASSERT(digest->algo <= ALGO_SHA512_256SESS);
96113498266Sopenharmony_ci  return auth_create_digest_http_message(data, userp, passwdp,
96213498266Sopenharmony_ci                                         request, uripath, digest,
96313498266Sopenharmony_ci                                         outptr, outlen,
96413498266Sopenharmony_ci                                         auth_digest_sha256_to_ascii,
96513498266Sopenharmony_ci                                         Curl_sha256it);
96613498266Sopenharmony_ci}
96713498266Sopenharmony_ci
96813498266Sopenharmony_ci/*
96913498266Sopenharmony_ci * Curl_auth_digest_cleanup()
97013498266Sopenharmony_ci *
97113498266Sopenharmony_ci * This is used to clean up the digest specific data.
97213498266Sopenharmony_ci *
97313498266Sopenharmony_ci * Parameters:
97413498266Sopenharmony_ci *
97513498266Sopenharmony_ci * digest    [in/out] - The digest data struct being cleaned up.
97613498266Sopenharmony_ci *
97713498266Sopenharmony_ci */
97813498266Sopenharmony_civoid Curl_auth_digest_cleanup(struct digestdata *digest)
97913498266Sopenharmony_ci{
98013498266Sopenharmony_ci  Curl_safefree(digest->nonce);
98113498266Sopenharmony_ci  Curl_safefree(digest->cnonce);
98213498266Sopenharmony_ci  Curl_safefree(digest->realm);
98313498266Sopenharmony_ci  Curl_safefree(digest->opaque);
98413498266Sopenharmony_ci  Curl_safefree(digest->qop);
98513498266Sopenharmony_ci  Curl_safefree(digest->algorithm);
98613498266Sopenharmony_ci
98713498266Sopenharmony_ci  digest->nc = 0;
98813498266Sopenharmony_ci  digest->algo = ALGO_MD5; /* default algorithm */
98913498266Sopenharmony_ci  digest->stale = FALSE; /* default means normal, not stale */
99013498266Sopenharmony_ci  digest->userhash = FALSE;
99113498266Sopenharmony_ci}
99213498266Sopenharmony_ci#endif  /* !USE_WINDOWS_SSPI */
99313498266Sopenharmony_ci
99413498266Sopenharmony_ci#endif  /* !CURL_DISABLE_DIGEST_AUTH */
995