source: UsbWattMeter/trunk/curl-7.47.1/lib/curl_sasl_sspi.c@ 164

Last change on this file since 164 was 164, checked in by coas-nagasima, 6 years ago

TOPPERS/ECNLサンプルアプリ「USB充電器電力計」を追加

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-csrc
File size: 40.3 KB
RevLine 
[164]1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2014 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
9 * Copyright (C) 2014 - 2015, 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 */
61TCHAR *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 */
114CURLcode 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*/
291CURLcode 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 */
359CURLcode 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 */
399CURLcode 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 */
565void 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*/
593CURLcode 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*/
701CURLcode 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*/
752CURLcode 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 */
821void 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 gssapi 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 */
873CURLcode 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 gssapi 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 */
1041CURLcode 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 specific data.
1260 *
1261 * Parameters:
1262 *
1263 * krb5 [in/out] - The kerberos 5 data struct being cleaned up.
1264 *
1265 */
1266void 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 */
Note: See TracBrowser for help on using the repository browser.