f46376cc45537d09bd461176c1a721cca3cb11c3
[platform/upstream/curl.git] / lib / curl_sasl_sspi.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2014 - 2016 Daniel Stenberg, <daniel@haxx.se>, et al.
9  * Copyright (C) 2014 - 2016, 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 https://curl.haxx.se/docs/copyright.html.
14  *
15  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
16  * copies of the Software, and permit persons to whom the Software is
17  * furnished to do so, under the terms of the COPYING file.
18  *
19  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20  * KIND, either express or implied.
21  *
22  * 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/instance.
53  *
54  * Parameters:
55  *
56  * serivce  [in] - The service type such as www, smtp, pop or imap.
57  * instance [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 *instance)
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, instance);
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           free(identity->Domain);
320           identity->Domain = dup_domain.tbyte_ptr;
321           identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr));
322           dup_domain.tchar_ptr = NULL;
323
324           Curl_unicodefree(domain.tchar_ptr);
325         }
326         else {
327           /* unknown specifier, ignore it! */
328         }
329       }
330       else
331         break; /* we're done here */
332
333       /* Pass all additional spaces here */
334       while(*chlg && ISSPACE(*chlg))
335         chlg++;
336
337       /* Allow the list to be comma-separated */
338       if(',' == *chlg)
339         chlg++;
340     }
341   }
342
343   return CURLE_OK;
344 }
345
346 /*
347  * Curl_sasl_decode_digest_http_message()
348  *
349  * This is used to decode a HTTP DIGEST challenge message into the seperate
350  * attributes.
351  *
352  * Parameters:
353  *
354  * chlg    [in]     - The challenge message.
355  * digest  [in/out] - The digest data struct being used and modified.
356  *
357  * Returns CURLE_OK on success.
358  */
359 CURLcode Curl_sasl_decode_digest_http_message(const char *chlg,
360                                               struct digestdata *digest)
361 {
362   size_t chlglen = strlen(chlg);
363
364   /* We had an input token before and we got another one now. This means we
365   provided bad credentials in the previous request. */
366   if(digest->input_token)
367     return CURLE_BAD_CONTENT_ENCODING;
368
369   /* Simply store the challenge for use later */
370   digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen);
371   if(!digest->input_token)
372     return CURLE_OUT_OF_MEMORY;
373
374   digest->input_token_len = chlglen;
375
376   return CURLE_OK;
377 }
378
379 /*
380  * Curl_sasl_create_digest_http_message()
381  *
382  * This is used to generate a HTTP DIGEST response message ready for sending
383  * to the recipient.
384  *
385  * Parameters:
386  *
387  * data    [in]     - The session handle.
388  * userp   [in]     - The user name in the format User or Domain\User.
389  * passdwp [in]     - The user's password.
390  * request [in]     - The HTTP request.
391  * uripath [in]     - The path of the HTTP uri.
392  * digest  [in/out] - The digest data struct being used and modified.
393  * outptr  [in/out] - The address where a pointer to newly allocated memory
394  *                    holding the result will be stored upon completion.
395  * outlen  [out]    - The length of the output message.
396  *
397  * Returns CURLE_OK on success.
398  */
399 CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data,
400                                               const char *userp,
401                                               const char *passwdp,
402                                               const unsigned char *request,
403                                               const unsigned char *uripath,
404                                               struct digestdata *digest,
405                                               char **outptr, size_t *outlen)
406 {
407   size_t token_max;
408   CredHandle credentials;
409   CtxtHandle context;
410   char *resp;
411   BYTE *output_token;
412   PSecPkgInfo SecurityPackage;
413   SEC_WINNT_AUTH_IDENTITY identity;
414   SEC_WINNT_AUTH_IDENTITY *p_identity;
415   SecBuffer chlg_buf[3];
416   SecBuffer resp_buf;
417   SecBufferDesc chlg_desc;
418   SecBufferDesc resp_desc;
419   SECURITY_STATUS status;
420   unsigned long attrs;
421   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
422   TCHAR *spn;
423
424   (void) data;
425
426   /* Query the security package for DigestSSP */
427   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
428                                               &SecurityPackage);
429   if(status != SEC_E_OK)
430     return CURLE_NOT_BUILT_IN;
431
432   token_max = SecurityPackage->cbMaxToken;
433
434   /* Release the package buffer as it is not required anymore */
435   s_pSecFn->FreeContextBuffer(SecurityPackage);
436
437   /* Allocate the output buffer according to the max token size as indicated
438      by the security package */
439   output_token = malloc(token_max);
440   if(!output_token)
441     return CURLE_OUT_OF_MEMORY;
442
443   if(userp && *userp) {
444     /* Populate our identity structure */
445     if(Curl_create_sspi_identity(userp, passwdp, &identity))
446       return CURLE_OUT_OF_MEMORY;
447
448     /* Populate our identity domain */
449     if(Curl_override_sspi_http_realm((const char*)digest->input_token,
450                                      &identity))
451       return CURLE_OUT_OF_MEMORY;
452
453     /* Allow proper cleanup of the identity structure */
454     p_identity = &identity;
455   }
456   else
457     /* Use the current Windows user */
458     p_identity = NULL;
459
460   /* Acquire our credentials handle */
461   status = s_pSecFn->AcquireCredentialsHandle(NULL,
462                                               (TCHAR *) TEXT(SP_NAME_DIGEST),
463                                               SECPKG_CRED_OUTBOUND, NULL,
464                                               p_identity, NULL, NULL,
465                                               &credentials, &expiry);
466   if(status != SEC_E_OK) {
467     Curl_sspi_free_identity(p_identity);
468     free(output_token);
469
470     return CURLE_LOGIN_DENIED;
471   }
472
473   /* Setup the challenge "input" security buffer if present */
474   chlg_desc.ulVersion    = SECBUFFER_VERSION;
475   chlg_desc.cBuffers     = 3;
476   chlg_desc.pBuffers     = chlg_buf;
477   chlg_buf[0].BufferType = SECBUFFER_TOKEN;
478   chlg_buf[0].pvBuffer   = digest->input_token;
479   chlg_buf[0].cbBuffer   = curlx_uztoul(digest->input_token_len);
480   chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
481   chlg_buf[1].pvBuffer   = (void *)request;
482   chlg_buf[1].cbBuffer   = curlx_uztoul(strlen((const char *) request));
483   chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
484   chlg_buf[2].pvBuffer   = NULL;
485   chlg_buf[2].cbBuffer   = 0;
486
487   /* Setup the response "output" security buffer */
488   resp_desc.ulVersion = SECBUFFER_VERSION;
489   resp_desc.cBuffers  = 1;
490   resp_desc.pBuffers  = &resp_buf;
491   resp_buf.BufferType = SECBUFFER_TOKEN;
492   resp_buf.pvBuffer   = output_token;
493   resp_buf.cbBuffer   = curlx_uztoul(token_max);
494
495   spn = Curl_convert_UTF8_to_tchar((char *) uripath);
496   if(!spn) {
497     Curl_sspi_free_identity(p_identity);
498     free(output_token);
499
500     return CURLE_OUT_OF_MEMORY;
501   }
502
503   /* Generate our reponse message */
504   status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
505                                                spn,
506                                                ISC_REQ_USE_HTTP_STYLE, 0, 0,
507                                                &chlg_desc, 0, &context,
508                                                &resp_desc, &attrs, &expiry);
509   Curl_unicodefree(spn);
510
511   if(status == SEC_I_COMPLETE_NEEDED ||
512      status == SEC_I_COMPLETE_AND_CONTINUE)
513     s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
514   else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
515     s_pSecFn->FreeCredentialsHandle(&credentials);
516
517     Curl_sspi_free_identity(p_identity);
518     free(output_token);
519
520     return CURLE_OUT_OF_MEMORY;
521   }
522
523   resp = malloc(resp_buf.cbBuffer + 1);
524   if(!resp) {
525     s_pSecFn->DeleteSecurityContext(&context);
526     s_pSecFn->FreeCredentialsHandle(&credentials);
527
528     Curl_sspi_free_identity(p_identity);
529     free(output_token);
530
531     return CURLE_OUT_OF_MEMORY;
532   }
533
534   /* Copy the generated reponse */
535   memcpy(resp, resp_buf.pvBuffer, resp_buf.cbBuffer);
536   resp[resp_buf.cbBuffer] = 0x00;
537
538   /* Return the response */
539   *outptr = resp;
540   *outlen = resp_buf.cbBuffer;
541
542   /* Free our handles */
543   s_pSecFn->DeleteSecurityContext(&context);
544   s_pSecFn->FreeCredentialsHandle(&credentials);
545
546   /* Free the identity structure */
547   Curl_sspi_free_identity(p_identity);
548
549   /* Free the response buffer */
550   free(output_token);
551
552   return CURLE_OK;
553 }
554
555 /*
556  * Curl_sasl_digest_cleanup()
557  *
558  * This is used to clean up the digest specific data.
559  *
560  * Parameters:
561  *
562  * digest    [in/out] - The digest data struct being cleaned up.
563  *
564  */
565 void Curl_sasl_digest_cleanup(struct digestdata *digest)
566 {
567   /* Free the input token */
568   Curl_safefree(digest->input_token);
569
570   /* Reset any variables */
571   digest->input_token_len = 0;
572 }
573 #endif /* !CURL_DISABLE_CRYPTO_AUTH */
574
575 #if defined USE_NTLM
576 /*
577  * Curl_sasl_create_ntlm_type1_message()
578  *
579  * This is used to generate an already encoded NTLM type-1 message ready for
580  * sending to the recipient.
581  *
582  * Parameters:
583  *
584  * userp   [in]     - The user name in the format User or Domain\User.
585  * passdwp [in]     - The user's password.
586  * ntlm    [in/out] - The NTLM data struct being used and modified.
587  * outptr  [in/out] - The address where a pointer to newly allocated memory
588  *                    holding the result will be stored upon completion.
589  * outlen  [out]    - The length of the output message.
590  *
591  * Returns CURLE_OK on success.
592  */
593 CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp,
594                                              const char *passwdp,
595                                              struct ntlmdata *ntlm,
596                                              char **outptr, size_t *outlen)
597 {
598   PSecPkgInfo SecurityPackage;
599   SecBuffer type_1_buf;
600   SecBufferDesc type_1_desc;
601   SECURITY_STATUS status;
602   unsigned long attrs;
603   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
604
605   /* Clean up any former leftovers and initialise to defaults */
606   Curl_sasl_ntlm_cleanup(ntlm);
607
608   /* Query the security package for NTLM */
609   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
610                                               &SecurityPackage);
611   if(status != SEC_E_OK)
612     return CURLE_NOT_BUILT_IN;
613
614   ntlm->token_max = SecurityPackage->cbMaxToken;
615
616   /* Release the package buffer as it is not required anymore */
617   s_pSecFn->FreeContextBuffer(SecurityPackage);
618
619   /* Allocate our output buffer */
620   ntlm->output_token = malloc(ntlm->token_max);
621   if(!ntlm->output_token)
622     return CURLE_OUT_OF_MEMORY;
623
624   if(userp && *userp) {
625     CURLcode result;
626
627     /* Populate our identity structure */
628     result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity);
629     if(result)
630       return result;
631
632     /* Allow proper cleanup of the identity structure */
633     ntlm->p_identity = &ntlm->identity;
634   }
635   else
636     /* Use the current Windows user */
637     ntlm->p_identity = NULL;
638
639   /* Allocate our credentials handle */
640   ntlm->credentials = malloc(sizeof(CredHandle));
641   if(!ntlm->credentials)
642     return CURLE_OUT_OF_MEMORY;
643
644   memset(ntlm->credentials, 0, sizeof(CredHandle));
645
646   /* Acquire our credentials handle */
647   status = s_pSecFn->AcquireCredentialsHandle(NULL,
648                                               (TCHAR *) TEXT(SP_NAME_NTLM),
649                                               SECPKG_CRED_OUTBOUND, NULL,
650                                               ntlm->p_identity, NULL, NULL,
651                                               ntlm->credentials, &expiry);
652   if(status != SEC_E_OK)
653     return CURLE_LOGIN_DENIED;
654
655   /* Allocate our new context handle */
656   ntlm->context = malloc(sizeof(CtxtHandle));
657   if(!ntlm->context)
658     return CURLE_OUT_OF_MEMORY;
659
660   memset(ntlm->context, 0, sizeof(CtxtHandle));
661
662   /* Setup the type-1 "output" security buffer */
663   type_1_desc.ulVersion = SECBUFFER_VERSION;
664   type_1_desc.cBuffers  = 1;
665   type_1_desc.pBuffers  = &type_1_buf;
666   type_1_buf.BufferType = SECBUFFER_TOKEN;
667   type_1_buf.pvBuffer   = ntlm->output_token;
668   type_1_buf.cbBuffer   = curlx_uztoul(ntlm->token_max);
669
670   /* Generate our type-1 message */
671   status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL,
672                                                (TCHAR *) TEXT(""),
673                                                0, 0, SECURITY_NETWORK_DREP,
674                                                NULL, 0,
675                                                ntlm->context, &type_1_desc,
676                                                &attrs, &expiry);
677   if(status == SEC_I_COMPLETE_NEEDED ||
678     status == SEC_I_COMPLETE_AND_CONTINUE)
679     s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc);
680   else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
681     return CURLE_RECV_ERROR;
682
683   /* Base64 encode the response */
684   return Curl_base64_encode(NULL, (char *) ntlm->output_token,
685                             type_1_buf.cbBuffer, outptr, outlen);
686 }
687
688 /*
689  * Curl_sasl_decode_ntlm_type2_message()
690  *
691  * This is used to decode an already encoded NTLM type-2 message.
692  *
693  * Parameters:
694  *
695  * data     [in]     - The session handle.
696  * type2msg [in]     - The base64 encoded type-2 message.
697  * ntlm     [in/out] - The NTLM data struct being used and modified.
698  *
699  * Returns CURLE_OK on success.
700  */
701 CURLcode Curl_sasl_decode_ntlm_type2_message(struct SessionHandle *data,
702                                              const char *type2msg,
703                                              struct ntlmdata *ntlm)
704 {
705   CURLcode result = CURLE_OK;
706   unsigned char *type2 = NULL;
707   size_t type2_len = 0;
708
709 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
710   (void) data;
711 #endif
712
713   /* Decode the base-64 encoded type-2 message */
714   if(strlen(type2msg) && *type2msg != '=') {
715     result = Curl_base64_decode(type2msg, &type2, &type2_len);
716     if(result)
717       return result;
718   }
719
720   /* Ensure we have a valid type-2 message */
721   if(!type2) {
722     infof(data, "NTLM handshake failure (empty type-2 message)\n");
723
724     return CURLE_BAD_CONTENT_ENCODING;
725   }
726
727   /* Simply store the challenge for use later */
728   ntlm->input_token = type2;
729   ntlm->input_token_len = type2_len;
730
731   return result;
732 }
733
734 /*
735  * Curl_sasl_create_ntlm_type3_message()
736  *
737  * This is used to generate an already encoded NTLM type-3 message ready for
738  * sending to the recipient.
739  *
740  * Parameters:
741  *
742  * data    [in]     - The session handle.
743  * userp   [in]     - The user name in the format User or Domain\User.
744  * passdwp [in]     - The user's password.
745  * ntlm    [in/out] - The NTLM data struct being used and modified.
746  * outptr  [in/out] - The address where a pointer to newly allocated memory
747  *                    holding the result will be stored upon completion.
748  * outlen  [out]    - The length of the output message.
749  *
750  * Returns CURLE_OK on success.
751  */
752 CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data,
753                                              const char *userp,
754                                              const char *passwdp,
755                                              struct ntlmdata *ntlm,
756                                              char **outptr, size_t *outlen)
757 {
758   CURLcode result = CURLE_OK;
759   SecBuffer type_2_buf;
760   SecBuffer type_3_buf;
761   SecBufferDesc type_2_desc;
762   SecBufferDesc type_3_desc;
763   SECURITY_STATUS status;
764   unsigned long attrs;
765   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
766
767   (void) passwdp;
768   (void) userp;
769
770   /* Setup the type-2 "input" security buffer */
771   type_2_desc.ulVersion = SECBUFFER_VERSION;
772   type_2_desc.cBuffers  = 1;
773   type_2_desc.pBuffers  = &type_2_buf;
774   type_2_buf.BufferType = SECBUFFER_TOKEN;
775   type_2_buf.pvBuffer   = ntlm->input_token;
776   type_2_buf.cbBuffer   = curlx_uztoul(ntlm->input_token_len);
777
778   /* Setup the type-3 "output" security buffer */
779   type_3_desc.ulVersion = SECBUFFER_VERSION;
780   type_3_desc.cBuffers  = 1;
781   type_3_desc.pBuffers  = &type_3_buf;
782   type_3_buf.BufferType = SECBUFFER_TOKEN;
783   type_3_buf.pvBuffer   = ntlm->output_token;
784   type_3_buf.cbBuffer   = curlx_uztoul(ntlm->token_max);
785
786   /* Generate our type-3 message */
787   status = s_pSecFn->InitializeSecurityContext(ntlm->credentials,
788                                                ntlm->context,
789                                                (TCHAR *) TEXT(""),
790                                                0, 0, SECURITY_NETWORK_DREP,
791                                                &type_2_desc,
792                                                0, ntlm->context,
793                                                &type_3_desc,
794                                                &attrs, &expiry);
795   if(status != SEC_E_OK) {
796     infof(data, "NTLM handshake failure (type-3 message): Status=%x\n",
797           status);
798
799     return CURLE_RECV_ERROR;
800   }
801
802   /* Base64 encode the response */
803   result = Curl_base64_encode(data, (char *) ntlm->output_token,
804                               type_3_buf.cbBuffer, outptr, outlen);
805
806   Curl_sasl_ntlm_cleanup(ntlm);
807
808   return result;
809 }
810
811 /*
812  * Curl_sasl_ntlm_cleanup()
813  *
814  * This is used to clean up the NTLM specific data.
815  *
816  * Parameters:
817  *
818  * ntlm    [in/out] - The NTLM data struct being cleaned up.
819  *
820  */
821 void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm)
822 {
823   /* Free our security context */
824   if(ntlm->context) {
825     s_pSecFn->DeleteSecurityContext(ntlm->context);
826     free(ntlm->context);
827     ntlm->context = NULL;
828   }
829
830   /* Free our credentials handle */
831   if(ntlm->credentials) {
832     s_pSecFn->FreeCredentialsHandle(ntlm->credentials);
833     free(ntlm->credentials);
834     ntlm->credentials = NULL;
835   }
836
837   /* Free our identity */
838   Curl_sspi_free_identity(ntlm->p_identity);
839   ntlm->p_identity = NULL;
840
841   /* Free the input and output tokens */
842   Curl_safefree(ntlm->input_token);
843   Curl_safefree(ntlm->output_token);
844
845   /* Reset any variables */
846   ntlm->token_max = 0;
847 }
848 #endif /* USE_NTLM */
849
850 #if defined(USE_KERBEROS5)
851 /*
852  * Curl_sasl_create_gssapi_user_message()
853  *
854  * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
855  * message ready for sending to the recipient.
856  *
857  * Parameters:
858  *
859  * data        [in]     - The session handle.
860  * userp       [in]     - The user name in the format User or Domain\User.
861  * passdwp     [in]     - The user's password.
862  * service     [in]     - The service type such as www, smtp, pop or imap.
863  * mutual_auth [in]     - Flag specifing whether or not mutual authentication
864  *                        is enabled.
865  * chlg64      [in]     - The optional base64 encoded challenge message.
866  * krb5        [in/out] - The Kerberos 5 data struct being used and modified.
867  * outptr      [in/out] - The address where a pointer to newly allocated memory
868  *                        holding the result will be stored upon completion.
869  * outlen      [out]    - The length of the output message.
870  *
871  * Returns CURLE_OK on success.
872  */
873 CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data,
874                                               const char *userp,
875                                               const char *passwdp,
876                                               const char *service,
877                                               const bool mutual_auth,
878                                               const char *chlg64,
879                                               struct kerberos5data *krb5,
880                                               char **outptr, size_t *outlen)
881 {
882   CURLcode result = CURLE_OK;
883   size_t chlglen = 0;
884   unsigned char *chlg = NULL;
885   CtxtHandle context;
886   PSecPkgInfo SecurityPackage;
887   SecBuffer chlg_buf;
888   SecBuffer resp_buf;
889   SecBufferDesc chlg_desc;
890   SecBufferDesc resp_desc;
891   SECURITY_STATUS status;
892   unsigned long attrs;
893   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
894
895   if(!krb5->credentials) {
896     /* Query the security package for Kerberos */
897     status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
898                                                 TEXT(SP_NAME_KERBEROS),
899                                                 &SecurityPackage);
900     if(status != SEC_E_OK) {
901       return CURLE_NOT_BUILT_IN;
902     }
903
904     krb5->token_max = SecurityPackage->cbMaxToken;
905
906     /* Release the package buffer as it is not required anymore */
907     s_pSecFn->FreeContextBuffer(SecurityPackage);
908
909     /* Allocate our response buffer */
910     krb5->output_token = malloc(krb5->token_max);
911     if(!krb5->output_token)
912       return CURLE_OUT_OF_MEMORY;
913
914     /* Generate our SPN */
915     krb5->spn = Curl_sasl_build_spn(service, data->easy_conn->host.name);
916     if(!krb5->spn)
917       return CURLE_OUT_OF_MEMORY;
918
919     if(userp && *userp) {
920       /* Populate our identity structure */
921       result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity);
922       if(result)
923         return result;
924
925       /* Allow proper cleanup of the identity structure */
926       krb5->p_identity = &krb5->identity;
927     }
928     else
929       /* Use the current Windows user */
930       krb5->p_identity = NULL;
931
932     /* Allocate our credentials handle */
933     krb5->credentials = malloc(sizeof(CredHandle));
934     if(!krb5->credentials)
935       return CURLE_OUT_OF_MEMORY;
936
937     memset(krb5->credentials, 0, sizeof(CredHandle));
938
939     /* Acquire our credentials handle */
940     status = s_pSecFn->AcquireCredentialsHandle(NULL,
941                                                 (TCHAR *)
942                                                 TEXT(SP_NAME_KERBEROS),
943                                                 SECPKG_CRED_OUTBOUND, NULL,
944                                                 krb5->p_identity, NULL, NULL,
945                                                 krb5->credentials, &expiry);
946     if(status != SEC_E_OK)
947       return CURLE_LOGIN_DENIED;
948
949     /* Allocate our new context handle */
950     krb5->context = malloc(sizeof(CtxtHandle));
951     if(!krb5->context)
952       return CURLE_OUT_OF_MEMORY;
953
954     memset(krb5->context, 0, sizeof(CtxtHandle));
955   }
956   else {
957     /* Decode the base-64 encoded challenge message */
958     if(strlen(chlg64) && *chlg64 != '=') {
959       result = Curl_base64_decode(chlg64, &chlg, &chlglen);
960       if(result)
961         return result;
962     }
963
964     /* Ensure we have a valid challenge message */
965     if(!chlg) {
966       infof(data, "GSSAPI handshake failure (empty challenge message)\n");
967
968       return CURLE_BAD_CONTENT_ENCODING;
969     }
970
971     /* Setup the challenge "input" security buffer */
972     chlg_desc.ulVersion = SECBUFFER_VERSION;
973     chlg_desc.cBuffers  = 1;
974     chlg_desc.pBuffers  = &chlg_buf;
975     chlg_buf.BufferType = SECBUFFER_TOKEN;
976     chlg_buf.pvBuffer   = chlg;
977     chlg_buf.cbBuffer   = curlx_uztoul(chlglen);
978   }
979
980   /* Setup the response "output" security buffer */
981   resp_desc.ulVersion = SECBUFFER_VERSION;
982   resp_desc.cBuffers  = 1;
983   resp_desc.pBuffers  = &resp_buf;
984   resp_buf.BufferType = SECBUFFER_TOKEN;
985   resp_buf.pvBuffer   = krb5->output_token;
986   resp_buf.cbBuffer   = curlx_uztoul(krb5->token_max);
987
988   /* Generate our challenge-response message */
989   status = s_pSecFn->InitializeSecurityContext(krb5->credentials,
990                                                chlg ? krb5->context : NULL,
991                                                krb5->spn,
992                                                (mutual_auth ?
993                                                  ISC_REQ_MUTUAL_AUTH : 0),
994                                                0, SECURITY_NATIVE_DREP,
995                                                chlg ? &chlg_desc : NULL, 0,
996                                                &context,
997                                                &resp_desc, &attrs,
998                                                &expiry);
999
1000   if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
1001     free(chlg);
1002
1003     return CURLE_RECV_ERROR;
1004   }
1005
1006   if(memcmp(&context, krb5->context, sizeof(context))) {
1007     s_pSecFn->DeleteSecurityContext(krb5->context);
1008
1009     memcpy(krb5->context, &context, sizeof(context));
1010   }
1011
1012   if(resp_buf.cbBuffer) {
1013     /* Base64 encode the response */
1014     result = Curl_base64_encode(data, (char *)resp_buf.pvBuffer,
1015                                 resp_buf.cbBuffer, outptr, outlen);
1016   }
1017
1018   /* Free the decoded challenge */
1019   free(chlg);
1020
1021   return result;
1022 }
1023
1024 /*
1025  * Curl_sasl_create_gssapi_security_message()
1026  *
1027  * This is used to generate an already encoded GSSAPI (Kerberos V5) security
1028  * token message ready for sending to the recipient.
1029  *
1030  * Parameters:
1031  *
1032  * data    [in]     - The session handle.
1033  * chlg64  [in]     - The optional base64 encoded challenge message.
1034  * krb5    [in/out] - The Kerberos 5 data struct being used and modified.
1035  * outptr  [in/out] - The address where a pointer to newly allocated memory
1036  *                    holding the result will be stored upon completion.
1037  * outlen  [out]    - The length of the output message.
1038  *
1039  * Returns CURLE_OK on success.
1040  */
1041 CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data,
1042                                                   const char *chlg64,
1043                                                   struct kerberos5data *krb5,
1044                                                   char **outptr,
1045                                                   size_t *outlen)
1046 {
1047   CURLcode result = CURLE_OK;
1048   size_t offset = 0;
1049   size_t chlglen = 0;
1050   size_t messagelen = 0;
1051   size_t appdatalen = 0;
1052   unsigned char *chlg = NULL;
1053   unsigned char *trailer = NULL;
1054   unsigned char *message = NULL;
1055   unsigned char *padding = NULL;
1056   unsigned char *appdata = NULL;
1057   SecBuffer input_buf[2];
1058   SecBuffer wrap_buf[3];
1059   SecBufferDesc input_desc;
1060   SecBufferDesc wrap_desc;
1061   unsigned long indata = 0;
1062   unsigned long outdata = 0;
1063   unsigned long qop = 0;
1064   unsigned long sec_layer = 0;
1065   unsigned long max_size = 0;
1066   SecPkgContext_Sizes sizes;
1067   SecPkgCredentials_Names names;
1068   SECURITY_STATUS status;
1069   char *user_name;
1070
1071   /* Decode the base-64 encoded input message */
1072   if(strlen(chlg64) && *chlg64 != '=') {
1073     result = Curl_base64_decode(chlg64, &chlg, &chlglen);
1074     if(result)
1075       return result;
1076   }
1077
1078   /* Ensure we have a valid challenge message */
1079   if(!chlg) {
1080     infof(data, "GSSAPI handshake failure (empty security message)\n");
1081
1082     return CURLE_BAD_CONTENT_ENCODING;
1083   }
1084
1085   /* Get our response size information */
1086   status = s_pSecFn->QueryContextAttributes(krb5->context,
1087                                             SECPKG_ATTR_SIZES,
1088                                             &sizes);
1089   if(status != SEC_E_OK) {
1090     free(chlg);
1091
1092     return CURLE_OUT_OF_MEMORY;
1093   }
1094
1095   /* Get the fully qualified username back from the context */
1096   status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials,
1097                                                 SECPKG_CRED_ATTR_NAMES,
1098                                                 &names);
1099   if(status != SEC_E_OK) {
1100     free(chlg);
1101
1102     return CURLE_RECV_ERROR;
1103   }
1104
1105   /* Setup the "input" security buffer */
1106   input_desc.ulVersion = SECBUFFER_VERSION;
1107   input_desc.cBuffers = 2;
1108   input_desc.pBuffers = input_buf;
1109   input_buf[0].BufferType = SECBUFFER_STREAM;
1110   input_buf[0].pvBuffer = chlg;
1111   input_buf[0].cbBuffer = curlx_uztoul(chlglen);
1112   input_buf[1].BufferType = SECBUFFER_DATA;
1113   input_buf[1].pvBuffer = NULL;
1114   input_buf[1].cbBuffer = 0;
1115
1116   /* Decrypt the inbound challenge and obtain the qop */
1117   status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop);
1118   if(status != SEC_E_OK) {
1119     infof(data, "GSSAPI handshake failure (empty security message)\n");
1120
1121     free(chlg);
1122
1123     return CURLE_BAD_CONTENT_ENCODING;
1124   }
1125
1126   /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
1127   if(input_buf[1].cbBuffer != 4) {
1128     infof(data, "GSSAPI handshake failure (invalid security data)\n");
1129
1130     free(chlg);
1131
1132     return CURLE_BAD_CONTENT_ENCODING;
1133   }
1134
1135   /* Copy the data out and free the challenge as it is not required anymore */
1136   memcpy(&indata, input_buf[1].pvBuffer, 4);
1137   s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
1138   free(chlg);
1139
1140   /* Extract the security layer */
1141   sec_layer = indata & 0x000000FF;
1142   if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) {
1143     infof(data, "GSSAPI handshake failure (invalid security layer)\n");
1144
1145     return CURLE_BAD_CONTENT_ENCODING;
1146   }
1147
1148   /* Extract the maximum message size the server can receive */
1149   max_size = ntohl(indata & 0xFFFFFF00);
1150   if(max_size > 0) {
1151     /* The server has told us it supports a maximum receive buffer, however, as
1152        we don't require one unless we are encrypting data, we tell the server
1153        our receive buffer is zero. */
1154     max_size = 0;
1155   }
1156
1157   /* Allocate the trailer */
1158   trailer = malloc(sizes.cbSecurityTrailer);
1159   if(!trailer)
1160     return CURLE_OUT_OF_MEMORY;
1161
1162   /* Convert the user name to UTF8 when operating with Unicode */
1163   user_name = Curl_convert_tchar_to_UTF8(names.sUserName);
1164   if(!user_name) {
1165     free(trailer);
1166
1167     return CURLE_OUT_OF_MEMORY;
1168   }
1169
1170   /* Allocate our message */
1171   messagelen = sizeof(outdata) + strlen(user_name) + 1;
1172   message = malloc(messagelen);
1173   if(!message) {
1174     free(trailer);
1175     Curl_unicodefree(user_name);
1176
1177     return CURLE_OUT_OF_MEMORY;
1178   }
1179
1180   /* Populate the message with the security layer, client supported receive
1181      message size and authorization identity including the 0x00 based
1182      terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization
1183      identity is not terminated with the zero-valued (%x00) octet." it seems
1184      necessary to include it. */
1185   outdata = htonl(max_size) | sec_layer;
1186   memcpy(message, &outdata, sizeof(outdata));
1187   strcpy((char *) message + sizeof(outdata), user_name);
1188   Curl_unicodefree(user_name);
1189
1190   /* Allocate the padding */
1191   padding = malloc(sizes.cbBlockSize);
1192   if(!padding) {
1193     free(message);
1194     free(trailer);
1195
1196     return CURLE_OUT_OF_MEMORY;
1197   }
1198
1199   /* Setup the "authentication data" security buffer */
1200   wrap_desc.ulVersion    = SECBUFFER_VERSION;
1201   wrap_desc.cBuffers     = 3;
1202   wrap_desc.pBuffers     = wrap_buf;
1203   wrap_buf[0].BufferType = SECBUFFER_TOKEN;
1204   wrap_buf[0].pvBuffer   = trailer;
1205   wrap_buf[0].cbBuffer   = sizes.cbSecurityTrailer;
1206   wrap_buf[1].BufferType = SECBUFFER_DATA;
1207   wrap_buf[1].pvBuffer   = message;
1208   wrap_buf[1].cbBuffer   = curlx_uztoul(messagelen);
1209   wrap_buf[2].BufferType = SECBUFFER_PADDING;
1210   wrap_buf[2].pvBuffer   = padding;
1211   wrap_buf[2].cbBuffer   = sizes.cbBlockSize;
1212
1213   /* Encrypt the data */
1214   status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
1215                                     &wrap_desc, 0);
1216   if(status != SEC_E_OK) {
1217     free(padding);
1218     free(message);
1219     free(trailer);
1220
1221     return CURLE_OUT_OF_MEMORY;
1222   }
1223
1224   /* Allocate the encryption (wrap) buffer */
1225   appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer +
1226                wrap_buf[2].cbBuffer;
1227   appdata = malloc(appdatalen);
1228   if(!appdata) {
1229     free(padding);
1230     free(message);
1231     free(trailer);
1232
1233     return CURLE_OUT_OF_MEMORY;
1234   }
1235
1236   /* Populate the encryption buffer */
1237   memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer);
1238   offset += wrap_buf[0].cbBuffer;
1239   memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer);
1240   offset += wrap_buf[1].cbBuffer;
1241   memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer);
1242
1243   /* Base64 encode the response */
1244   result = Curl_base64_encode(data, (char *)appdata, appdatalen, outptr,
1245                               outlen);
1246
1247   /* Free all of our local buffers */
1248   free(appdata);
1249   free(padding);
1250   free(message);
1251   free(trailer);
1252
1253   return result;
1254 }
1255
1256 /*
1257  * Curl_sasl_gssapi_cleanup()
1258  *
1259  * This is used to clean up the GSSAPI (Kerberos V5) specific data.
1260  *
1261  * Parameters:
1262  *
1263  * krb5     [in/out] - The Kerberos 5 data struct being cleaned up.
1264  *
1265  */
1266 void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5)
1267 {
1268   /* Free our security context */
1269   if(krb5->context) {
1270     s_pSecFn->DeleteSecurityContext(krb5->context);
1271     free(krb5->context);
1272     krb5->context = NULL;
1273   }
1274
1275   /* Free our credentials handle */
1276   if(krb5->credentials) {
1277     s_pSecFn->FreeCredentialsHandle(krb5->credentials);
1278     free(krb5->credentials);
1279     krb5->credentials = NULL;
1280   }
1281
1282   /* Free our identity */
1283   Curl_sspi_free_identity(krb5->p_identity);
1284   krb5->p_identity = NULL;
1285
1286   /* Free the SPN and output token */
1287   Curl_safefree(krb5->spn);
1288   Curl_safefree(krb5->output_token);
1289
1290   /* Reset any variables */
1291   krb5->token_max = 0;
1292 }
1293 #endif /* USE_KERBEROS5 */
1294
1295 #endif /* USE_WINDOWS_SSPI */