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