quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

digest.c (31424B)


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