Annotation of embedaddon/curl/lib/vauth/digest.c, revision 1.1.1.1

1.1       misho       1: /***************************************************************************
                      2:  *                                  _   _ ____  _
                      3:  *  Project                     ___| | | |  _ \| |
                      4:  *                             / __| | | | |_) | |
                      5:  *                            | (__| |_| |  _ <| |___
                      6:  *                             \___|\___/|_| \_\_____|
                      7:  *
                      8:  * Copyright (C) 1998 - 2020, 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.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, b, strlen(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:   size_t i;
                    361:   MD5_context *ctxt;
                    362:   char *response = NULL;
                    363:   unsigned char digest[MD5_DIGEST_LEN];
                    364:   char HA1_hex[2 * MD5_DIGEST_LEN + 1];
                    365:   char HA2_hex[2 * MD5_DIGEST_LEN + 1];
                    366:   char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
                    367:   char nonce[64];
                    368:   char realm[128];
                    369:   char algorithm[64];
                    370:   char qop_options[64];
                    371:   int qop_values;
                    372:   char cnonce[33];
                    373:   char nonceCount[] = "00000001";
                    374:   char method[]     = "AUTHENTICATE";
                    375:   char qop[]        = DIGEST_QOP_VALUE_STRING_AUTH;
                    376:   char *spn         = NULL;
                    377: 
                    378:   /* Decode the challenge message */
                    379:   CURLcode result = auth_decode_digest_md5_message(chlg64, nonce,
                    380:                                                    sizeof(nonce), realm,
                    381:                                                    sizeof(realm), algorithm,
                    382:                                                    sizeof(algorithm),
                    383:                                                    qop_options,
                    384:                                                    sizeof(qop_options));
                    385:   if(result)
                    386:     return result;
                    387: 
                    388:   /* We only support md5 sessions */
                    389:   if(strcmp(algorithm, "md5-sess") != 0)
                    390:     return CURLE_BAD_CONTENT_ENCODING;
                    391: 
                    392:   /* Get the qop-values from the qop-options */
                    393:   result = auth_digest_get_qop_values(qop_options, &qop_values);
                    394:   if(result)
                    395:     return result;
                    396: 
                    397:   /* We only support auth quality-of-protection */
                    398:   if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
                    399:     return CURLE_BAD_CONTENT_ENCODING;
                    400: 
                    401:   /* Generate 32 random hex chars, 32 bytes + 1 zero termination */
                    402:   result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce));
                    403:   if(result)
                    404:     return result;
                    405: 
                    406:   /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
                    407:   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
                    408:   if(!ctxt)
                    409:     return CURLE_OUT_OF_MEMORY;
                    410: 
                    411:   Curl_MD5_update(ctxt, (const unsigned char *) userp,
                    412:                   curlx_uztoui(strlen(userp)));
                    413:   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
                    414:   Curl_MD5_update(ctxt, (const unsigned char *) realm,
                    415:                   curlx_uztoui(strlen(realm)));
                    416:   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
                    417:   Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
                    418:                   curlx_uztoui(strlen(passwdp)));
                    419:   Curl_MD5_final(ctxt, digest);
                    420: 
                    421:   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
                    422:   if(!ctxt)
                    423:     return CURLE_OUT_OF_MEMORY;
                    424: 
                    425:   Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
                    426:   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
                    427:   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
                    428:                   curlx_uztoui(strlen(nonce)));
                    429:   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
                    430:   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
                    431:                   curlx_uztoui(strlen(cnonce)));
                    432:   Curl_MD5_final(ctxt, digest);
                    433: 
                    434:   /* Convert calculated 16 octet hex into 32 bytes string */
                    435:   for(i = 0; i < MD5_DIGEST_LEN; i++)
                    436:     msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
                    437: 
                    438:   /* Generate our SPN */
                    439:   spn = Curl_auth_build_spn(service, realm, NULL);
                    440:   if(!spn)
                    441:     return CURLE_OUT_OF_MEMORY;
                    442: 
                    443:   /* Calculate H(A2) */
                    444:   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
                    445:   if(!ctxt) {
                    446:     free(spn);
                    447: 
                    448:     return CURLE_OUT_OF_MEMORY;
                    449:   }
                    450: 
                    451:   Curl_MD5_update(ctxt, (const unsigned char *) method,
                    452:                   curlx_uztoui(strlen(method)));
                    453:   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
                    454:   Curl_MD5_update(ctxt, (const unsigned char *) spn,
                    455:                   curlx_uztoui(strlen(spn)));
                    456:   Curl_MD5_final(ctxt, digest);
                    457: 
                    458:   for(i = 0; i < MD5_DIGEST_LEN; i++)
                    459:     msnprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
                    460: 
                    461:   /* Now calculate the response hash */
                    462:   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
                    463:   if(!ctxt) {
                    464:     free(spn);
                    465: 
                    466:     return CURLE_OUT_OF_MEMORY;
                    467:   }
                    468: 
                    469:   Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
                    470:   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
                    471:   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
                    472:                   curlx_uztoui(strlen(nonce)));
                    473:   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
                    474: 
                    475:   Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
                    476:                   curlx_uztoui(strlen(nonceCount)));
                    477:   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
                    478:   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
                    479:                   curlx_uztoui(strlen(cnonce)));
                    480:   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
                    481:   Curl_MD5_update(ctxt, (const unsigned char *) qop,
                    482:                   curlx_uztoui(strlen(qop)));
                    483:   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
                    484: 
                    485:   Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
                    486:   Curl_MD5_final(ctxt, digest);
                    487: 
                    488:   for(i = 0; i < MD5_DIGEST_LEN; i++)
                    489:     msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
                    490: 
                    491:   /* Generate the response */
                    492:   response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
                    493:                      "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
                    494:                      "qop=%s",
                    495:                      userp, realm, nonce,
                    496:                      cnonce, nonceCount, spn, resp_hash_hex, qop);
                    497:   free(spn);
                    498:   if(!response)
                    499:     return CURLE_OUT_OF_MEMORY;
                    500: 
                    501:   /* Base64 encode the response */
                    502:   result = Curl_base64_encode(data, response, 0, outptr, outlen);
                    503: 
                    504:   free(response);
                    505: 
                    506:   return result;
                    507: }
                    508: 
                    509: /*
                    510:  * Curl_auth_decode_digest_http_message()
                    511:  *
                    512:  * This is used to decode a HTTP DIGEST challenge message into the separate
                    513:  * attributes.
                    514:  *
                    515:  * Parameters:
                    516:  *
                    517:  * chlg    [in]     - The challenge message.
                    518:  * digest  [in/out] - The digest data struct being used and modified.
                    519:  *
                    520:  * Returns CURLE_OK on success.
                    521:  */
                    522: CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
                    523:                                               struct digestdata *digest)
                    524: {
                    525:   bool before = FALSE; /* got a nonce before */
                    526:   bool foundAuth = FALSE;
                    527:   bool foundAuthInt = FALSE;
                    528:   char *token = NULL;
                    529:   char *tmp = NULL;
                    530: 
                    531:   /* If we already have received a nonce, keep that in mind */
                    532:   if(digest->nonce)
                    533:     before = TRUE;
                    534: 
                    535:   /* Clean up any former leftovers and initialise to defaults */
                    536:   Curl_auth_digest_cleanup(digest);
                    537: 
                    538:   for(;;) {
                    539:     char value[DIGEST_MAX_VALUE_LENGTH];
                    540:     char content[DIGEST_MAX_CONTENT_LENGTH];
                    541: 
                    542:     /* Pass all additional spaces here */
                    543:     while(*chlg && ISSPACE(*chlg))
                    544:       chlg++;
                    545: 
                    546:     /* Extract a value=content pair */
                    547:     if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
                    548:       if(strcasecompare(value, "nonce")) {
                    549:         free(digest->nonce);
                    550:         digest->nonce = strdup(content);
                    551:         if(!digest->nonce)
                    552:           return CURLE_OUT_OF_MEMORY;
                    553:       }
                    554:       else if(strcasecompare(value, "stale")) {
                    555:         if(strcasecompare(content, "true")) {
                    556:           digest->stale = TRUE;
                    557:           digest->nc = 1; /* we make a new nonce now */
                    558:         }
                    559:       }
                    560:       else if(strcasecompare(value, "realm")) {
                    561:         free(digest->realm);
                    562:         digest->realm = strdup(content);
                    563:         if(!digest->realm)
                    564:           return CURLE_OUT_OF_MEMORY;
                    565:       }
                    566:       else if(strcasecompare(value, "opaque")) {
                    567:         free(digest->opaque);
                    568:         digest->opaque = strdup(content);
                    569:         if(!digest->opaque)
                    570:           return CURLE_OUT_OF_MEMORY;
                    571:       }
                    572:       else if(strcasecompare(value, "qop")) {
                    573:         char *tok_buf = NULL;
                    574:         /* Tokenize the list and choose auth if possible, use a temporary
                    575:            clone of the buffer since strtok_r() ruins it */
                    576:         tmp = strdup(content);
                    577:         if(!tmp)
                    578:           return CURLE_OUT_OF_MEMORY;
                    579: 
                    580:         token = strtok_r(tmp, ",", &tok_buf);
                    581:         while(token != NULL) {
                    582:           if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
                    583:             foundAuth = TRUE;
                    584:           }
                    585:           else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
                    586:             foundAuthInt = TRUE;
                    587:           }
                    588:           token = strtok_r(NULL, ",", &tok_buf);
                    589:         }
                    590: 
                    591:         free(tmp);
                    592: 
                    593:         /* Select only auth or auth-int. Otherwise, ignore */
                    594:         if(foundAuth) {
                    595:           free(digest->qop);
                    596:           digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
                    597:           if(!digest->qop)
                    598:             return CURLE_OUT_OF_MEMORY;
                    599:         }
                    600:         else if(foundAuthInt) {
                    601:           free(digest->qop);
                    602:           digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
                    603:           if(!digest->qop)
                    604:             return CURLE_OUT_OF_MEMORY;
                    605:         }
                    606:       }
                    607:       else if(strcasecompare(value, "algorithm")) {
                    608:         free(digest->algorithm);
                    609:         digest->algorithm = strdup(content);
                    610:         if(!digest->algorithm)
                    611:           return CURLE_OUT_OF_MEMORY;
                    612: 
                    613:         if(strcasecompare(content, "MD5-sess"))
                    614:           digest->algo = CURLDIGESTALGO_MD5SESS;
                    615:         else if(strcasecompare(content, "MD5"))
                    616:           digest->algo = CURLDIGESTALGO_MD5;
                    617:         else if(strcasecompare(content, "SHA-256"))
                    618:           digest->algo = CURLDIGESTALGO_SHA256;
                    619:         else if(strcasecompare(content, "SHA-256-SESS"))
                    620:           digest->algo = CURLDIGESTALGO_SHA256SESS;
                    621:         else if(strcasecompare(content, "SHA-512-256"))
                    622:           digest->algo = CURLDIGESTALGO_SHA512_256;
                    623:         else if(strcasecompare(content, "SHA-512-256-SESS"))
                    624:           digest->algo = CURLDIGESTALGO_SHA512_256SESS;
                    625:         else
                    626:           return CURLE_BAD_CONTENT_ENCODING;
                    627:       }
                    628:       else if(strcasecompare(value, "userhash")) {
                    629:         if(strcasecompare(content, "true")) {
                    630:           digest->userhash = TRUE;
                    631:         }
                    632:       }
                    633:       else {
                    634:         /* Unknown specifier, ignore it! */
                    635:       }
                    636:     }
                    637:     else
                    638:       break; /* We're done here */
                    639: 
                    640:     /* Pass all additional spaces here */
                    641:     while(*chlg && ISSPACE(*chlg))
                    642:       chlg++;
                    643: 
                    644:     /* Allow the list to be comma-separated */
                    645:     if(',' == *chlg)
                    646:       chlg++;
                    647:   }
                    648: 
                    649:   /* We had a nonce since before, and we got another one now without
                    650:      'stale=true'. This means we provided bad credentials in the previous
                    651:      request */
                    652:   if(before && !digest->stale)
                    653:     return CURLE_BAD_CONTENT_ENCODING;
                    654: 
                    655:   /* We got this header without a nonce, that's a bad Digest line! */
                    656:   if(!digest->nonce)
                    657:     return CURLE_BAD_CONTENT_ENCODING;
                    658: 
                    659:   return CURLE_OK;
                    660: }
                    661: 
                    662: /*
                    663:  * auth_create_digest_http_message()
                    664:  *
                    665:  * This is used to generate a HTTP DIGEST response message ready for sending
                    666:  * to the recipient.
                    667:  *
                    668:  * Parameters:
                    669:  *
                    670:  * data    [in]     - The session handle.
                    671:  * userp   [in]     - The user name.
                    672:  * passwdp [in]     - The user's password.
                    673:  * request [in]     - The HTTP request.
                    674:  * uripath [in]     - The path of the HTTP uri.
                    675:  * digest  [in/out] - The digest data struct being used and modified.
                    676:  * outptr  [in/out] - The address where a pointer to newly allocated memory
                    677:  *                    holding the result will be stored upon completion.
                    678:  * outlen  [out]    - The length of the output message.
                    679:  *
                    680:  * Returns CURLE_OK on success.
                    681:  */
                    682: static CURLcode auth_create_digest_http_message(
                    683:                   struct Curl_easy *data,
                    684:                   const char *userp,
                    685:                   const char *passwdp,
                    686:                   const unsigned char *request,
                    687:                   const unsigned char *uripath,
                    688:                   struct digestdata *digest,
                    689:                   char **outptr, size_t *outlen,
                    690:                   void (*convert_to_ascii)(unsigned char *, unsigned char *),
                    691:                   void (*hash)(unsigned char *, const unsigned char *,
                    692:                                const size_t))
                    693: {
                    694:   CURLcode result;
                    695:   unsigned char hashbuf[32]; /* 32 bytes/256 bits */
                    696:   unsigned char request_digest[65];
                    697:   unsigned char ha1[65];    /* 64 digits and 1 zero byte */
                    698:   unsigned char ha2[65];    /* 64 digits and 1 zero byte */
                    699:   char userh[65];
                    700:   char *cnonce = NULL;
                    701:   size_t cnonce_sz = 0;
                    702:   char *userp_quoted;
                    703:   char *response = NULL;
                    704:   char *hashthis = NULL;
                    705:   char *tmp = NULL;
                    706: 
                    707:   if(!digest->nc)
                    708:     digest->nc = 1;
                    709: 
                    710:   if(!digest->cnonce) {
                    711:     char cnoncebuf[33];
                    712:     result = Curl_rand_hex(data, (unsigned char *)cnoncebuf,
                    713:                            sizeof(cnoncebuf));
                    714:     if(result)
                    715:       return result;
                    716: 
                    717:     result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
                    718:                                 &cnonce, &cnonce_sz);
                    719:     if(result)
                    720:       return result;
                    721: 
                    722:     digest->cnonce = cnonce;
                    723:   }
                    724: 
                    725:   if(digest->userhash) {
                    726:     hashthis = aprintf("%s:%s", userp, digest->realm);
                    727:     if(!hashthis)
                    728:       return CURLE_OUT_OF_MEMORY;
                    729: 
                    730:     CURL_OUTPUT_DIGEST_CONV(data, hashthis);
                    731:     hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
                    732:     free(hashthis);
                    733:     convert_to_ascii(hashbuf, (unsigned char *)userh);
                    734:   }
                    735: 
                    736:   /*
                    737:     If the algorithm is "MD5" or unspecified (which then defaults to MD5):
                    738: 
                    739:       A1 = unq(username-value) ":" unq(realm-value) ":" passwd
                    740: 
                    741:     If the algorithm is "MD5-sess" then:
                    742: 
                    743:       A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":"
                    744:            unq(nonce-value) ":" unq(cnonce-value)
                    745:   */
                    746: 
                    747:   hashthis = aprintf("%s:%s:%s", digest->userhash ? userh : userp,
                    748:                                  digest->realm, passwdp);
                    749:   if(!hashthis)
                    750:     return CURLE_OUT_OF_MEMORY;
                    751: 
                    752:   CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
                    753:   hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
                    754:   free(hashthis);
                    755:   convert_to_ascii(hashbuf, ha1);
                    756: 
                    757:   if(digest->algo == CURLDIGESTALGO_MD5SESS ||
                    758:      digest->algo == CURLDIGESTALGO_SHA256SESS ||
                    759:      digest->algo == CURLDIGESTALGO_SHA512_256SESS) {
                    760:     /* nonce and cnonce are OUTSIDE the hash */
                    761:     tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
                    762:     if(!tmp)
                    763:       return CURLE_OUT_OF_MEMORY;
                    764: 
                    765:     CURL_OUTPUT_DIGEST_CONV(data, tmp); /* Convert on non-ASCII machines */
                    766:     hash(hashbuf, (unsigned char *) tmp, strlen(tmp));
                    767:     free(tmp);
                    768:     convert_to_ascii(hashbuf, ha1);
                    769:   }
                    770: 
                    771:   /*
                    772:     If the "qop" directive's value is "auth" or is unspecified, then A2 is:
                    773: 
                    774:       A2 = Method ":" digest-uri-value
                    775: 
                    776:     If the "qop" value is "auth-int", then A2 is:
                    777: 
                    778:       A2 = Method ":" digest-uri-value ":" H(entity-body)
                    779: 
                    780:     (The "Method" value is the HTTP request method as specified in section
                    781:     5.1.1 of RFC 2616)
                    782:   */
                    783: 
                    784:   hashthis = aprintf("%s:%s", request, uripath);
                    785:   if(!hashthis)
                    786:     return CURLE_OUT_OF_MEMORY;
                    787: 
                    788:   if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
                    789:     /* We don't support auth-int for PUT or POST */
                    790:     char hashed[65];
                    791:     char *hashthis2;
                    792: 
                    793:     hash(hashbuf, (const unsigned char *)"", 0);
                    794:     convert_to_ascii(hashbuf, (unsigned char *)hashed);
                    795: 
                    796:     hashthis2 = 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, (unsigned char *) hashthis, strlen(hashthis));
                    806:   free(hashthis);
                    807:   convert_to_ascii(hashbuf, ha2);
                    808: 
                    809:   if(digest->qop) {
                    810:     hashthis = aprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc,
                    811:                        digest->cnonce, digest->qop, ha2);
                    812:   }
                    813:   else {
                    814:     hashthis = aprintf("%s:%s:%s", ha1, digest->nonce, ha2);
                    815:   }
                    816: 
                    817:   if(!hashthis)
                    818:     return CURLE_OUT_OF_MEMORY;
                    819: 
                    820:   CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
                    821:   hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
                    822:   free(hashthis);
                    823:   convert_to_ascii(hashbuf, request_digest);
                    824: 
                    825:   /* For test case 64 (snooped from a Mozilla 1.3a request)
                    826: 
                    827:      Authorization: Digest username="testuser", realm="testrealm", \
                    828:      nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
                    829: 
                    830:      Digest parameters are all quoted strings.  Username which is provided by
                    831:      the user will need double quotes and backslashes within it escaped.  For
                    832:      the other fields, this shouldn't be an issue.  realm, nonce, and opaque
                    833:      are copied as is from the server, escapes and all.  cnonce is generated
                    834:      with web-safe characters.  uri is already percent encoded.  nc is 8 hex
                    835:      characters.  algorithm and qop with standard values only contain web-safe
                    836:      characters.
                    837:   */
                    838:   userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
                    839:   if(!userp_quoted)
                    840:     return CURLE_OUT_OF_MEMORY;
                    841: 
                    842:   if(digest->qop) {
                    843:     response = aprintf("username=\"%s\", "
                    844:                        "realm=\"%s\", "
                    845:                        "nonce=\"%s\", "
                    846:                        "uri=\"%s\", "
                    847:                        "cnonce=\"%s\", "
                    848:                        "nc=%08x, "
                    849:                        "qop=%s, "
                    850:                        "response=\"%s\"",
                    851:                        userp_quoted,
                    852:                        digest->realm,
                    853:                        digest->nonce,
                    854:                        uripath,
                    855:                        digest->cnonce,
                    856:                        digest->nc,
                    857:                        digest->qop,
                    858:                        request_digest);
                    859: 
                    860:     if(strcasecompare(digest->qop, "auth"))
                    861:       digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0
                    862:                        padded which tells to the server how many times you are
                    863:                        using the same nonce in the qop=auth mode */
                    864:   }
                    865:   else {
                    866:     response = aprintf("username=\"%s\", "
                    867:                        "realm=\"%s\", "
                    868:                        "nonce=\"%s\", "
                    869:                        "uri=\"%s\", "
                    870:                        "response=\"%s\"",
                    871:                        userp_quoted,
                    872:                        digest->realm,
                    873:                        digest->nonce,
                    874:                        uripath,
                    875:                        request_digest);
                    876:   }
                    877:   free(userp_quoted);
                    878:   if(!response)
                    879:     return CURLE_OUT_OF_MEMORY;
                    880: 
                    881:   /* Add the optional fields */
                    882:   if(digest->opaque) {
                    883:     /* Append the opaque */
                    884:     tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
                    885:     free(response);
                    886:     if(!tmp)
                    887:       return CURLE_OUT_OF_MEMORY;
                    888: 
                    889:     response = tmp;
                    890:   }
                    891: 
                    892:   if(digest->algorithm) {
                    893:     /* Append the algorithm */
                    894:     tmp = aprintf("%s, algorithm=%s", response, digest->algorithm);
                    895:     free(response);
                    896:     if(!tmp)
                    897:       return CURLE_OUT_OF_MEMORY;
                    898: 
                    899:     response = tmp;
                    900:   }
                    901: 
                    902:   if(digest->userhash) {
                    903:     /* Append the userhash */
                    904:     tmp = aprintf("%s, userhash=true", response);
                    905:     free(response);
                    906:     if(!tmp)
                    907:       return CURLE_OUT_OF_MEMORY;
                    908: 
                    909:     response = tmp;
                    910:   }
                    911: 
                    912:   /* Return the output */
                    913:   *outptr = response;
                    914:   *outlen = strlen(response);
                    915: 
                    916:   return CURLE_OK;
                    917: }
                    918: 
                    919: /*
                    920:  * Curl_auth_create_digest_http_message()
                    921:  *
                    922:  * This is used to generate a HTTP DIGEST response message ready for sending
                    923:  * to the recipient.
                    924:  *
                    925:  * Parameters:
                    926:  *
                    927:  * data    [in]     - The session handle.
                    928:  * userp   [in]     - The user name.
                    929:  * passwdp [in]     - The user's password.
                    930:  * request [in]     - The HTTP request.
                    931:  * uripath [in]     - The path of the HTTP uri.
                    932:  * digest  [in/out] - The digest data struct being used and modified.
                    933:  * outptr  [in/out] - The address where a pointer to newly allocated memory
                    934:  *                    holding the result will be stored upon completion.
                    935:  * outlen  [out]    - The length of the output message.
                    936:  *
                    937:  * Returns CURLE_OK on success.
                    938:  */
                    939: CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
                    940:                                               const char *userp,
                    941:                                               const char *passwdp,
                    942:                                               const unsigned char *request,
                    943:                                               const unsigned char *uripath,
                    944:                                               struct digestdata *digest,
                    945:                                               char **outptr, size_t *outlen)
                    946: {
                    947:   switch(digest->algo) {
                    948:   case CURLDIGESTALGO_MD5:
                    949:   case CURLDIGESTALGO_MD5SESS:
                    950:     return auth_create_digest_http_message(data, userp, passwdp,
                    951:                                            request, uripath, digest,
                    952:                                            outptr, outlen,
                    953:                                            auth_digest_md5_to_ascii,
                    954:                                            Curl_md5it);
                    955: 
                    956:   case CURLDIGESTALGO_SHA256:
                    957:   case CURLDIGESTALGO_SHA256SESS:
                    958:   case CURLDIGESTALGO_SHA512_256:
                    959:   case CURLDIGESTALGO_SHA512_256SESS:
                    960:     return auth_create_digest_http_message(data, userp, passwdp,
                    961:                                            request, uripath, digest,
                    962:                                            outptr, outlen,
                    963:                                            auth_digest_sha256_to_ascii,
                    964:                                            Curl_sha256it);
                    965: 
                    966:   default:
                    967:     return CURLE_UNSUPPORTED_PROTOCOL;
                    968:   }
                    969: }
                    970: 
                    971: /*
                    972:  * Curl_auth_digest_cleanup()
                    973:  *
                    974:  * This is used to clean up the digest specific data.
                    975:  *
                    976:  * Parameters:
                    977:  *
                    978:  * digest    [in/out] - The digest data struct being cleaned up.
                    979:  *
                    980:  */
                    981: void Curl_auth_digest_cleanup(struct digestdata *digest)
                    982: {
                    983:   Curl_safefree(digest->nonce);
                    984:   Curl_safefree(digest->cnonce);
                    985:   Curl_safefree(digest->realm);
                    986:   Curl_safefree(digest->opaque);
                    987:   Curl_safefree(digest->qop);
                    988:   Curl_safefree(digest->algorithm);
                    989: 
                    990:   digest->nc = 0;
                    991:   digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
                    992:   digest->stale = FALSE; /* default means normal, not stale */
                    993:   digest->userhash = FALSE;
                    994: }
                    995: #endif  /* !USE_WINDOWS_SSPI */
                    996: 
                    997: #endif  /* CURL_DISABLE_CRYPTO_AUTH */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>