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