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