1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
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.
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"
59 #include "curl_printf.h"
60 #include "curl_memory.h"
61 /* The last #include file should be: */
64 /* Uncomment to force verbose output
65 * #define infof(x, y, ...) printf(y, __VA_ARGS__)
66 * #define failf(x, y, ...) printf(y, __VA_ARGS__)
69 static Curl_recv schannel_recv;
70 static Curl_send schannel_send;
73 static CURLcode verify_certificate(struct connectdata *conn, int sockindex);
76 static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType,
77 void *BufDataPtr, unsigned long BufByteSize)
79 buffer->cbBuffer = BufByteSize;
80 buffer->BufferType = BufType;
81 buffer->pvBuffer = BufDataPtr;
84 static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr,
85 unsigned long NumArrElem)
87 desc->ulVersion = SECBUFFER_VERSION;
88 desc->pBuffers = BufArr;
89 desc->cBuffers = NumArrElem;
93 schannel_connect_step1(struct connectdata *conn, int sockindex)
96 struct SessionHandle *data = conn->data;
97 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
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;
105 struct in6_addr addr6;
110 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
111 conn->host.name, conn->remote_port);
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");
119 /* setup Schannel API options */
120 memset(&schannel_cred, 0, sizeof(schannel_cred));
121 schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
123 if(data->set.ssl.verifypeer) {
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;
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;
136 schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
138 if(data->set.ssl_no_revoke)
139 infof(data, "schannel: disabled server certificate revocation "
142 infof(data, "schannel: checking server certificate revocation\n");
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");
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");
158 switch(data->set.ssl.version) {
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;
166 case CURL_SSLVERSION_TLSv1_0:
167 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT;
169 case CURL_SSLVERSION_TLSv1_1:
170 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT;
172 case CURL_SSLVERSION_TLSv1_2:
173 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
175 case CURL_SSLVERSION_SSLv3:
176 schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
178 case CURL_SSLVERSION_SSLv2:
179 schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
183 /* allocate memory for the re-usable credential handle */
184 connssl->cred = (struct curl_schannel_cred *)
185 malloc(sizeof(struct curl_schannel_cred));
187 failf(data, "schannel: unable to allocate memory");
188 return CURLE_OUT_OF_MEMORY;
190 memset(connssl->cred, 0, sizeof(struct curl_schannel_cred));
192 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx
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);
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));
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;
213 /* Warn if SNI is disabled due to use of an IP address */
214 if(Curl_inet_pton(AF_INET, conn->host.name, &addr)
216 || Curl_inet_pton(AF_INET6, conn->host.name, &addr6)
219 infof(data, "schannel: using IP address, SNI is not supported by OS.\n");
222 /* setup output buffer */
223 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
224 InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
226 /* setup request flags */
227 connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
228 ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
231 /* allocate memory for the security context handle */
232 connssl->ctxt = (struct curl_schannel_ctxt *)
233 malloc(sizeof(struct curl_schannel_ctxt));
235 failf(data, "schannel: unable to allocate memory");
236 return CURLE_OUT_OF_MEMORY;
238 memset(connssl->ctxt, 0, sizeof(struct curl_schannel_ctxt));
240 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
242 return CURLE_OUT_OF_MEMORY;
244 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
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);
251 Curl_unicodefree(host_name);
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));
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;
264 infof(data, "schannel: sending initial handshake data: "
265 "sending %lu bytes...\n", outbuf.cbBuffer);
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;
277 infof(data, "schannel: sent initial handshake data: "
278 "sent %zd bytes\n", written);
280 connssl->recv_unrecoverable_err = CURLE_OK;
281 connssl->recv_sspi_close_notify = false;
282 connssl->recv_connection_closed = false;
284 /* continue to second handshake step */
285 connssl->connecting_state = ssl_connect_2;
291 schannel_connect_step2(struct connectdata *conn, int sockindex)
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;
300 SecBufferDesc outbuf_desc;
302 SecBufferDesc inbuf_desc;
303 SECURITY_STATUS sspi_status = SEC_E_OK;
308 doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
310 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
311 conn->host.name, conn->remote_port);
313 if(!connssl->cred || !connssl->ctxt)
314 return CURLE_SSL_CONNECT_ERROR;
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;
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;
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,
347 if(reallocated_buffer == NULL) {
348 failf(data, "schannel: unable to re-allocate memory");
349 return CURLE_OUT_OF_MEMORY;
352 connssl->encdata_buffer = reallocated_buffer;
353 connssl->encdata_length = reallocated_length;
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,
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, "
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;
379 /* increase encrypted data buffer offset */
380 connssl->encdata_offset += nread;
383 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
384 connssl->encdata_offset, connssl->encdata_length);
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);
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);
398 if(inbuf[0].pvBuffer == NULL) {
399 failf(data, "schannel: unable to allocate memory");
400 return CURLE_OUT_OF_MEMORY;
403 /* copy received handshake data into input buffer */
404 memcpy(inbuf[0].pvBuffer, connssl->encdata_buffer,
405 connssl->encdata_offset);
407 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
409 return CURLE_OUT_OF_MEMORY;
411 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
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);
418 Curl_unicodefree(host_name);
420 /* free buffer for received handshake data */
421 Curl_safefree(inbuf[0].pvBuffer);
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");
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");
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);
449 /* send handshake token to server */
450 result = Curl_write_plain(conn, conn->sock[sockindex],
451 outbuf[i].pvBuffer, outbuf[i].cbBuffer,
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;
461 /* free obsolete buffer */
462 if(outbuf[i].pvBuffer != NULL) {
463 s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
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));
472 failf(data, "schannel: next InitializeSecurityContext failed: %s",
473 Curl_sspi_strerror(conn, sspi_status));
474 return CURLE_SSL_CONNECT_ERROR;
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);
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.
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) {
504 connssl->encdata_offset = 0;
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;
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");
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);
532 schannel_connect_step3(struct connectdata *conn, int sockindex)
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;
540 DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
542 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
543 conn->host.name, conn->remote_port);
546 return CURLE_SSL_CONNECT_ERROR;
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;
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);
570 /* save the current session data for possible re-use */
571 incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL));
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);
581 result = Curl_ssl_addsessionid(conn, (void *)connssl->cred,
582 sizeof(struct curl_schannel_cred));
584 failf(data, "schannel: failed to store credential handle");
588 connssl->cred->cached = TRUE;
589 infof(data, "schannel: stored credential handle in session cache\n");
593 connssl->connecting_state = ssl_connect_done;
599 schannel_connect_common(struct connectdata *conn, int sockindex,
600 bool nonblocking, bool *done)
603 struct SessionHandle *data = conn->data;
604 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
605 curl_socket_t sockfd = conn->sock[sockindex];
609 /* check if the connection has already been established */
610 if(ssl_connection_complete == connssl->state) {
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);
620 /* no need to continue if time already is up */
621 failf(data, "SSL/TLS connection timeout");
622 return CURLE_OPERATION_TIMEDOUT;
625 result = schannel_connect_step1(conn, sockindex);
630 while(ssl_connect_2 == connssl->connecting_state ||
631 ssl_connect_2_reading == connssl->connecting_state ||
632 ssl_connect_2_writing == connssl->connecting_state) {
634 /* check out how much more time we're allowed */
635 timeout_ms = Curl_timeleft(data, NULL, TRUE);
638 /* no need to continue if time already is up */
639 failf(data, "SSL/TLS connection timeout");
640 return CURLE_OPERATION_TIMEDOUT;
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) {
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;
652 what = Curl_socket_ready(readfd, writefd, nonblocking ? 0 : timeout_ms);
655 failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
656 return CURLE_SSL_CONNECT_ERROR;
665 failf(data, "SSL/TLS connection timeout");
666 return CURLE_OPERATION_TIMEDOUT;
669 /* socket is readable or writable */
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.
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)))
686 } /* repeat step2 until all transactions are done. */
688 if(ssl_connect_3 == connssl->connecting_state) {
689 result = schannel_connect_step3(conn, sockindex);
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;
703 /* reset our connection state machine */
704 connssl->connecting_state = ssl_connect_1;
710 schannel_send(struct connectdata *conn, int sockindex,
711 const void *buf, size_t len, CURLcode *err)
713 ssize_t written = -1;
715 unsigned char *data = NULL;
716 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
718 SecBufferDesc outbuf_desc;
719 SECURITY_STATUS sspi_status = SEC_E_OK;
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;
734 /* check if the buffer is longer than the maximum message length */
735 if(len > connssl->stream_sizes.cbMaximumMessage) {
736 *err = CURLE_SEND_ERROR;
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);
745 *err = CURLE_OUT_OF_MEMORY;
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);
760 /* copy data into output buffer */
761 memcpy(outbuf[1].pvBuffer, buf, len);
763 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
764 sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt->ctxt_handle, 0,
767 /* check if the message was encrypted */
768 if(sspi_status == SEC_E_OK) {
771 /* send the encrypted message including header, data and trailer */
772 len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
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.
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.
790 /* send entire message or fail */
791 while(len > (size_t)written) {
798 timeleft = Curl_timeleft(conn->data, NULL, FALSE);
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;
808 what = Curl_socket_ready(CURL_SOCKET_BAD, conn->sock[sockindex],
812 failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
813 *err = CURLE_SEND_ERROR;
818 failf(conn->data, "schannel: timed out sending data "
819 "(bytes sent: %zd)", written);
820 *err = CURLE_OPERATION_TIMEDOUT;
824 /* socket is writable */
826 result = Curl_write_plain(conn, conn->sock[sockindex], data + written,
827 len - written, &this_write);
828 if(result == CURLE_AGAIN)
830 else if(result != CURLE_OK) {
836 written += this_write;
839 else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) {
840 *err = CURLE_OUT_OF_MEMORY;
843 *err = CURLE_SEND_ERROR;
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;
857 schannel_recv(struct connectdata *conn, int sockindex,
858 char *buf, size_t len, CURLcode *err)
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;
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;
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.
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.
884 infof(data, "schannel: client wants to read %zu bytes\n", len);
887 if(len && len <= connssl->decdata_offset) {
888 infof(data, "schannel: enough decrypted data is already available\n");
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");
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");
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.
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;
918 reallocated_buffer = realloc(connssl->encdata_buffer,
920 if(reallocated_buffer == NULL) {
921 *err = CURLE_OUT_OF_MEMORY;
922 failf(data, "schannel: unable to re-allocate memory");
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);
933 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
934 connssl->encdata_offset, connssl->encdata_length);
936 /* read encrypted data from socket */
937 *err = Curl_read_plain(conn->sock[sockindex],
938 (char *)(connssl->encdata_buffer +
939 connssl->encdata_offset),
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");
948 infof(data, "schannel: Curl_read_plain returned error %d\n", *err);
950 else if(nread == 0) {
951 connssl->recv_connection_closed = true;
952 infof(data, "schannel: server closed the connection\n");
955 connssl->encdata_offset += (size_t)nread;
956 infof(data, "schannel: encrypted data got %zd\n", nread);
960 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
961 connssl->encdata_offset, connssl->encdata_length);
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));
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);
977 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx
979 sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
980 &inbuf_desc, 0, NULL);
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",
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;
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");
1010 connssl->decdata_buffer = reallocated_buffer;
1011 connssl->decdata_length = reallocated_length;
1014 /* copy decrypted data to internal buffer */
1015 size = inbuf[1].cbBuffer;
1017 memcpy(connssl->decdata_buffer + connssl->decdata_offset,
1018 inbuf[1].pvBuffer, size);
1019 connssl->decdata_offset += size;
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);
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",
1032 /* check if the remaining data is less than the total amount
1033 * and therefore begins after the already processed data
1035 if(connssl->encdata_offset > inbuf[3].cbBuffer) {
1036 /* move remaining encrypted data forward to the beginning of
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;
1044 infof(data, "schannel: encrypted data cached: offset %zu length %zu\n",
1045 connssl->encdata_offset, connssl->encdata_length);
1048 /* reset encrypted buffer offset, because there is no data remaining */
1049 connssl->encdata_offset = 0;
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");
1059 if(connssl->encdata_offset) {
1060 *err = CURLE_RECV_ERROR;
1061 infof(data, "schannel: can't renogotiate, "
1062 "encrypted data available\n");
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);
1071 infof(data, "schannel: renegotiation failed\n");
1074 /* now retry receiving data */
1075 sspi_status = SEC_E_OK;
1076 infof(data, "schannel: SSL/TLS connection renegotiated\n");
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");
1091 else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
1094 infof(data, "schannel: failed to decrypt data, need more data\n");
1098 *err = CURLE_RECV_ERROR;
1099 infof(data, "schannel: failed to read data from server: %s\n",
1100 Curl_sspi_strerror(conn, sspi_status));
1105 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
1106 connssl->encdata_offset, connssl->encdata_length);
1108 infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
1109 connssl->decdata_offset, connssl->decdata_length);
1112 /* Warning- there is no guarantee the encdata state is valid at this point */
1113 infof(data, "schannel: schannel_recv cleanup\n");
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.
1123 if(len && !connssl->decdata_offset && connssl->recv_connection_closed &&
1124 !connssl->recv_sspi_close_notify) {
1125 bool isWin2k = FALSE;
1127 #if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_WIN2K) || \
1128 (_WIN32_WINNT < _WIN32_WINNT_WIN2K)
1129 OSVERSIONINFO osver;
1131 memset(&osver, 0, sizeof(osver));
1132 osver.dwOSVersionInfoSize = sizeof(osver);
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)
1142 OSVERSIONINFOEX osver;
1144 memset(&osver, 0, sizeof(osver));
1145 osver.dwOSVersionInfoSize = sizeof(osver);
1146 osver.dwMajorVersion = 5;
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);
1153 if(VerifyVersionInfo(&osver, (VER_MAJORVERSION | VER_MINORVERSION |
1154 VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR),
1159 if(isWin2k && sspi_status == SEC_E_OK)
1160 connssl->recv_sspi_close_notify = true;
1162 *err = CURLE_RECV_ERROR;
1163 infof(data, "schannel: server closed abruptly (missing close_notify)\n");
1167 /* Any error other than CURLE_AGAIN is an unrecoverable error. */
1168 if(*err && *err != CURLE_AGAIN)
1169 connssl->recv_unrecoverable_err = *err;
1171 size = len < connssl->decdata_offset ? len : connssl->decdata_offset;
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;
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);
1182 return (ssize_t)size;
1185 if(!*err && !connssl->recv_connection_closed)
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.
1194 return *err ? -1 : 0;
1198 Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex,
1201 return schannel_connect_common(conn, sockindex, TRUE, done);
1205 Curl_schannel_connect(struct connectdata *conn, int sockindex)
1210 result = schannel_connect_common(conn, sockindex, FALSE, &done);
1219 bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex)
1221 const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1223 if(connssl->use) /* SSL/TLS is in use */
1224 return (connssl->encdata_offset > 0 ||
1225 connssl->decdata_offset > 0 ) ? TRUE : FALSE;
1230 void Curl_schannel_close(struct connectdata *conn, int sockindex)
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);
1237 int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
1239 /* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
1240 * Shutting Down an Schannel Connection
1242 struct SessionHandle *data = conn->data;
1243 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1245 infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
1246 conn->host.name, conn->remote_port);
1248 if(connssl->cred && connssl->ctxt) {
1249 SecBufferDesc BuffDesc;
1251 SECURITY_STATUS sspi_status;
1253 SecBufferDesc outbuf_desc;
1256 DWORD dwshut = SCHANNEL_SHUTDOWN;
1258 InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
1259 InitSecBufferDesc(&BuffDesc, &Buffer, 1);
1261 sspi_status = s_pSecFn->ApplyControlToken(&connssl->ctxt->ctxt_handle,
1264 if(sspi_status != SEC_E_OK)
1265 failf(data, "schannel: ApplyControlToken failure: %s",
1266 Curl_sspi_strerror(conn, sspi_status));
1268 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
1270 return CURLE_OUT_OF_MEMORY;
1272 /* setup output buffer */
1273 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
1274 InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
1276 sspi_status = s_pSecFn->InitializeSecurityContext(
1277 &connssl->cred->cred_handle,
1278 &connssl->ctxt->ctxt_handle,
1285 &connssl->ctxt->ctxt_handle,
1287 &connssl->ret_flags,
1288 &connssl->ctxt->time_stamp);
1290 Curl_unicodefree(host_name);
1292 if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
1293 /* send close message which is in output buffer */
1295 result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
1296 outbuf.cbBuffer, &written);
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);
1306 /* free SSPI Schannel API security context handle */
1308 infof(data, "schannel: clear security context handle\n");
1309 s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
1310 Curl_safefree(connssl->ctxt);
1313 /* free SSPI Schannel API credential handle */
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);
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);
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;
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;
1347 void Curl_schannel_session_free(void *ptr)
1349 struct curl_schannel_cred *cred = ptr;
1351 if(cred && cred->cached) {
1352 if(cred->refcount == 0) {
1353 s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
1354 Curl_safefree(cred);
1357 cred->cached = FALSE;
1362 int Curl_schannel_init(void)
1364 return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
1367 void Curl_schannel_cleanup(void)
1369 Curl_sspi_global_cleanup();
1372 size_t Curl_schannel_version(char *buffer, size_t size)
1374 size = snprintf(buffer, size, "WinSSL");
1379 int Curl_schannel_random(unsigned char *entropy, size_t length)
1381 HCRYPTPROV hCryptProv = 0;
1383 if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
1384 CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
1387 if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
1388 CryptReleaseContext(hCryptProv, 0UL);
1392 CryptReleaseContext(hCryptProv, 0UL);
1397 static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
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;
1406 status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
1407 SECPKG_ATTR_REMOTE_CERT_CONTEXT,
1408 &pCertContextServer);
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;
1416 if(result == CURLE_OK) {
1417 CERT_CHAIN_PARA ChainPara;
1418 memset(&ChainPara, 0, sizeof(ChainPara));
1419 ChainPara.cbSize = sizeof(ChainPara);
1421 if(!CertGetCertificateChain(NULL,
1424 pCertContextServer->hCertStore,
1426 (data->set.ssl_no_revoke ? 0 :
1427 CERT_CHAIN_REVOCATION_CHECK_CHAIN),
1430 failf(data, "schannel: CertGetCertificateChain failed: %s",
1431 Curl_sspi_strerror(conn, GetLastError()));
1432 pChainContext = NULL;
1433 result = CURLE_PEER_FAILED_VERIFICATION;
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");
1454 failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
1456 result = CURLE_PEER_FAILED_VERIFICATION;
1461 if(result == CURLE_OK) {
1462 if(data->set.ssl.verifyhost) {
1463 TCHAR cert_hostname_buff[128];
1465 xcharp_u cert_hostname;
1468 cert_hostname.const_tchar_ptr = cert_hostname_buff;
1469 hostname.tchar_ptr = Curl_convert_UTF8_to_tchar(conn->host.name);
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)
1479 len = CertGetNameString(pCertContextServer,
1483 cert_hostname.tchar_ptr,
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;
1493 else if(len == 0 || _tcsicmp(hostname.const_tchar_ptr,
1494 cert_hostname.const_tchar_ptr) != 0) {
1495 result = CURLE_PEER_FAILED_VERIFICATION;
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);
1505 Curl_unicodefree(hostname.tchar_ptr);
1510 CertFreeCertificateChain(pChainContext);
1512 if(pCertContextServer)
1513 CertFreeCertificateContext(pCertContextServer);
1517 #endif /* _WIN32_WCE */
1519 #endif /* USE_SCHANNEL */