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