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