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

Last change on this file since 164 was 164, checked in by coas-nagasima, 8 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: 53.1 KB
RevLine 
[164]1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2012 - 2015, Marc Hoersken, <info@marc-hoersken.de>
9 * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
10 * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
11 *
12 * This software is licensed as described in the file COPYING, which
13 * you should have received as part of this distribution. The terms
14 * are also available at https://curl.haxx.se/docs/copyright.html.
15 *
16 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
17 * copies of the Software, and permit persons to whom the Software is
18 * furnished to do so, under the terms of the COPYING file.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ***************************************************************************/
24
25/*
26 * Source file for all SChannel-specific code for the TLS/SSL layer. No code
27 * but vtls.c should ever call or use these functions.
28 *
29 */
30
31/*
32 * Based upon the PolarSSL implementation in polarssl.c and polarssl.h:
33 * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
34 *
35 * Based upon the CyaSSL implementation in cyassl.c and cyassl.h:
36 * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
37 *
38 * Thanks for code and inspiration!
39 */
40
41#include "curl_setup.h"
42
43#ifdef USE_SCHANNEL
44
45#ifndef USE_WINDOWS_SSPI
46# error "Can't compile SCHANNEL support without SSPI."
47#endif
48
49#include "curl_sspi.h"
50#include "schannel.h"
51#include "vtls.h"
52#include "sendf.h"
53#include "connect.h" /* for the connect timeout */
54#include "strerror.h"
55#include "select.h" /* for the socket readyness */
56#include "inet_pton.h" /* for IP addr SNI check */
57#include "curl_multibyte.h"
58#include "warnless.h"
59#include "curl_printf.h"
60#include "curl_memory.h"
61/* The last #include file should be: */
62#include "memdebug.h"
63
64/* Uncomment to force verbose output
65 * #define infof(x, y, ...) printf(y, __VA_ARGS__)
66 * #define failf(x, y, ...) printf(y, __VA_ARGS__)
67 */
68
69static Curl_recv schannel_recv;
70static Curl_send schannel_send;
71
72#ifdef _WIN32_WCE
73static CURLcode verify_certificate(struct connectdata *conn, int sockindex);
74#endif
75
76static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType,
77 void *BufDataPtr, unsigned long BufByteSize)
78{
79 buffer->cbBuffer = BufByteSize;
80 buffer->BufferType = BufType;
81 buffer->pvBuffer = BufDataPtr;
82}
83
84static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr,
85 unsigned long NumArrElem)
86{
87 desc->ulVersion = SECBUFFER_VERSION;
88 desc->pBuffers = BufArr;
89 desc->cBuffers = NumArrElem;
90}
91
92static CURLcode
93schannel_connect_step1(struct connectdata *conn, int sockindex)
94{
95 ssize_t written = -1;
96 struct SessionHandle *data = conn->data;
97 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
98 SecBuffer outbuf;
99 SecBufferDesc outbuf_desc;
100 SCHANNEL_CRED schannel_cred;
101 SECURITY_STATUS sspi_status = SEC_E_OK;
102 struct curl_schannel_cred *old_cred = NULL;
103 struct in_addr addr;
104#ifdef ENABLE_IPV6
105 struct in6_addr addr6;
106#endif
107 TCHAR *host_name;
108 CURLcode result;
109
110 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
111 conn->host.name, conn->remote_port);
112
113 /* check for an existing re-usable credential handle */
114 if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL)) {
115 connssl->cred = old_cred;
116 infof(data, "schannel: re-using existing credential handle\n");
117 }
118 else {
119 /* setup Schannel API options */
120 memset(&schannel_cred, 0, sizeof(schannel_cred));
121 schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
122
123 if(data->set.ssl.verifypeer) {
124#ifdef _WIN32_WCE
125 /* certificate validation on CE doesn't seem to work right; we'll
126 do it following a more manual process. */
127 schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
128 SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
129 SCH_CRED_IGNORE_REVOCATION_OFFLINE;
130#else
131 schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION;
132 if(data->set.ssl_no_revoke)
133 schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
134 SCH_CRED_IGNORE_REVOCATION_OFFLINE;
135 else
136 schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
137#endif
138 if(data->set.ssl_no_revoke)
139 infof(data, "schannel: disabled server certificate revocation "
140 "checks\n");
141 else
142 infof(data, "schannel: checking server certificate revocation\n");
143 }
144 else {
145 schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
146 SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
147 SCH_CRED_IGNORE_REVOCATION_OFFLINE;
148 infof(data, "schannel: disabled server certificate revocation checks\n");
149 }
150
151 if(!data->set.ssl.verifyhost) {
152 schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
153 infof(data, "schannel: verifyhost setting prevents Schannel from "
154 "comparing the supplied target name with the subject "
155 "names in server certificates. Also disables SNI.\n");
156 }
157
158 switch(data->set.ssl.version) {
159 default:
160 case CURL_SSLVERSION_DEFAULT:
161 case CURL_SSLVERSION_TLSv1:
162 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT |
163 SP_PROT_TLS1_1_CLIENT |
164 SP_PROT_TLS1_2_CLIENT;
165 break;
166 case CURL_SSLVERSION_TLSv1_0:
167 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT;
168 break;
169 case CURL_SSLVERSION_TLSv1_1:
170 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT;
171 break;
172 case CURL_SSLVERSION_TLSv1_2:
173 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
174 break;
175 case CURL_SSLVERSION_SSLv3:
176 schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
177 break;
178 case CURL_SSLVERSION_SSLv2:
179 schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
180 break;
181 }
182
183 /* allocate memory for the re-usable credential handle */
184 connssl->cred = (struct curl_schannel_cred *)
185 malloc(sizeof(struct curl_schannel_cred));
186 if(!connssl->cred) {
187 failf(data, "schannel: unable to allocate memory");
188 return CURLE_OUT_OF_MEMORY;
189 }
190 memset(connssl->cred, 0, sizeof(struct curl_schannel_cred));
191
192 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx
193 */
194 sspi_status =
195 s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
196 SECPKG_CRED_OUTBOUND, NULL,
197 &schannel_cred, NULL, NULL,
198 &connssl->cred->cred_handle,
199 &connssl->cred->time_stamp);
200
201 if(sspi_status != SEC_E_OK) {
202 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
203 failf(data, "schannel: SNI or certificate check failed: %s",
204 Curl_sspi_strerror(conn, sspi_status));
205 else
206 failf(data, "schannel: AcquireCredentialsHandle failed: %s",
207 Curl_sspi_strerror(conn, sspi_status));
208 Curl_safefree(connssl->cred);
209 return CURLE_SSL_CONNECT_ERROR;
210 }
211 }
212
213 /* Warn if SNI is disabled due to use of an IP address */
214 if(Curl_inet_pton(AF_INET, conn->host.name, &addr)
215#ifdef ENABLE_IPV6
216 || Curl_inet_pton(AF_INET6, conn->host.name, &addr6)
217#endif
218 ) {
219 infof(data, "schannel: using IP address, SNI is not supported by OS.\n");
220 }
221
222 /* setup output buffer */
223 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
224 InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
225
226 /* setup request flags */
227 connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
228 ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
229 ISC_REQ_STREAM;
230
231 /* allocate memory for the security context handle */
232 connssl->ctxt = (struct curl_schannel_ctxt *)
233 malloc(sizeof(struct curl_schannel_ctxt));
234 if(!connssl->ctxt) {
235 failf(data, "schannel: unable to allocate memory");
236 return CURLE_OUT_OF_MEMORY;
237 }
238 memset(connssl->ctxt, 0, sizeof(struct curl_schannel_ctxt));
239
240 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
241 if(!host_name)
242 return CURLE_OUT_OF_MEMORY;
243
244 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
245
246 sspi_status = s_pSecFn->InitializeSecurityContext(
247 &connssl->cred->cred_handle, NULL, host_name,
248 connssl->req_flags, 0, 0, NULL, 0, &connssl->ctxt->ctxt_handle,
249 &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
250
251 Curl_unicodefree(host_name);
252
253 if(sspi_status != SEC_I_CONTINUE_NEEDED) {
254 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
255 failf(data, "schannel: SNI or certificate check failed: %s",
256 Curl_sspi_strerror(conn, sspi_status));
257 else
258 failf(data, "schannel: initial InitializeSecurityContext failed: %s",
259 Curl_sspi_strerror(conn, sspi_status));
260 Curl_safefree(connssl->ctxt);
261 return CURLE_SSL_CONNECT_ERROR;
262 }
263
264 infof(data, "schannel: sending initial handshake data: "
265 "sending %lu bytes...\n", outbuf.cbBuffer);
266
267 /* send initial handshake data which is now stored in output buffer */
268 result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
269 outbuf.cbBuffer, &written);
270 s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
271 if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
272 failf(data, "schannel: failed to send initial handshake data: "
273 "sent %zd of %lu bytes", written, outbuf.cbBuffer);
274 return CURLE_SSL_CONNECT_ERROR;
275 }
276
277 infof(data, "schannel: sent initial handshake data: "
278 "sent %zd bytes\n", written);
279
280 connssl->recv_unrecoverable_err = CURLE_OK;
281 connssl->recv_sspi_close_notify = false;
282 connssl->recv_connection_closed = false;
283
284 /* continue to second handshake step */
285 connssl->connecting_state = ssl_connect_2;
286
287 return CURLE_OK;
288}
289
290static CURLcode
291schannel_connect_step2(struct connectdata *conn, int sockindex)
292{
293 int i;
294 ssize_t nread = -1, written = -1;
295 struct SessionHandle *data = conn->data;
296 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
297 unsigned char *reallocated_buffer;
298 size_t reallocated_length;
299 SecBuffer outbuf[3];
300 SecBufferDesc outbuf_desc;
301 SecBuffer inbuf[2];
302 SecBufferDesc inbuf_desc;
303 SECURITY_STATUS sspi_status = SEC_E_OK;
304 TCHAR *host_name;
305 CURLcode result;
306 bool doread;
307
308 doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
309
310 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
311 conn->host.name, conn->remote_port);
312
313 if(!connssl->cred || !connssl->ctxt)
314 return CURLE_SSL_CONNECT_ERROR;
315
316 /* buffer to store previously received and decrypted data */
317 if(connssl->decdata_buffer == NULL) {
318 connssl->decdata_offset = 0;
319 connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
320 connssl->decdata_buffer = malloc(connssl->decdata_length);
321 if(connssl->decdata_buffer == NULL) {
322 failf(data, "schannel: unable to allocate memory");
323 return CURLE_OUT_OF_MEMORY;
324 }
325 }
326
327 /* buffer to store previously received and encrypted data */
328 if(connssl->encdata_buffer == NULL) {
329 connssl->encdata_offset = 0;
330 connssl->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
331 connssl->encdata_buffer = malloc(connssl->encdata_length);
332 if(connssl->encdata_buffer == NULL) {
333 failf(data, "schannel: unable to allocate memory");
334 return CURLE_OUT_OF_MEMORY;
335 }
336 }
337
338 /* if we need a bigger buffer to read a full message, increase buffer now */
339 if(connssl->encdata_length - connssl->encdata_offset <
340 CURL_SCHANNEL_BUFFER_FREE_SIZE) {
341 /* increase internal encrypted data buffer */
342 reallocated_length = connssl->encdata_offset +
343 CURL_SCHANNEL_BUFFER_FREE_SIZE;
344 reallocated_buffer = realloc(connssl->encdata_buffer,
345 reallocated_length);
346
347 if(reallocated_buffer == NULL) {
348 failf(data, "schannel: unable to re-allocate memory");
349 return CURLE_OUT_OF_MEMORY;
350 }
351 else {
352 connssl->encdata_buffer = reallocated_buffer;
353 connssl->encdata_length = reallocated_length;
354 }
355 }
356
357 for(;;) {
358 if(doread) {
359 /* read encrypted handshake data from socket */
360 result = Curl_read_plain(conn->sock[sockindex],
361 (char *) (connssl->encdata_buffer +
362 connssl->encdata_offset),
363 connssl->encdata_length -
364 connssl->encdata_offset,
365 &nread);
366 if(result == CURLE_AGAIN) {
367 if(connssl->connecting_state != ssl_connect_2_writing)
368 connssl->connecting_state = ssl_connect_2_reading;
369 infof(data, "schannel: failed to receive handshake, "
370 "need more data\n");
371 return CURLE_OK;
372 }
373 else if((result != CURLE_OK) || (nread == 0)) {
374 failf(data, "schannel: failed to receive handshake, "
375 "SSL/TLS connection failed");
376 return CURLE_SSL_CONNECT_ERROR;
377 }
378
379 /* increase encrypted data buffer offset */
380 connssl->encdata_offset += nread;
381 }
382
383 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
384 connssl->encdata_offset, connssl->encdata_length);
385
386 /* setup input buffers */
387 InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(connssl->encdata_offset),
388 curlx_uztoul(connssl->encdata_offset));
389 InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
390 InitSecBufferDesc(&inbuf_desc, inbuf, 2);
391
392 /* setup output buffers */
393 InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
394 InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
395 InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0);
396 InitSecBufferDesc(&outbuf_desc, outbuf, 3);
397
398 if(inbuf[0].pvBuffer == NULL) {
399 failf(data, "schannel: unable to allocate memory");
400 return CURLE_OUT_OF_MEMORY;
401 }
402
403 /* copy received handshake data into input buffer */
404 memcpy(inbuf[0].pvBuffer, connssl->encdata_buffer,
405 connssl->encdata_offset);
406
407 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
408 if(!host_name)
409 return CURLE_OUT_OF_MEMORY;
410
411 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
412 */
413 sspi_status = s_pSecFn->InitializeSecurityContext(
414 &connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle,
415 host_name, connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL,
416 &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
417
418 Curl_unicodefree(host_name);
419
420 /* free buffer for received handshake data */
421 Curl_safefree(inbuf[0].pvBuffer);
422
423 /* check if the handshake was incomplete */
424 if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
425 connssl->connecting_state = ssl_connect_2_reading;
426 infof(data, "schannel: received incomplete message, need more data\n");
427 return CURLE_OK;
428 }
429
430 /* If the server has requested a client certificate, attempt to continue
431 the handshake without one. This will allow connections to servers which
432 request a client certificate but do not require it. */
433 if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS &&
434 !(connssl->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
435 connssl->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
436 connssl->connecting_state = ssl_connect_2_writing;
437 infof(data, "schannel: a client certificate has been requested\n");
438 return CURLE_OK;
439 }
440
441 /* check if the handshake needs to be continued */
442 if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
443 for(i = 0; i < 3; i++) {
444 /* search for handshake tokens that need to be send */
445 if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
446 infof(data, "schannel: sending next handshake data: "
447 "sending %lu bytes...\n", outbuf[i].cbBuffer);
448
449 /* send handshake token to server */
450 result = Curl_write_plain(conn, conn->sock[sockindex],
451 outbuf[i].pvBuffer, outbuf[i].cbBuffer,
452 &written);
453 if((result != CURLE_OK) ||
454 (outbuf[i].cbBuffer != (size_t) written)) {
455 failf(data, "schannel: failed to send next handshake data: "
456 "sent %zd of %lu bytes", written, outbuf[i].cbBuffer);
457 return CURLE_SSL_CONNECT_ERROR;
458 }
459 }
460
461 /* free obsolete buffer */
462 if(outbuf[i].pvBuffer != NULL) {
463 s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
464 }
465 }
466 }
467 else {
468 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
469 failf(data, "schannel: SNI or certificate check failed: %s",
470 Curl_sspi_strerror(conn, sspi_status));
471 else
472 failf(data, "schannel: next InitializeSecurityContext failed: %s",
473 Curl_sspi_strerror(conn, sspi_status));
474 return CURLE_SSL_CONNECT_ERROR;
475 }
476
477 /* check if there was additional remaining encrypted data */
478 if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
479 infof(data, "schannel: encrypted data length: %lu\n", inbuf[1].cbBuffer);
480 /*
481 There are two cases where we could be getting extra data here:
482 1) If we're renegotiating a connection and the handshake is already
483 complete (from the server perspective), it can encrypted app data
484 (not handshake data) in an extra buffer at this point.
485 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a
486 connection and this extra data is part of the handshake.
487 We should process the data immediately; waiting for the socket to
488 be ready may fail since the server is done sending handshake data.
489 */
490 /* check if the remaining data is less than the total amount
491 and therefore begins after the already processed data */
492 if(connssl->encdata_offset > inbuf[1].cbBuffer) {
493 memmove(connssl->encdata_buffer,
494 (connssl->encdata_buffer + connssl->encdata_offset) -
495 inbuf[1].cbBuffer, inbuf[1].cbBuffer);
496 connssl->encdata_offset = inbuf[1].cbBuffer;
497 if(sspi_status == SEC_I_CONTINUE_NEEDED) {
498 doread = FALSE;
499 continue;
500 }
501 }
502 }
503 else {
504 connssl->encdata_offset = 0;
505 }
506 break;
507 }
508
509 /* check if the handshake needs to be continued */
510 if(sspi_status == SEC_I_CONTINUE_NEEDED) {
511 connssl->connecting_state = ssl_connect_2_reading;
512 return CURLE_OK;
513 }
514
515 /* check if the handshake is complete */
516 if(sspi_status == SEC_E_OK) {
517 connssl->connecting_state = ssl_connect_3;
518 infof(data, "schannel: SSL/TLS handshake complete\n");
519 }
520
521#ifdef _WIN32_WCE
522 /* Windows CE doesn't do any server certificate validation.
523 We have to do it manually. */
524 if(data->set.ssl.verifypeer)
525 return verify_certificate(conn, sockindex);
526#endif
527
528 return CURLE_OK;
529}
530
531static CURLcode
532schannel_connect_step3(struct connectdata *conn, int sockindex)
533{
534 CURLcode result = CURLE_OK;
535 struct SessionHandle *data = conn->data;
536 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
537 struct curl_schannel_cred *old_cred = NULL;
538 bool incache;
539
540 DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
541
542 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
543 conn->host.name, conn->remote_port);
544
545 if(!connssl->cred)
546 return CURLE_SSL_CONNECT_ERROR;
547
548 /* check if the required context attributes are met */
549 if(connssl->ret_flags != connssl->req_flags) {
550 if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
551 failf(data, "schannel: failed to setup sequence detection");
552 if(!(connssl->ret_flags & ISC_RET_REPLAY_DETECT))
553 failf(data, "schannel: failed to setup replay detection");
554 if(!(connssl->ret_flags & ISC_RET_CONFIDENTIALITY))
555 failf(data, "schannel: failed to setup confidentiality");
556 if(!(connssl->ret_flags & ISC_RET_ALLOCATED_MEMORY))
557 failf(data, "schannel: failed to setup memory allocation");
558 if(!(connssl->ret_flags & ISC_RET_STREAM))
559 failf(data, "schannel: failed to setup stream orientation");
560 return CURLE_SSL_CONNECT_ERROR;
561 }
562
563 /* increment the reference counter of the credential/session handle */
564 if(connssl->cred && connssl->ctxt) {
565 connssl->cred->refcount++;
566 infof(data, "schannel: incremented credential handle refcount = %d\n",
567 connssl->cred->refcount);
568 }
569
570 /* save the current session data for possible re-use */
571 incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL));
572 if(incache) {
573 if(old_cred != connssl->cred) {
574 infof(data, "schannel: old credential handle is stale, removing\n");
575 Curl_ssl_delsessionid(conn, (void *)old_cred);
576 incache = FALSE;
577 }
578 }
579
580 if(!incache) {
581 result = Curl_ssl_addsessionid(conn, (void *)connssl->cred,
582 sizeof(struct curl_schannel_cred));
583 if(result) {
584 failf(data, "schannel: failed to store credential handle");
585 return result;
586 }
587 else {
588 connssl->cred->cached = TRUE;
589 infof(data, "schannel: stored credential handle in session cache\n");
590 }
591 }
592
593 connssl->connecting_state = ssl_connect_done;
594
595 return CURLE_OK;
596}
597
598static CURLcode
599schannel_connect_common(struct connectdata *conn, int sockindex,
600 bool nonblocking, bool *done)
601{
602 CURLcode result;
603 struct SessionHandle *data = conn->data;
604 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
605 curl_socket_t sockfd = conn->sock[sockindex];
606 long timeout_ms;
607 int what;
608
609 /* check if the connection has already been established */
610 if(ssl_connection_complete == connssl->state) {
611 *done = TRUE;
612 return CURLE_OK;
613 }
614
615 if(ssl_connect_1 == connssl->connecting_state) {
616 /* check out how much more time we're allowed */
617 timeout_ms = Curl_timeleft(data, NULL, TRUE);
618
619 if(timeout_ms < 0) {
620 /* no need to continue if time already is up */
621 failf(data, "SSL/TLS connection timeout");
622 return CURLE_OPERATION_TIMEDOUT;
623 }
624
625 result = schannel_connect_step1(conn, sockindex);
626 if(result)
627 return result;
628 }
629
630 while(ssl_connect_2 == connssl->connecting_state ||
631 ssl_connect_2_reading == connssl->connecting_state ||
632 ssl_connect_2_writing == connssl->connecting_state) {
633
634 /* check out how much more time we're allowed */
635 timeout_ms = Curl_timeleft(data, NULL, TRUE);
636
637 if(timeout_ms < 0) {
638 /* no need to continue if time already is up */
639 failf(data, "SSL/TLS connection timeout");
640 return CURLE_OPERATION_TIMEDOUT;
641 }
642
643 /* if ssl is expecting something, check if it's available. */
644 if(connssl->connecting_state == ssl_connect_2_reading
645 || connssl->connecting_state == ssl_connect_2_writing) {
646
647 curl_socket_t writefd = ssl_connect_2_writing ==
648 connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
649 curl_socket_t readfd = ssl_connect_2_reading ==
650 connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
651
652 what = Curl_socket_ready(readfd, writefd, nonblocking ? 0 : timeout_ms);
653 if(what < 0) {
654 /* fatal error */
655 failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
656 return CURLE_SSL_CONNECT_ERROR;
657 }
658 else if(0 == what) {
659 if(nonblocking) {
660 *done = FALSE;
661 return CURLE_OK;
662 }
663 else {
664 /* timeout */
665 failf(data, "SSL/TLS connection timeout");
666 return CURLE_OPERATION_TIMEDOUT;
667 }
668 }
669 /* socket is readable or writable */
670 }
671
672 /* Run transaction, and return to the caller if it failed or if
673 * this connection is part of a multi handle and this loop would
674 * execute again. This permits the owner of a multi handle to
675 * abort a connection attempt before step2 has completed while
676 * ensuring that a client using select() or epoll() will always
677 * have a valid fdset to wait on.
678 */
679 result = schannel_connect_step2(conn, sockindex);
680 if(result || (nonblocking &&
681 (ssl_connect_2 == connssl->connecting_state ||
682 ssl_connect_2_reading == connssl->connecting_state ||
683 ssl_connect_2_writing == connssl->connecting_state)))
684 return result;
685
686 } /* repeat step2 until all transactions are done. */
687
688 if(ssl_connect_3 == connssl->connecting_state) {
689 result = schannel_connect_step3(conn, sockindex);
690 if(result)
691 return result;
692 }
693
694 if(ssl_connect_done == connssl->connecting_state) {
695 connssl->state = ssl_connection_complete;
696 conn->recv[sockindex] = schannel_recv;
697 conn->send[sockindex] = schannel_send;
698 *done = TRUE;
699 }
700 else
701 *done = FALSE;
702
703 /* reset our connection state machine */
704 connssl->connecting_state = ssl_connect_1;
705
706 return CURLE_OK;
707}
708
709static ssize_t
710schannel_send(struct connectdata *conn, int sockindex,
711 const void *buf, size_t len, CURLcode *err)
712{
713 ssize_t written = -1;
714 size_t data_len = 0;
715 unsigned char *data = NULL;
716 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
717 SecBuffer outbuf[4];
718 SecBufferDesc outbuf_desc;
719 SECURITY_STATUS sspi_status = SEC_E_OK;
720 CURLcode result;
721
722 /* check if the maximum stream sizes were queried */
723 if(connssl->stream_sizes.cbMaximumMessage == 0) {
724 sspi_status = s_pSecFn->QueryContextAttributes(
725 &connssl->ctxt->ctxt_handle,
726 SECPKG_ATTR_STREAM_SIZES,
727 &connssl->stream_sizes);
728 if(sspi_status != SEC_E_OK) {
729 *err = CURLE_SEND_ERROR;
730 return -1;
731 }
732 }
733
734 /* check if the buffer is longer than the maximum message length */
735 if(len > connssl->stream_sizes.cbMaximumMessage) {
736 *err = CURLE_SEND_ERROR;
737 return -1;
738 }
739
740 /* calculate the complete message length and allocate a buffer for it */
741 data_len = connssl->stream_sizes.cbHeader + len +
742 connssl->stream_sizes.cbTrailer;
743 data = (unsigned char *) malloc(data_len);
744 if(data == NULL) {
745 *err = CURLE_OUT_OF_MEMORY;
746 return -1;
747 }
748
749 /* setup output buffers (header, data, trailer, empty) */
750 InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
751 data, connssl->stream_sizes.cbHeader);
752 InitSecBuffer(&outbuf[1], SECBUFFER_DATA,
753 data + connssl->stream_sizes.cbHeader, curlx_uztoul(len));
754 InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
755 data + connssl->stream_sizes.cbHeader + len,
756 connssl->stream_sizes.cbTrailer);
757 InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0);
758 InitSecBufferDesc(&outbuf_desc, outbuf, 4);
759
760 /* copy data into output buffer */
761 memcpy(outbuf[1].pvBuffer, buf, len);
762
763 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
764 sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt->ctxt_handle, 0,
765 &outbuf_desc, 0);
766
767 /* check if the message was encrypted */
768 if(sspi_status == SEC_E_OK) {
769 written = 0;
770
771 /* send the encrypted message including header, data and trailer */
772 len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
773
774 /*
775 It's important to send the full message which includes the header,
776 encrypted payload, and trailer. Until the client receives all the
777 data a coherent message has not been delivered and the client
778 can't read any of it.
779
780 If we wanted to buffer the unwritten encrypted bytes, we would
781 tell the client that all data it has requested to be sent has been
782 sent. The unwritten encrypted bytes would be the first bytes to
783 send on the next invocation.
784 Here's the catch with this - if we tell the client that all the
785 bytes have been sent, will the client call this method again to
786 send the buffered data? Looking at who calls this function, it
787 seems the answer is NO.
788 */
789
790 /* send entire message or fail */
791 while(len > (size_t)written) {
792 ssize_t this_write;
793 long timeleft;
794 int what;
795
796 this_write = 0;
797
798 timeleft = Curl_timeleft(conn->data, NULL, FALSE);
799 if(timeleft < 0) {
800 /* we already got the timeout */
801 failf(conn->data, "schannel: timed out sending data "
802 "(bytes sent: %zd)", written);
803 *err = CURLE_OPERATION_TIMEDOUT;
804 written = -1;
805 break;
806 }
807
808 what = Curl_socket_ready(CURL_SOCKET_BAD, conn->sock[sockindex],
809 timeleft);
810 if(what < 0) {
811 /* fatal error */
812 failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
813 *err = CURLE_SEND_ERROR;
814 written = -1;
815 break;
816 }
817 else if(0 == what) {
818 failf(conn->data, "schannel: timed out sending data "
819 "(bytes sent: %zd)", written);
820 *err = CURLE_OPERATION_TIMEDOUT;
821 written = -1;
822 break;
823 }
824 /* socket is writable */
825
826 result = Curl_write_plain(conn, conn->sock[sockindex], data + written,
827 len - written, &this_write);
828 if(result == CURLE_AGAIN)
829 continue;
830 else if(result != CURLE_OK) {
831 *err = result;
832 written = -1;
833 break;
834 }
835
836 written += this_write;
837 }
838 }
839 else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) {
840 *err = CURLE_OUT_OF_MEMORY;
841 }
842 else{
843 *err = CURLE_SEND_ERROR;
844 }
845
846 Curl_safefree(data);
847
848 if(len == (size_t)written)
849 /* Encrypted message including header, data and trailer entirely sent.
850 The return value is the number of unencrypted bytes that were sent. */
851 written = outbuf[1].cbBuffer;
852
853 return written;
854}
855
856static ssize_t
857schannel_recv(struct connectdata *conn, int sockindex,
858 char *buf, size_t len, CURLcode *err)
859{
860 size_t size = 0;
861 ssize_t nread = -1;
862 struct SessionHandle *data = conn->data;
863 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
864 unsigned char *reallocated_buffer;
865 size_t reallocated_length;
866 bool done = FALSE;
867 SecBuffer inbuf[4];
868 SecBufferDesc inbuf_desc;
869 SECURITY_STATUS sspi_status = SEC_E_OK;
870 /* we want the length of the encrypted buffer to be at least large enough
871 that it can hold all the bytes requested and some TLS record overhead. */
872 size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE;
873
874 /****************************************************************************
875 * Don't return or set connssl->recv_unrecoverable_err unless in the cleanup.
876 * The pattern for return error is set *err, optional infof, goto cleanup.
877 *
878 * Our priority is to always return as much decrypted data to the caller as
879 * possible, even if an error occurs. The state of the decrypted buffer must
880 * always be valid. Transfer of decrypted data to the caller's buffer is
881 * handled in the cleanup.
882 */
883
884 infof(data, "schannel: client wants to read %zu bytes\n", len);
885 *err = CURLE_OK;
886
887 if(len && len <= connssl->decdata_offset) {
888 infof(data, "schannel: enough decrypted data is already available\n");
889 goto cleanup;
890 }
891 else if(connssl->recv_unrecoverable_err) {
892 *err = connssl->recv_unrecoverable_err;
893 infof(data, "schannel: an unrecoverable error occurred in a prior call\n");
894 goto cleanup;
895 }
896 else if(connssl->recv_sspi_close_notify) {
897 /* once a server has indicated shutdown there is no more encrypted data */
898 infof(data, "schannel: server indicated shutdown in a prior call\n");
899 goto cleanup;
900 }
901 else if(!len) {
902 /* It's debatable what to return when !len. Regardless we can't return
903 immediately because there may be data to decrypt (in the case we want to
904 decrypt all encrypted cached data) so handle !len later in cleanup.
905 */
906 ; /* do nothing */
907 }
908 else if(!connssl->recv_connection_closed) {
909 /* increase enc buffer in order to fit the requested amount of data */
910 size = connssl->encdata_length - connssl->encdata_offset;
911 if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE ||
912 connssl->encdata_length < min_encdata_length) {
913 reallocated_length = connssl->encdata_offset +
914 CURL_SCHANNEL_BUFFER_FREE_SIZE;
915 if(reallocated_length < min_encdata_length) {
916 reallocated_length = min_encdata_length;
917 }
918 reallocated_buffer = realloc(connssl->encdata_buffer,
919 reallocated_length);
920 if(reallocated_buffer == NULL) {
921 *err = CURLE_OUT_OF_MEMORY;
922 failf(data, "schannel: unable to re-allocate memory");
923 goto cleanup;
924 }
925
926 connssl->encdata_buffer = reallocated_buffer;
927 connssl->encdata_length = reallocated_length;
928 size = connssl->encdata_length - connssl->encdata_offset;
929 infof(data, "schannel: encdata_buffer resized %zu\n",
930 connssl->encdata_length);
931 }
932
933 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
934 connssl->encdata_offset, connssl->encdata_length);
935
936 /* read encrypted data from socket */
937 *err = Curl_read_plain(conn->sock[sockindex],
938 (char *)(connssl->encdata_buffer +
939 connssl->encdata_offset),
940 size, &nread);
941 if(*err) {
942 nread = -1;
943 if(*err == CURLE_AGAIN)
944 infof(data, "schannel: Curl_read_plain returned CURLE_AGAIN\n");
945 else if(*err == CURLE_RECV_ERROR)
946 infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR\n");
947 else
948 infof(data, "schannel: Curl_read_plain returned error %d\n", *err);
949 }
950 else if(nread == 0) {
951 connssl->recv_connection_closed = true;
952 infof(data, "schannel: server closed the connection\n");
953 }
954 else if(nread > 0) {
955 connssl->encdata_offset += (size_t)nread;
956 infof(data, "schannel: encrypted data got %zd\n", nread);
957 }
958 }
959
960 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
961 connssl->encdata_offset, connssl->encdata_length);
962
963 /* decrypt loop */
964 while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK &&
965 (!len || connssl->decdata_offset < len ||
966 connssl->recv_connection_closed)) {
967 /* prepare data buffer for DecryptMessage call */
968 InitSecBuffer(&inbuf[0], SECBUFFER_DATA, connssl->encdata_buffer,
969 curlx_uztoul(connssl->encdata_offset));
970
971 /* we need 3 more empty input buffers for possible output */
972 InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
973 InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0);
974 InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0);
975 InitSecBufferDesc(&inbuf_desc, inbuf, 4);
976
977 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx
978 */
979 sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
980 &inbuf_desc, 0, NULL);
981
982 /* check if everything went fine (server may want to renegotiate
983 or shutdown the connection context) */
984 if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE ||
985 sspi_status == SEC_I_CONTEXT_EXPIRED) {
986 /* check for successfully decrypted data, even before actual
987 renegotiation or shutdown of the connection context */
988 if(inbuf[1].BufferType == SECBUFFER_DATA) {
989 infof(data, "schannel: decrypted data length: %lu\n",
990 inbuf[1].cbBuffer);
991
992 /* increase buffer in order to fit the received amount of data */
993 size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ?
994 inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE;
995 if(connssl->decdata_length - connssl->decdata_offset < size ||
996 connssl->decdata_length < len) {
997 /* increase internal decrypted data buffer */
998 reallocated_length = connssl->decdata_offset + size;
999 /* make sure that the requested amount of data fits */
1000 if(reallocated_length < len) {
1001 reallocated_length = len;
1002 }
1003 reallocated_buffer = realloc(connssl->decdata_buffer,
1004 reallocated_length);
1005 if(reallocated_buffer == NULL) {
1006 *err = CURLE_OUT_OF_MEMORY;
1007 failf(data, "schannel: unable to re-allocate memory");
1008 goto cleanup;
1009 }
1010 connssl->decdata_buffer = reallocated_buffer;
1011 connssl->decdata_length = reallocated_length;
1012 }
1013
1014 /* copy decrypted data to internal buffer */
1015 size = inbuf[1].cbBuffer;
1016 if(size) {
1017 memcpy(connssl->decdata_buffer + connssl->decdata_offset,
1018 inbuf[1].pvBuffer, size);
1019 connssl->decdata_offset += size;
1020 }
1021
1022 infof(data, "schannel: decrypted data added: %zu\n", size);
1023 infof(data, "schannel: decrypted data cached: offset %zu length %zu\n",
1024 connssl->decdata_offset, connssl->decdata_length);
1025 }
1026
1027 /* check for remaining encrypted data */
1028 if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
1029 infof(data, "schannel: encrypted data length: %lu\n",
1030 inbuf[3].cbBuffer);
1031
1032 /* check if the remaining data is less than the total amount
1033 * and therefore begins after the already processed data
1034 */
1035 if(connssl->encdata_offset > inbuf[3].cbBuffer) {
1036 /* move remaining encrypted data forward to the beginning of
1037 buffer */
1038 memmove(connssl->encdata_buffer,
1039 (connssl->encdata_buffer + connssl->encdata_offset) -
1040 inbuf[3].cbBuffer, inbuf[3].cbBuffer);
1041 connssl->encdata_offset = inbuf[3].cbBuffer;
1042 }
1043
1044 infof(data, "schannel: encrypted data cached: offset %zu length %zu\n",
1045 connssl->encdata_offset, connssl->encdata_length);
1046 }
1047 else {
1048 /* reset encrypted buffer offset, because there is no data remaining */
1049 connssl->encdata_offset = 0;
1050 }
1051
1052 /* check if server wants to renegotiate the connection context */
1053 if(sspi_status == SEC_I_RENEGOTIATE) {
1054 infof(data, "schannel: remote party requests renegotiation\n");
1055 if(*err && *err != CURLE_AGAIN) {
1056 infof(data, "schannel: can't renogotiate, an error is pending\n");
1057 goto cleanup;
1058 }
1059 if(connssl->encdata_offset) {
1060 *err = CURLE_RECV_ERROR;
1061 infof(data, "schannel: can't renogotiate, "
1062 "encrypted data available\n");
1063 goto cleanup;
1064 }
1065 /* begin renegotiation */
1066 infof(data, "schannel: renegotiating SSL/TLS connection\n");
1067 connssl->state = ssl_connection_negotiating;
1068 connssl->connecting_state = ssl_connect_2_writing;
1069 *err = schannel_connect_common(conn, sockindex, FALSE, &done);
1070 if(*err) {
1071 infof(data, "schannel: renegotiation failed\n");
1072 goto cleanup;
1073 }
1074 /* now retry receiving data */
1075 sspi_status = SEC_E_OK;
1076 infof(data, "schannel: SSL/TLS connection renegotiated\n");
1077 continue;
1078 }
1079 /* check if the server closed the connection */
1080 else if(sspi_status == SEC_I_CONTEXT_EXPIRED) {
1081 /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not
1082 returned so we have to work around that in cleanup. */
1083 connssl->recv_sspi_close_notify = true;
1084 if(!connssl->recv_connection_closed) {
1085 connssl->recv_connection_closed = true;
1086 infof(data, "schannel: server closed the connection\n");
1087 }
1088 goto cleanup;
1089 }
1090 }
1091 else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
1092 if(!*err)
1093 *err = CURLE_AGAIN;
1094 infof(data, "schannel: failed to decrypt data, need more data\n");
1095 goto cleanup;
1096 }
1097 else {
1098 *err = CURLE_RECV_ERROR;
1099 infof(data, "schannel: failed to read data from server: %s\n",
1100 Curl_sspi_strerror(conn, sspi_status));
1101 goto cleanup;
1102 }
1103 }
1104
1105 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
1106 connssl->encdata_offset, connssl->encdata_length);
1107
1108 infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
1109 connssl->decdata_offset, connssl->decdata_length);
1110
1111cleanup:
1112 /* Warning- there is no guarantee the encdata state is valid at this point */
1113 infof(data, "schannel: schannel_recv cleanup\n");
1114
1115 /* Error if the connection has closed without a close_notify.
1116 Behavior here is a matter of debate. We don't want to be vulnerable to a
1117 truncation attack however there's some browser precedent for ignoring the
1118 close_notify for compatibility reasons.
1119 Additionally, Windows 2000 (v5.0) is a special case since it seems it doesn't
1120 return close_notify. In that case if the connection was closed we assume it
1121 was graceful (close_notify) since there doesn't seem to be a way to tell.
1122 */
1123 if(len && !connssl->decdata_offset && connssl->recv_connection_closed &&
1124 !connssl->recv_sspi_close_notify) {
1125 bool isWin2k = FALSE;
1126
1127#if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_WIN2K) || \
1128 (_WIN32_WINNT < _WIN32_WINNT_WIN2K)
1129 OSVERSIONINFO osver;
1130
1131 memset(&osver, 0, sizeof(osver));
1132 osver.dwOSVersionInfoSize = sizeof(osver);
1133
1134 /* Find out the Windows version */
1135 if(GetVersionEx(&osver)) {
1136 /* Verify the version number is 5.0 */
1137 if(osver.dwMajorVersion == 5 && osver.dwMinorVersion == 0)
1138 isWin2k = TRUE;
1139 }
1140#else
1141 ULONGLONG cm;
1142 OSVERSIONINFOEX osver;
1143
1144 memset(&osver, 0, sizeof(osver));
1145 osver.dwOSVersionInfoSize = sizeof(osver);
1146 osver.dwMajorVersion = 5;
1147
1148 cm = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
1149 cm = VerSetConditionMask(cm, VER_MINORVERSION, VER_EQUAL);
1150 cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
1151 cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
1152
1153 if(VerifyVersionInfo(&osver, (VER_MAJORVERSION | VER_MINORVERSION |
1154 VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR),
1155 cm))
1156 isWin2k = TRUE;
1157#endif
1158
1159 if(isWin2k && sspi_status == SEC_E_OK)
1160 connssl->recv_sspi_close_notify = true;
1161 else {
1162 *err = CURLE_RECV_ERROR;
1163 infof(data, "schannel: server closed abruptly (missing close_notify)\n");
1164 }
1165 }
1166
1167 /* Any error other than CURLE_AGAIN is an unrecoverable error. */
1168 if(*err && *err != CURLE_AGAIN)
1169 connssl->recv_unrecoverable_err = *err;
1170
1171 size = len < connssl->decdata_offset ? len : connssl->decdata_offset;
1172 if(size) {
1173 memcpy(buf, connssl->decdata_buffer, size);
1174 memmove(connssl->decdata_buffer, connssl->decdata_buffer + size,
1175 connssl->decdata_offset - size);
1176 connssl->decdata_offset -= size;
1177
1178 infof(data, "schannel: decrypted data returned %zu\n", size);
1179 infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
1180 connssl->decdata_offset, connssl->decdata_length);
1181 *err = CURLE_OK;
1182 return (ssize_t)size;
1183 }
1184
1185 if(!*err && !connssl->recv_connection_closed)
1186 *err = CURLE_AGAIN;
1187
1188 /* It's debatable what to return when !len. We could return whatever error we
1189 got from decryption but instead we override here so the return is consistent.
1190 */
1191 if(!len)
1192 *err = CURLE_OK;
1193
1194 return *err ? -1 : 0;
1195}
1196
1197CURLcode
1198Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex,
1199 bool *done)
1200{
1201 return schannel_connect_common(conn, sockindex, TRUE, done);
1202}
1203
1204CURLcode
1205Curl_schannel_connect(struct connectdata *conn, int sockindex)
1206{
1207 CURLcode result;
1208 bool done = FALSE;
1209
1210 result = schannel_connect_common(conn, sockindex, FALSE, &done);
1211 if(result)
1212 return result;
1213
1214 DEBUGASSERT(done);
1215
1216 return CURLE_OK;
1217}
1218
1219bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex)
1220{
1221 const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1222
1223 if(connssl->use) /* SSL/TLS is in use */
1224 return (connssl->encdata_offset > 0 ||
1225 connssl->decdata_offset > 0 ) ? TRUE : FALSE;
1226 else
1227 return FALSE;
1228}
1229
1230void Curl_schannel_close(struct connectdata *conn, int sockindex)
1231{
1232 if(conn->ssl[sockindex].use)
1233 /* if the SSL/TLS channel hasn't been shut down yet, do that now. */
1234 Curl_ssl_shutdown(conn, sockindex);
1235}
1236
1237int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
1238{
1239 /* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
1240 * Shutting Down an Schannel Connection
1241 */
1242 struct SessionHandle *data = conn->data;
1243 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1244
1245 infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
1246 conn->host.name, conn->remote_port);
1247
1248 if(connssl->cred && connssl->ctxt) {
1249 SecBufferDesc BuffDesc;
1250 SecBuffer Buffer;
1251 SECURITY_STATUS sspi_status;
1252 SecBuffer outbuf;
1253 SecBufferDesc outbuf_desc;
1254 CURLcode result;
1255 TCHAR *host_name;
1256 DWORD dwshut = SCHANNEL_SHUTDOWN;
1257
1258 InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
1259 InitSecBufferDesc(&BuffDesc, &Buffer, 1);
1260
1261 sspi_status = s_pSecFn->ApplyControlToken(&connssl->ctxt->ctxt_handle,
1262 &BuffDesc);
1263
1264 if(sspi_status != SEC_E_OK)
1265 failf(data, "schannel: ApplyControlToken failure: %s",
1266 Curl_sspi_strerror(conn, sspi_status));
1267
1268 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
1269 if(!host_name)
1270 return CURLE_OUT_OF_MEMORY;
1271
1272 /* setup output buffer */
1273 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
1274 InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
1275
1276 sspi_status = s_pSecFn->InitializeSecurityContext(
1277 &connssl->cred->cred_handle,
1278 &connssl->ctxt->ctxt_handle,
1279 host_name,
1280 connssl->req_flags,
1281 0,
1282 0,
1283 NULL,
1284 0,
1285 &connssl->ctxt->ctxt_handle,
1286 &outbuf_desc,
1287 &connssl->ret_flags,
1288 &connssl->ctxt->time_stamp);
1289
1290 Curl_unicodefree(host_name);
1291
1292 if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
1293 /* send close message which is in output buffer */
1294 ssize_t written;
1295 result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
1296 outbuf.cbBuffer, &written);
1297
1298 s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
1299 if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
1300 infof(data, "schannel: failed to send close msg: %s"
1301 " (bytes written: %zd)\n", curl_easy_strerror(result), written);
1302 }
1303 }
1304 }
1305
1306 /* free SSPI Schannel API security context handle */
1307 if(connssl->ctxt) {
1308 infof(data, "schannel: clear security context handle\n");
1309 s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
1310 Curl_safefree(connssl->ctxt);
1311 }
1312
1313 /* free SSPI Schannel API credential handle */
1314 if(connssl->cred) {
1315 /* decrement the reference counter of the credential/session handle */
1316 if(connssl->cred->refcount > 0) {
1317 connssl->cred->refcount--;
1318 infof(data, "schannel: decremented credential handle refcount = %d\n",
1319 connssl->cred->refcount);
1320 }
1321
1322 /* if the handle was not cached and the refcount is zero */
1323 if(!connssl->cred->cached && connssl->cred->refcount == 0) {
1324 infof(data, "schannel: clear credential handle\n");
1325 s_pSecFn->FreeCredentialsHandle(&connssl->cred->cred_handle);
1326 Curl_safefree(connssl->cred);
1327 }
1328 }
1329
1330 /* free internal buffer for received encrypted data */
1331 if(connssl->encdata_buffer != NULL) {
1332 Curl_safefree(connssl->encdata_buffer);
1333 connssl->encdata_length = 0;
1334 connssl->encdata_offset = 0;
1335 }
1336
1337 /* free internal buffer for received decrypted data */
1338 if(connssl->decdata_buffer != NULL) {
1339 Curl_safefree(connssl->decdata_buffer);
1340 connssl->decdata_length = 0;
1341 connssl->decdata_offset = 0;
1342 }
1343
1344 return CURLE_OK;
1345}
1346
1347void Curl_schannel_session_free(void *ptr)
1348{
1349 struct curl_schannel_cred *cred = ptr;
1350
1351 if(cred && cred->cached) {
1352 if(cred->refcount == 0) {
1353 s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
1354 Curl_safefree(cred);
1355 }
1356 else {
1357 cred->cached = FALSE;
1358 }
1359 }
1360}
1361
1362int Curl_schannel_init(void)
1363{
1364 return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
1365}
1366
1367void Curl_schannel_cleanup(void)
1368{
1369 Curl_sspi_global_cleanup();
1370}
1371
1372size_t Curl_schannel_version(char *buffer, size_t size)
1373{
1374 size = snprintf(buffer, size, "WinSSL");
1375
1376 return size;
1377}
1378
1379int Curl_schannel_random(unsigned char *entropy, size_t length)
1380{
1381 HCRYPTPROV hCryptProv = 0;
1382
1383 if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
1384 CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
1385 return 1;
1386
1387 if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
1388 CryptReleaseContext(hCryptProv, 0UL);
1389 return 1;
1390 }
1391
1392 CryptReleaseContext(hCryptProv, 0UL);
1393 return 0;
1394}
1395
1396#ifdef _WIN32_WCE
1397static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
1398{
1399 SECURITY_STATUS status;
1400 struct SessionHandle *data = conn->data;
1401 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1402 CURLcode result = CURLE_OK;
1403 CERT_CONTEXT *pCertContextServer = NULL;
1404 const CERT_CHAIN_CONTEXT *pChainContext = NULL;
1405
1406 status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
1407 SECPKG_ATTR_REMOTE_CERT_CONTEXT,
1408 &pCertContextServer);
1409
1410 if((status != SEC_E_OK) || (pCertContextServer == NULL)) {
1411 failf(data, "schannel: Failed to read remote certificate context: %s",
1412 Curl_sspi_strerror(conn, status));
1413 result = CURLE_PEER_FAILED_VERIFICATION;
1414 }
1415
1416 if(result == CURLE_OK) {
1417 CERT_CHAIN_PARA ChainPara;
1418 memset(&ChainPara, 0, sizeof(ChainPara));
1419 ChainPara.cbSize = sizeof(ChainPara);
1420
1421 if(!CertGetCertificateChain(NULL,
1422 pCertContextServer,
1423 NULL,
1424 pCertContextServer->hCertStore,
1425 &ChainPara,
1426 (data->set.ssl_no_revoke ? 0 :
1427 CERT_CHAIN_REVOCATION_CHECK_CHAIN),
1428 NULL,
1429 &pChainContext)) {
1430 failf(data, "schannel: CertGetCertificateChain failed: %s",
1431 Curl_sspi_strerror(conn, GetLastError()));
1432 pChainContext = NULL;
1433 result = CURLE_PEER_FAILED_VERIFICATION;
1434 }
1435
1436 if(result == CURLE_OK) {
1437 CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0];
1438 DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED);
1439 dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
1440 if(dwTrustErrorMask) {
1441 if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED)
1442 failf(data, "schannel: CertGetCertificateChain trust error"
1443 " CERT_TRUST_IS_REVOKED");
1444 else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
1445 failf(data, "schannel: CertGetCertificateChain trust error"
1446 " CERT_TRUST_IS_PARTIAL_CHAIN");
1447 else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
1448 failf(data, "schannel: CertGetCertificateChain trust error"
1449 " CERT_TRUST_IS_UNTRUSTED_ROOT");
1450 else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
1451 failf(data, "schannel: CertGetCertificateChain trust error"
1452 " CERT_TRUST_IS_NOT_TIME_VALID");
1453 else
1454 failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
1455 dwTrustErrorMask);
1456 result = CURLE_PEER_FAILED_VERIFICATION;
1457 }
1458 }
1459 }
1460
1461 if(result == CURLE_OK) {
1462 if(data->set.ssl.verifyhost) {
1463 TCHAR cert_hostname_buff[128];
1464 xcharp_u hostname;
1465 xcharp_u cert_hostname;
1466 DWORD len;
1467
1468 cert_hostname.const_tchar_ptr = cert_hostname_buff;
1469 hostname.tchar_ptr = Curl_convert_UTF8_to_tchar(conn->host.name);
1470
1471 /* TODO: Fix this for certificates with multiple alternative names.
1472 Right now we're only asking for the first preferred alternative name.
1473 Instead we'd need to do all via CERT_NAME_SEARCH_ALL_NAMES_FLAG
1474 (if WinCE supports that?) and run this section in a loop for each.
1475 https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx
1476 curl: (51) schannel: CertGetNameString() certificate hostname
1477 (.google.com) did not match connection (google.com)
1478 */
1479 len = CertGetNameString(pCertContextServer,
1480 CERT_NAME_DNS_TYPE,
1481 0,
1482 NULL,
1483 cert_hostname.tchar_ptr,
1484 128);
1485 if(len > 0 && *cert_hostname.tchar_ptr == '*') {
1486 /* this is a wildcard cert. try matching the last len - 1 chars */
1487 int hostname_len = strlen(conn->host.name);
1488 cert_hostname.tchar_ptr++;
1489 if(_tcsicmp(cert_hostname.const_tchar_ptr,
1490 hostname.const_tchar_ptr + hostname_len - len + 2) != 0)
1491 result = CURLE_PEER_FAILED_VERIFICATION;
1492 }
1493 else if(len == 0 || _tcsicmp(hostname.const_tchar_ptr,
1494 cert_hostname.const_tchar_ptr) != 0) {
1495 result = CURLE_PEER_FAILED_VERIFICATION;
1496 }
1497 if(result == CURLE_PEER_FAILED_VERIFICATION) {
1498 char *_cert_hostname;
1499 _cert_hostname = Curl_convert_tchar_to_UTF8(cert_hostname.tchar_ptr);
1500 failf(data, "schannel: CertGetNameString() certificate hostname "
1501 "(%s) did not match connection (%s)",
1502 _cert_hostname, conn->host.name);
1503 Curl_unicodefree(_cert_hostname);
1504 }
1505 Curl_unicodefree(hostname.tchar_ptr);
1506 }
1507 }
1508
1509 if(pChainContext)
1510 CertFreeCertificateChain(pChainContext);
1511
1512 if(pCertContextServer)
1513 CertFreeCertificateContext(pCertContextServer);
1514
1515 return result;
1516}
1517#endif /* _WIN32_WCE */
1518
1519#endif /* USE_SCHANNEL */
Note: See TracBrowser for help on using the repository browser.