1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2014 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
9  * Copyright (C) 2014, Steve Holme, <steve_holme@hotmail.com>.
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 http://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  * RFC2617 Basic and Digest Access Authentication
23  * RFC2831 DIGEST-MD5 authentication
24  * RFC4422 Simple Authentication and Security Layer (SASL)
25  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
26  *
27  ***************************************************************************/
28 
29 #include "curl_setup.h"
30 
31 #if defined(USE_WINDOWS_SSPI)
32 
33 #include <curl/curl.h>
34 
35 #include "curl_sasl.h"
36 #include "urldata.h"
37 #include "curl_base64.h"
38 #include "warnless.h"
39 #include "curl_multibyte.h"
40 #include "sendf.h"
41 #include "strdup.h"
42 #include "curl_printf.h"
43 #include "rawstr.h"
44 
45 /* The last #include files should be: */
46 #include "curl_memory.h"
47 #include "memdebug.h"
48 
49 /*
50  * Curl_sasl_build_spn()
51  *
52  * This is used to build a SPN string in the format service/host.
53  *
54  * Parameters:
55  *
56  * serivce  [in] - The service type such as www, smtp, pop or imap.
57  * host     [in] - The host name or realm.
58  *
59  * Returns a pointer to the newly allocated SPN.
60  */
Curl_sasl_build_spn(const char * service,const char * host)61 TCHAR *Curl_sasl_build_spn(const char *service, const char *host)
62 {
63   char *utf8_spn = NULL;
64   TCHAR *tchar_spn = NULL;
65 
66   /* Note: We could use DsMakeSPN() or DsClientMakeSpnForTargetServer() rather
67      than doing this ourselves but the first is only available in Windows XP
68      and Windows Server 2003 and the latter is only available in Windows 2000
69      but not Windows95/98/ME or Windows NT4.0 unless the Active Directory
70      Client Extensions are installed. As such it is far simpler for us to
71      formulate the SPN instead. */
72 
73   /* Allocate our UTF8 based SPN */
74   utf8_spn = aprintf("%s/%s", service, host);
75   if(!utf8_spn) {
76     return NULL;
77   }
78 
79   /* Allocate our TCHAR based SPN */
80   tchar_spn = Curl_convert_UTF8_to_tchar(utf8_spn);
81   if(!tchar_spn) {
82     free(utf8_spn);
83 
84     return NULL;
85   }
86 
87   /* Release the UTF8 variant when operating with Unicode */
88   Curl_unicodefree(utf8_spn);
89 
90   /* Return our newly allocated SPN */
91   return tchar_spn;
92 }
93 
94 #if !defined(CURL_DISABLE_CRYPTO_AUTH)
95 /*
96  * Curl_sasl_create_digest_md5_message()
97  *
98  * This is used to generate an already encoded DIGEST-MD5 response message
99  * ready for sending to the recipient.
100  *
101  * Parameters:
102  *
103  * data    [in]     - The session handle.
104  * chlg64  [in]     - The base64 encoded challenge message.
105  * userp   [in]     - The user name in the format User or Domain\User.
106  * passdwp [in]     - The user's password.
107  * service [in]     - The service type such as www, smtp, pop or imap.
108  * outptr  [in/out] - The address where a pointer to newly allocated memory
109  *                    holding the result will be stored upon completion.
110  * outlen  [out]    - The length of the output message.
111  *
112  * Returns CURLE_OK on success.
113  */
Curl_sasl_create_digest_md5_message(struct SessionHandle * data,const char * chlg64,const char * userp,const char * passwdp,const char * service,char ** outptr,size_t * outlen)114 CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,
115                                              const char *chlg64,
116                                              const char *userp,
117                                              const char *passwdp,
118                                              const char *service,
119                                              char **outptr, size_t *outlen)
120 {
121   CURLcode result = CURLE_OK;
122   TCHAR *spn = NULL;
123   size_t chlglen = 0;
124   size_t token_max = 0;
125   unsigned char *input_token = NULL;
126   unsigned char *output_token = NULL;
127   CredHandle credentials;
128   CtxtHandle context;
129   PSecPkgInfo SecurityPackage;
130   SEC_WINNT_AUTH_IDENTITY identity;
131   SEC_WINNT_AUTH_IDENTITY *p_identity;
132   SecBuffer chlg_buf;
133   SecBuffer resp_buf;
134   SecBufferDesc chlg_desc;
135   SecBufferDesc resp_desc;
136   SECURITY_STATUS status;
137   unsigned long attrs;
138   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
139 
140   /* Decode the base-64 encoded challenge message */
141   if(strlen(chlg64) && *chlg64 != '=') {
142     result = Curl_base64_decode(chlg64, &input_token, &chlglen);
143     if(result)
144       return result;
145   }
146 
147   /* Ensure we have a valid challenge message */
148   if(!input_token) {
149     infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n");
150 
151     return CURLE_BAD_CONTENT_ENCODING;
152   }
153 
154   /* Query the security package for DigestSSP */
155   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
156                                               &SecurityPackage);
157   if(status != SEC_E_OK) {
158     free(input_token);
159 
160     return CURLE_NOT_BUILT_IN;
161   }
162 
163   token_max = SecurityPackage->cbMaxToken;
164 
165   /* Release the package buffer as it is not required anymore */
166   s_pSecFn->FreeContextBuffer(SecurityPackage);
167 
168   /* Allocate our response buffer */
169   output_token = malloc(token_max);
170   if(!output_token) {
171     free(input_token);
172 
173     return CURLE_OUT_OF_MEMORY;
174   }
175 
176   /* Generate our SPN */
177   spn = Curl_sasl_build_spn(service, data->easy_conn->host.name);
178   if(!spn) {
179     free(output_token);
180     free(input_token);
181 
182     return CURLE_OUT_OF_MEMORY;
183   }
184 
185   if(userp && *userp) {
186     /* Populate our identity structure */
187     result = Curl_create_sspi_identity(userp, passwdp, &identity);
188     if(result) {
189       free(spn);
190       free(output_token);
191       free(input_token);
192 
193       return result;
194     }
195 
196     /* Allow proper cleanup of the identity structure */
197     p_identity = &identity;
198   }
199   else
200     /* Use the current Windows user */
201     p_identity = NULL;
202 
203   /* Acquire our credentials handle */
204   status = s_pSecFn->AcquireCredentialsHandle(NULL,
205                                               (TCHAR *) TEXT(SP_NAME_DIGEST),
206                                               SECPKG_CRED_OUTBOUND, NULL,
207                                               p_identity, NULL, NULL,
208                                               &credentials, &expiry);
209 
210   if(status != SEC_E_OK) {
211     Curl_sspi_free_identity(p_identity);
212     free(spn);
213     free(output_token);
214     free(input_token);
215 
216     return CURLE_LOGIN_DENIED;
217   }
218 
219   /* Setup the challenge "input" security buffer */
220   chlg_desc.ulVersion = SECBUFFER_VERSION;
221   chlg_desc.cBuffers  = 1;
222   chlg_desc.pBuffers  = &chlg_buf;
223   chlg_buf.BufferType = SECBUFFER_TOKEN;
224   chlg_buf.pvBuffer   = input_token;
225   chlg_buf.cbBuffer   = curlx_uztoul(chlglen);
226 
227   /* Setup the response "output" security buffer */
228   resp_desc.ulVersion = SECBUFFER_VERSION;
229   resp_desc.cBuffers  = 1;
230   resp_desc.pBuffers  = &resp_buf;
231   resp_buf.BufferType = SECBUFFER_TOKEN;
232   resp_buf.pvBuffer   = output_token;
233   resp_buf.cbBuffer   = curlx_uztoul(token_max);
234 
235   /* Generate our response message */
236   status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn,
237                                                0, 0, 0, &chlg_desc, 0,
238                                                &context, &resp_desc, &attrs,
239                                                &expiry);
240 
241   if(status == SEC_I_COMPLETE_NEEDED ||
242      status == SEC_I_COMPLETE_AND_CONTINUE)
243     s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
244   else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
245     s_pSecFn->FreeCredentialsHandle(&credentials);
246     Curl_sspi_free_identity(p_identity);
247     free(spn);
248     free(output_token);
249     free(input_token);
250 
251     return CURLE_RECV_ERROR;
252   }
253 
254   /* Base64 encode the response */
255   result = Curl_base64_encode(data, (char *) output_token, resp_buf.cbBuffer,
256                               outptr, outlen);
257 
258   /* Free our handles */
259   s_pSecFn->DeleteSecurityContext(&context);
260   s_pSecFn->FreeCredentialsHandle(&credentials);
261 
262   /* Free the identity structure */
263   Curl_sspi_free_identity(p_identity);
264 
265   /* Free the SPN */
266   free(spn);
267 
268   /* Free the response buffer */
269   free(output_token);
270 
271   /* Free the decoded challenge message */
272   free(input_token);
273 
274   return result;
275 }
276 
277 /*
278 * Curl_override_sspi_http_realm()
279 *
280 * This is used to populate the domain in a SSPI identity structure
281 * The realm is extracted from the challenge message and used as the
282 * domain if it is not already explicitly set.
283 *
284 * Parameters:
285 *
286 * chlg     [in]     - The challenge message.
287 * identity [in/out] - The identity structure.
288 *
289 * Returns CURLE_OK on success.
290 */
Curl_override_sspi_http_realm(const char * chlg,SEC_WINNT_AUTH_IDENTITY * identity)291 CURLcode Curl_override_sspi_http_realm(const char *chlg,
292                                        SEC_WINNT_AUTH_IDENTITY *identity)
293 {
294   xcharp_u domain, dup_domain;
295 
296   /* If domain is blank or unset, check challenge message for realm */
297   if(!identity->Domain || !identity->DomainLength) {
298     for(;;) {
299       char value[DIGEST_MAX_VALUE_LENGTH];
300       char content[DIGEST_MAX_CONTENT_LENGTH];
301 
302       /* Pass all additional spaces here */
303       while(*chlg && ISSPACE(*chlg))
304         chlg++;
305 
306       /* Extract a value=content pair */
307       if(!Curl_sasl_digest_get_pair(chlg, value, content, &chlg)) {
308         if(Curl_raw_equal(value, "realm")) {
309 
310           /* Setup identity's domain and length */
311           domain.tchar_ptr = Curl_convert_UTF8_to_tchar((char *)content);
312           if(!domain.tchar_ptr)
313             return CURLE_OUT_OF_MEMORY;
314           dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr);
315           if(!dup_domain.tchar_ptr) {
316             Curl_unicodefree(domain.tchar_ptr);
317             return CURLE_OUT_OF_MEMORY;
318           }
319           identity->Domain = dup_domain.tbyte_ptr;
320           identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr));
321           dup_domain.tchar_ptr = NULL;
322 
323           Curl_unicodefree(domain.tchar_ptr);
324         }
325         else {
326           /* unknown specifier, ignore it! */
327         }
328       }
329       else
330         break; /* we're done here */
331 
332       /* Pass all additional spaces here */
333       while(*chlg && ISSPACE(*chlg))
334         chlg++;
335 
336       /* Allow the list to be comma-separated */
337       if(',' == *chlg)
338         chlg++;
339     }
340   }
341 
342   return CURLE_OK;
343 }
344 
345 /*
346  * Curl_sasl_decode_digest_http_message()
347  *
348  * This is used to decode a HTTP DIGEST challenge message into the seperate
349  * attributes.
350  *
351  * Parameters:
352  *
353  * chlg    [in]     - The challenge message.
354  * digest  [in/out] - The digest data struct being used and modified.
355  *
356  * Returns CURLE_OK on success.
357  */
Curl_sasl_decode_digest_http_message(const char * chlg,struct digestdata * digest)358 CURLcode Curl_sasl_decode_digest_http_message(const char *chlg,
359                                               struct digestdata *digest)
360 {
361   size_t chlglen = strlen(chlg);
362 
363   /* We had an input token before and we got another one now. This means we
364   provided bad credentials in the previous request. */
365   if(digest->input_token)
366     return CURLE_BAD_CONTENT_ENCODING;
367 
368   /* Simply store the challenge for use later */
369   digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen);
370   if(!digest->input_token)
371     return CURLE_OUT_OF_MEMORY;
372 
373   digest->input_token_len = chlglen;
374 
375   return CURLE_OK;
376 }
377 
378 /*
379  * Curl_sasl_create_digest_http_message()
380  *
381  * This is used to generate a HTTP DIGEST response message ready for sending
382  * to the recipient.
383  *
384  * Parameters:
385  *
386  * data    [in]     - The session handle.
387  * userp   [in]     - The user name in the format User or Domain\User.
388  * passdwp [in]     - The user's password.
389  * request [in]     - The HTTP request.
390  * uripath [in]     - The path of the HTTP uri.
391  * digest  [in/out] - The digest data struct being used and modified.
392  * outptr  [in/out] - The address where a pointer to newly allocated memory
393  *                    holding the result will be stored upon completion.
394  * outlen  [out]    - The length of the output message.
395  *
396  * Returns CURLE_OK on success.
397  */
Curl_sasl_create_digest_http_message(struct SessionHandle * data,const char * userp,const char * passwdp,const unsigned char * request,const unsigned char * uripath,struct digestdata * digest,char ** outptr,size_t * outlen)398 CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data,
399                                               const char *userp,
400                                               const char *passwdp,
401                                               const unsigned char *request,
402                                               const unsigned char *uripath,
403                                               struct digestdata *digest,
404                                               char **outptr, size_t *outlen)
405 {
406   size_t token_max;
407   CredHandle credentials;
408   CtxtHandle context;
409   char *resp;
410   BYTE *output_token;
411   PSecPkgInfo SecurityPackage;
412   SEC_WINNT_AUTH_IDENTITY identity;
413   SEC_WINNT_AUTH_IDENTITY *p_identity;
414   SecBuffer chlg_buf[3];
415   SecBuffer resp_buf;
416   SecBufferDesc chlg_desc;
417   SecBufferDesc resp_desc;
418   SECURITY_STATUS status;
419   unsigned long attrs;
420   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
421 
422   (void) data;
423 
424   /* Query the security package for DigestSSP */
425   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
426                                               &SecurityPackage);
427   if(status != SEC_E_OK)
428     return CURLE_NOT_BUILT_IN;
429 
430   token_max = SecurityPackage->cbMaxToken;
431 
432   /* Release the package buffer as it is not required anymore */
433   s_pSecFn->FreeContextBuffer(SecurityPackage);
434 
435   /* Allocate the output buffer according to the max token size as indicated
436      by the security package */
437   output_token = malloc(token_max);
438   if(!output_token)
439     return CURLE_OUT_OF_MEMORY;
440 
441   if(userp && *userp) {
442     /* Populate our identity structure */
443     if(Curl_create_sspi_identity(userp, passwdp, &identity))
444       return CURLE_OUT_OF_MEMORY;
445 
446     /* Populate our identity domain */
447     if(Curl_override_sspi_http_realm((const char*)digest->input_token,
448                                      &identity))
449       return CURLE_OUT_OF_MEMORY;
450 
451     /* Allow proper cleanup of the identity structure */
452     p_identity = &identity;
453   }
454   else
455     /* Use the current Windows user */
456     p_identity = NULL;
457 
458   /* Acquire our credentials handle */
459   status = s_pSecFn->AcquireCredentialsHandle(NULL,
460                                               (TCHAR *) TEXT(SP_NAME_DIGEST),
461                                               SECPKG_CRED_OUTBOUND, NULL,
462                                               p_identity, NULL, NULL,
463                                               &credentials, &expiry);
464   if(status != SEC_E_OK) {
465     free(output_token);
466 
467     return CURLE_LOGIN_DENIED;
468   }
469 
470   /* Setup the challenge "input" security buffer if present */
471   chlg_desc.ulVersion    = SECBUFFER_VERSION;
472   chlg_desc.cBuffers     = 3;
473   chlg_desc.pBuffers     = chlg_buf;
474   chlg_buf[0].BufferType = SECBUFFER_TOKEN;
475   chlg_buf[0].pvBuffer   = digest->input_token;
476   chlg_buf[0].cbBuffer   = curlx_uztoul(digest->input_token_len);
477   chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
478   chlg_buf[1].pvBuffer   = (void *)request;
479   chlg_buf[1].cbBuffer   = curlx_uztoul(strlen((const char *) request));
480   chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
481   chlg_buf[2].pvBuffer   = NULL;
482   chlg_buf[2].cbBuffer   = 0;
483 
484   /* Setup the response "output" security buffer */
485   resp_desc.ulVersion = SECBUFFER_VERSION;
486   resp_desc.cBuffers  = 1;
487   resp_desc.pBuffers  = &resp_buf;
488   resp_buf.BufferType = SECBUFFER_TOKEN;
489   resp_buf.pvBuffer   = output_token;
490   resp_buf.cbBuffer   = curlx_uztoul(token_max);
491 
492   /* Generate our reponse message */
493   status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
494                                                (TCHAR *) uripath,
495                                                ISC_REQ_USE_HTTP_STYLE, 0, 0,
496                                                &chlg_desc, 0, &context,
497                                                &resp_desc, &attrs, &expiry);
498 
499   if(status == SEC_I_COMPLETE_NEEDED ||
500      status == SEC_I_COMPLETE_AND_CONTINUE)
501     s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
502   else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
503     s_pSecFn->FreeCredentialsHandle(&credentials);
504 
505     free(output_token);
506 
507     return CURLE_OUT_OF_MEMORY;
508   }
509 
510   resp = malloc(resp_buf.cbBuffer + 1);
511   if(!resp) {
512     s_pSecFn->DeleteSecurityContext(&context);
513     s_pSecFn->FreeCredentialsHandle(&credentials);
514 
515     free(output_token);
516 
517     return CURLE_OUT_OF_MEMORY;
518   }
519 
520   /* Copy the generated reponse */
521   memcpy(resp, resp_buf.pvBuffer, resp_buf.cbBuffer);
522   resp[resp_buf.cbBuffer] = 0x00;
523 
524   /* Return the response */
525   *outptr = resp;
526   *outlen = resp_buf.cbBuffer;
527 
528   /* Free our handles */
529   s_pSecFn->DeleteSecurityContext(&context);
530   s_pSecFn->FreeCredentialsHandle(&credentials);
531 
532   /* Free the identity structure */
533   Curl_sspi_free_identity(p_identity);
534 
535   /* Free the response buffer */
536   free(output_token);
537 
538   return CURLE_OK;
539 }
540 
541 /*
542  * Curl_sasl_digest_cleanup()
543  *
544  * This is used to clean up the digest specific data.
545  *
546  * Parameters:
547  *
548  * digest    [in/out] - The digest data struct being cleaned up.
549  *
550  */
Curl_sasl_digest_cleanup(struct digestdata * digest)551 void Curl_sasl_digest_cleanup(struct digestdata *digest)
552 {
553   /* Free the input token */
554   Curl_safefree(digest->input_token);
555 
556   /* Reset any variables */
557   digest->input_token_len = 0;
558 }
559 #endif /* !CURL_DISABLE_CRYPTO_AUTH */
560 
561 #if defined USE_NTLM
562 /*
563 * Curl_sasl_create_ntlm_type1_message()
564 *
565 * This is used to generate an already encoded NTLM type-1 message ready for
566 * sending to the recipient.
567 *
568 * Parameters:
569 *
570 * userp   [in]     - The user name in the format User or Domain\User.
571 * passdwp [in]     - The user's password.
572 * ntlm    [in/out] - The ntlm data struct being used and modified.
573 * outptr  [in/out] - The address where a pointer to newly allocated memory
574 *                    holding the result will be stored upon completion.
575 * outlen  [out]    - The length of the output message.
576 *
577 * Returns CURLE_OK on success.
578 */
Curl_sasl_create_ntlm_type1_message(const char * userp,const char * passwdp,struct ntlmdata * ntlm,char ** outptr,size_t * outlen)579 CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp,
580                                              const char *passwdp,
581                                              struct ntlmdata *ntlm,
582                                              char **outptr, size_t *outlen)
583 {
584   PSecPkgInfo SecurityPackage;
585   SecBuffer type_1_buf;
586   SecBufferDesc type_1_desc;
587   SECURITY_STATUS status;
588   unsigned long attrs;
589   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
590 
591   /* Clean up any former leftovers and initialise to defaults */
592   Curl_sasl_ntlm_cleanup(ntlm);
593 
594   /* Query the security package for NTLM */
595   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
596                                               &SecurityPackage);
597   if(status != SEC_E_OK)
598     return CURLE_NOT_BUILT_IN;
599 
600   ntlm->token_max = SecurityPackage->cbMaxToken;
601 
602   /* Release the package buffer as it is not required anymore */
603   s_pSecFn->FreeContextBuffer(SecurityPackage);
604 
605   /* Allocate our output buffer */
606   ntlm->output_token = malloc(ntlm->token_max);
607   if(!ntlm->output_token)
608     return CURLE_OUT_OF_MEMORY;
609 
610   if(userp && *userp) {
611     CURLcode result;
612 
613     /* Populate our identity structure */
614     result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity);
615     if(result)
616       return result;
617 
618     /* Allow proper cleanup of the identity structure */
619     ntlm->p_identity = &ntlm->identity;
620   }
621   else
622     /* Use the current Windows user */
623     ntlm->p_identity = NULL;
624 
625   /* Allocate our credentials handle */
626   ntlm->credentials = malloc(sizeof(CredHandle));
627   if(!ntlm->credentials)
628     return CURLE_OUT_OF_MEMORY;
629 
630   memset(ntlm->credentials, 0, sizeof(CredHandle));
631 
632   /* Acquire our credentials handle */
633   status = s_pSecFn->AcquireCredentialsHandle(NULL,
634                                               (TCHAR *) TEXT(SP_NAME_NTLM),
635                                               SECPKG_CRED_OUTBOUND, NULL,
636                                               ntlm->p_identity, NULL, NULL,
637                                               ntlm->credentials, &expiry);
638   if(status != SEC_E_OK)
639     return CURLE_LOGIN_DENIED;
640 
641   /* Allocate our new context handle */
642   ntlm->context = malloc(sizeof(CtxtHandle));
643   if(!ntlm->context)
644     return CURLE_OUT_OF_MEMORY;
645 
646   memset(ntlm->context, 0, sizeof(CtxtHandle));
647 
648   /* Setup the type-1 "output" security buffer */
649   type_1_desc.ulVersion = SECBUFFER_VERSION;
650   type_1_desc.cBuffers  = 1;
651   type_1_desc.pBuffers  = &type_1_buf;
652   type_1_buf.BufferType = SECBUFFER_TOKEN;
653   type_1_buf.pvBuffer   = ntlm->output_token;
654   type_1_buf.cbBuffer   = curlx_uztoul(ntlm->token_max);
655 
656   /* Generate our type-1 message */
657   status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL,
658                                                (TCHAR *) TEXT(""),
659                                                0, 0, SECURITY_NETWORK_DREP,
660                                                NULL, 0,
661                                                ntlm->context, &type_1_desc,
662                                                &attrs, &expiry);
663   if(status == SEC_I_COMPLETE_NEEDED ||
664     status == SEC_I_COMPLETE_AND_CONTINUE)
665     s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc);
666   else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
667     return CURLE_RECV_ERROR;
668 
669   /* Base64 encode the response */
670   return Curl_base64_encode(NULL, (char *) ntlm->output_token,
671                             type_1_buf.cbBuffer, outptr, outlen);
672 }
673 
674 /*
675 * Curl_sasl_decode_ntlm_type2_message()
676 *
677 * This is used to decode an already encoded NTLM type-2 message.
678 *
679 * Parameters:
680 *
681 * data     [in]     - The session handle.
682 * type2msg [in]     - The base64 encoded type-2 message.
683 * ntlm     [in/out] - The ntlm data struct being used and modified.
684 *
685 * Returns CURLE_OK on success.
686 */
Curl_sasl_decode_ntlm_type2_message(struct SessionHandle * data,const char * type2msg,struct ntlmdata * ntlm)687 CURLcode Curl_sasl_decode_ntlm_type2_message(struct SessionHandle *data,
688                                              const char *type2msg,
689                                              struct ntlmdata *ntlm)
690 {
691   CURLcode result = CURLE_OK;
692   unsigned char *type2 = NULL;
693   size_t type2_len = 0;
694 
695 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
696   (void) data;
697 #endif
698 
699   /* Decode the base-64 encoded type-2 message */
700   if(strlen(type2msg) && *type2msg != '=') {
701     result = Curl_base64_decode(type2msg, &type2, &type2_len);
702     if(result)
703       return result;
704   }
705 
706   /* Ensure we have a valid type-2 message */
707   if(!type2) {
708     infof(data, "NTLM handshake failure (empty type-2 message)\n");
709 
710     return CURLE_BAD_CONTENT_ENCODING;
711   }
712 
713   /* Simply store the challenge for use later */
714   ntlm->input_token = type2;
715   ntlm->input_token_len = type2_len;
716 
717   return result;
718 }
719 
720 /*
721 * Curl_sasl_create_ntlm_type3_message()
722 *
723 * This is used to generate an already encoded NTLM type-3 message ready for
724 * sending to the recipient.
725 *
726 * Parameters:
727 *
728 * data    [in]     - The session handle.
729 * userp   [in]     - The user name in the format User or Domain\User.
730 * passdwp [in]     - The user's password.
731 * ntlm    [in/out] - The ntlm data struct being used and modified.
732 * outptr  [in/out] - The address where a pointer to newly allocated memory
733 *                    holding the result will be stored upon completion.
734 * outlen  [out]    - The length of the output message.
735 *
736 * Returns CURLE_OK on success.
737 */
Curl_sasl_create_ntlm_type3_message(struct SessionHandle * data,const char * userp,const char * passwdp,struct ntlmdata * ntlm,char ** outptr,size_t * outlen)738 CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data,
739                                              const char *userp,
740                                              const char *passwdp,
741                                              struct ntlmdata *ntlm,
742                                              char **outptr, size_t *outlen)
743 {
744   CURLcode result = CURLE_OK;
745   SecBuffer type_2_buf;
746   SecBuffer type_3_buf;
747   SecBufferDesc type_2_desc;
748   SecBufferDesc type_3_desc;
749   SECURITY_STATUS status;
750   unsigned long attrs;
751   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
752 
753   (void) passwdp;
754   (void) userp;
755 
756   /* Setup the type-2 "input" security buffer */
757   type_2_desc.ulVersion = SECBUFFER_VERSION;
758   type_2_desc.cBuffers  = 1;
759   type_2_desc.pBuffers  = &type_2_buf;
760   type_2_buf.BufferType = SECBUFFER_TOKEN;
761   type_2_buf.pvBuffer   = ntlm->input_token;
762   type_2_buf.cbBuffer   = curlx_uztoul(ntlm->input_token_len);
763 
764   /* Setup the type-3 "output" security buffer */
765   type_3_desc.ulVersion = SECBUFFER_VERSION;
766   type_3_desc.cBuffers  = 1;
767   type_3_desc.pBuffers  = &type_3_buf;
768   type_3_buf.BufferType = SECBUFFER_TOKEN;
769   type_3_buf.pvBuffer   = ntlm->output_token;
770   type_3_buf.cbBuffer   = curlx_uztoul(ntlm->token_max);
771 
772   /* Generate our type-3 message */
773   status = s_pSecFn->InitializeSecurityContext(ntlm->credentials,
774                                                ntlm->context,
775                                                (TCHAR *) TEXT(""),
776                                                0, 0, SECURITY_NETWORK_DREP,
777                                                &type_2_desc,
778                                                0, ntlm->context,
779                                                &type_3_desc,
780                                                &attrs, &expiry);
781   if(status != SEC_E_OK) {
782     infof(data, "NTLM handshake failure (type-3 message): Status=%x\n",
783           status);
784 
785     return CURLE_RECV_ERROR;
786   }
787 
788   /* Base64 encode the response */
789   result = Curl_base64_encode(data, (char *) ntlm->output_token,
790                               type_3_buf.cbBuffer, outptr, outlen);
791 
792   Curl_sasl_ntlm_cleanup(ntlm);
793 
794   return result;
795 }
796 
797 /*
798  * Curl_sasl_ntlm_cleanup()
799  *
800  * This is used to clean up the ntlm specific data.
801  *
802  * Parameters:
803  *
804  * ntlm    [in/out] - The ntlm data struct being cleaned up.
805  *
806  */
Curl_sasl_ntlm_cleanup(struct ntlmdata * ntlm)807 void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm)
808 {
809   /* Free our security context */
810   if(ntlm->context) {
811     s_pSecFn->DeleteSecurityContext(ntlm->context);
812     free(ntlm->context);
813     ntlm->context = NULL;
814   }
815 
816   /* Free our credentials handle */
817   if(ntlm->credentials) {
818     s_pSecFn->FreeCredentialsHandle(ntlm->credentials);
819     free(ntlm->credentials);
820     ntlm->credentials = NULL;
821   }
822 
823   /* Free our identity */
824   Curl_sspi_free_identity(ntlm->p_identity);
825   ntlm->p_identity = NULL;
826 
827   /* Free the input and output tokens */
828   Curl_safefree(ntlm->input_token);
829   Curl_safefree(ntlm->output_token);
830 
831   /* Reset any variables */
832   ntlm->token_max = 0;
833 }
834 #endif /* USE_NTLM */
835 
836 #if defined(USE_KERBEROS5)
837 /*
838  * Curl_sasl_create_gssapi_user_message()
839  *
840  * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
841  * message ready for sending to the recipient.
842  *
843  * Parameters:
844  *
845  * data        [in]     - The session handle.
846  * userp       [in]     - The user name in the format User or Domain\User.
847  * passdwp     [in]     - The user's password.
848  * service     [in]     - The service type such as www, smtp, pop or imap.
849  * mutual_auth [in]     - Flag specifing whether or not mutual authentication
850  *                        is enabled.
851  * chlg64      [in]     - The optional base64 encoded challenge message.
852  * krb5        [in/out] - The gssapi data struct being used and modified.
853  * outptr      [in/out] - The address where a pointer to newly allocated memory
854  *                        holding the result will be stored upon completion.
855  * outlen      [out]    - The length of the output message.
856  *
857  * Returns CURLE_OK on success.
858  */
Curl_sasl_create_gssapi_user_message(struct SessionHandle * data,const char * userp,const char * passwdp,const char * service,const bool mutual_auth,const char * chlg64,struct kerberos5data * krb5,char ** outptr,size_t * outlen)859 CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data,
860                                               const char *userp,
861                                               const char *passwdp,
862                                               const char *service,
863                                               const bool mutual_auth,
864                                               const char *chlg64,
865                                               struct kerberos5data *krb5,
866                                               char **outptr, size_t *outlen)
867 {
868   CURLcode result = CURLE_OK;
869   size_t chlglen = 0;
870   unsigned char *chlg = NULL;
871   CtxtHandle context;
872   PSecPkgInfo SecurityPackage;
873   SecBuffer chlg_buf;
874   SecBuffer resp_buf;
875   SecBufferDesc chlg_desc;
876   SecBufferDesc resp_desc;
877   SECURITY_STATUS status;
878   unsigned long attrs;
879   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
880 
881   if(!krb5->credentials) {
882     /* Query the security package for Kerberos */
883     status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
884                                                 TEXT(SP_NAME_KERBEROS),
885                                                 &SecurityPackage);
886     if(status != SEC_E_OK) {
887       return CURLE_NOT_BUILT_IN;
888     }
889 
890     krb5->token_max = SecurityPackage->cbMaxToken;
891 
892     /* Release the package buffer as it is not required anymore */
893     s_pSecFn->FreeContextBuffer(SecurityPackage);
894 
895     /* Allocate our response buffer */
896     krb5->output_token = malloc(krb5->token_max);
897     if(!krb5->output_token)
898       return CURLE_OUT_OF_MEMORY;
899 
900     /* Generate our SPN */
901     krb5->spn = Curl_sasl_build_spn(service, data->easy_conn->host.name);
902     if(!krb5->spn)
903       return CURLE_OUT_OF_MEMORY;
904 
905     if(userp && *userp) {
906       /* Populate our identity structure */
907       result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity);
908       if(result)
909         return result;
910 
911       /* Allow proper cleanup of the identity structure */
912       krb5->p_identity = &krb5->identity;
913     }
914     else
915       /* Use the current Windows user */
916       krb5->p_identity = NULL;
917 
918     /* Allocate our credentials handle */
919     krb5->credentials = malloc(sizeof(CredHandle));
920     if(!krb5->credentials)
921       return CURLE_OUT_OF_MEMORY;
922 
923     memset(krb5->credentials, 0, sizeof(CredHandle));
924 
925     /* Acquire our credentials handle */
926     status = s_pSecFn->AcquireCredentialsHandle(NULL,
927                                                 (TCHAR *)
928                                                 TEXT(SP_NAME_KERBEROS),
929                                                 SECPKG_CRED_OUTBOUND, NULL,
930                                                 krb5->p_identity, NULL, NULL,
931                                                 krb5->credentials, &expiry);
932     if(status != SEC_E_OK)
933       return CURLE_LOGIN_DENIED;
934 
935     /* Allocate our new context handle */
936     krb5->context = malloc(sizeof(CtxtHandle));
937     if(!krb5->context)
938       return CURLE_OUT_OF_MEMORY;
939 
940     memset(krb5->context, 0, sizeof(CtxtHandle));
941   }
942   else {
943     /* Decode the base-64 encoded challenge message */
944     if(strlen(chlg64) && *chlg64 != '=') {
945       result = Curl_base64_decode(chlg64, &chlg, &chlglen);
946       if(result)
947         return result;
948     }
949 
950     /* Ensure we have a valid challenge message */
951     if(!chlg) {
952       infof(data, "GSSAPI handshake failure (empty challenge message)\n");
953 
954       return CURLE_BAD_CONTENT_ENCODING;
955     }
956 
957     /* Setup the challenge "input" security buffer */
958     chlg_desc.ulVersion = SECBUFFER_VERSION;
959     chlg_desc.cBuffers  = 1;
960     chlg_desc.pBuffers  = &chlg_buf;
961     chlg_buf.BufferType = SECBUFFER_TOKEN;
962     chlg_buf.pvBuffer   = chlg;
963     chlg_buf.cbBuffer   = curlx_uztoul(chlglen);
964   }
965 
966   /* Setup the response "output" security buffer */
967   resp_desc.ulVersion = SECBUFFER_VERSION;
968   resp_desc.cBuffers  = 1;
969   resp_desc.pBuffers  = &resp_buf;
970   resp_buf.BufferType = SECBUFFER_TOKEN;
971   resp_buf.pvBuffer   = krb5->output_token;
972   resp_buf.cbBuffer   = curlx_uztoul(krb5->token_max);
973 
974   /* Generate our challenge-response message */
975   status = s_pSecFn->InitializeSecurityContext(krb5->credentials,
976                                                chlg ? krb5->context : NULL,
977                                                krb5->spn,
978                                                (mutual_auth ?
979                                                  ISC_REQ_MUTUAL_AUTH : 0),
980                                                0, SECURITY_NATIVE_DREP,
981                                                chlg ? &chlg_desc : NULL, 0,
982                                                &context,
983                                                &resp_desc, &attrs,
984                                                &expiry);
985 
986   if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
987     free(chlg);
988 
989     return CURLE_RECV_ERROR;
990   }
991 
992   if(memcmp(&context, krb5->context, sizeof(context))) {
993     s_pSecFn->DeleteSecurityContext(krb5->context);
994 
995     memcpy(krb5->context, &context, sizeof(context));
996   }
997 
998   if(resp_buf.cbBuffer) {
999     /* Base64 encode the response */
1000     result = Curl_base64_encode(data, (char *)resp_buf.pvBuffer,
1001                                 resp_buf.cbBuffer, outptr, outlen);
1002   }
1003 
1004   /* Free the decoded challenge */
1005   free(chlg);
1006 
1007   return result;
1008 }
1009 
1010 /*
1011  * Curl_sasl_create_gssapi_security_message()
1012  *
1013  * This is used to generate an already encoded GSSAPI (Kerberos V5) security
1014  * token message ready for sending to the recipient.
1015  *
1016  * Parameters:
1017  *
1018  * data    [in]     - The session handle.
1019  * chlg64  [in]     - The optional base64 encoded challenge message.
1020  * krb5    [in/out] - The gssapi data struct being used and modified.
1021  * outptr  [in/out] - The address where a pointer to newly allocated memory
1022  *                    holding the result will be stored upon completion.
1023  * outlen  [out]    - The length of the output message.
1024  *
1025  * Returns CURLE_OK on success.
1026  */
Curl_sasl_create_gssapi_security_message(struct SessionHandle * data,const char * chlg64,struct kerberos5data * krb5,char ** outptr,size_t * outlen)1027 CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data,
1028                                                   const char *chlg64,
1029                                                   struct kerberos5data *krb5,
1030                                                   char **outptr,
1031                                                   size_t *outlen)
1032 {
1033   CURLcode result = CURLE_OK;
1034   size_t offset = 0;
1035   size_t chlglen = 0;
1036   size_t messagelen = 0;
1037   size_t appdatalen = 0;
1038   unsigned char *chlg = NULL;
1039   unsigned char *trailer = NULL;
1040   unsigned char *message = NULL;
1041   unsigned char *padding = NULL;
1042   unsigned char *appdata = NULL;
1043   SecBuffer input_buf[2];
1044   SecBuffer wrap_buf[3];
1045   SecBufferDesc input_desc;
1046   SecBufferDesc wrap_desc;
1047   unsigned long indata = 0;
1048   unsigned long outdata = 0;
1049   unsigned long qop = 0;
1050   unsigned long sec_layer = 0;
1051   unsigned long max_size = 0;
1052   SecPkgContext_Sizes sizes;
1053   SecPkgCredentials_Names names;
1054   SECURITY_STATUS status;
1055   char *user_name;
1056 
1057   /* Decode the base-64 encoded input message */
1058   if(strlen(chlg64) && *chlg64 != '=') {
1059     result = Curl_base64_decode(chlg64, &chlg, &chlglen);
1060     if(result)
1061       return result;
1062   }
1063 
1064   /* Ensure we have a valid challenge message */
1065   if(!chlg) {
1066     infof(data, "GSSAPI handshake failure (empty security message)\n");
1067 
1068     return CURLE_BAD_CONTENT_ENCODING;
1069   }
1070 
1071   /* Get our response size information */
1072   status = s_pSecFn->QueryContextAttributes(krb5->context,
1073                                             SECPKG_ATTR_SIZES,
1074                                             &sizes);
1075   if(status != SEC_E_OK) {
1076     free(chlg);
1077 
1078     return CURLE_OUT_OF_MEMORY;
1079   }
1080 
1081   /* Get the fully qualified username back from the context */
1082   status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials,
1083                                                 SECPKG_CRED_ATTR_NAMES,
1084                                                 &names);
1085   if(status != SEC_E_OK) {
1086     free(chlg);
1087 
1088     return CURLE_RECV_ERROR;
1089   }
1090 
1091   /* Setup the "input" security buffer */
1092   input_desc.ulVersion = SECBUFFER_VERSION;
1093   input_desc.cBuffers = 2;
1094   input_desc.pBuffers = input_buf;
1095   input_buf[0].BufferType = SECBUFFER_STREAM;
1096   input_buf[0].pvBuffer = chlg;
1097   input_buf[0].cbBuffer = curlx_uztoul(chlglen);
1098   input_buf[1].BufferType = SECBUFFER_DATA;
1099   input_buf[1].pvBuffer = NULL;
1100   input_buf[1].cbBuffer = 0;
1101 
1102   /* Decrypt the inbound challenge and obtain the qop */
1103   status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop);
1104   if(status != SEC_E_OK) {
1105     infof(data, "GSSAPI handshake failure (empty security message)\n");
1106 
1107     free(chlg);
1108 
1109     return CURLE_BAD_CONTENT_ENCODING;
1110   }
1111 
1112   /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
1113   if(input_buf[1].cbBuffer != 4) {
1114     infof(data, "GSSAPI handshake failure (invalid security data)\n");
1115 
1116     free(chlg);
1117 
1118     return CURLE_BAD_CONTENT_ENCODING;
1119   }
1120 
1121   /* Copy the data out and free the challenge as it is not required anymore */
1122   memcpy(&indata, input_buf[1].pvBuffer, 4);
1123   s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
1124   free(chlg);
1125 
1126   /* Extract the security layer */
1127   sec_layer = indata & 0x000000FF;
1128   if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) {
1129     infof(data, "GSSAPI handshake failure (invalid security layer)\n");
1130 
1131     return CURLE_BAD_CONTENT_ENCODING;
1132   }
1133 
1134   /* Extract the maximum message size the server can receive */
1135   max_size = ntohl(indata & 0xFFFFFF00);
1136   if(max_size > 0) {
1137     /* The server has told us it supports a maximum receive buffer, however, as
1138        we don't require one unless we are encrypting data, we tell the server
1139        our receive buffer is zero. */
1140     max_size = 0;
1141   }
1142 
1143   /* Allocate the trailer */
1144   trailer = malloc(sizes.cbSecurityTrailer);
1145   if(!trailer)
1146     return CURLE_OUT_OF_MEMORY;
1147 
1148   /* Convert the user name to UTF8 when operating with Unicode */
1149   user_name = Curl_convert_tchar_to_UTF8(names.sUserName);
1150   if(!user_name) {
1151     free(trailer);
1152 
1153     return CURLE_OUT_OF_MEMORY;
1154   }
1155 
1156   /* Allocate our message */
1157   messagelen = sizeof(outdata) + strlen(user_name) + 1;
1158   message = malloc(messagelen);
1159   if(!message) {
1160     free(trailer);
1161     Curl_unicodefree(user_name);
1162 
1163     return CURLE_OUT_OF_MEMORY;
1164   }
1165 
1166   /* Populate the message with the security layer, client supported receive
1167      message size and authorization identity including the 0x00 based
1168      terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization
1169      identity is not terminated with the zero-valued (%x00) octet." it seems
1170      necessary to include it. */
1171   outdata = htonl(max_size) | sec_layer;
1172   memcpy(message, &outdata, sizeof(outdata));
1173   strcpy((char *) message + sizeof(outdata), user_name);
1174   Curl_unicodefree(user_name);
1175 
1176   /* Allocate the padding */
1177   padding = malloc(sizes.cbBlockSize);
1178   if(!padding) {
1179     free(message);
1180     free(trailer);
1181 
1182     return CURLE_OUT_OF_MEMORY;
1183   }
1184 
1185   /* Setup the "authentication data" security buffer */
1186   wrap_desc.ulVersion    = SECBUFFER_VERSION;
1187   wrap_desc.cBuffers     = 3;
1188   wrap_desc.pBuffers     = wrap_buf;
1189   wrap_buf[0].BufferType = SECBUFFER_TOKEN;
1190   wrap_buf[0].pvBuffer   = trailer;
1191   wrap_buf[0].cbBuffer   = sizes.cbSecurityTrailer;
1192   wrap_buf[1].BufferType = SECBUFFER_DATA;
1193   wrap_buf[1].pvBuffer   = message;
1194   wrap_buf[1].cbBuffer   = curlx_uztoul(messagelen);
1195   wrap_buf[2].BufferType = SECBUFFER_PADDING;
1196   wrap_buf[2].pvBuffer   = padding;
1197   wrap_buf[2].cbBuffer   = sizes.cbBlockSize;
1198 
1199   /* Encrypt the data */
1200   status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
1201                                     &wrap_desc, 0);
1202   if(status != SEC_E_OK) {
1203     free(padding);
1204     free(message);
1205     free(trailer);
1206 
1207     return CURLE_OUT_OF_MEMORY;
1208   }
1209 
1210   /* Allocate the encryption (wrap) buffer */
1211   appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer +
1212                wrap_buf[2].cbBuffer;
1213   appdata = malloc(appdatalen);
1214   if(!appdata) {
1215     free(padding);
1216     free(message);
1217     free(trailer);
1218 
1219     return CURLE_OUT_OF_MEMORY;
1220   }
1221 
1222   /* Populate the encryption buffer */
1223   memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer);
1224   offset += wrap_buf[0].cbBuffer;
1225   memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer);
1226   offset += wrap_buf[1].cbBuffer;
1227   memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer);
1228 
1229   /* Base64 encode the response */
1230   result = Curl_base64_encode(data, (char *)appdata, appdatalen, outptr,
1231                               outlen);
1232 
1233   /* Free all of our local buffers */
1234   free(appdata);
1235   free(padding);
1236   free(message);
1237   free(trailer);
1238 
1239   return result;
1240 }
1241 
1242 /*
1243  * Curl_sasl_gssapi_cleanup()
1244  *
1245  * This is used to clean up the gssapi specific data.
1246  *
1247  * Parameters:
1248  *
1249  * krb5     [in/out] - The kerberos 5 data struct being cleaned up.
1250  *
1251  */
Curl_sasl_gssapi_cleanup(struct kerberos5data * krb5)1252 void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5)
1253 {
1254   /* Free our security context */
1255   if(krb5->context) {
1256     s_pSecFn->DeleteSecurityContext(krb5->context);
1257     free(krb5->context);
1258     krb5->context = NULL;
1259   }
1260 
1261   /* Free our credentials handle */
1262   if(krb5->credentials) {
1263     s_pSecFn->FreeCredentialsHandle(krb5->credentials);
1264     free(krb5->credentials);
1265     krb5->credentials = NULL;
1266   }
1267 
1268   /* Free our identity */
1269   Curl_sspi_free_identity(krb5->p_identity);
1270   krb5->p_identity = NULL;
1271 
1272   /* Free the SPN and output token */
1273   Curl_safefree(krb5->spn);
1274   Curl_safefree(krb5->output_token);
1275 
1276   /* Reset any variables */
1277   krb5->token_max = 0;
1278 }
1279 #endif /* USE_KERBEROS5 */
1280 
1281 #endif /* USE_WINDOWS_SSPI */
1282