Imported Upstream version 7.44.0
[platform/upstream/curl.git] / lib / curl_sasl_sspi.c
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  */
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  */
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 */
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  */
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  */
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  */
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 */
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 */
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 */
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  */
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  */
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  */
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  */
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 */