1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
9 * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
10 * Copyright (C) 2012 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
23 ***************************************************************************/
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.
32 * Based upon the PolarSSL implementation in polarssl.c and polarssl.h:
33 * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
35 * Based upon the CyaSSL implementation in cyassl.c and cyassl.h:
36 * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
38 * Thanks for code and inspiration!
41 #include "curl_setup.h"
45 #ifndef USE_WINDOWS_SSPI
46 # error "Can't compile SCHANNEL support without SSPI."
49 #include "curl_sspi.h"
53 #include "connect.h" /* for the connect timeout */
55 #include "select.h" /* for the socket readyness */
56 #include "inet_pton.h" /* for IP addr SNI check */
57 #include "curl_multibyte.h"
60 #include "curl_printf.h"
61 #include "system_win32.h"
63 /* The last #include file should be: */
64 #include "curl_memory.h"
67 /* ALPN requires version 8.1 of the Windows SDK, which was
68 shipped with Visual Studio 2013, aka _MSC_VER 1800:
70 https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx
72 #if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_)
76 /* Uncomment to force verbose output
77 * #define infof(x, y, ...) printf(y, __VA_ARGS__)
78 * #define failf(x, y, ...) printf(y, __VA_ARGS__)
81 static Curl_recv schannel_recv;
82 static Curl_send schannel_send;
85 static CURLcode verify_certificate(struct connectdata *conn, int sockindex);
88 static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType,
89 void *BufDataPtr, unsigned long BufByteSize)
91 buffer->cbBuffer = BufByteSize;
92 buffer->BufferType = BufType;
93 buffer->pvBuffer = BufDataPtr;
96 static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr,
97 unsigned long NumArrElem)
99 desc->ulVersion = SECBUFFER_VERSION;
100 desc->pBuffers = BufArr;
101 desc->cBuffers = NumArrElem;
105 schannel_connect_step1(struct connectdata *conn, int sockindex)
107 ssize_t written = -1;
108 struct Curl_easy *data = conn->data;
109 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
111 SecBufferDesc outbuf_desc;
113 SecBufferDesc inbuf_desc;
115 unsigned char alpn_buffer[128];
117 SCHANNEL_CRED schannel_cred;
118 SECURITY_STATUS sspi_status = SEC_E_OK;
119 struct curl_schannel_cred *old_cred = NULL;
122 struct in6_addr addr6;
127 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
128 conn->host.name, conn->remote_port);
131 /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above.
132 Also it doesn't seem to be supported for Wine, see curl bug #983. */
133 connssl->use_alpn = conn->bits.tls_enable_alpn &&
134 !GetProcAddress(GetModuleHandleA("ntdll"),
135 "wine_get_version") &&
136 Curl_verify_windows_version(6, 3, PLATFORM_WINNT,
137 VERSION_GREATER_THAN_EQUAL);
139 connssl->use_alpn = false;
142 connssl->cred = NULL;
144 /* check for an existing re-usable credential handle */
145 if(conn->ssl_config.sessionid) {
146 Curl_ssl_sessionid_lock(conn);
147 if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL)) {
148 connssl->cred = old_cred;
149 infof(data, "schannel: re-using existing credential handle\n");
151 /* increment the reference counter of the credential/session handle */
152 connssl->cred->refcount++;
153 infof(data, "schannel: incremented credential handle refcount = %d\n",
154 connssl->cred->refcount);
156 Curl_ssl_sessionid_unlock(conn);
160 /* setup Schannel API options */
161 memset(&schannel_cred, 0, sizeof(schannel_cred));
162 schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
164 if(data->set.ssl.verifypeer) {
166 /* certificate validation on CE doesn't seem to work right; we'll
167 do it following a more manual process. */
168 schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
169 SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
170 SCH_CRED_IGNORE_REVOCATION_OFFLINE;
172 schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION;
173 if(data->set.ssl_no_revoke)
174 schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
175 SCH_CRED_IGNORE_REVOCATION_OFFLINE;
177 schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
179 if(data->set.ssl_no_revoke)
180 infof(data, "schannel: disabled server certificate revocation "
183 infof(data, "schannel: checking server certificate revocation\n");
186 schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
187 SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
188 SCH_CRED_IGNORE_REVOCATION_OFFLINE;
189 infof(data, "schannel: disabled server certificate revocation checks\n");
192 if(!data->set.ssl.verifyhost) {
193 schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
194 infof(data, "schannel: verifyhost setting prevents Schannel from "
195 "comparing the supplied target name with the subject "
196 "names in server certificates. Also disables SNI.\n");
199 switch(data->set.ssl.version) {
201 case CURL_SSLVERSION_DEFAULT:
202 case CURL_SSLVERSION_TLSv1:
203 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT |
204 SP_PROT_TLS1_1_CLIENT |
205 SP_PROT_TLS1_2_CLIENT;
207 case CURL_SSLVERSION_TLSv1_0:
208 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT;
210 case CURL_SSLVERSION_TLSv1_1:
211 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT;
213 case CURL_SSLVERSION_TLSv1_2:
214 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
216 case CURL_SSLVERSION_SSLv3:
217 schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
219 case CURL_SSLVERSION_SSLv2:
220 schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
224 /* allocate memory for the re-usable credential handle */
225 connssl->cred = (struct curl_schannel_cred *)
226 malloc(sizeof(struct curl_schannel_cred));
228 failf(data, "schannel: unable to allocate memory");
229 return CURLE_OUT_OF_MEMORY;
231 memset(connssl->cred, 0, sizeof(struct curl_schannel_cred));
232 connssl->cred->refcount = 1;
234 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx
237 s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
238 SECPKG_CRED_OUTBOUND, NULL,
239 &schannel_cred, NULL, NULL,
240 &connssl->cred->cred_handle,
241 &connssl->cred->time_stamp);
243 if(sspi_status != SEC_E_OK) {
244 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
245 failf(data, "schannel: SNI or certificate check failed: %s",
246 Curl_sspi_strerror(conn, sspi_status));
248 failf(data, "schannel: AcquireCredentialsHandle failed: %s",
249 Curl_sspi_strerror(conn, sspi_status));
250 Curl_safefree(connssl->cred);
251 return CURLE_SSL_CONNECT_ERROR;
255 /* Warn if SNI is disabled due to use of an IP address */
256 if(Curl_inet_pton(AF_INET, conn->host.name, &addr)
258 || Curl_inet_pton(AF_INET6, conn->host.name, &addr6)
261 infof(data, "schannel: using IP address, SNI is not supported by OS.\n");
265 if(connssl->use_alpn) {
267 int list_start_index = 0;
268 unsigned int* extension_len = NULL;
269 unsigned short* list_len = NULL;
271 /* The first four bytes will be an unsigned int indicating number
272 of bytes of data in the rest of the the buffer. */
273 extension_len = (unsigned int*)(&alpn_buffer[cur]);
274 cur += sizeof(unsigned int);
276 /* The next four bytes are an indicator that this buffer will contain
277 ALPN data, as opposed to NPN, for example. */
278 *(unsigned int*)&alpn_buffer[cur] =
279 SecApplicationProtocolNegotiationExt_ALPN;
280 cur += sizeof(unsigned int);
282 /* The next two bytes will be an unsigned short indicating the number
283 of bytes used to list the preferred protocols. */
284 list_len = (unsigned short*)(&alpn_buffer[cur]);
285 cur += sizeof(unsigned short);
287 list_start_index = cur;
290 if(data->set.httpversion >= CURL_HTTP_VERSION_2) {
291 memcpy(&alpn_buffer[cur], NGHTTP2_PROTO_ALPN, NGHTTP2_PROTO_ALPN_LEN);
292 cur += NGHTTP2_PROTO_ALPN_LEN;
293 infof(data, "schannel: ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
297 alpn_buffer[cur++] = ALPN_HTTP_1_1_LENGTH;
298 memcpy(&alpn_buffer[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
299 cur += ALPN_HTTP_1_1_LENGTH;
300 infof(data, "schannel: ALPN, offering %s\n", ALPN_HTTP_1_1);
302 *list_len = curlx_uitous(cur - list_start_index);
303 *extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short);
305 InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur);
306 InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
310 InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
311 InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
314 InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
315 InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
318 /* setup output buffer */
319 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
320 InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
322 /* setup request flags */
323 connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
324 ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
327 /* allocate memory for the security context handle */
328 connssl->ctxt = (struct curl_schannel_ctxt *)
329 malloc(sizeof(struct curl_schannel_ctxt));
331 failf(data, "schannel: unable to allocate memory");
332 return CURLE_OUT_OF_MEMORY;
334 memset(connssl->ctxt, 0, sizeof(struct curl_schannel_ctxt));
336 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
338 return CURLE_OUT_OF_MEMORY;
340 /* Schannel InitializeSecurityContext:
341 https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
343 At the moment we don't pass inbuf unless we're using ALPN since we only
344 use it for that, and Wine (for which we currently disable ALPN) is giving
345 us problems with inbuf regardless. https://github.com/curl/curl/issues/983
347 sspi_status = s_pSecFn->InitializeSecurityContext(
348 &connssl->cred->cred_handle, NULL, host_name, connssl->req_flags, 0, 0,
349 (connssl->use_alpn ? &inbuf_desc : NULL),
350 0, &connssl->ctxt->ctxt_handle,
351 &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
353 Curl_unicodefree(host_name);
355 if(sspi_status != SEC_I_CONTINUE_NEEDED) {
356 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
357 failf(data, "schannel: SNI or certificate check failed: %s",
358 Curl_sspi_strerror(conn, sspi_status));
360 failf(data, "schannel: initial InitializeSecurityContext failed: %s",
361 Curl_sspi_strerror(conn, sspi_status));
362 Curl_safefree(connssl->ctxt);
363 return CURLE_SSL_CONNECT_ERROR;
366 infof(data, "schannel: sending initial handshake data: "
367 "sending %lu bytes...\n", outbuf.cbBuffer);
369 /* send initial handshake data which is now stored in output buffer */
370 result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
371 outbuf.cbBuffer, &written);
372 s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
373 if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
374 failf(data, "schannel: failed to send initial handshake data: "
375 "sent %zd of %lu bytes", written, outbuf.cbBuffer);
376 return CURLE_SSL_CONNECT_ERROR;
379 infof(data, "schannel: sent initial handshake data: "
380 "sent %zd bytes\n", written);
382 connssl->recv_unrecoverable_err = CURLE_OK;
383 connssl->recv_sspi_close_notify = false;
384 connssl->recv_connection_closed = false;
386 /* continue to second handshake step */
387 connssl->connecting_state = ssl_connect_2;
393 schannel_connect_step2(struct connectdata *conn, int sockindex)
396 ssize_t nread = -1, written = -1;
397 struct Curl_easy *data = conn->data;
398 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
399 unsigned char *reallocated_buffer;
400 size_t reallocated_length;
402 SecBufferDesc outbuf_desc;
404 SecBufferDesc inbuf_desc;
405 SECURITY_STATUS sspi_status = SEC_E_OK;
410 doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
412 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
413 conn->host.name, conn->remote_port);
415 if(!connssl->cred || !connssl->ctxt)
416 return CURLE_SSL_CONNECT_ERROR;
418 /* buffer to store previously received and decrypted data */
419 if(connssl->decdata_buffer == NULL) {
420 connssl->decdata_offset = 0;
421 connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
422 connssl->decdata_buffer = malloc(connssl->decdata_length);
423 if(connssl->decdata_buffer == NULL) {
424 failf(data, "schannel: unable to allocate memory");
425 return CURLE_OUT_OF_MEMORY;
429 /* buffer to store previously received and encrypted data */
430 if(connssl->encdata_buffer == NULL) {
431 connssl->encdata_offset = 0;
432 connssl->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
433 connssl->encdata_buffer = malloc(connssl->encdata_length);
434 if(connssl->encdata_buffer == NULL) {
435 failf(data, "schannel: unable to allocate memory");
436 return CURLE_OUT_OF_MEMORY;
440 /* if we need a bigger buffer to read a full message, increase buffer now */
441 if(connssl->encdata_length - connssl->encdata_offset <
442 CURL_SCHANNEL_BUFFER_FREE_SIZE) {
443 /* increase internal encrypted data buffer */
444 reallocated_length = connssl->encdata_offset +
445 CURL_SCHANNEL_BUFFER_FREE_SIZE;
446 reallocated_buffer = realloc(connssl->encdata_buffer,
449 if(reallocated_buffer == NULL) {
450 failf(data, "schannel: unable to re-allocate memory");
451 return CURLE_OUT_OF_MEMORY;
454 connssl->encdata_buffer = reallocated_buffer;
455 connssl->encdata_length = reallocated_length;
461 /* read encrypted handshake data from socket */
462 result = Curl_read_plain(conn->sock[sockindex],
463 (char *) (connssl->encdata_buffer +
464 connssl->encdata_offset),
465 connssl->encdata_length -
466 connssl->encdata_offset,
468 if(result == CURLE_AGAIN) {
469 if(connssl->connecting_state != ssl_connect_2_writing)
470 connssl->connecting_state = ssl_connect_2_reading;
471 infof(data, "schannel: failed to receive handshake, "
475 else if((result != CURLE_OK) || (nread == 0)) {
476 failf(data, "schannel: failed to receive handshake, "
477 "SSL/TLS connection failed");
478 return CURLE_SSL_CONNECT_ERROR;
481 /* increase encrypted data buffer offset */
482 connssl->encdata_offset += nread;
485 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
486 connssl->encdata_offset, connssl->encdata_length);
488 /* setup input buffers */
489 InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(connssl->encdata_offset),
490 curlx_uztoul(connssl->encdata_offset));
491 InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
492 InitSecBufferDesc(&inbuf_desc, inbuf, 2);
494 /* setup output buffers */
495 InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
496 InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
497 InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0);
498 InitSecBufferDesc(&outbuf_desc, outbuf, 3);
500 if(inbuf[0].pvBuffer == NULL) {
501 failf(data, "schannel: unable to allocate memory");
502 return CURLE_OUT_OF_MEMORY;
505 /* copy received handshake data into input buffer */
506 memcpy(inbuf[0].pvBuffer, connssl->encdata_buffer,
507 connssl->encdata_offset);
509 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
511 return CURLE_OUT_OF_MEMORY;
513 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
515 sspi_status = s_pSecFn->InitializeSecurityContext(
516 &connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle,
517 host_name, connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL,
518 &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
520 Curl_unicodefree(host_name);
522 /* free buffer for received handshake data */
523 Curl_safefree(inbuf[0].pvBuffer);
525 /* check if the handshake was incomplete */
526 if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
527 connssl->connecting_state = ssl_connect_2_reading;
528 infof(data, "schannel: received incomplete message, need more data\n");
532 /* If the server has requested a client certificate, attempt to continue
533 the handshake without one. This will allow connections to servers which
534 request a client certificate but do not require it. */
535 if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS &&
536 !(connssl->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
537 connssl->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
538 connssl->connecting_state = ssl_connect_2_writing;
539 infof(data, "schannel: a client certificate has been requested\n");
543 /* check if the handshake needs to be continued */
544 if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
545 for(i = 0; i < 3; i++) {
546 /* search for handshake tokens that need to be send */
547 if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
548 infof(data, "schannel: sending next handshake data: "
549 "sending %lu bytes...\n", outbuf[i].cbBuffer);
551 /* send handshake token to server */
552 result = Curl_write_plain(conn, conn->sock[sockindex],
553 outbuf[i].pvBuffer, outbuf[i].cbBuffer,
555 if((result != CURLE_OK) ||
556 (outbuf[i].cbBuffer != (size_t) written)) {
557 failf(data, "schannel: failed to send next handshake data: "
558 "sent %zd of %lu bytes", written, outbuf[i].cbBuffer);
559 return CURLE_SSL_CONNECT_ERROR;
563 /* free obsolete buffer */
564 if(outbuf[i].pvBuffer != NULL) {
565 s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
570 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
571 failf(data, "schannel: SNI or certificate check failed: %s",
572 Curl_sspi_strerror(conn, sspi_status));
574 failf(data, "schannel: next InitializeSecurityContext failed: %s",
575 Curl_sspi_strerror(conn, sspi_status));
576 return CURLE_SSL_CONNECT_ERROR;
579 /* check if there was additional remaining encrypted data */
580 if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
581 infof(data, "schannel: encrypted data length: %lu\n", inbuf[1].cbBuffer);
583 There are two cases where we could be getting extra data here:
584 1) If we're renegotiating a connection and the handshake is already
585 complete (from the server perspective), it can encrypted app data
586 (not handshake data) in an extra buffer at this point.
587 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a
588 connection and this extra data is part of the handshake.
589 We should process the data immediately; waiting for the socket to
590 be ready may fail since the server is done sending handshake data.
592 /* check if the remaining data is less than the total amount
593 and therefore begins after the already processed data */
594 if(connssl->encdata_offset > inbuf[1].cbBuffer) {
595 memmove(connssl->encdata_buffer,
596 (connssl->encdata_buffer + connssl->encdata_offset) -
597 inbuf[1].cbBuffer, inbuf[1].cbBuffer);
598 connssl->encdata_offset = inbuf[1].cbBuffer;
599 if(sspi_status == SEC_I_CONTINUE_NEEDED) {
606 connssl->encdata_offset = 0;
611 /* check if the handshake needs to be continued */
612 if(sspi_status == SEC_I_CONTINUE_NEEDED) {
613 connssl->connecting_state = ssl_connect_2_reading;
617 /* check if the handshake is complete */
618 if(sspi_status == SEC_E_OK) {
619 connssl->connecting_state = ssl_connect_3;
620 infof(data, "schannel: SSL/TLS handshake complete\n");
624 /* Windows CE doesn't do any server certificate validation.
625 We have to do it manually. */
626 if(data->set.ssl.verifypeer)
627 return verify_certificate(conn, sockindex);
634 schannel_connect_step3(struct connectdata *conn, int sockindex)
636 CURLcode result = CURLE_OK;
637 struct Curl_easy *data = conn->data;
638 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
639 SECURITY_STATUS sspi_status = SEC_E_OK;
640 CERT_CONTEXT *ccert_context = NULL;
642 SecPkgContext_ApplicationProtocol alpn_result;
645 DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
647 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
648 conn->host.name, conn->remote_port);
651 return CURLE_SSL_CONNECT_ERROR;
653 /* check if the required context attributes are met */
654 if(connssl->ret_flags != connssl->req_flags) {
655 if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
656 failf(data, "schannel: failed to setup sequence detection");
657 if(!(connssl->ret_flags & ISC_RET_REPLAY_DETECT))
658 failf(data, "schannel: failed to setup replay detection");
659 if(!(connssl->ret_flags & ISC_RET_CONFIDENTIALITY))
660 failf(data, "schannel: failed to setup confidentiality");
661 if(!(connssl->ret_flags & ISC_RET_ALLOCATED_MEMORY))
662 failf(data, "schannel: failed to setup memory allocation");
663 if(!(connssl->ret_flags & ISC_RET_STREAM))
664 failf(data, "schannel: failed to setup stream orientation");
665 return CURLE_SSL_CONNECT_ERROR;
669 if(connssl->use_alpn) {
670 sspi_status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
671 SECPKG_ATTR_APPLICATION_PROTOCOL, &alpn_result);
673 if(sspi_status != SEC_E_OK) {
674 failf(data, "schannel: failed to retrieve ALPN result");
675 return CURLE_SSL_CONNECT_ERROR;
678 if(alpn_result.ProtoNegoStatus ==
679 SecApplicationProtocolNegotiationStatus_Success) {
681 infof(data, "schannel: ALPN, server accepted to use %.*s\n",
682 alpn_result.ProtocolIdSize, alpn_result.ProtocolId);
685 if(alpn_result.ProtocolIdSize == NGHTTP2_PROTO_VERSION_ID_LEN &&
686 !memcmp(NGHTTP2_PROTO_VERSION_ID, alpn_result.ProtocolId,
687 NGHTTP2_PROTO_VERSION_ID_LEN)) {
688 conn->negnpn = CURL_HTTP_VERSION_2;
692 if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH &&
693 !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId,
694 ALPN_HTTP_1_1_LENGTH)) {
695 conn->negnpn = CURL_HTTP_VERSION_1_1;
699 infof(data, "ALPN, server did not agree to a protocol\n");
703 /* save the current session data for possible re-use */
704 if(conn->ssl_config.sessionid) {
706 struct curl_schannel_cred *old_cred = NULL;
708 Curl_ssl_sessionid_lock(conn);
709 incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL));
711 if(old_cred != connssl->cred) {
712 infof(data, "schannel: old credential handle is stale, removing\n");
713 /* we're not taking old_cred ownership here, no refcount++ is needed */
714 Curl_ssl_delsessionid(conn, (void *)old_cred);
719 result = Curl_ssl_addsessionid(conn, (void *)connssl->cred,
720 sizeof(struct curl_schannel_cred));
722 Curl_ssl_sessionid_unlock(conn);
723 failf(data, "schannel: failed to store credential handle");
727 /* this cred session is now also referenced by sessionid cache */
728 connssl->cred->refcount++;
729 infof(data, "schannel: stored credential handle in session cache\n");
732 Curl_ssl_sessionid_unlock(conn);
735 if(data->set.ssl.certinfo) {
736 sspi_status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
737 SECPKG_ATTR_REMOTE_CERT_CONTEXT, &ccert_context);
739 if((sspi_status != SEC_E_OK) || (ccert_context == NULL)) {
740 failf(data, "schannel: failed to retrieve remote cert context");
741 return CURLE_SSL_CONNECT_ERROR;
744 result = Curl_ssl_init_certinfo(data, 1);
746 if(((ccert_context->dwCertEncodingType & X509_ASN_ENCODING) != 0) &&
747 (ccert_context->cbCertEncoded > 0)) {
749 const char *beg = (const char *) ccert_context->pbCertEncoded;
750 const char *end = beg + ccert_context->cbCertEncoded;
751 result = Curl_extract_certinfo(conn, 0, beg, end);
754 CertFreeCertificateContext(ccert_context);
759 connssl->connecting_state = ssl_connect_done;
765 schannel_connect_common(struct connectdata *conn, int sockindex,
766 bool nonblocking, bool *done)
769 struct Curl_easy *data = conn->data;
770 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
771 curl_socket_t sockfd = conn->sock[sockindex];
775 /* check if the connection has already been established */
776 if(ssl_connection_complete == connssl->state) {
781 if(ssl_connect_1 == connssl->connecting_state) {
782 /* check out how much more time we're allowed */
783 timeout_ms = Curl_timeleft(data, NULL, TRUE);
786 /* no need to continue if time already is up */
787 failf(data, "SSL/TLS connection timeout");
788 return CURLE_OPERATION_TIMEDOUT;
791 result = schannel_connect_step1(conn, sockindex);
796 while(ssl_connect_2 == connssl->connecting_state ||
797 ssl_connect_2_reading == connssl->connecting_state ||
798 ssl_connect_2_writing == connssl->connecting_state) {
800 /* check out how much more time we're allowed */
801 timeout_ms = Curl_timeleft(data, NULL, TRUE);
804 /* no need to continue if time already is up */
805 failf(data, "SSL/TLS connection timeout");
806 return CURLE_OPERATION_TIMEDOUT;
809 /* if ssl is expecting something, check if it's available. */
810 if(connssl->connecting_state == ssl_connect_2_reading
811 || connssl->connecting_state == ssl_connect_2_writing) {
813 curl_socket_t writefd = ssl_connect_2_writing ==
814 connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
815 curl_socket_t readfd = ssl_connect_2_reading ==
816 connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
818 what = Curl_socket_ready(readfd, writefd, nonblocking ? 0 : timeout_ms);
821 failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
822 return CURLE_SSL_CONNECT_ERROR;
831 failf(data, "SSL/TLS connection timeout");
832 return CURLE_OPERATION_TIMEDOUT;
835 /* socket is readable or writable */
838 /* Run transaction, and return to the caller if it failed or if
839 * this connection is part of a multi handle and this loop would
840 * execute again. This permits the owner of a multi handle to
841 * abort a connection attempt before step2 has completed while
842 * ensuring that a client using select() or epoll() will always
843 * have a valid fdset to wait on.
845 result = schannel_connect_step2(conn, sockindex);
846 if(result || (nonblocking &&
847 (ssl_connect_2 == connssl->connecting_state ||
848 ssl_connect_2_reading == connssl->connecting_state ||
849 ssl_connect_2_writing == connssl->connecting_state)))
852 } /* repeat step2 until all transactions are done. */
854 if(ssl_connect_3 == connssl->connecting_state) {
855 result = schannel_connect_step3(conn, sockindex);
860 if(ssl_connect_done == connssl->connecting_state) {
861 connssl->state = ssl_connection_complete;
862 conn->recv[sockindex] = schannel_recv;
863 conn->send[sockindex] = schannel_send;
869 /* reset our connection state machine */
870 connssl->connecting_state = ssl_connect_1;
876 schannel_send(struct connectdata *conn, int sockindex,
877 const void *buf, size_t len, CURLcode *err)
879 ssize_t written = -1;
881 unsigned char *data = NULL;
882 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
884 SecBufferDesc outbuf_desc;
885 SECURITY_STATUS sspi_status = SEC_E_OK;
888 /* check if the maximum stream sizes were queried */
889 if(connssl->stream_sizes.cbMaximumMessage == 0) {
890 sspi_status = s_pSecFn->QueryContextAttributes(
891 &connssl->ctxt->ctxt_handle,
892 SECPKG_ATTR_STREAM_SIZES,
893 &connssl->stream_sizes);
894 if(sspi_status != SEC_E_OK) {
895 *err = CURLE_SEND_ERROR;
900 /* check if the buffer is longer than the maximum message length */
901 if(len > connssl->stream_sizes.cbMaximumMessage) {
902 *err = CURLE_SEND_ERROR;
906 /* calculate the complete message length and allocate a buffer for it */
907 data_len = connssl->stream_sizes.cbHeader + len +
908 connssl->stream_sizes.cbTrailer;
909 data = (unsigned char *) malloc(data_len);
911 *err = CURLE_OUT_OF_MEMORY;
915 /* setup output buffers (header, data, trailer, empty) */
916 InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
917 data, connssl->stream_sizes.cbHeader);
918 InitSecBuffer(&outbuf[1], SECBUFFER_DATA,
919 data + connssl->stream_sizes.cbHeader, curlx_uztoul(len));
920 InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
921 data + connssl->stream_sizes.cbHeader + len,
922 connssl->stream_sizes.cbTrailer);
923 InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0);
924 InitSecBufferDesc(&outbuf_desc, outbuf, 4);
926 /* copy data into output buffer */
927 memcpy(outbuf[1].pvBuffer, buf, len);
929 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
930 sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt->ctxt_handle, 0,
933 /* check if the message was encrypted */
934 if(sspi_status == SEC_E_OK) {
937 /* send the encrypted message including header, data and trailer */
938 len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
941 It's important to send the full message which includes the header,
942 encrypted payload, and trailer. Until the client receives all the
943 data a coherent message has not been delivered and the client
944 can't read any of it.
946 If we wanted to buffer the unwritten encrypted bytes, we would
947 tell the client that all data it has requested to be sent has been
948 sent. The unwritten encrypted bytes would be the first bytes to
949 send on the next invocation.
950 Here's the catch with this - if we tell the client that all the
951 bytes have been sent, will the client call this method again to
952 send the buffered data? Looking at who calls this function, it
953 seems the answer is NO.
956 /* send entire message or fail */
957 while(len > (size_t)written) {
964 timeleft = Curl_timeleft(conn->data, NULL, FALSE);
966 /* we already got the timeout */
967 failf(conn->data, "schannel: timed out sending data "
968 "(bytes sent: %zd)", written);
969 *err = CURLE_OPERATION_TIMEDOUT;
974 what = Curl_socket_ready(CURL_SOCKET_BAD, conn->sock[sockindex],
978 failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
979 *err = CURLE_SEND_ERROR;
984 failf(conn->data, "schannel: timed out sending data "
985 "(bytes sent: %zd)", written);
986 *err = CURLE_OPERATION_TIMEDOUT;
990 /* socket is writable */
992 result = Curl_write_plain(conn, conn->sock[sockindex], data + written,
993 len - written, &this_write);
994 if(result == CURLE_AGAIN)
996 else if(result != CURLE_OK) {
1002 written += this_write;
1005 else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) {
1006 *err = CURLE_OUT_OF_MEMORY;
1009 *err = CURLE_SEND_ERROR;
1012 Curl_safefree(data);
1014 if(len == (size_t)written)
1015 /* Encrypted message including header, data and trailer entirely sent.
1016 The return value is the number of unencrypted bytes that were sent. */
1017 written = outbuf[1].cbBuffer;
1023 schannel_recv(struct connectdata *conn, int sockindex,
1024 char *buf, size_t len, CURLcode *err)
1028 struct Curl_easy *data = conn->data;
1029 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1030 unsigned char *reallocated_buffer;
1031 size_t reallocated_length;
1034 SecBufferDesc inbuf_desc;
1035 SECURITY_STATUS sspi_status = SEC_E_OK;
1036 /* we want the length of the encrypted buffer to be at least large enough
1037 that it can hold all the bytes requested and some TLS record overhead. */
1038 size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE;
1040 /****************************************************************************
1041 * Don't return or set connssl->recv_unrecoverable_err unless in the cleanup.
1042 * The pattern for return error is set *err, optional infof, goto cleanup.
1044 * Our priority is to always return as much decrypted data to the caller as
1045 * possible, even if an error occurs. The state of the decrypted buffer must
1046 * always be valid. Transfer of decrypted data to the caller's buffer is
1047 * handled in the cleanup.
1050 infof(data, "schannel: client wants to read %zu bytes\n", len);
1053 if(len && len <= connssl->decdata_offset) {
1054 infof(data, "schannel: enough decrypted data is already available\n");
1057 else if(connssl->recv_unrecoverable_err) {
1058 *err = connssl->recv_unrecoverable_err;
1059 infof(data, "schannel: an unrecoverable error occurred in a prior call\n");
1062 else if(connssl->recv_sspi_close_notify) {
1063 /* once a server has indicated shutdown there is no more encrypted data */
1064 infof(data, "schannel: server indicated shutdown in a prior call\n");
1068 /* It's debatable what to return when !len. Regardless we can't return
1069 immediately because there may be data to decrypt (in the case we want to
1070 decrypt all encrypted cached data) so handle !len later in cleanup.
1074 else if(!connssl->recv_connection_closed) {
1075 /* increase enc buffer in order to fit the requested amount of data */
1076 size = connssl->encdata_length - connssl->encdata_offset;
1077 if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE ||
1078 connssl->encdata_length < min_encdata_length) {
1079 reallocated_length = connssl->encdata_offset +
1080 CURL_SCHANNEL_BUFFER_FREE_SIZE;
1081 if(reallocated_length < min_encdata_length) {
1082 reallocated_length = min_encdata_length;
1084 reallocated_buffer = realloc(connssl->encdata_buffer,
1085 reallocated_length);
1086 if(reallocated_buffer == NULL) {
1087 *err = CURLE_OUT_OF_MEMORY;
1088 failf(data, "schannel: unable to re-allocate memory");
1092 connssl->encdata_buffer = reallocated_buffer;
1093 connssl->encdata_length = reallocated_length;
1094 size = connssl->encdata_length - connssl->encdata_offset;
1095 infof(data, "schannel: encdata_buffer resized %zu\n",
1096 connssl->encdata_length);
1099 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
1100 connssl->encdata_offset, connssl->encdata_length);
1102 /* read encrypted data from socket */
1103 *err = Curl_read_plain(conn->sock[sockindex],
1104 (char *)(connssl->encdata_buffer +
1105 connssl->encdata_offset),
1109 if(*err == CURLE_AGAIN)
1110 infof(data, "schannel: Curl_read_plain returned CURLE_AGAIN\n");
1111 else if(*err == CURLE_RECV_ERROR)
1112 infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR\n");
1114 infof(data, "schannel: Curl_read_plain returned error %d\n", *err);
1116 else if(nread == 0) {
1117 connssl->recv_connection_closed = true;
1118 infof(data, "schannel: server closed the connection\n");
1120 else if(nread > 0) {
1121 connssl->encdata_offset += (size_t)nread;
1122 infof(data, "schannel: encrypted data got %zd\n", nread);
1126 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
1127 connssl->encdata_offset, connssl->encdata_length);
1130 while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK &&
1131 (!len || connssl->decdata_offset < len ||
1132 connssl->recv_connection_closed)) {
1133 /* prepare data buffer for DecryptMessage call */
1134 InitSecBuffer(&inbuf[0], SECBUFFER_DATA, connssl->encdata_buffer,
1135 curlx_uztoul(connssl->encdata_offset));
1137 /* we need 3 more empty input buffers for possible output */
1138 InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
1139 InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0);
1140 InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0);
1141 InitSecBufferDesc(&inbuf_desc, inbuf, 4);
1143 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx
1145 sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
1146 &inbuf_desc, 0, NULL);
1148 /* check if everything went fine (server may want to renegotiate
1149 or shutdown the connection context) */
1150 if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE ||
1151 sspi_status == SEC_I_CONTEXT_EXPIRED) {
1152 /* check for successfully decrypted data, even before actual
1153 renegotiation or shutdown of the connection context */
1154 if(inbuf[1].BufferType == SECBUFFER_DATA) {
1155 infof(data, "schannel: decrypted data length: %lu\n",
1158 /* increase buffer in order to fit the received amount of data */
1159 size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ?
1160 inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE;
1161 if(connssl->decdata_length - connssl->decdata_offset < size ||
1162 connssl->decdata_length < len) {
1163 /* increase internal decrypted data buffer */
1164 reallocated_length = connssl->decdata_offset + size;
1165 /* make sure that the requested amount of data fits */
1166 if(reallocated_length < len) {
1167 reallocated_length = len;
1169 reallocated_buffer = realloc(connssl->decdata_buffer,
1170 reallocated_length);
1171 if(reallocated_buffer == NULL) {
1172 *err = CURLE_OUT_OF_MEMORY;
1173 failf(data, "schannel: unable to re-allocate memory");
1176 connssl->decdata_buffer = reallocated_buffer;
1177 connssl->decdata_length = reallocated_length;
1180 /* copy decrypted data to internal buffer */
1181 size = inbuf[1].cbBuffer;
1183 memcpy(connssl->decdata_buffer + connssl->decdata_offset,
1184 inbuf[1].pvBuffer, size);
1185 connssl->decdata_offset += size;
1188 infof(data, "schannel: decrypted data added: %zu\n", size);
1189 infof(data, "schannel: decrypted data cached: offset %zu length %zu\n",
1190 connssl->decdata_offset, connssl->decdata_length);
1193 /* check for remaining encrypted data */
1194 if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
1195 infof(data, "schannel: encrypted data length: %lu\n",
1198 /* check if the remaining data is less than the total amount
1199 * and therefore begins after the already processed data
1201 if(connssl->encdata_offset > inbuf[3].cbBuffer) {
1202 /* move remaining encrypted data forward to the beginning of
1204 memmove(connssl->encdata_buffer,
1205 (connssl->encdata_buffer + connssl->encdata_offset) -
1206 inbuf[3].cbBuffer, inbuf[3].cbBuffer);
1207 connssl->encdata_offset = inbuf[3].cbBuffer;
1210 infof(data, "schannel: encrypted data cached: offset %zu length %zu\n",
1211 connssl->encdata_offset, connssl->encdata_length);
1214 /* reset encrypted buffer offset, because there is no data remaining */
1215 connssl->encdata_offset = 0;
1218 /* check if server wants to renegotiate the connection context */
1219 if(sspi_status == SEC_I_RENEGOTIATE) {
1220 infof(data, "schannel: remote party requests renegotiation\n");
1221 if(*err && *err != CURLE_AGAIN) {
1222 infof(data, "schannel: can't renogotiate, an error is pending\n");
1225 if(connssl->encdata_offset) {
1226 *err = CURLE_RECV_ERROR;
1227 infof(data, "schannel: can't renogotiate, "
1228 "encrypted data available\n");
1231 /* begin renegotiation */
1232 infof(data, "schannel: renegotiating SSL/TLS connection\n");
1233 connssl->state = ssl_connection_negotiating;
1234 connssl->connecting_state = ssl_connect_2_writing;
1235 *err = schannel_connect_common(conn, sockindex, FALSE, &done);
1237 infof(data, "schannel: renegotiation failed\n");
1240 /* now retry receiving data */
1241 sspi_status = SEC_E_OK;
1242 infof(data, "schannel: SSL/TLS connection renegotiated\n");
1245 /* check if the server closed the connection */
1246 else if(sspi_status == SEC_I_CONTEXT_EXPIRED) {
1247 /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not
1248 returned so we have to work around that in cleanup. */
1249 connssl->recv_sspi_close_notify = true;
1250 if(!connssl->recv_connection_closed) {
1251 connssl->recv_connection_closed = true;
1252 infof(data, "schannel: server closed the connection\n");
1257 else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
1260 infof(data, "schannel: failed to decrypt data, need more data\n");
1264 *err = CURLE_RECV_ERROR;
1265 infof(data, "schannel: failed to read data from server: %s\n",
1266 Curl_sspi_strerror(conn, sspi_status));
1271 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
1272 connssl->encdata_offset, connssl->encdata_length);
1274 infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
1275 connssl->decdata_offset, connssl->decdata_length);
1278 /* Warning- there is no guarantee the encdata state is valid at this point */
1279 infof(data, "schannel: schannel_recv cleanup\n");
1281 /* Error if the connection has closed without a close_notify.
1282 Behavior here is a matter of debate. We don't want to be vulnerable to a
1283 truncation attack however there's some browser precedent for ignoring the
1284 close_notify for compatibility reasons.
1285 Additionally, Windows 2000 (v5.0) is a special case since it seems it doesn't
1286 return close_notify. In that case if the connection was closed we assume it
1287 was graceful (close_notify) since there doesn't seem to be a way to tell.
1289 if(len && !connssl->decdata_offset && connssl->recv_connection_closed &&
1290 !connssl->recv_sspi_close_notify) {
1291 bool isWin2k = Curl_verify_windows_version(5, 0, PLATFORM_WINNT,
1294 if(isWin2k && sspi_status == SEC_E_OK)
1295 connssl->recv_sspi_close_notify = true;
1297 *err = CURLE_RECV_ERROR;
1298 infof(data, "schannel: server closed abruptly (missing close_notify)\n");
1302 /* Any error other than CURLE_AGAIN is an unrecoverable error. */
1303 if(*err && *err != CURLE_AGAIN)
1304 connssl->recv_unrecoverable_err = *err;
1306 size = len < connssl->decdata_offset ? len : connssl->decdata_offset;
1308 memcpy(buf, connssl->decdata_buffer, size);
1309 memmove(connssl->decdata_buffer, connssl->decdata_buffer + size,
1310 connssl->decdata_offset - size);
1311 connssl->decdata_offset -= size;
1313 infof(data, "schannel: decrypted data returned %zu\n", size);
1314 infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
1315 connssl->decdata_offset, connssl->decdata_length);
1317 return (ssize_t)size;
1320 if(!*err && !connssl->recv_connection_closed)
1323 /* It's debatable what to return when !len. We could return whatever error we
1324 got from decryption but instead we override here so the return is consistent.
1329 return *err ? -1 : 0;
1333 Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex,
1336 return schannel_connect_common(conn, sockindex, TRUE, done);
1340 Curl_schannel_connect(struct connectdata *conn, int sockindex)
1345 result = schannel_connect_common(conn, sockindex, FALSE, &done);
1354 bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex)
1356 const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1358 if(connssl->use) /* SSL/TLS is in use */
1359 return (connssl->encdata_offset > 0 ||
1360 connssl->decdata_offset > 0) ? TRUE : FALSE;
1365 void Curl_schannel_close(struct connectdata *conn, int sockindex)
1367 if(conn->ssl[sockindex].use)
1368 /* if the SSL/TLS channel hasn't been shut down yet, do that now. */
1369 Curl_ssl_shutdown(conn, sockindex);
1372 int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
1374 /* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
1375 * Shutting Down an Schannel Connection
1377 struct Curl_easy *data = conn->data;
1378 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1380 infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
1381 conn->host.name, conn->remote_port);
1383 if(connssl->cred && connssl->ctxt) {
1384 SecBufferDesc BuffDesc;
1386 SECURITY_STATUS sspi_status;
1388 SecBufferDesc outbuf_desc;
1391 DWORD dwshut = SCHANNEL_SHUTDOWN;
1393 InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
1394 InitSecBufferDesc(&BuffDesc, &Buffer, 1);
1396 sspi_status = s_pSecFn->ApplyControlToken(&connssl->ctxt->ctxt_handle,
1399 if(sspi_status != SEC_E_OK)
1400 failf(data, "schannel: ApplyControlToken failure: %s",
1401 Curl_sspi_strerror(conn, sspi_status));
1403 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
1405 return CURLE_OUT_OF_MEMORY;
1407 /* setup output buffer */
1408 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
1409 InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
1411 sspi_status = s_pSecFn->InitializeSecurityContext(
1412 &connssl->cred->cred_handle,
1413 &connssl->ctxt->ctxt_handle,
1420 &connssl->ctxt->ctxt_handle,
1422 &connssl->ret_flags,
1423 &connssl->ctxt->time_stamp);
1425 Curl_unicodefree(host_name);
1427 if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
1428 /* send close message which is in output buffer */
1430 result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
1431 outbuf.cbBuffer, &written);
1433 s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
1434 if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
1435 infof(data, "schannel: failed to send close msg: %s"
1436 " (bytes written: %zd)\n", curl_easy_strerror(result), written);
1441 /* free SSPI Schannel API security context handle */
1443 infof(data, "schannel: clear security context handle\n");
1444 s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
1445 Curl_safefree(connssl->ctxt);
1448 /* free SSPI Schannel API credential handle */
1450 Curl_ssl_sessionid_lock(conn);
1451 Curl_schannel_session_free(connssl->cred);
1452 Curl_ssl_sessionid_unlock(conn);
1453 connssl->cred = NULL;
1456 /* free internal buffer for received encrypted data */
1457 if(connssl->encdata_buffer != NULL) {
1458 Curl_safefree(connssl->encdata_buffer);
1459 connssl->encdata_length = 0;
1460 connssl->encdata_offset = 0;
1463 /* free internal buffer for received decrypted data */
1464 if(connssl->decdata_buffer != NULL) {
1465 Curl_safefree(connssl->decdata_buffer);
1466 connssl->decdata_length = 0;
1467 connssl->decdata_offset = 0;
1473 void Curl_schannel_session_free(void *ptr)
1475 /* this is expected to be called under sessionid lock */
1476 struct curl_schannel_cred *cred = ptr;
1479 if(cred->refcount == 0) {
1480 s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
1481 Curl_safefree(cred);
1485 int Curl_schannel_init(void)
1487 return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
1490 void Curl_schannel_cleanup(void)
1492 Curl_sspi_global_cleanup();
1495 size_t Curl_schannel_version(char *buffer, size_t size)
1497 size = snprintf(buffer, size, "WinSSL");
1502 int Curl_schannel_random(unsigned char *entropy, size_t length)
1504 HCRYPTPROV hCryptProv = 0;
1506 if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
1507 CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
1510 if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
1511 CryptReleaseContext(hCryptProv, 0UL);
1515 CryptReleaseContext(hCryptProv, 0UL);
1520 static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
1522 SECURITY_STATUS status;
1523 struct Curl_easy *data = conn->data;
1524 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1525 CURLcode result = CURLE_OK;
1526 CERT_CONTEXT *pCertContextServer = NULL;
1527 const CERT_CHAIN_CONTEXT *pChainContext = NULL;
1529 status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
1530 SECPKG_ATTR_REMOTE_CERT_CONTEXT,
1531 &pCertContextServer);
1533 if((status != SEC_E_OK) || (pCertContextServer == NULL)) {
1534 failf(data, "schannel: Failed to read remote certificate context: %s",
1535 Curl_sspi_strerror(conn, status));
1536 result = CURLE_PEER_FAILED_VERIFICATION;
1539 if(result == CURLE_OK) {
1540 CERT_CHAIN_PARA ChainPara;
1541 memset(&ChainPara, 0, sizeof(ChainPara));
1542 ChainPara.cbSize = sizeof(ChainPara);
1544 if(!CertGetCertificateChain(NULL,
1547 pCertContextServer->hCertStore,
1549 (data->set.ssl_no_revoke ? 0 :
1550 CERT_CHAIN_REVOCATION_CHECK_CHAIN),
1553 failf(data, "schannel: CertGetCertificateChain failed: %s",
1554 Curl_sspi_strerror(conn, GetLastError()));
1555 pChainContext = NULL;
1556 result = CURLE_PEER_FAILED_VERIFICATION;
1559 if(result == CURLE_OK) {
1560 CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0];
1561 DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED);
1562 dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
1563 if(dwTrustErrorMask) {
1564 if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED)
1565 failf(data, "schannel: CertGetCertificateChain trust error"
1566 " CERT_TRUST_IS_REVOKED");
1567 else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
1568 failf(data, "schannel: CertGetCertificateChain trust error"
1569 " CERT_TRUST_IS_PARTIAL_CHAIN");
1570 else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
1571 failf(data, "schannel: CertGetCertificateChain trust error"
1572 " CERT_TRUST_IS_UNTRUSTED_ROOT");
1573 else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
1574 failf(data, "schannel: CertGetCertificateChain trust error"
1575 " CERT_TRUST_IS_NOT_TIME_VALID");
1577 failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
1579 result = CURLE_PEER_FAILED_VERIFICATION;
1584 if(result == CURLE_OK) {
1585 if(data->set.ssl.verifyhost) {
1586 TCHAR cert_hostname_buff[128];
1588 xcharp_u cert_hostname;
1591 cert_hostname.const_tchar_ptr = cert_hostname_buff;
1592 hostname.tchar_ptr = Curl_convert_UTF8_to_tchar(conn->host.name);
1594 /* TODO: Fix this for certificates with multiple alternative names.
1595 Right now we're only asking for the first preferred alternative name.
1596 Instead we'd need to do all via CERT_NAME_SEARCH_ALL_NAMES_FLAG
1597 (if WinCE supports that?) and run this section in a loop for each.
1598 https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx
1599 curl: (51) schannel: CertGetNameString() certificate hostname
1600 (.google.com) did not match connection (google.com)
1602 len = CertGetNameString(pCertContextServer,
1606 cert_hostname.tchar_ptr,
1608 if(len > 0 && *cert_hostname.tchar_ptr == '*') {
1609 /* this is a wildcard cert. try matching the last len - 1 chars */
1610 int hostname_len = strlen(conn->host.name);
1611 cert_hostname.tchar_ptr++;
1612 if(_tcsicmp(cert_hostname.const_tchar_ptr,
1613 hostname.const_tchar_ptr + hostname_len - len + 2) != 0)
1614 result = CURLE_PEER_FAILED_VERIFICATION;
1616 else if(len == 0 || _tcsicmp(hostname.const_tchar_ptr,
1617 cert_hostname.const_tchar_ptr) != 0) {
1618 result = CURLE_PEER_FAILED_VERIFICATION;
1620 if(result == CURLE_PEER_FAILED_VERIFICATION) {
1621 char *_cert_hostname;
1622 _cert_hostname = Curl_convert_tchar_to_UTF8(cert_hostname.tchar_ptr);
1623 failf(data, "schannel: CertGetNameString() certificate hostname "
1624 "(%s) did not match connection (%s)",
1625 _cert_hostname, conn->host.name);
1626 Curl_unicodefree(_cert_hostname);
1628 Curl_unicodefree(hostname.tchar_ptr);
1633 CertFreeCertificateChain(pChainContext);
1635 if(pCertContextServer)
1636 CertFreeCertificateContext(pCertContextServer);
1640 #endif /* _WIN32_WCE */
1642 #endif /* USE_SCHANNEL */