00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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"
00042 #include "curl_printf.h"
00043 #include "rand.h"
00044
00045
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
00059
00060
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
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
00083 return FALSE;
00084
00085 if('\"' == *str) {
00086
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
00096 escape = TRUE;
00097 *content++ = '\\';
00098
00099 continue;
00100 }
00101 break;
00102
00103 case ',':
00104 if(!starts_with_quote) {
00105
00106
00107 c = 0;
00108 continue;
00109 }
00110 break;
00111
00112 case '\r':
00113 case '\n':
00114
00115 c = 0;
00116 continue;
00117
00118 case '\"':
00119 if(!escape && starts_with_quote) {
00120
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
00139 static void auth_digest_md5_to_ascii(unsigned char *source,
00140 unsigned char *dest)
00141 {
00142 int i;
00143 for(i = 0; i < 16; i++)
00144 snprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
00145 }
00146
00147
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;
00153
00154
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
00180
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
00211 *value = 0;
00212
00213
00214
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
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
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
00268 if(chlg64len && *chlg64 != '=') {
00269 result = Curl_base64_decode(chlg64, &chlg, &chlglen);
00270 if(result)
00271 return result;
00272 }
00273
00274
00275 if(!chlg)
00276 return CURLE_BAD_CONTENT_ENCODING;
00277
00278
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
00286 if(!auth_digest_get_key_value((char *) chlg, "realm=\"", realm, rlen,
00287 '\"')) {
00288
00289 strcpy(realm, "");
00290 }
00291
00292
00293 if(!auth_digest_get_key_value((char *) chlg, "algorithm=", alg, alen, ',')) {
00294 free(chlg);
00295 return CURLE_BAD_CONTENT_ENCODING;
00296 }
00297
00298
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
00311
00312
00313
00314
00315
00316
00317
00318 bool Curl_auth_is_digest_supported(void)
00319 {
00320 return TRUE;
00321 }
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
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
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
00378 if(strcmp(algorithm, "md5-sess") != 0)
00379 return CURLE_BAD_CONTENT_ENCODING;
00380
00381
00382 result = auth_digest_get_qop_values(qop_options, &qop_values);
00383 if(result)
00384 return result;
00385
00386
00387 if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
00388 return CURLE_BAD_CONTENT_ENCODING;
00389
00390
00391 result = Curl_rand(data, &entropy[0], 4);
00392 if(result)
00393 return result;
00394
00395
00396 snprintf(cnonce, sizeof(cnonce), "%08x%08x%08x%08x",
00397 entropy[0], entropy[1], entropy[2], entropy[3]);
00398
00399
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
00428 for(i = 0; i < MD5_DIGEST_LEN; i++)
00429 snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
00430
00431
00432 spn = Curl_auth_build_spn(service, realm, NULL);
00433 if(!spn)
00434 return CURLE_OUT_OF_MEMORY;
00435
00436
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
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
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
00495 result = Curl_base64_encode(data, response, 0, outptr, outlen);
00496
00497 free(response);
00498
00499 return result;
00500 }
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515 CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
00516 struct digestdata *digest)
00517 {
00518 bool before = FALSE;
00519 bool foundAuth = FALSE;
00520 bool foundAuthInt = FALSE;
00521 char *token = NULL;
00522 char *tmp = NULL;
00523
00524
00525 if(digest->nonce)
00526 before = TRUE;
00527
00528
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
00536 while(*chlg && ISSPACE(*chlg))
00537 chlg++;
00538
00539
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;
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
00568
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
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
00615 }
00616 }
00617 else
00618 break;
00619
00620
00621 while(*chlg && ISSPACE(*chlg))
00622 chlg++;
00623
00624
00625 if(',' == *chlg)
00626 chlg++;
00627 }
00628
00629
00630
00631
00632 if(before && !digest->stale)
00633 return CURLE_BAD_CONTENT_ENCODING;
00634
00635
00636 if(!digest->nonce)
00637 return CURLE_BAD_CONTENT_ENCODING;
00638
00639 return CURLE_OK;
00640 }
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
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];
00672 unsigned char request_digest[33];
00673 unsigned char *md5this;
00674 unsigned char ha1[33];
00675 unsigned char ha2[33];
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
00704
00705
00706
00707
00708
00709
00710
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);
00719 Curl_md5it(md5buf, md5this);
00720 free(md5this);
00721 auth_digest_md5_to_ascii(md5buf, ha1);
00722
00723 if(digest->algo == CURLDIGESTALGO_MD5SESS) {
00724
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);
00730 Curl_md5it(md5buf, (unsigned char *) tmp);
00731 free(tmp);
00732 auth_digest_md5_to_ascii(md5buf, ha1);
00733 }
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748 md5this = (unsigned char *) aprintf("%s:%s", request, uripath);
00749
00750 if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
00751
00752
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);
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);
00787 Curl_md5it(md5buf, md5this);
00788 free(md5this);
00789 auth_digest_md5_to_ascii(md5buf, request_digest);
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
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++;
00828
00829
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
00848 if(digest->opaque) {
00849
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
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
00869 *outptr = response;
00870 *outlen = strlen(response);
00871
00872 return CURLE_OK;
00873 }
00874
00875
00876
00877
00878
00879
00880
00881
00882
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;
00896 digest->stale = FALSE;
00897 }
00898 #endif
00899
00900 #endif