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