digest.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  * RFC2831 DIGEST-MD5 authentication
00022  *
00023  ***************************************************************************/
00024 
00025 #include "curl_setup.h"
00026 
00027 #if !defined(CURL_DISABLE_CRYPTO_AUTH)
00028 
00029 #include <curl/curl.h>
00030 
00031 #include "vauth/vauth.h"
00032 #include "vauth/digest.h"
00033 #include "urldata.h"
00034 #include "curl_base64.h"
00035 #include "curl_hmac.h"
00036 #include "curl_md5.h"
00037 #include "vtls/vtls.h"
00038 #include "warnless.h"
00039 #include "strtok.h"
00040 #include "strcase.h"
00041 #include "non-ascii.h" /* included for Curl_convert_... prototypes */
00042 #include "curl_printf.h"
00043 #include "rand.h"
00044 
00045 /* The last #include files should be: */
00046 #include "curl_memory.h"
00047 #include "memdebug.h"
00048 
00049 #if !defined(USE_WINDOWS_SSPI)
00050 #define DIGEST_QOP_VALUE_AUTH             (1 << 0)
00051 #define DIGEST_QOP_VALUE_AUTH_INT         (1 << 1)
00052 #define DIGEST_QOP_VALUE_AUTH_CONF        (1 << 2)
00053 
00054 #define DIGEST_QOP_VALUE_STRING_AUTH      "auth"
00055 #define DIGEST_QOP_VALUE_STRING_AUTH_INT  "auth-int"
00056 #define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf"
00057 
00058 /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
00059    It converts digest text to ASCII so the MD5 will be correct for
00060    what ultimately goes over the network.
00061 */
00062 #define CURL_OUTPUT_DIGEST_CONV(a, b) \
00063   result = Curl_convert_to_network(a, (char *)b, strlen((const char *)b)); \
00064   if(result) { \
00065     free(b); \
00066     return result; \
00067   }
00068 #endif /* !USE_WINDOWS_SSPI */
00069 
00070 bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
00071                                const char **endptr)
00072 {
00073   int c;
00074   bool starts_with_quote = FALSE;
00075   bool escape = FALSE;
00076 
00077   for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--);)
00078     *value++ = *str++;
00079   *value = 0;
00080 
00081   if('=' != *str++)
00082     /* eek, no match */
00083     return FALSE;
00084 
00085   if('\"' == *str) {
00086     /* This starts with a quote so it must end with one as well! */
00087     str++;
00088     starts_with_quote = TRUE;
00089   }
00090 
00091   for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
00092     switch(*str) {
00093     case '\\':
00094       if(!escape) {
00095         /* possibly the start of an escaped quote */
00096         escape = TRUE;
00097         *content++ = '\\'; /* Even though this is an escape character, we still
00098                               store it as-is in the target buffer */
00099         continue;
00100       }
00101       break;
00102 
00103     case ',':
00104       if(!starts_with_quote) {
00105         /* This signals the end of the content if we didn't get a starting
00106            quote and then we do "sloppy" parsing */
00107         c = 0; /* the end */
00108         continue;
00109       }
00110       break;
00111 
00112     case '\r':
00113     case '\n':
00114       /* end of string */
00115       c = 0;
00116       continue;
00117 
00118     case '\"':
00119       if(!escape && starts_with_quote) {
00120         /* end of string */
00121         c = 0;
00122         continue;
00123       }
00124       break;
00125     }
00126 
00127     escape = FALSE;
00128     *content++ = *str;
00129   }
00130 
00131   *content = 0;
00132   *endptr = str;
00133 
00134   return TRUE;
00135 }
00136 
00137 #if !defined(USE_WINDOWS_SSPI)
00138 /* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
00139 static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
00140                                      unsigned char *dest) /* 33 bytes */
00141 {
00142   int i;
00143   for(i = 0; i < 16; i++)
00144     snprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
00145 }
00146 
00147 /* Perform quoted-string escaping as described in RFC2616 and its errata */
00148 static char *auth_digest_string_quoted(const char *source)
00149 {
00150   char *dest, *d;
00151   const char *s = source;
00152   size_t n = 1; /* null terminator */
00153 
00154   /* Calculate size needed */
00155   while(*s) {
00156     ++n;
00157     if(*s == '"' || *s == '\\') {
00158       ++n;
00159     }
00160     ++s;
00161   }
00162 
00163   dest = malloc(n);
00164   if(dest) {
00165     s = source;
00166     d = dest;
00167     while(*s) {
00168       if(*s == '"' || *s == '\\') {
00169         *d++ = '\\';
00170       }
00171       *d++ = *s++;
00172     }
00173     *d = 0;
00174   }
00175 
00176   return dest;
00177 }
00178 
00179 /* Retrieves the value for a corresponding key from the challenge string
00180  * returns TRUE if the key could be found, FALSE if it does not exists
00181  */
00182 static bool auth_digest_get_key_value(const char *chlg,
00183                                       const char *key,
00184                                       char *value,
00185                                       size_t max_val_len,
00186                                       char end_char)
00187 {
00188   char *find_pos;
00189   size_t i;
00190 
00191   find_pos = strstr(chlg, key);
00192   if(!find_pos)
00193     return FALSE;
00194 
00195   find_pos += strlen(key);
00196 
00197   for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i)
00198     value[i] = *find_pos++;
00199   value[i] = '\0';
00200 
00201   return TRUE;
00202 }
00203 
00204 static CURLcode auth_digest_get_qop_values(const char *options, int *value)
00205 {
00206   char *tmp;
00207   char *token;
00208   char *tok_buf;
00209 
00210   /* Initialise the output */
00211   *value = 0;
00212 
00213   /* Tokenise the list of qop values. Use a temporary clone of the buffer since
00214      strtok_r() ruins it. */
00215   tmp = strdup(options);
00216   if(!tmp)
00217     return CURLE_OUT_OF_MEMORY;
00218 
00219   token = strtok_r(tmp, ",", &tok_buf);
00220   while(token != NULL) {
00221     if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH))
00222       *value |= DIGEST_QOP_VALUE_AUTH;
00223     else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT))
00224       *value |= DIGEST_QOP_VALUE_AUTH_INT;
00225     else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF))
00226       *value |= DIGEST_QOP_VALUE_AUTH_CONF;
00227 
00228     token = strtok_r(NULL, ",", &tok_buf);
00229   }
00230 
00231   free(tmp);
00232 
00233   return CURLE_OK;
00234 }
00235 
00236 /*
00237  * auth_decode_digest_md5_message()
00238  *
00239  * This is used internally to decode an already encoded DIGEST-MD5 challenge
00240  * message into the seperate attributes.
00241  *
00242  * Parameters:
00243  *
00244  * chlg64  [in]     - The base64 encoded challenge message.
00245  * nonce   [in/out] - The buffer where the nonce will be stored.
00246  * nlen    [in]     - The length of the nonce buffer.
00247  * realm   [in/out] - The buffer where the realm will be stored.
00248  * rlen    [in]     - The length of the realm buffer.
00249  * alg     [in/out] - The buffer where the algorithm will be stored.
00250  * alen    [in]     - The length of the algorithm buffer.
00251  * qop     [in/out] - The buffer where the qop-options will be stored.
00252  * qlen    [in]     - The length of the qop buffer.
00253  *
00254  * Returns CURLE_OK on success.
00255  */
00256 static CURLcode auth_decode_digest_md5_message(const char *chlg64,
00257                                                char *nonce, size_t nlen,
00258                                                char *realm, size_t rlen,
00259                                                char *alg, size_t alen,
00260                                                char *qop, size_t qlen)
00261 {
00262   CURLcode result = CURLE_OK;
00263   unsigned char *chlg = NULL;
00264   size_t chlglen = 0;
00265   size_t chlg64len = strlen(chlg64);
00266 
00267   /* Decode the base-64 encoded challenge message */
00268   if(chlg64len && *chlg64 != '=') {
00269     result = Curl_base64_decode(chlg64, &chlg, &chlglen);
00270     if(result)
00271       return result;
00272   }
00273 
00274   /* Ensure we have a valid challenge message */
00275   if(!chlg)
00276     return CURLE_BAD_CONTENT_ENCODING;
00277 
00278   /* Retrieve nonce string from the challenge */
00279   if(!auth_digest_get_key_value((char *) chlg, "nonce=\"", nonce, nlen,
00280                                 '\"')) {
00281     free(chlg);
00282     return CURLE_BAD_CONTENT_ENCODING;
00283   }
00284 
00285   /* Retrieve realm string from the challenge */
00286   if(!auth_digest_get_key_value((char *) chlg, "realm=\"", realm, rlen,
00287                                 '\"')) {
00288     /* Challenge does not have a realm, set empty string [RFC2831] page 6 */
00289     strcpy(realm, "");
00290   }
00291 
00292   /* Retrieve algorithm string from the challenge */
00293   if(!auth_digest_get_key_value((char *) chlg, "algorithm=", alg, alen, ',')) {
00294     free(chlg);
00295     return CURLE_BAD_CONTENT_ENCODING;
00296   }
00297 
00298   /* Retrieve qop-options string from the challenge */
00299   if(!auth_digest_get_key_value((char *) chlg, "qop=\"", qop, qlen, '\"')) {
00300     free(chlg);
00301     return CURLE_BAD_CONTENT_ENCODING;
00302   }
00303 
00304   free(chlg);
00305 
00306   return CURLE_OK;
00307 }
00308 
00309 /*
00310  * Curl_auth_is_digest_supported()
00311  *
00312  * This is used to evaluate if DIGEST is supported.
00313  *
00314  * Parameters: None
00315  *
00316  * Returns TRUE as DIGEST as handled by libcurl.
00317  */
00318 bool Curl_auth_is_digest_supported(void)
00319 {
00320   return TRUE;
00321 }
00322 
00323 /*
00324  * Curl_auth_create_digest_md5_message()
00325  *
00326  * This is used to generate an already encoded DIGEST-MD5 response message
00327  * ready for sending to the recipient.
00328  *
00329  * Parameters:
00330  *
00331  * data    [in]     - The session handle.
00332  * chlg64  [in]     - The base64 encoded challenge message.
00333  * userp   [in]     - The user name.
00334  * passdwp [in]     - The user's password.
00335  * service [in]     - The service type such as http, smtp, pop or imap.
00336  * outptr  [in/out] - The address where a pointer to newly allocated memory
00337  *                    holding the result will be stored upon completion.
00338  * outlen  [out]    - The length of the output message.
00339  *
00340  * Returns CURLE_OK on success.
00341  */
00342 CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
00343                                              const char *chlg64,
00344                                              const char *userp,
00345                                              const char *passwdp,
00346                                              const char *service,
00347                                              char **outptr, size_t *outlen)
00348 {
00349   CURLcode result = CURLE_OK;
00350   size_t i;
00351   MD5_context *ctxt;
00352   char *response = NULL;
00353   unsigned char digest[MD5_DIGEST_LEN];
00354   char HA1_hex[2 * MD5_DIGEST_LEN + 1];
00355   char HA2_hex[2 * MD5_DIGEST_LEN + 1];
00356   char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
00357   char nonce[64];
00358   char realm[128];
00359   char algorithm[64];
00360   char qop_options[64];
00361   int qop_values;
00362   char cnonce[33];
00363   unsigned int entropy[4];
00364   char nonceCount[] = "00000001";
00365   char method[]     = "AUTHENTICATE";
00366   char qop[]        = DIGEST_QOP_VALUE_STRING_AUTH;
00367   char *spn         = NULL;
00368 
00369   /* Decode the challange message */
00370   result = auth_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
00371                                           realm, sizeof(realm),
00372                                           algorithm, sizeof(algorithm),
00373                                           qop_options, sizeof(qop_options));
00374   if(result)
00375     return result;
00376 
00377   /* We only support md5 sessions */
00378   if(strcmp(algorithm, "md5-sess") != 0)
00379     return CURLE_BAD_CONTENT_ENCODING;
00380 
00381   /* Get the qop-values from the qop-options */
00382   result = auth_digest_get_qop_values(qop_options, &qop_values);
00383   if(result)
00384     return result;
00385 
00386   /* We only support auth quality-of-protection */
00387   if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
00388     return CURLE_BAD_CONTENT_ENCODING;
00389 
00390   /* Generate 16 bytes of random data */
00391   result = Curl_rand(data, &entropy[0], 4);
00392   if(result)
00393     return result;
00394 
00395   /* Convert the random data into a 32 byte hex string */
00396   snprintf(cnonce, sizeof(cnonce), "%08x%08x%08x%08x",
00397            entropy[0], entropy[1], entropy[2], entropy[3]);
00398 
00399   /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
00400   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
00401   if(!ctxt)
00402     return CURLE_OUT_OF_MEMORY;
00403 
00404   Curl_MD5_update(ctxt, (const unsigned char *) userp,
00405                   curlx_uztoui(strlen(userp)));
00406   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
00407   Curl_MD5_update(ctxt, (const unsigned char *) realm,
00408                   curlx_uztoui(strlen(realm)));
00409   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
00410   Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
00411                   curlx_uztoui(strlen(passwdp)));
00412   Curl_MD5_final(ctxt, digest);
00413 
00414   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
00415   if(!ctxt)
00416     return CURLE_OUT_OF_MEMORY;
00417 
00418   Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
00419   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
00420   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
00421                   curlx_uztoui(strlen(nonce)));
00422   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
00423   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
00424                   curlx_uztoui(strlen(cnonce)));
00425   Curl_MD5_final(ctxt, digest);
00426 
00427   /* Convert calculated 16 octet hex into 32 bytes string */
00428   for(i = 0; i < MD5_DIGEST_LEN; i++)
00429     snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
00430 
00431   /* Generate our SPN */
00432   spn = Curl_auth_build_spn(service, realm, NULL);
00433   if(!spn)
00434     return CURLE_OUT_OF_MEMORY;
00435 
00436   /* Calculate H(A2) */
00437   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
00438   if(!ctxt) {
00439     free(spn);
00440 
00441     return CURLE_OUT_OF_MEMORY;
00442   }
00443 
00444   Curl_MD5_update(ctxt, (const unsigned char *) method,
00445                   curlx_uztoui(strlen(method)));
00446   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
00447   Curl_MD5_update(ctxt, (const unsigned char *) spn,
00448                   curlx_uztoui(strlen(spn)));
00449   Curl_MD5_final(ctxt, digest);
00450 
00451   for(i = 0; i < MD5_DIGEST_LEN; i++)
00452     snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
00453 
00454   /* Now calculate the response hash */
00455   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
00456   if(!ctxt) {
00457     free(spn);
00458 
00459     return CURLE_OUT_OF_MEMORY;
00460   }
00461 
00462   Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
00463   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
00464   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
00465                   curlx_uztoui(strlen(nonce)));
00466   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
00467 
00468   Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
00469                   curlx_uztoui(strlen(nonceCount)));
00470   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
00471   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
00472                   curlx_uztoui(strlen(cnonce)));
00473   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
00474   Curl_MD5_update(ctxt, (const unsigned char *) qop,
00475                   curlx_uztoui(strlen(qop)));
00476   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
00477 
00478   Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
00479   Curl_MD5_final(ctxt, digest);
00480 
00481   for(i = 0; i < MD5_DIGEST_LEN; i++)
00482     snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
00483 
00484   /* Generate the response */
00485   response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
00486                      "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
00487                      "qop=%s",
00488                      userp, realm, nonce,
00489                      cnonce, nonceCount, spn, resp_hash_hex, qop);
00490   free(spn);
00491   if(!response)
00492     return CURLE_OUT_OF_MEMORY;
00493 
00494   /* Base64 encode the response */
00495   result = Curl_base64_encode(data, response, 0, outptr, outlen);
00496 
00497   free(response);
00498 
00499   return result;
00500 }
00501 
00502 /*
00503  * Curl_auth_decode_digest_http_message()
00504  *
00505  * This is used to decode a HTTP DIGEST challenge message into the seperate
00506  * attributes.
00507  *
00508  * Parameters:
00509  *
00510  * chlg    [in]     - The challenge message.
00511  * digest  [in/out] - The digest data struct being used and modified.
00512  *
00513  * Returns CURLE_OK on success.
00514  */
00515 CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
00516                                               struct digestdata *digest)
00517 {
00518   bool before = FALSE; /* got a nonce before */
00519   bool foundAuth = FALSE;
00520   bool foundAuthInt = FALSE;
00521   char *token = NULL;
00522   char *tmp = NULL;
00523 
00524   /* If we already have received a nonce, keep that in mind */
00525   if(digest->nonce)
00526     before = TRUE;
00527 
00528   /* Clean up any former leftovers and initialise to defaults */
00529   Curl_auth_digest_cleanup(digest);
00530 
00531   for(;;) {
00532     char value[DIGEST_MAX_VALUE_LENGTH];
00533     char content[DIGEST_MAX_CONTENT_LENGTH];
00534 
00535     /* Pass all additional spaces here */
00536     while(*chlg && ISSPACE(*chlg))
00537       chlg++;
00538 
00539     /* Extract a value=content pair */
00540     if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
00541       if(strcasecompare(value, "nonce")) {
00542         free(digest->nonce);
00543         digest->nonce = strdup(content);
00544         if(!digest->nonce)
00545           return CURLE_OUT_OF_MEMORY;
00546       }
00547       else if(strcasecompare(value, "stale")) {
00548         if(strcasecompare(content, "true")) {
00549           digest->stale = TRUE;
00550           digest->nc = 1; /* we make a new nonce now */
00551         }
00552       }
00553       else if(strcasecompare(value, "realm")) {
00554         free(digest->realm);
00555         digest->realm = strdup(content);
00556         if(!digest->realm)
00557           return CURLE_OUT_OF_MEMORY;
00558       }
00559       else if(strcasecompare(value, "opaque")) {
00560         free(digest->opaque);
00561         digest->opaque = strdup(content);
00562         if(!digest->opaque)
00563           return CURLE_OUT_OF_MEMORY;
00564       }
00565       else if(strcasecompare(value, "qop")) {
00566         char *tok_buf;
00567         /* Tokenize the list and choose auth if possible, use a temporary
00568            clone of the buffer since strtok_r() ruins it */
00569         tmp = strdup(content);
00570         if(!tmp)
00571           return CURLE_OUT_OF_MEMORY;
00572 
00573         token = strtok_r(tmp, ",", &tok_buf);
00574         while(token != NULL) {
00575           if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
00576             foundAuth = TRUE;
00577           }
00578           else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
00579             foundAuthInt = TRUE;
00580           }
00581           token = strtok_r(NULL, ",", &tok_buf);
00582         }
00583 
00584         free(tmp);
00585 
00586         /* Select only auth or auth-int. Otherwise, ignore */
00587         if(foundAuth) {
00588           free(digest->qop);
00589           digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
00590           if(!digest->qop)
00591             return CURLE_OUT_OF_MEMORY;
00592         }
00593         else if(foundAuthInt) {
00594           free(digest->qop);
00595           digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
00596           if(!digest->qop)
00597             return CURLE_OUT_OF_MEMORY;
00598         }
00599       }
00600       else if(strcasecompare(value, "algorithm")) {
00601         free(digest->algorithm);
00602         digest->algorithm = strdup(content);
00603         if(!digest->algorithm)
00604           return CURLE_OUT_OF_MEMORY;
00605 
00606         if(strcasecompare(content, "MD5-sess"))
00607           digest->algo = CURLDIGESTALGO_MD5SESS;
00608         else if(strcasecompare(content, "MD5"))
00609           digest->algo = CURLDIGESTALGO_MD5;
00610         else
00611           return CURLE_BAD_CONTENT_ENCODING;
00612       }
00613       else {
00614         /* Unknown specifier, ignore it! */
00615       }
00616     }
00617     else
00618       break; /* We're done here */
00619 
00620     /* Pass all additional spaces here */
00621     while(*chlg && ISSPACE(*chlg))
00622       chlg++;
00623 
00624     /* Allow the list to be comma-separated */
00625     if(',' == *chlg)
00626       chlg++;
00627   }
00628 
00629   /* We had a nonce since before, and we got another one now without
00630      'stale=true'. This means we provided bad credentials in the previous
00631      request */
00632   if(before && !digest->stale)
00633     return CURLE_BAD_CONTENT_ENCODING;
00634 
00635   /* We got this header without a nonce, that's a bad Digest line! */
00636   if(!digest->nonce)
00637     return CURLE_BAD_CONTENT_ENCODING;
00638 
00639   return CURLE_OK;
00640 }
00641 
00642 /*
00643  * Curl_auth_create_digest_http_message()
00644  *
00645  * This is used to generate a HTTP DIGEST response message ready for sending
00646  * to the recipient.
00647  *
00648  * Parameters:
00649  *
00650  * data    [in]     - The session handle.
00651  * userp   [in]     - The user name.
00652  * passdwp [in]     - The user's password.
00653  * request [in]     - The HTTP request.
00654  * uripath [in]     - The path of the HTTP uri.
00655  * digest  [in/out] - The digest data struct being used and modified.
00656  * outptr  [in/out] - The address where a pointer to newly allocated memory
00657  *                    holding the result will be stored upon completion.
00658  * outlen  [out]    - The length of the output message.
00659  *
00660  * Returns CURLE_OK on success.
00661  */
00662 CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
00663                                               const char *userp,
00664                                               const char *passwdp,
00665                                               const unsigned char *request,
00666                                               const unsigned char *uripath,
00667                                               struct digestdata *digest,
00668                                               char **outptr, size_t *outlen)
00669 {
00670   CURLcode result;
00671   unsigned char md5buf[16]; /* 16 bytes/128 bits */
00672   unsigned char request_digest[33];
00673   unsigned char *md5this;
00674   unsigned char ha1[33];    /* 32 digits and 1 zero byte */
00675   unsigned char ha2[33];    /* 32 digits and 1 zero byte */
00676   char cnoncebuf[33];
00677   char *cnonce = NULL;
00678   size_t cnonce_sz = 0;
00679   char *userp_quoted;
00680   char *response = NULL;
00681   char *tmp = NULL;
00682 
00683   if(!digest->nc)
00684     digest->nc = 1;
00685 
00686   if(!digest->cnonce) {
00687     unsigned int rnd[4];
00688     result = Curl_rand(data, &rnd[0], 4);
00689     if(result)
00690       return result;
00691     snprintf(cnoncebuf, sizeof(cnoncebuf), "%08x%08x%08x%08x",
00692              rnd[0], rnd[1], rnd[2], rnd[3]);
00693 
00694     result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
00695                                 &cnonce, &cnonce_sz);
00696     if(result)
00697       return result;
00698 
00699     digest->cnonce = cnonce;
00700   }
00701 
00702   /*
00703     If the algorithm is "MD5" or unspecified (which then defaults to MD5):
00704 
00705       A1 = unq(username-value) ":" unq(realm-value) ":" passwd
00706 
00707     If the algorithm is "MD5-sess" then:
00708 
00709       A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":"
00710            unq(nonce-value) ":" unq(cnonce-value)
00711   */
00712 
00713   md5this = (unsigned char *)
00714     aprintf("%s:%s:%s", userp, digest->realm, passwdp);
00715   if(!md5this)
00716     return CURLE_OUT_OF_MEMORY;
00717 
00718   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
00719   Curl_md5it(md5buf, md5this);
00720   free(md5this);
00721   auth_digest_md5_to_ascii(md5buf, ha1);
00722 
00723   if(digest->algo == CURLDIGESTALGO_MD5SESS) {
00724     /* nonce and cnonce are OUTSIDE the hash */
00725     tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
00726     if(!tmp)
00727       return CURLE_OUT_OF_MEMORY;
00728 
00729     CURL_OUTPUT_DIGEST_CONV(data, tmp); /* Convert on non-ASCII machines */
00730     Curl_md5it(md5buf, (unsigned char *) tmp);
00731     free(tmp);
00732     auth_digest_md5_to_ascii(md5buf, ha1);
00733   }
00734 
00735   /*
00736     If the "qop" directive's value is "auth" or is unspecified, then A2 is:
00737 
00738       A2 = Method ":" digest-uri-value
00739 
00740     If the "qop" value is "auth-int", then A2 is:
00741 
00742       A2 = Method ":" digest-uri-value ":" H(entity-body)
00743 
00744     (The "Method" value is the HTTP request method as specified in section
00745     5.1.1 of RFC 2616)
00746   */
00747 
00748   md5this = (unsigned char *) aprintf("%s:%s", request, uripath);
00749 
00750   if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
00751     /* We don't support auth-int for PUT or POST at the moment.
00752        TODO: replace md5 of empty string with entity-body for PUT/POST */
00753     unsigned char *md5this2 = (unsigned char *)
00754       aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e");
00755     free(md5this);
00756     md5this = md5this2;
00757   }
00758 
00759   if(!md5this)
00760     return CURLE_OUT_OF_MEMORY;
00761 
00762   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
00763   Curl_md5it(md5buf, md5this);
00764   free(md5this);
00765   auth_digest_md5_to_ascii(md5buf, ha2);
00766 
00767   if(digest->qop) {
00768     md5this = (unsigned char *) aprintf("%s:%s:%08x:%s:%s:%s",
00769                                         ha1,
00770                                         digest->nonce,
00771                                         digest->nc,
00772                                         digest->cnonce,
00773                                         digest->qop,
00774                                         ha2);
00775   }
00776   else {
00777     md5this = (unsigned char *) aprintf("%s:%s:%s",
00778                                         ha1,
00779                                         digest->nonce,
00780                                         ha2);
00781   }
00782 
00783   if(!md5this)
00784     return CURLE_OUT_OF_MEMORY;
00785 
00786   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
00787   Curl_md5it(md5buf, md5this);
00788   free(md5this);
00789   auth_digest_md5_to_ascii(md5buf, request_digest);
00790 
00791   /* For test case 64 (snooped from a Mozilla 1.3a request)
00792 
00793      Authorization: Digest username="testuser", realm="testrealm", \
00794      nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
00795 
00796      Digest parameters are all quoted strings.  Username which is provided by
00797      the user will need double quotes and backslashes within it escaped.  For
00798      the other fields, this shouldn't be an issue.  realm, nonce, and opaque
00799      are copied as is from the server, escapes and all.  cnonce is generated
00800      with web-safe characters.  uri is already percent encoded.  nc is 8 hex
00801      characters.  algorithm and qop with standard values only contain web-safe
00802      characters.
00803   */
00804   userp_quoted = auth_digest_string_quoted(userp);
00805   if(!userp_quoted)
00806     return CURLE_OUT_OF_MEMORY;
00807 
00808   if(digest->qop) {
00809     response = aprintf("username=\"%s\", "
00810                        "realm=\"%s\", "
00811                        "nonce=\"%s\", "
00812                        "uri=\"%s\", "
00813                        "cnonce=\"%s\", "
00814                        "nc=%08x, "
00815                        "qop=%s, "
00816                        "response=\"%s\"",
00817                        userp_quoted,
00818                        digest->realm,
00819                        digest->nonce,
00820                        uripath,
00821                        digest->cnonce,
00822                        digest->nc,
00823                        digest->qop,
00824                        request_digest);
00825 
00826     if(strcasecompare(digest->qop, "auth"))
00827       digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0
00828                        padded which tells to the server how many times you are
00829                        using the same nonce in the qop=auth mode */
00830   }
00831   else {
00832     response = aprintf("username=\"%s\", "
00833                        "realm=\"%s\", "
00834                        "nonce=\"%s\", "
00835                        "uri=\"%s\", "
00836                        "response=\"%s\"",
00837                        userp_quoted,
00838                        digest->realm,
00839                        digest->nonce,
00840                        uripath,
00841                        request_digest);
00842   }
00843   free(userp_quoted);
00844   if(!response)
00845     return CURLE_OUT_OF_MEMORY;
00846 
00847   /* Add the optional fields */
00848   if(digest->opaque) {
00849     /* Append the opaque */
00850     tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
00851     free(response);
00852     if(!tmp)
00853       return CURLE_OUT_OF_MEMORY;
00854 
00855     response = tmp;
00856   }
00857 
00858   if(digest->algorithm) {
00859     /* Append the algorithm */
00860     tmp = aprintf("%s, algorithm=\"%s\"", response, digest->algorithm);
00861     free(response);
00862     if(!tmp)
00863       return CURLE_OUT_OF_MEMORY;
00864 
00865     response = tmp;
00866   }
00867 
00868   /* Return the output */
00869   *outptr = response;
00870   *outlen = strlen(response);
00871 
00872   return CURLE_OK;
00873 }
00874 
00875 /*
00876  * Curl_auth_digest_cleanup()
00877  *
00878  * This is used to clean up the digest specific data.
00879  *
00880  * Parameters:
00881  *
00882  * digest    [in/out] - The digest data struct being cleaned up.
00883  *
00884  */
00885 void Curl_auth_digest_cleanup(struct digestdata *digest)
00886 {
00887   Curl_safefree(digest->nonce);
00888   Curl_safefree(digest->cnonce);
00889   Curl_safefree(digest->realm);
00890   Curl_safefree(digest->opaque);
00891   Curl_safefree(digest->qop);
00892   Curl_safefree(digest->algorithm);
00893 
00894   digest->nc = 0;
00895   digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
00896   digest->stale = FALSE; /* default means normal, not stale */
00897 }
00898 #endif  /* !USE_WINDOWS_SSPI */
00899 
00900 #endif  /* CURL_DISABLE_CRYPTO_AUTH */


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:02