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 http://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 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */
194 s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
195 SECPKG_CRED_OUTBOUND, NULL,
196 &schannel_cred, NULL, NULL,
197 &connssl->cred->cred_handle,
198 &connssl->cred->time_stamp);
200 if(sspi_status != SEC_E_OK) {
201 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
202 failf(data, "schannel: SNI or certificate check failed: %s",
203 Curl_sspi_strerror(conn, sspi_status));
205 failf(data, "schannel: AcquireCredentialsHandle failed: %s",
206 Curl_sspi_strerror(conn, sspi_status));
207 Curl_safefree(connssl->cred);
208 return CURLE_SSL_CONNECT_ERROR;
212 /* Warn if SNI is disabled due to use of an IP address */
213 if(Curl_inet_pton(AF_INET, conn->host.name, &addr)
215 || Curl_inet_pton(AF_INET6, conn->host.name, &addr6)
218 infof(data, "schannel: using IP address, SNI is not supported by OS.\n");
221 /* setup output buffer */
222 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
223 InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
225 /* setup request flags */
226 connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
227 ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
230 /* allocate memory for the security context handle */
231 connssl->ctxt = (struct curl_schannel_ctxt *)
232 malloc(sizeof(struct curl_schannel_ctxt));
234 failf(data, "schannel: unable to allocate memory");
235 return CURLE_OUT_OF_MEMORY;
237 memset(connssl->ctxt, 0, sizeof(struct curl_schannel_ctxt));
239 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
241 return CURLE_OUT_OF_MEMORY;
243 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
245 sspi_status = s_pSecFn->InitializeSecurityContext(
246 &connssl->cred->cred_handle, NULL, host_name,
247 connssl->req_flags, 0, 0, NULL, 0, &connssl->ctxt->ctxt_handle,
248 &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
250 Curl_unicodefree(host_name);
252 if(sspi_status != SEC_I_CONTINUE_NEEDED) {
253 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
254 failf(data, "schannel: SNI or certificate check failed: %s",
255 Curl_sspi_strerror(conn, sspi_status));
257 failf(data, "schannel: initial InitializeSecurityContext failed: %s",
258 Curl_sspi_strerror(conn, sspi_status));
259 Curl_safefree(connssl->ctxt);
260 return CURLE_SSL_CONNECT_ERROR;
263 infof(data, "schannel: sending initial handshake data: "
264 "sending %lu bytes...\n", outbuf.cbBuffer);
266 /* send initial handshake data which is now stored in output buffer */
267 result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
268 outbuf.cbBuffer, &written);
269 s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
270 if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
271 failf(data, "schannel: failed to send initial handshake data: "
272 "sent %zd of %lu bytes", written, outbuf.cbBuffer);
273 return CURLE_SSL_CONNECT_ERROR;
276 infof(data, "schannel: sent initial handshake data: "
277 "sent %zd bytes\n", written);
279 connssl->recv_unrecoverable_err = CURLE_OK;
280 connssl->recv_sspi_close_notify = false;
281 connssl->recv_connection_closed = false;
283 /* continue to second handshake step */
284 connssl->connecting_state = ssl_connect_2;
290 schannel_connect_step2(struct connectdata *conn, int sockindex)
293 ssize_t nread = -1, written = -1;
294 struct SessionHandle *data = conn->data;
295 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
296 unsigned char *reallocated_buffer;
297 size_t reallocated_length;
299 SecBufferDesc outbuf_desc;
301 SecBufferDesc inbuf_desc;
302 SECURITY_STATUS sspi_status = SEC_E_OK;
307 doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
309 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
310 conn->host.name, conn->remote_port);
312 if(!connssl->cred || !connssl->ctxt)
313 return CURLE_SSL_CONNECT_ERROR;
315 /* buffer to store previously received and decrypted data */
316 if(connssl->decdata_buffer == NULL) {
317 connssl->decdata_offset = 0;
318 connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
319 connssl->decdata_buffer = malloc(connssl->decdata_length);
320 if(connssl->decdata_buffer == NULL) {
321 failf(data, "schannel: unable to allocate memory");
322 return CURLE_OUT_OF_MEMORY;
326 /* buffer to store previously received and encrypted data */
327 if(connssl->encdata_buffer == NULL) {
328 connssl->encdata_offset = 0;
329 connssl->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
330 connssl->encdata_buffer = malloc(connssl->encdata_length);
331 if(connssl->encdata_buffer == NULL) {
332 failf(data, "schannel: unable to allocate memory");
333 return CURLE_OUT_OF_MEMORY;
337 /* if we need a bigger buffer to read a full message, increase buffer now */
338 if(connssl->encdata_length - connssl->encdata_offset <
339 CURL_SCHANNEL_BUFFER_FREE_SIZE) {
340 /* increase internal encrypted data buffer */
341 reallocated_length = connssl->encdata_offset +
342 CURL_SCHANNEL_BUFFER_FREE_SIZE;
343 reallocated_buffer = realloc(connssl->encdata_buffer,
346 if(reallocated_buffer == NULL) {
347 failf(data, "schannel: unable to re-allocate memory");
348 return CURLE_OUT_OF_MEMORY;
351 connssl->encdata_buffer = reallocated_buffer;
352 connssl->encdata_length = reallocated_length;
358 /* read encrypted handshake data from socket */
359 result = Curl_read_plain(conn->sock[sockindex],
360 (char *) (connssl->encdata_buffer +
361 connssl->encdata_offset),
362 connssl->encdata_length -
363 connssl->encdata_offset,
365 if(result == CURLE_AGAIN) {
366 if(connssl->connecting_state != ssl_connect_2_writing)
367 connssl->connecting_state = ssl_connect_2_reading;
368 infof(data, "schannel: failed to receive handshake, "
372 else if((result != CURLE_OK) || (nread == 0)) {
373 failf(data, "schannel: failed to receive handshake, "
374 "SSL/TLS connection failed");
375 return CURLE_SSL_CONNECT_ERROR;
378 /* increase encrypted data buffer offset */
379 connssl->encdata_offset += nread;
382 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
383 connssl->encdata_offset, connssl->encdata_length);
385 /* setup input buffers */
386 InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(connssl->encdata_offset),
387 curlx_uztoul(connssl->encdata_offset));
388 InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
389 InitSecBufferDesc(&inbuf_desc, inbuf, 2);
391 /* setup output buffers */
392 InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
393 InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
394 InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0);
395 InitSecBufferDesc(&outbuf_desc, outbuf, 3);
397 if(inbuf[0].pvBuffer == NULL) {
398 failf(data, "schannel: unable to allocate memory");
399 return CURLE_OUT_OF_MEMORY;
402 /* copy received handshake data into input buffer */
403 memcpy(inbuf[0].pvBuffer, connssl->encdata_buffer,
404 connssl->encdata_offset);
406 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
408 return CURLE_OUT_OF_MEMORY;
410 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
412 sspi_status = s_pSecFn->InitializeSecurityContext(
413 &connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle,
414 host_name, connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL,
415 &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
417 Curl_unicodefree(host_name);
419 /* free buffer for received handshake data */
420 Curl_safefree(inbuf[0].pvBuffer);
422 /* check if the handshake was incomplete */
423 if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
424 connssl->connecting_state = ssl_connect_2_reading;
425 infof(data, "schannel: received incomplete message, need more data\n");
429 /* If the server has requested a client certificate, attempt to continue
430 the handshake without one. This will allow connections to servers which
431 request a client certificate but do not require it. */
432 if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS &&
433 !(connssl->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
434 connssl->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
435 connssl->connecting_state = ssl_connect_2_writing;
436 infof(data, "schannel: a client certificate has been requested\n");
440 /* check if the handshake needs to be continued */
441 if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
442 for(i = 0; i < 3; i++) {
443 /* search for handshake tokens that need to be send */
444 if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
445 infof(data, "schannel: sending next handshake data: "
446 "sending %lu bytes...\n", outbuf[i].cbBuffer);
448 /* send handshake token to server */
449 result = Curl_write_plain(conn, conn->sock[sockindex],
450 outbuf[i].pvBuffer, outbuf[i].cbBuffer,
452 if((result != CURLE_OK) ||
453 (outbuf[i].cbBuffer != (size_t) written)) {
454 failf(data, "schannel: failed to send next handshake data: "
455 "sent %zd of %lu bytes", written, outbuf[i].cbBuffer);
456 return CURLE_SSL_CONNECT_ERROR;
460 /* free obsolete buffer */
461 if(outbuf[i].pvBuffer != NULL) {
462 s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
467 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
468 failf(data, "schannel: SNI or certificate check failed: %s",
469 Curl_sspi_strerror(conn, sspi_status));
471 failf(data, "schannel: next InitializeSecurityContext failed: %s",
472 Curl_sspi_strerror(conn, sspi_status));
473 return CURLE_SSL_CONNECT_ERROR;
476 /* check if there was additional remaining encrypted data */
477 if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
478 infof(data, "schannel: encrypted data length: %lu\n", inbuf[1].cbBuffer);
480 There are two cases where we could be getting extra data here:
481 1) If we're renegotiating a connection and the handshake is already
482 complete (from the server perspective), it can encrypted app data
483 (not handshake data) in an extra buffer at this point.
484 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a
485 connection and this extra data is part of the handshake.
486 We should process the data immediately; waiting for the socket to
487 be ready may fail since the server is done sending handshake data.
489 /* check if the remaining data is less than the total amount
490 and therefore begins after the already processed data */
491 if(connssl->encdata_offset > inbuf[1].cbBuffer) {
492 memmove(connssl->encdata_buffer,
493 (connssl->encdata_buffer + connssl->encdata_offset) -
494 inbuf[1].cbBuffer, inbuf[1].cbBuffer);
495 connssl->encdata_offset = inbuf[1].cbBuffer;
496 if(sspi_status == SEC_I_CONTINUE_NEEDED) {
503 connssl->encdata_offset = 0;
508 /* check if the handshake needs to be continued */
509 if(sspi_status == SEC_I_CONTINUE_NEEDED) {
510 connssl->connecting_state = ssl_connect_2_reading;
514 /* check if the handshake is complete */
515 if(sspi_status == SEC_E_OK) {
516 connssl->connecting_state = ssl_connect_3;
517 infof(data, "schannel: SSL/TLS handshake complete\n");
521 /* Windows CE doesn't do any server certificate validation.
522 We have to do it manually. */
523 if(data->set.ssl.verifypeer)
524 return verify_certificate(conn, sockindex);
531 schannel_connect_step3(struct connectdata *conn, int sockindex)
533 CURLcode result = CURLE_OK;
534 struct SessionHandle *data = conn->data;
535 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
536 struct curl_schannel_cred *old_cred = NULL;
539 DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
541 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
542 conn->host.name, conn->remote_port);
545 return CURLE_SSL_CONNECT_ERROR;
547 /* check if the required context attributes are met */
548 if(connssl->ret_flags != connssl->req_flags) {
549 if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
550 failf(data, "schannel: failed to setup sequence detection");
551 if(!(connssl->ret_flags & ISC_RET_REPLAY_DETECT))
552 failf(data, "schannel: failed to setup replay detection");
553 if(!(connssl->ret_flags & ISC_RET_CONFIDENTIALITY))
554 failf(data, "schannel: failed to setup confidentiality");
555 if(!(connssl->ret_flags & ISC_RET_ALLOCATED_MEMORY))
556 failf(data, "schannel: failed to setup memory allocation");
557 if(!(connssl->ret_flags & ISC_RET_STREAM))
558 failf(data, "schannel: failed to setup stream orientation");
559 return CURLE_SSL_CONNECT_ERROR;
562 /* increment the reference counter of the credential/session handle */
563 if(connssl->cred && connssl->ctxt) {
564 connssl->cred->refcount++;
565 infof(data, "schannel: incremented credential handle refcount = %d\n",
566 connssl->cred->refcount);
569 /* save the current session data for possible re-use */
570 incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL));
572 if(old_cred != connssl->cred) {
573 infof(data, "schannel: old credential handle is stale, removing\n");
574 Curl_ssl_delsessionid(conn, (void *)old_cred);
580 result = Curl_ssl_addsessionid(conn, (void *)connssl->cred,
581 sizeof(struct curl_schannel_cred));
583 failf(data, "schannel: failed to store credential handle");
587 connssl->cred->cached = TRUE;
588 infof(data, "schannel: stored credential handle in session cache\n");
592 connssl->connecting_state = ssl_connect_done;
598 schannel_connect_common(struct connectdata *conn, int sockindex,
599 bool nonblocking, bool *done)
602 struct SessionHandle *data = conn->data;
603 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
604 curl_socket_t sockfd = conn->sock[sockindex];
608 /* check if the connection has already been established */
609 if(ssl_connection_complete == connssl->state) {
614 if(ssl_connect_1 == connssl->connecting_state) {
615 /* check out how much more time we're allowed */
616 timeout_ms = Curl_timeleft(data, NULL, TRUE);
619 /* no need to continue if time already is up */
620 failf(data, "SSL/TLS connection timeout");
621 return CURLE_OPERATION_TIMEDOUT;
624 result = schannel_connect_step1(conn, sockindex);
629 while(ssl_connect_2 == connssl->connecting_state ||
630 ssl_connect_2_reading == connssl->connecting_state ||
631 ssl_connect_2_writing == connssl->connecting_state) {
633 /* check out how much more time we're allowed */
634 timeout_ms = Curl_timeleft(data, NULL, TRUE);
637 /* no need to continue if time already is up */
638 failf(data, "SSL/TLS connection timeout");
639 return CURLE_OPERATION_TIMEDOUT;
642 /* if ssl is expecting something, check if it's available. */
643 if(connssl->connecting_state == ssl_connect_2_reading
644 || connssl->connecting_state == ssl_connect_2_writing) {
646 curl_socket_t writefd = ssl_connect_2_writing ==
647 connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
648 curl_socket_t readfd = ssl_connect_2_reading ==
649 connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
651 what = Curl_socket_ready(readfd, writefd, nonblocking ? 0 : timeout_ms);
654 failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
655 return CURLE_SSL_CONNECT_ERROR;
664 failf(data, "SSL/TLS connection timeout");
665 return CURLE_OPERATION_TIMEDOUT;
668 /* socket is readable or writable */
671 /* Run transaction, and return to the caller if it failed or if
672 * this connection is part of a multi handle and this loop would
673 * execute again. This permits the owner of a multi handle to
674 * abort a connection attempt before step2 has completed while
675 * ensuring that a client using select() or epoll() will always
676 * have a valid fdset to wait on.
678 result = schannel_connect_step2(conn, sockindex);
679 if(result || (nonblocking &&
680 (ssl_connect_2 == connssl->connecting_state ||
681 ssl_connect_2_reading == connssl->connecting_state ||
682 ssl_connect_2_writing == connssl->connecting_state)))
685 } /* repeat step2 until all transactions are done. */
687 if(ssl_connect_3 == connssl->connecting_state) {
688 result = schannel_connect_step3(conn, sockindex);
693 if(ssl_connect_done == connssl->connecting_state) {
694 connssl->state = ssl_connection_complete;
695 conn->recv[sockindex] = schannel_recv;
696 conn->send[sockindex] = schannel_send;
702 /* reset our connection state machine */
703 connssl->connecting_state = ssl_connect_1;
709 schannel_send(struct connectdata *conn, int sockindex,
710 const void *buf, size_t len, CURLcode *err)
712 ssize_t written = -1;
714 unsigned char *data = NULL;
715 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
717 SecBufferDesc outbuf_desc;
718 SECURITY_STATUS sspi_status = SEC_E_OK;
721 /* check if the maximum stream sizes were queried */
722 if(connssl->stream_sizes.cbMaximumMessage == 0) {
723 sspi_status = s_pSecFn->QueryContextAttributes(
724 &connssl->ctxt->ctxt_handle,
725 SECPKG_ATTR_STREAM_SIZES,
726 &connssl->stream_sizes);
727 if(sspi_status != SEC_E_OK) {
728 *err = CURLE_SEND_ERROR;
733 /* check if the buffer is longer than the maximum message length */
734 if(len > connssl->stream_sizes.cbMaximumMessage) {
735 *err = CURLE_SEND_ERROR;
739 /* calculate the complete message length and allocate a buffer for it */
740 data_len = connssl->stream_sizes.cbHeader + len +
741 connssl->stream_sizes.cbTrailer;
742 data = (unsigned char *) malloc(data_len);
744 *err = CURLE_OUT_OF_MEMORY;
748 /* setup output buffers (header, data, trailer, empty) */
749 InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
750 data, connssl->stream_sizes.cbHeader);
751 InitSecBuffer(&outbuf[1], SECBUFFER_DATA,
752 data + connssl->stream_sizes.cbHeader, curlx_uztoul(len));
753 InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
754 data + connssl->stream_sizes.cbHeader + len,
755 connssl->stream_sizes.cbTrailer);
756 InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0);
757 InitSecBufferDesc(&outbuf_desc, outbuf, 4);
759 /* copy data into output buffer */
760 memcpy(outbuf[1].pvBuffer, buf, len);
762 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
763 sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt->ctxt_handle, 0,
766 /* check if the message was encrypted */
767 if(sspi_status == SEC_E_OK) {
770 /* send the encrypted message including header, data and trailer */
771 len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
774 It's important to send the full message which includes the header,
775 encrypted payload, and trailer. Until the client receives all the
776 data a coherent message has not been delivered and the client
777 can't read any of it.
779 If we wanted to buffer the unwritten encrypted bytes, we would
780 tell the client that all data it has requested to be sent has been
781 sent. The unwritten encrypted bytes would be the first bytes to
782 send on the next invocation.
783 Here's the catch with this - if we tell the client that all the
784 bytes have been sent, will the client call this method again to
785 send the buffered data? Looking at who calls this function, it
786 seems the answer is NO.
789 /* send entire message or fail */
790 while(len > (size_t)written) {
797 timeleft = Curl_timeleft(conn->data, NULL, FALSE);
799 /* we already got the timeout */
800 failf(conn->data, "schannel: timed out sending data "
801 "(bytes sent: %zd)", written);
802 *err = CURLE_OPERATION_TIMEDOUT;
807 what = Curl_socket_ready(CURL_SOCKET_BAD, conn->sock[sockindex],
811 failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
812 *err = CURLE_SEND_ERROR;
817 failf(conn->data, "schannel: timed out sending data "
818 "(bytes sent: %zd)", written);
819 *err = CURLE_OPERATION_TIMEDOUT;
823 /* socket is writable */
825 result = Curl_write_plain(conn, conn->sock[sockindex], data + written,
826 len - written, &this_write);
827 if(result == CURLE_AGAIN)
829 else if(result != CURLE_OK) {
835 written += this_write;
838 else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) {
839 *err = CURLE_OUT_OF_MEMORY;
842 *err = CURLE_SEND_ERROR;
847 if(len == (size_t)written)
848 /* Encrypted message including header, data and trailer entirely sent.
849 The return value is the number of unencrypted bytes that were sent. */
850 written = outbuf[1].cbBuffer;
856 schannel_recv(struct connectdata *conn, int sockindex,
857 char *buf, size_t len, CURLcode *err)
861 struct SessionHandle *data = conn->data;
862 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
863 unsigned char *reallocated_buffer;
864 size_t reallocated_length;
867 SecBufferDesc inbuf_desc;
868 SECURITY_STATUS sspi_status = SEC_E_OK;
869 /* we want the length of the encrypted buffer to be at least large enough
870 that it can hold all the bytes requested and some TLS record overhead. */
871 size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE;
873 /****************************************************************************
874 * Don't return or set connssl->recv_unrecoverable_err unless in the cleanup.
875 * The pattern for return error is set *err, optional infof, goto cleanup.
877 * Our priority is to always return as much decrypted data to the caller as
878 * possible, even if an error occurs. The state of the decrypted buffer must
879 * always be valid. Transfer of decrypted data to the caller's buffer is
880 * handled in the cleanup.
883 infof(data, "schannel: client wants to read %zu bytes\n", len);
886 if(len && len <= connssl->decdata_offset) {
887 infof(data, "schannel: enough decrypted data is already available\n");
890 else if(connssl->recv_unrecoverable_err) {
891 *err = connssl->recv_unrecoverable_err;
892 infof(data, "schannel: an unrecoverable error occurred in a prior call\n");
895 else if(connssl->recv_sspi_close_notify) {
896 /* once a server has indicated shutdown there is no more encrypted data */
897 infof(data, "schannel: server indicated shutdown in a prior call\n");
901 /* It's debatable what to return when !len. Regardless we can't return
902 immediately because there may be data to decrypt (in the case we want to
903 decrypt all encrypted cached data) so handle !len later in cleanup.
907 else if(!connssl->recv_connection_closed) {
908 /* increase enc buffer in order to fit the requested amount of data */
909 size = connssl->encdata_length - connssl->encdata_offset;
910 if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE ||
911 connssl->encdata_length < min_encdata_length) {
912 reallocated_length = connssl->encdata_offset +
913 CURL_SCHANNEL_BUFFER_FREE_SIZE;
914 if(reallocated_length < min_encdata_length) {
915 reallocated_length = min_encdata_length;
917 reallocated_buffer = realloc(connssl->encdata_buffer,
919 if(reallocated_buffer == NULL) {
920 *err = CURLE_OUT_OF_MEMORY;
921 failf(data, "schannel: unable to re-allocate memory");
925 connssl->encdata_buffer = reallocated_buffer;
926 connssl->encdata_length = reallocated_length;
927 size = connssl->encdata_length - connssl->encdata_offset;
928 infof(data, "schannel: encdata_buffer resized %zu\n",
929 connssl->encdata_length);
932 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
933 connssl->encdata_offset, connssl->encdata_length);
935 /* read encrypted data from socket */
936 *err = Curl_read_plain(conn->sock[sockindex],
937 (char *)(connssl->encdata_buffer +
938 connssl->encdata_offset),
942 if(*err == CURLE_AGAIN)
943 infof(data, "schannel: Curl_read_plain returned CURLE_AGAIN\n");
944 else if(*err == CURLE_RECV_ERROR)
945 infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR\n");
947 infof(data, "schannel: Curl_read_plain returned error %d\n", *err);
949 else if(nread == 0) {
950 connssl->recv_connection_closed = true;
951 infof(data, "schannel: server closed the connection\n");
954 connssl->encdata_offset += (size_t)nread;
955 infof(data, "schannel: encrypted data got %zd\n", nread);
959 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
960 connssl->encdata_offset, connssl->encdata_length);
963 while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK &&
964 (!len || connssl->decdata_offset < len ||
965 connssl->recv_connection_closed)) {
966 /* prepare data buffer for DecryptMessage call */
967 InitSecBuffer(&inbuf[0], SECBUFFER_DATA, connssl->encdata_buffer,
968 curlx_uztoul(connssl->encdata_offset));
970 /* we need 3 more empty input buffers for possible output */
971 InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
972 InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0);
973 InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0);
974 InitSecBufferDesc(&inbuf_desc, inbuf, 4);
976 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */
977 sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
978 &inbuf_desc, 0, NULL);
980 /* check if everything went fine (server may want to renegotiate
981 or shutdown the connection context) */
982 if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE ||
983 sspi_status == SEC_I_CONTEXT_EXPIRED) {
984 /* check for successfully decrypted data, even before actual
985 renegotiation or shutdown of the connection context */
986 if(inbuf[1].BufferType == SECBUFFER_DATA) {
987 infof(data, "schannel: decrypted data length: %lu\n",
990 /* increase buffer in order to fit the received amount of data */
991 size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ?
992 inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE;
993 if(connssl->decdata_length - connssl->decdata_offset < size ||
994 connssl->decdata_length < len) {
995 /* increase internal decrypted data buffer */
996 reallocated_length = connssl->decdata_offset + size;
997 /* make sure that the requested amount of data fits */
998 if(reallocated_length < len) {
999 reallocated_length = len;
1001 reallocated_buffer = realloc(connssl->decdata_buffer,
1002 reallocated_length);
1003 if(reallocated_buffer == NULL) {
1004 *err = CURLE_OUT_OF_MEMORY;
1005 failf(data, "schannel: unable to re-allocate memory");
1008 connssl->decdata_buffer = reallocated_buffer;
1009 connssl->decdata_length = reallocated_length;
1012 /* copy decrypted data to internal buffer */
1013 size = inbuf[1].cbBuffer;
1015 memcpy(connssl->decdata_buffer + connssl->decdata_offset,
1016 inbuf[1].pvBuffer, size);
1017 connssl->decdata_offset += size;
1020 infof(data, "schannel: decrypted data added: %zu\n", size);
1021 infof(data, "schannel: decrypted data cached: offset %zu length %zu\n",
1022 connssl->decdata_offset, connssl->decdata_length);
1025 /* check for remaining encrypted data */
1026 if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
1027 infof(data, "schannel: encrypted data length: %lu\n",
1030 /* check if the remaining data is less than the total amount
1031 * and therefore begins after the already processed data
1033 if(connssl->encdata_offset > inbuf[3].cbBuffer) {
1034 /* move remaining encrypted data forward to the beginning of
1036 memmove(connssl->encdata_buffer,
1037 (connssl->encdata_buffer + connssl->encdata_offset) -
1038 inbuf[3].cbBuffer, inbuf[3].cbBuffer);
1039 connssl->encdata_offset = inbuf[3].cbBuffer;
1042 infof(data, "schannel: encrypted data cached: offset %zu length %zu\n",
1043 connssl->encdata_offset, connssl->encdata_length);
1046 /* reset encrypted buffer offset, because there is no data remaining */
1047 connssl->encdata_offset = 0;
1050 /* check if server wants to renegotiate the connection context */
1051 if(sspi_status == SEC_I_RENEGOTIATE) {
1052 infof(data, "schannel: remote party requests renegotiation\n");
1053 if(*err && *err != CURLE_AGAIN) {
1054 infof(data, "schannel: can't renogotiate, an error is pending\n");
1057 if(connssl->encdata_offset) {
1058 *err = CURLE_RECV_ERROR;
1059 infof(data, "schannel: can't renogotiate, "
1060 "encrypted data available\n");
1063 /* begin renegotiation */
1064 infof(data, "schannel: renegotiating SSL/TLS connection\n");
1065 connssl->state = ssl_connection_negotiating;
1066 connssl->connecting_state = ssl_connect_2_writing;
1067 *err = schannel_connect_common(conn, sockindex, FALSE, &done);
1069 infof(data, "schannel: renegotiation failed\n");
1072 /* now retry receiving data */
1073 sspi_status = SEC_E_OK;
1074 infof(data, "schannel: SSL/TLS connection renegotiated\n");
1077 /* check if the server closed the connection */
1078 else if(sspi_status == SEC_I_CONTEXT_EXPIRED) {
1079 /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not
1080 returned so we have to work around that in cleanup. */
1081 connssl->recv_sspi_close_notify = true;
1082 if(!connssl->recv_connection_closed) {
1083 connssl->recv_connection_closed = true;
1084 infof(data, "schannel: server closed the connection\n");
1089 else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
1092 infof(data, "schannel: failed to decrypt data, need more data\n");
1096 *err = CURLE_RECV_ERROR;
1097 infof(data, "schannel: failed to read data from server: %s\n",
1098 Curl_sspi_strerror(conn, sspi_status));
1103 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
1104 connssl->encdata_offset, connssl->encdata_length);
1106 infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
1107 connssl->decdata_offset, connssl->decdata_length);
1110 /* Warning- there is no guarantee the encdata state is valid at this point */
1111 infof(data, "schannel: schannel_recv cleanup\n");
1113 /* Error if the connection has closed without a close_notify.
1114 Behavior here is a matter of debate. We don't want to be vulnerable to a
1115 truncation attack however there's some browser precedent for ignoring the
1116 close_notify for compatibility reasons.
1117 Additionally, Windows 2000 (v5.0) is a special case since it seems it doesn't
1118 return close_notify. In that case if the connection was closed we assume it
1119 was graceful (close_notify) since there doesn't seem to be a way to tell.
1121 if(len && !connssl->decdata_offset && connssl->recv_connection_closed &&
1122 !connssl->recv_sspi_close_notify) {
1125 OSVERSIONINFOEX osver;
1127 memset(&osver, 0, sizeof(osver));
1128 osver.dwOSVersionInfoSize = sizeof(osver);
1129 osver.dwMajorVersion = 5;
1131 cm = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
1132 cm = VerSetConditionMask(cm, VER_MINORVERSION, VER_EQUAL);
1133 cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
1134 cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
1136 isWin2k = VerifyVersionInfo(&osver,
1137 (VER_MAJORVERSION | VER_MINORVERSION |
1138 VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR),
1141 if(isWin2k && sspi_status == SEC_E_OK)
1142 connssl->recv_sspi_close_notify = true;
1144 *err = CURLE_RECV_ERROR;
1145 infof(data, "schannel: server closed abruptly (missing close_notify)\n");
1149 /* Any error other than CURLE_AGAIN is an unrecoverable error. */
1150 if(*err && *err != CURLE_AGAIN)
1151 connssl->recv_unrecoverable_err = *err;
1153 size = len < connssl->decdata_offset ? len : connssl->decdata_offset;
1155 memcpy(buf, connssl->decdata_buffer, size);
1156 memmove(connssl->decdata_buffer, connssl->decdata_buffer + size,
1157 connssl->decdata_offset - size);
1158 connssl->decdata_offset -= size;
1160 infof(data, "schannel: decrypted data returned %zu\n", size);
1161 infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
1162 connssl->decdata_offset, connssl->decdata_length);
1164 return (ssize_t)size;
1167 if(!*err && !connssl->recv_connection_closed)
1170 /* It's debatable what to return when !len. We could return whatever error we
1171 got from decryption but instead we override here so the return is consistent.
1176 return *err ? -1 : 0;
1180 Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex,
1183 return schannel_connect_common(conn, sockindex, TRUE, done);
1187 Curl_schannel_connect(struct connectdata *conn, int sockindex)
1192 result = schannel_connect_common(conn, sockindex, FALSE, &done);
1201 bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex)
1203 const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1205 if(connssl->use) /* SSL/TLS is in use */
1206 return (connssl->encdata_offset > 0 ||
1207 connssl->decdata_offset > 0 ) ? TRUE : FALSE;
1212 void Curl_schannel_close(struct connectdata *conn, int sockindex)
1214 if(conn->ssl[sockindex].use)
1215 /* if the SSL/TLS channel hasn't been shut down yet, do that now. */
1216 Curl_ssl_shutdown(conn, sockindex);
1219 int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
1221 /* See http://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
1222 * Shutting Down an Schannel Connection
1224 struct SessionHandle *data = conn->data;
1225 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1227 infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
1228 conn->host.name, conn->remote_port);
1230 if(connssl->cred && connssl->ctxt) {
1231 SecBufferDesc BuffDesc;
1233 SECURITY_STATUS sspi_status;
1235 SecBufferDesc outbuf_desc;
1238 DWORD dwshut = SCHANNEL_SHUTDOWN;
1240 InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
1241 InitSecBufferDesc(&BuffDesc, &Buffer, 1);
1243 sspi_status = s_pSecFn->ApplyControlToken(&connssl->ctxt->ctxt_handle,
1246 if(sspi_status != SEC_E_OK)
1247 failf(data, "schannel: ApplyControlToken failure: %s",
1248 Curl_sspi_strerror(conn, sspi_status));
1250 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
1252 return CURLE_OUT_OF_MEMORY;
1254 /* setup output buffer */
1255 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
1256 InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
1258 sspi_status = s_pSecFn->InitializeSecurityContext(
1259 &connssl->cred->cred_handle,
1260 &connssl->ctxt->ctxt_handle,
1267 &connssl->ctxt->ctxt_handle,
1269 &connssl->ret_flags,
1270 &connssl->ctxt->time_stamp);
1272 Curl_unicodefree(host_name);
1274 if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
1275 /* send close message which is in output buffer */
1277 result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
1278 outbuf.cbBuffer, &written);
1280 s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
1281 if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
1282 infof(data, "schannel: failed to send close msg: %s"
1283 " (bytes written: %zd)\n", curl_easy_strerror(result), written);
1288 /* free SSPI Schannel API security context handle */
1290 infof(data, "schannel: clear security context handle\n");
1291 s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
1292 Curl_safefree(connssl->ctxt);
1295 /* free SSPI Schannel API credential handle */
1297 /* decrement the reference counter of the credential/session handle */
1298 if(connssl->cred->refcount > 0) {
1299 connssl->cred->refcount--;
1300 infof(data, "schannel: decremented credential handle refcount = %d\n",
1301 connssl->cred->refcount);
1304 /* if the handle was not cached and the refcount is zero */
1305 if(!connssl->cred->cached && connssl->cred->refcount == 0) {
1306 infof(data, "schannel: clear credential handle\n");
1307 s_pSecFn->FreeCredentialsHandle(&connssl->cred->cred_handle);
1308 Curl_safefree(connssl->cred);
1312 /* free internal buffer for received encrypted data */
1313 if(connssl->encdata_buffer != NULL) {
1314 Curl_safefree(connssl->encdata_buffer);
1315 connssl->encdata_length = 0;
1316 connssl->encdata_offset = 0;
1319 /* free internal buffer for received decrypted data */
1320 if(connssl->decdata_buffer != NULL) {
1321 Curl_safefree(connssl->decdata_buffer);
1322 connssl->decdata_length = 0;
1323 connssl->decdata_offset = 0;
1329 void Curl_schannel_session_free(void *ptr)
1331 struct curl_schannel_cred *cred = ptr;
1333 if(cred && cred->cached) {
1334 if(cred->refcount == 0) {
1335 s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
1336 Curl_safefree(cred);
1339 cred->cached = FALSE;
1344 int Curl_schannel_init(void)
1346 return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
1349 void Curl_schannel_cleanup(void)
1351 Curl_sspi_global_cleanup();
1354 size_t Curl_schannel_version(char *buffer, size_t size)
1356 size = snprintf(buffer, size, "WinSSL");
1361 int Curl_schannel_random(unsigned char *entropy, size_t length)
1363 HCRYPTPROV hCryptProv = 0;
1365 if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
1366 CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
1369 if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
1370 CryptReleaseContext(hCryptProv, 0UL);
1374 CryptReleaseContext(hCryptProv, 0UL);
1379 static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
1381 SECURITY_STATUS status;
1382 struct SessionHandle *data = conn->data;
1383 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1384 CURLcode result = CURLE_OK;
1385 CERT_CONTEXT *pCertContextServer = NULL;
1386 const CERT_CHAIN_CONTEXT *pChainContext = NULL;
1388 status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
1389 SECPKG_ATTR_REMOTE_CERT_CONTEXT,
1390 &pCertContextServer);
1392 if((status != SEC_E_OK) || (pCertContextServer == NULL)) {
1393 failf(data, "schannel: Failed to read remote certificate context: %s",
1394 Curl_sspi_strerror(conn, status));
1395 result = CURLE_PEER_FAILED_VERIFICATION;
1398 if(result == CURLE_OK) {
1399 CERT_CHAIN_PARA ChainPara;
1400 memset(&ChainPara, 0, sizeof(ChainPara));
1401 ChainPara.cbSize = sizeof(ChainPara);
1403 if(!CertGetCertificateChain(NULL,
1406 pCertContextServer->hCertStore,
1408 (data->set.ssl_no_revoke ? 0 :
1409 CERT_CHAIN_REVOCATION_CHECK_CHAIN),
1412 failf(data, "schannel: CertGetCertificateChain failed: %s",
1413 Curl_sspi_strerror(conn, GetLastError()));
1414 pChainContext = NULL;
1415 result = CURLE_PEER_FAILED_VERIFICATION;
1418 if(result == CURLE_OK) {
1419 CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0];
1420 DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED);
1421 dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
1422 if(dwTrustErrorMask) {
1423 if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED)
1424 failf(data, "schannel: CertGetCertificateChain trust error"
1425 " CERT_TRUST_IS_REVOKED");
1426 else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
1427 failf(data, "schannel: CertGetCertificateChain trust error"
1428 " CERT_TRUST_IS_PARTIAL_CHAIN");
1429 else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
1430 failf(data, "schannel: CertGetCertificateChain trust error"
1431 " CERT_TRUST_IS_UNTRUSTED_ROOT");
1432 else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
1433 failf(data, "schannel: CertGetCertificateChain trust error"
1434 " CERT_TRUST_IS_NOT_TIME_VALID");
1436 failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
1438 result = CURLE_PEER_FAILED_VERIFICATION;
1443 if(result == CURLE_OK) {
1444 if(data->set.ssl.verifyhost) {
1445 TCHAR cert_hostname_buff[128];
1447 xcharp_u cert_hostname;
1450 cert_hostname.const_tchar_ptr = cert_hostname_buff;
1451 hostname.tchar_ptr = Curl_convert_UTF8_to_tchar(conn->host.name);
1453 /* TODO: Fix this for certificates with multiple alternative names.
1454 Right now we're only asking for the first preferred alternative name.
1455 Instead we'd need to do all via CERT_NAME_SEARCH_ALL_NAMES_FLAG
1456 (if WinCE supports that?) and run this section in a loop for each.
1457 https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx
1458 curl: (51) schannel: CertGetNameString() certificate hostname
1459 (.google.com) did not match connection (google.com)
1461 len = CertGetNameString(pCertContextServer,
1465 cert_hostname.tchar_ptr,
1467 if(len > 0 && *cert_hostname.tchar_ptr == '*') {
1468 /* this is a wildcard cert. try matching the last len - 1 chars */
1469 int hostname_len = strlen(conn->host.name);
1470 cert_hostname.tchar_ptr++;
1471 if(_tcsicmp(cert_hostname.const_tchar_ptr,
1472 hostname.const_tchar_ptr + hostname_len - len + 2) != 0)
1473 result = CURLE_PEER_FAILED_VERIFICATION;
1475 else if(len == 0 || _tcsicmp(hostname.const_tchar_ptr,
1476 cert_hostname.const_tchar_ptr) != 0) {
1477 result = CURLE_PEER_FAILED_VERIFICATION;
1479 if(result == CURLE_PEER_FAILED_VERIFICATION) {
1480 char *_cert_hostname;
1481 _cert_hostname = Curl_convert_tchar_to_UTF8(cert_hostname.tchar_ptr);
1482 failf(data, "schannel: CertGetNameString() certificate hostname "
1483 "(%s) did not match connection (%s)",
1484 _cert_hostname, conn->host.name);
1485 Curl_unicodefree(_cert_hostname);
1487 Curl_unicodefree(hostname.tchar_ptr);
1492 CertFreeCertificateChain(pChainContext);
1494 if(pCertContextServer)
1495 CertFreeCertificateContext(pCertContextServer);
1499 #endif /* _WIN32_WCE */
1501 #endif /* USE_SCHANNEL */