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