quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

digest_sspi.c (21191B)


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