Home | History | Annotate | Download | only in vauth
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme (at) hotmail.com>.
      9  * Copyright (C) 2015 - 2019, Daniel Stenberg, <daniel (at) haxx.se>, et al.
     10  *
     11  * This software is licensed as described in the file COPYING, which
     12  * you should have received as part of this distribution. The terms
     13  * are also available at https://curl.haxx.se/docs/copyright.html.
     14  *
     15  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     16  * copies of the Software, and permit persons to whom the Software is
     17  * furnished to do so, under the terms of the COPYING file.
     18  *
     19  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     20  * KIND, either express or implied.
     21  *
     22  * RFC2831 DIGEST-MD5 authentication
     23  *
     24  ***************************************************************************/
     25 
     26 #include "curl_setup.h"
     27 
     28 #if defined(USE_WINDOWS_SSPI) && !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 "warnless.h"
     37 #include "curl_multibyte.h"
     38 #include "sendf.h"
     39 #include "strdup.h"
     40 #include "strcase.h"
     41 
     42 /* The last #include files should be: */
     43 #include "curl_memory.h"
     44 #include "memdebug.h"
     45 
     46 /*
     47 * Curl_auth_is_digest_supported()
     48 *
     49 * This is used to evaluate if DIGEST is supported.
     50 *
     51 * Parameters: None
     52 *
     53 * Returns TRUE if DIGEST is supported by Windows SSPI.
     54 */
     55 bool Curl_auth_is_digest_supported(void)
     56 {
     57   PSecPkgInfo SecurityPackage;
     58   SECURITY_STATUS status;
     59 
     60   /* Query the security package for Digest */
     61   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
     62                                               &SecurityPackage);
     63 
     64   return (status == SEC_E_OK ? TRUE : FALSE);
     65 }
     66 
     67 /*
     68  * Curl_auth_create_digest_md5_message()
     69  *
     70  * This is used to generate an already encoded DIGEST-MD5 response message
     71  * ready for sending to the recipient.
     72  *
     73  * Parameters:
     74  *
     75  * data    [in]     - The session handle.
     76  * chlg64  [in]     - The base64 encoded challenge message.
     77  * userp   [in]     - The user name in the format User or Domain\User.
     78  * passwdp [in]     - The user's password.
     79  * service [in]     - The service type such as http, smtp, pop or imap.
     80  * outptr  [in/out] - The address where a pointer to newly allocated memory
     81  *                    holding the result will be stored upon completion.
     82  * outlen  [out]    - The length of the output message.
     83  *
     84  * Returns CURLE_OK on success.
     85  */
     86 CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
     87                                              const char *chlg64,
     88                                              const char *userp,
     89                                              const char *passwdp,
     90                                              const char *service,
     91                                              char **outptr, size_t *outlen)
     92 {
     93   CURLcode result = CURLE_OK;
     94   TCHAR *spn = NULL;
     95   size_t chlglen = 0;
     96   size_t token_max = 0;
     97   unsigned char *input_token = NULL;
     98   unsigned char *output_token = NULL;
     99   CredHandle credentials;
    100   CtxtHandle context;
    101   PSecPkgInfo SecurityPackage;
    102   SEC_WINNT_AUTH_IDENTITY identity;
    103   SEC_WINNT_AUTH_IDENTITY *p_identity;
    104   SecBuffer chlg_buf;
    105   SecBuffer resp_buf;
    106   SecBufferDesc chlg_desc;
    107   SecBufferDesc resp_desc;
    108   SECURITY_STATUS status;
    109   unsigned long attrs;
    110   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
    111 
    112   /* Decode the base-64 encoded challenge message */
    113   if(strlen(chlg64) && *chlg64 != '=') {
    114     result = Curl_base64_decode(chlg64, &input_token, &chlglen);
    115     if(result)
    116       return result;
    117   }
    118 
    119   /* Ensure we have a valid challenge message */
    120   if(!input_token) {
    121     infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n");
    122 
    123     return CURLE_BAD_CONTENT_ENCODING;
    124   }
    125 
    126   /* Query the security package for DigestSSP */
    127   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
    128                                               &SecurityPackage);
    129   if(status != SEC_E_OK) {
    130     free(input_token);
    131 
    132     return CURLE_NOT_BUILT_IN;
    133   }
    134 
    135   token_max = SecurityPackage->cbMaxToken;
    136 
    137   /* Release the package buffer as it is not required anymore */
    138   s_pSecFn->FreeContextBuffer(SecurityPackage);
    139 
    140   /* Allocate our response buffer */
    141   output_token = malloc(token_max);
    142   if(!output_token) {
    143     free(input_token);
    144 
    145     return CURLE_OUT_OF_MEMORY;
    146   }
    147 
    148   /* Generate our SPN */
    149   spn = Curl_auth_build_spn(service, data->conn->host.name, NULL);
    150   if(!spn) {
    151     free(output_token);
    152     free(input_token);
    153 
    154     return CURLE_OUT_OF_MEMORY;
    155   }
    156 
    157   if(userp && *userp) {
    158     /* Populate our identity structure */
    159     result = Curl_create_sspi_identity(userp, passwdp, &identity);
    160     if(result) {
    161       free(spn);
    162       free(output_token);
    163       free(input_token);
    164 
    165       return result;
    166     }
    167 
    168     /* Allow proper cleanup of the identity structure */
    169     p_identity = &identity;
    170   }
    171   else
    172     /* Use the current Windows user */
    173     p_identity = NULL;
    174 
    175   /* Acquire our credentials handle */
    176   status = s_pSecFn->AcquireCredentialsHandle(NULL,
    177                                               (TCHAR *) TEXT(SP_NAME_DIGEST),
    178                                               SECPKG_CRED_OUTBOUND, NULL,
    179                                               p_identity, NULL, NULL,
    180                                               &credentials, &expiry);
    181 
    182   if(status != SEC_E_OK) {
    183     Curl_sspi_free_identity(p_identity);
    184     free(spn);
    185     free(output_token);
    186     free(input_token);
    187 
    188     return CURLE_LOGIN_DENIED;
    189   }
    190 
    191   /* Setup the challenge "input" security buffer */
    192   chlg_desc.ulVersion = SECBUFFER_VERSION;
    193   chlg_desc.cBuffers  = 1;
    194   chlg_desc.pBuffers  = &chlg_buf;
    195   chlg_buf.BufferType = SECBUFFER_TOKEN;
    196   chlg_buf.pvBuffer   = input_token;
    197   chlg_buf.cbBuffer   = curlx_uztoul(chlglen);
    198 
    199   /* Setup the response "output" security buffer */
    200   resp_desc.ulVersion = SECBUFFER_VERSION;
    201   resp_desc.cBuffers  = 1;
    202   resp_desc.pBuffers  = &resp_buf;
    203   resp_buf.BufferType = SECBUFFER_TOKEN;
    204   resp_buf.pvBuffer   = output_token;
    205   resp_buf.cbBuffer   = curlx_uztoul(token_max);
    206 
    207   /* Generate our response message */
    208   status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn,
    209                                                0, 0, 0, &chlg_desc, 0,
    210                                                &context, &resp_desc, &attrs,
    211                                                &expiry);
    212 
    213   if(status == SEC_I_COMPLETE_NEEDED ||
    214      status == SEC_I_COMPLETE_AND_CONTINUE)
    215     s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
    216   else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
    217     s_pSecFn->FreeCredentialsHandle(&credentials);
    218     Curl_sspi_free_identity(p_identity);
    219     free(spn);
    220     free(output_token);
    221     free(input_token);
    222 
    223     return CURLE_RECV_ERROR;
    224   }
    225 
    226   /* Base64 encode the response */
    227   result = Curl_base64_encode(data, (char *) output_token, resp_buf.cbBuffer,
    228                               outptr, outlen);
    229 
    230   /* Free our handles */
    231   s_pSecFn->DeleteSecurityContext(&context);
    232   s_pSecFn->FreeCredentialsHandle(&credentials);
    233 
    234   /* Free the identity structure */
    235   Curl_sspi_free_identity(p_identity);
    236 
    237   /* Free the SPN */
    238   free(spn);
    239 
    240   /* Free the response buffer */
    241   free(output_token);
    242 
    243   /* Free the decoded challenge message */
    244   free(input_token);
    245 
    246   return result;
    247 }
    248 
    249 /*
    250  * Curl_override_sspi_http_realm()
    251  *
    252  * This is used to populate the domain in a SSPI identity structure
    253  * The realm is extracted from the challenge message and used as the
    254  * domain if it is not already explicitly set.
    255  *
    256  * Parameters:
    257  *
    258  * chlg     [in]     - The challenge message.
    259  * identity [in/out] - The identity structure.
    260  *
    261  * Returns CURLE_OK on success.
    262  */
    263 CURLcode Curl_override_sspi_http_realm(const char *chlg,
    264                                        SEC_WINNT_AUTH_IDENTITY *identity)
    265 {
    266   xcharp_u domain, dup_domain;
    267 
    268   /* If domain is blank or unset, check challenge message for realm */
    269   if(!identity->Domain || !identity->DomainLength) {
    270     for(;;) {
    271       char value[DIGEST_MAX_VALUE_LENGTH];
    272       char content[DIGEST_MAX_CONTENT_LENGTH];
    273 
    274       /* Pass all additional spaces here */
    275       while(*chlg && ISSPACE(*chlg))
    276         chlg++;
    277 
    278       /* Extract a value=content pair */
    279       if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
    280         if(strcasecompare(value, "realm")) {
    281 
    282           /* Setup identity's domain and length */
    283           domain.tchar_ptr = Curl_convert_UTF8_to_tchar((char *) content);
    284           if(!domain.tchar_ptr)
    285             return CURLE_OUT_OF_MEMORY;
    286 
    287           dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr);
    288           if(!dup_domain.tchar_ptr) {
    289             Curl_unicodefree(domain.tchar_ptr);
    290             return CURLE_OUT_OF_MEMORY;
    291           }
    292 
    293           free(identity->Domain);
    294           identity->Domain = dup_domain.tbyte_ptr;
    295           identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr));
    296           dup_domain.tchar_ptr = NULL;
    297 
    298           Curl_unicodefree(domain.tchar_ptr);
    299         }
    300         else {
    301           /* Unknown specifier, ignore it! */
    302         }
    303       }
    304       else
    305         break; /* We're done here */
    306 
    307       /* Pass all additional spaces here */
    308       while(*chlg && ISSPACE(*chlg))
    309         chlg++;
    310 
    311       /* Allow the list to be comma-separated */
    312       if(',' == *chlg)
    313         chlg++;
    314     }
    315   }
    316 
    317   return CURLE_OK;
    318 }
    319 
    320 /*
    321  * Curl_auth_decode_digest_http_message()
    322  *
    323  * This is used to decode a HTTP DIGEST challenge message into the separate
    324  * attributes.
    325  *
    326  * Parameters:
    327  *
    328  * chlg    [in]     - The challenge message.
    329  * digest  [in/out] - The digest data struct being used and modified.
    330  *
    331  * Returns CURLE_OK on success.
    332  */
    333 CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
    334                                               struct digestdata *digest)
    335 {
    336   size_t chlglen = strlen(chlg);
    337 
    338   /* We had an input token before so if there's another one now that means we
    339      provided bad credentials in the previous request or it's stale. */
    340   if(digest->input_token) {
    341     bool stale = false;
    342     const char *p = chlg;
    343 
    344     /* Check for the 'stale' directive */
    345     for(;;) {
    346       char value[DIGEST_MAX_VALUE_LENGTH];
    347       char content[DIGEST_MAX_CONTENT_LENGTH];
    348 
    349       while(*p && ISSPACE(*p))
    350         p++;
    351 
    352       if(!Curl_auth_digest_get_pair(p, value, content, &p))
    353         break;
    354 
    355       if(strcasecompare(value, "stale") &&
    356          strcasecompare(content, "true")) {
    357         stale = true;
    358         break;
    359       }
    360 
    361       while(*p && ISSPACE(*p))
    362         p++;
    363 
    364       if(',' == *p)
    365         p++;
    366     }
    367 
    368     if(stale)
    369       Curl_auth_digest_cleanup(digest);
    370     else
    371       return CURLE_LOGIN_DENIED;
    372   }
    373 
    374   /* Store the challenge for use later */
    375   digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen + 1);
    376   if(!digest->input_token)
    377     return CURLE_OUT_OF_MEMORY;
    378 
    379   digest->input_token_len = chlglen;
    380 
    381   return CURLE_OK;
    382 }
    383 
    384 /*
    385  * Curl_auth_create_digest_http_message()
    386  *
    387  * This is used to generate a HTTP DIGEST response message ready for sending
    388  * to the recipient.
    389  *
    390  * Parameters:
    391  *
    392  * data    [in]     - The session handle.
    393  * userp   [in]     - The user name in the format User or Domain\User.
    394  * passwdp [in]     - The user's password.
    395  * request [in]     - The HTTP request.
    396  * uripath [in]     - The path of the HTTP uri.
    397  * digest  [in/out] - The digest data struct being used and modified.
    398  * outptr  [in/out] - The address where a pointer to newly allocated memory
    399  *                    holding the result will be stored upon completion.
    400  * outlen  [out]    - The length of the output message.
    401  *
    402  * Returns CURLE_OK on success.
    403  */
    404 CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
    405                                               const char *userp,
    406                                               const char *passwdp,
    407                                               const unsigned char *request,
    408                                               const unsigned char *uripath,
    409                                               struct digestdata *digest,
    410                                               char **outptr, size_t *outlen)
    411 {
    412   size_t token_max;
    413   char *resp;
    414   BYTE *output_token;
    415   size_t output_token_len = 0;
    416   PSecPkgInfo SecurityPackage;
    417   SecBuffer chlg_buf[5];
    418   SecBufferDesc chlg_desc;
    419   SECURITY_STATUS status;
    420 
    421   (void) data;
    422 
    423   /* Query the security package for DigestSSP */
    424   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
    425                                               &SecurityPackage);
    426   if(status != SEC_E_OK)
    427     return CURLE_NOT_BUILT_IN;
    428 
    429   token_max = SecurityPackage->cbMaxToken;
    430 
    431   /* Release the package buffer as it is not required anymore */
    432   s_pSecFn->FreeContextBuffer(SecurityPackage);
    433 
    434   /* Allocate the output buffer according to the max token size as indicated
    435      by the security package */
    436   output_token = malloc(token_max);
    437   if(!output_token) {
    438     return CURLE_OUT_OF_MEMORY;
    439   }
    440 
    441   /* If the user/passwd that was used to make the identity for http_context
    442      has changed then delete that context. */
    443   if((userp && !digest->user) || (!userp && digest->user) ||
    444      (passwdp && !digest->passwd) || (!passwdp && digest->passwd) ||
    445      (userp && digest->user && strcmp(userp, digest->user)) ||
    446      (passwdp && digest->passwd && strcmp(passwdp, digest->passwd))) {
    447     if(digest->http_context) {
    448       s_pSecFn->DeleteSecurityContext(digest->http_context);
    449       Curl_safefree(digest->http_context);
    450     }
    451     Curl_safefree(digest->user);
    452     Curl_safefree(digest->passwd);
    453   }
    454 
    455   if(digest->http_context) {
    456     chlg_desc.ulVersion    = SECBUFFER_VERSION;
    457     chlg_desc.cBuffers     = 5;
    458     chlg_desc.pBuffers     = chlg_buf;
    459     chlg_buf[0].BufferType = SECBUFFER_TOKEN;
    460     chlg_buf[0].pvBuffer   = NULL;
    461     chlg_buf[0].cbBuffer   = 0;
    462     chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
    463     chlg_buf[1].pvBuffer   = (void *) request;
    464     chlg_buf[1].cbBuffer   = curlx_uztoul(strlen((const char *) request));
    465     chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
    466     chlg_buf[2].pvBuffer   = (void *) uripath;
    467     chlg_buf[2].cbBuffer   = curlx_uztoul(strlen((const char *) uripath));
    468     chlg_buf[3].BufferType = SECBUFFER_PKG_PARAMS;
    469     chlg_buf[3].pvBuffer   = NULL;
    470     chlg_buf[3].cbBuffer   = 0;
    471     chlg_buf[4].BufferType = SECBUFFER_PADDING;
    472     chlg_buf[4].pvBuffer   = output_token;
    473     chlg_buf[4].cbBuffer   = curlx_uztoul(token_max);
    474 
    475     status = s_pSecFn->MakeSignature(digest->http_context, 0, &chlg_desc, 0);
    476     if(status == SEC_E_OK)
    477       output_token_len = chlg_buf[4].cbBuffer;
    478     else { /* delete the context so a new one can be made */
    479       infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx\n",
    480             (long)status);
    481       s_pSecFn->DeleteSecurityContext(digest->http_context);
    482       Curl_safefree(digest->http_context);
    483     }
    484   }
    485 
    486   if(!digest->http_context) {
    487     CredHandle credentials;
    488     SEC_WINNT_AUTH_IDENTITY identity;
    489     SEC_WINNT_AUTH_IDENTITY *p_identity;
    490     SecBuffer resp_buf;
    491     SecBufferDesc resp_desc;
    492     unsigned long attrs;
    493     TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
    494     TCHAR *spn;
    495 
    496     /* free the copy of user/passwd used to make the previous identity */
    497     Curl_safefree(digest->user);
    498     Curl_safefree(digest->passwd);
    499 
    500     if(userp && *userp) {
    501       /* Populate our identity structure */
    502       if(Curl_create_sspi_identity(userp, passwdp, &identity)) {
    503         free(output_token);
    504         return CURLE_OUT_OF_MEMORY;
    505       }
    506 
    507       /* Populate our identity domain */
    508       if(Curl_override_sspi_http_realm((const char *) digest->input_token,
    509                                        &identity)) {
    510         free(output_token);
    511         return CURLE_OUT_OF_MEMORY;
    512       }
    513 
    514       /* Allow proper cleanup of the identity structure */
    515       p_identity = &identity;
    516     }
    517     else
    518       /* Use the current Windows user */
    519       p_identity = NULL;
    520 
    521     if(userp) {
    522       digest->user = strdup(userp);
    523 
    524       if(!digest->user) {
    525         free(output_token);
    526         return CURLE_OUT_OF_MEMORY;
    527       }
    528     }
    529 
    530     if(passwdp) {
    531       digest->passwd = strdup(passwdp);
    532 
    533       if(!digest->passwd) {
    534         free(output_token);
    535         Curl_safefree(digest->user);
    536         return CURLE_OUT_OF_MEMORY;
    537       }
    538     }
    539 
    540     /* Acquire our credentials handle */
    541     status = s_pSecFn->AcquireCredentialsHandle(NULL,
    542                                                 (TCHAR *) TEXT(SP_NAME_DIGEST),
    543                                                 SECPKG_CRED_OUTBOUND, NULL,
    544                                                 p_identity, NULL, NULL,
    545                                                 &credentials, &expiry);
    546     if(status != SEC_E_OK) {
    547       Curl_sspi_free_identity(p_identity);
    548       free(output_token);
    549 
    550       return CURLE_LOGIN_DENIED;
    551     }
    552 
    553     /* Setup the challenge "input" security buffer if present */
    554     chlg_desc.ulVersion    = SECBUFFER_VERSION;
    555     chlg_desc.cBuffers     = 3;
    556     chlg_desc.pBuffers     = chlg_buf;
    557     chlg_buf[0].BufferType = SECBUFFER_TOKEN;
    558     chlg_buf[0].pvBuffer   = digest->input_token;
    559     chlg_buf[0].cbBuffer   = curlx_uztoul(digest->input_token_len);
    560     chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
    561     chlg_buf[1].pvBuffer   = (void *) request;
    562     chlg_buf[1].cbBuffer   = curlx_uztoul(strlen((const char *) request));
    563     chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
    564     chlg_buf[2].pvBuffer   = NULL;
    565     chlg_buf[2].cbBuffer   = 0;
    566 
    567     /* Setup the response "output" security buffer */
    568     resp_desc.ulVersion = SECBUFFER_VERSION;
    569     resp_desc.cBuffers  = 1;
    570     resp_desc.pBuffers  = &resp_buf;
    571     resp_buf.BufferType = SECBUFFER_TOKEN;
    572     resp_buf.pvBuffer   = output_token;
    573     resp_buf.cbBuffer   = curlx_uztoul(token_max);
    574 
    575     spn = Curl_convert_UTF8_to_tchar((char *) uripath);
    576     if(!spn) {
    577       s_pSecFn->FreeCredentialsHandle(&credentials);
    578 
    579       Curl_sspi_free_identity(p_identity);
    580       free(output_token);
    581 
    582       return CURLE_OUT_OF_MEMORY;
    583     }
    584 
    585     /* Allocate our new context handle */
    586     digest->http_context = calloc(1, sizeof(CtxtHandle));
    587     if(!digest->http_context)
    588       return CURLE_OUT_OF_MEMORY;
    589 
    590     /* Generate our response message */
    591     status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
    592                                                  spn,
    593                                                  ISC_REQ_USE_HTTP_STYLE, 0, 0,
    594                                                  &chlg_desc, 0,
    595                                                  digest->http_context,
    596                                                  &resp_desc, &attrs, &expiry);
    597     Curl_unicodefree(spn);
    598 
    599     if(status == SEC_I_COMPLETE_NEEDED ||
    600        status == SEC_I_COMPLETE_AND_CONTINUE)
    601       s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
    602     else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
    603       s_pSecFn->FreeCredentialsHandle(&credentials);
    604 
    605       Curl_sspi_free_identity(p_identity);
    606       free(output_token);
    607 
    608       Curl_safefree(digest->http_context);
    609 
    610       return CURLE_OUT_OF_MEMORY;
    611     }
    612 
    613     output_token_len = resp_buf.cbBuffer;
    614 
    615     s_pSecFn->FreeCredentialsHandle(&credentials);
    616     Curl_sspi_free_identity(p_identity);
    617   }
    618 
    619   resp = malloc(output_token_len + 1);
    620   if(!resp) {
    621     free(output_token);
    622 
    623     return CURLE_OUT_OF_MEMORY;
    624   }
    625 
    626   /* Copy the generated response */
    627   memcpy(resp, output_token, output_token_len);
    628   resp[output_token_len] = 0;
    629 
    630   /* Return the response */
    631   *outptr = resp;
    632   *outlen = output_token_len;
    633 
    634   /* Free the response buffer */
    635   free(output_token);
    636 
    637   return CURLE_OK;
    638 }
    639 
    640 /*
    641  * Curl_auth_digest_cleanup()
    642  *
    643  * This is used to clean up the digest specific data.
    644  *
    645  * Parameters:
    646  *
    647  * digest    [in/out] - The digest data struct being cleaned up.
    648  *
    649  */
    650 void Curl_auth_digest_cleanup(struct digestdata *digest)
    651 {
    652   /* Free the input token */
    653   Curl_safefree(digest->input_token);
    654 
    655   /* Reset any variables */
    656   digest->input_token_len = 0;
    657 
    658   /* Delete security context */
    659   if(digest->http_context) {
    660     s_pSecFn->DeleteSecurityContext(digest->http_context);
    661     Curl_safefree(digest->http_context);
    662   }
    663 
    664   /* Free the copy of user/passwd used to make the identity for http_context */
    665   Curl_safefree(digest->user);
    666   Curl_safefree(digest->passwd);
    667 }
    668 
    669 #endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_CRYPTO_AUTH */
    670