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