1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2012 - 2014, Marc Hoersken, <info@marc-hoersken.de>
9 * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
10 * Copyright (C) 2012 - 2014, 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!
42 * TODO list for TLS/SSL implementation:
43 * - implement client certificate authentication
44 * - implement custom server certificate validation
45 * - implement cipher/algorithm option
47 * Related articles on MSDN:
48 * - Getting a Certificate for Schannel
49 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa375447.aspx
50 * - Specifying Schannel Ciphers and Cipher Strengths
51 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa380161.aspx
54 #include "curl_setup.h"
58 #ifndef USE_WINDOWS_SSPI
59 # error "Can't compile SCHANNEL support without SSPI."
62 #include "curl_sspi.h"
63 #include "curl_schannel.h"
66 #include "connect.h" /* for the connect timeout */
68 #include "select.h" /* for the socket readyness */
69 #include "inet_pton.h" /* for IP addr SNI check */
70 #include "curl_multibyte.h"
73 #define _MPRINTF_REPLACE /* use our functions only */
74 #include <curl/mprintf.h>
76 #include "curl_memory.h"
77 /* The last #include file should be: */
80 /* Uncomment to force verbose output
81 * #define infof(x, y, ...) printf(y, __VA_ARGS__)
82 * #define failf(x, y, ...) printf(y, __VA_ARGS__)
85 static Curl_recv schannel_recv;
86 static Curl_send schannel_send;
89 static CURLcode verify_certificate(struct connectdata *conn, int sockindex);
92 static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType,
93 void *BufDataPtr, unsigned long BufByteSize)
95 buffer->cbBuffer = BufByteSize;
96 buffer->BufferType = BufType;
97 buffer->pvBuffer = BufDataPtr;
100 static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr,
101 unsigned long NumArrElem)
103 desc->ulVersion = SECBUFFER_VERSION;
104 desc->pBuffers = BufArr;
105 desc->cBuffers = NumArrElem;
109 schannel_connect_step1(struct connectdata *conn, int sockindex)
111 ssize_t written = -1;
112 struct SessionHandle *data = conn->data;
113 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
115 SecBufferDesc outbuf_desc;
116 SCHANNEL_CRED schannel_cred;
117 SECURITY_STATUS sspi_status = SEC_E_OK;
118 struct curl_schannel_cred *old_cred = NULL;
121 struct in6_addr addr6;
126 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
127 conn->host.name, conn->remote_port);
129 /* check for an existing re-usable credential handle */
130 if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL)) {
131 connssl->cred = old_cred;
132 infof(data, "schannel: re-using existing credential handle\n");
135 /* setup Schannel API options */
136 memset(&schannel_cred, 0, sizeof(schannel_cred));
137 schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
139 if(data->set.ssl.verifypeer) {
141 /* certificate validation on CE doesn't seem to work right; we'll
142 do it following a more manual process. */
143 schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
144 SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
145 SCH_CRED_IGNORE_REVOCATION_OFFLINE;
147 schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
148 SCH_CRED_REVOCATION_CHECK_CHAIN;
150 infof(data, "schannel: checking server certificate revocation\n");
153 schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
154 SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
155 SCH_CRED_IGNORE_REVOCATION_OFFLINE;
156 infof(data, "schannel: disable server certificate revocation checks\n");
159 if(!data->set.ssl.verifyhost) {
160 schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
161 infof(data, "schannel: verifyhost setting prevents Schannel from "
162 "comparing the supplied target name with the subject "
163 "names in server certificates. Also disables SNI.\n");
166 switch(data->set.ssl.version) {
168 case CURL_SSLVERSION_DEFAULT:
169 case CURL_SSLVERSION_TLSv1:
170 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT |
171 SP_PROT_TLS1_1_CLIENT |
172 SP_PROT_TLS1_2_CLIENT;
174 case CURL_SSLVERSION_TLSv1_0:
175 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT;
177 case CURL_SSLVERSION_TLSv1_1:
178 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT;
180 case CURL_SSLVERSION_TLSv1_2:
181 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
183 case CURL_SSLVERSION_SSLv3:
184 schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
186 case CURL_SSLVERSION_SSLv2:
187 schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
191 /* allocate memory for the re-usable credential handle */
192 connssl->cred = (struct curl_schannel_cred *)
193 malloc(sizeof(struct curl_schannel_cred));
195 failf(data, "schannel: unable to allocate memory");
196 return CURLE_OUT_OF_MEMORY;
198 memset(connssl->cred, 0, sizeof(struct curl_schannel_cred));
200 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */
201 sspi_status = s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
202 SECPKG_CRED_OUTBOUND, NULL, &schannel_cred, NULL, NULL,
203 &connssl->cred->cred_handle, &connssl->cred->time_stamp);
205 if(sspi_status != SEC_E_OK) {
206 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
207 failf(data, "schannel: SNI or certificate check failed: %s",
208 Curl_sspi_strerror(conn, sspi_status));
210 failf(data, "schannel: AcquireCredentialsHandle failed: %s",
211 Curl_sspi_strerror(conn, sspi_status));
212 Curl_safefree(connssl->cred);
213 return CURLE_SSL_CONNECT_ERROR;
217 /* Warn if SNI is disabled due to use of an IP address */
218 if(Curl_inet_pton(AF_INET, conn->host.name, &addr)
220 || Curl_inet_pton(AF_INET6, conn->host.name, &addr6)
223 infof(data, "schannel: using IP address, SNI is not supported by OS.\n");
226 /* setup output buffer */
227 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
228 InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
230 /* setup request flags */
231 connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
232 ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
235 /* allocate memory for the security context handle */
236 connssl->ctxt = (struct curl_schannel_ctxt *)
237 malloc(sizeof(struct curl_schannel_ctxt));
239 failf(data, "schannel: unable to allocate memory");
240 return CURLE_OUT_OF_MEMORY;
242 memset(connssl->ctxt, 0, sizeof(struct curl_schannel_ctxt));
244 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
246 return CURLE_OUT_OF_MEMORY;
248 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
250 sspi_status = s_pSecFn->InitializeSecurityContext(
251 &connssl->cred->cred_handle, NULL, host_name,
252 connssl->req_flags, 0, 0, NULL, 0, &connssl->ctxt->ctxt_handle,
253 &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
255 Curl_unicodefree(host_name);
257 if(sspi_status != SEC_I_CONTINUE_NEEDED) {
258 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
259 failf(data, "schannel: SNI or certificate check failed: %s",
260 Curl_sspi_strerror(conn, sspi_status));
262 failf(data, "schannel: initial InitializeSecurityContext failed: %s",
263 Curl_sspi_strerror(conn, sspi_status));
264 Curl_safefree(connssl->ctxt);
265 return CURLE_SSL_CONNECT_ERROR;
268 infof(data, "schannel: sending initial handshake data: "
269 "sending %lu bytes...\n", outbuf.cbBuffer);
271 /* send initial handshake data which is now stored in output buffer */
272 code = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
273 outbuf.cbBuffer, &written);
274 s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
275 if((code != CURLE_OK) || (outbuf.cbBuffer != (size_t)written)) {
276 failf(data, "schannel: failed to send initial handshake data: "
277 "sent %zd of %lu bytes", written, outbuf.cbBuffer);
278 return CURLE_SSL_CONNECT_ERROR;
281 infof(data, "schannel: sent initial handshake data: "
282 "sent %zd bytes\n", written);
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 encrypted data */
317 if(connssl->encdata_buffer == NULL) {
318 connssl->encdata_offset = 0;
319 connssl->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
320 connssl->encdata_buffer = malloc(connssl->encdata_length);
321 if(connssl->encdata_buffer == NULL) {
322 failf(data, "schannel: unable to allocate memory");
323 return CURLE_OUT_OF_MEMORY;
327 /* if we need a bigger buffer to read a full message, increase buffer now */
328 if(connssl->encdata_length - connssl->encdata_offset <
329 CURL_SCHANNEL_BUFFER_FREE_SIZE) {
330 /* increase internal encrypted data buffer */
331 reallocated_length = connssl->encdata_offset +
332 CURL_SCHANNEL_BUFFER_FREE_SIZE;
333 reallocated_buffer = realloc(connssl->encdata_buffer,
336 if(reallocated_buffer == NULL) {
337 failf(data, "schannel: unable to re-allocate memory");
338 return CURLE_OUT_OF_MEMORY;
341 connssl->encdata_buffer = reallocated_buffer;
342 connssl->encdata_length = reallocated_length;
348 /* read encrypted handshake data from socket */
349 code = Curl_read_plain(conn->sock[sockindex],
350 (char *) (connssl->encdata_buffer + connssl->encdata_offset),
351 connssl->encdata_length - connssl->encdata_offset,
353 if(code == CURLE_AGAIN) {
354 if(connssl->connecting_state != ssl_connect_2_writing)
355 connssl->connecting_state = ssl_connect_2_reading;
356 infof(data, "schannel: failed to receive handshake, "
360 else if((code != CURLE_OK) || (nread == 0)) {
361 failf(data, "schannel: failed to receive handshake, "
362 "SSL/TLS connection failed");
363 return CURLE_SSL_CONNECT_ERROR;
366 /* increase encrypted data buffer offset */
367 connssl->encdata_offset += nread;
370 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
371 connssl->encdata_offset, connssl->encdata_length);
373 /* setup input buffers */
374 InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(connssl->encdata_offset),
375 curlx_uztoul(connssl->encdata_offset));
376 InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
377 InitSecBufferDesc(&inbuf_desc, inbuf, 2);
379 /* setup output buffers */
380 InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
381 InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
382 InitSecBufferDesc(&outbuf_desc, outbuf, 2);
384 if(inbuf[0].pvBuffer == NULL) {
385 failf(data, "schannel: unable to allocate memory");
386 return CURLE_OUT_OF_MEMORY;
389 /* copy received handshake data into input buffer */
390 memcpy(inbuf[0].pvBuffer, connssl->encdata_buffer,
391 connssl->encdata_offset);
393 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
395 return CURLE_OUT_OF_MEMORY;
397 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
399 sspi_status = s_pSecFn->InitializeSecurityContext(
400 &connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle,
401 host_name, connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL,
402 &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
404 Curl_unicodefree(host_name);
406 /* free buffer for received handshake data */
407 Curl_safefree(inbuf[0].pvBuffer);
409 /* check if the handshake was incomplete */
410 if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
411 connssl->connecting_state = ssl_connect_2_reading;
412 infof(data, "schannel: received incomplete message, need more data\n");
416 /* check if the handshake needs to be continued */
417 if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
418 for(i = 0; i < 2; i++) {
419 /* search for handshake tokens that need to be send */
420 if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
421 infof(data, "schannel: sending next handshake data: "
422 "sending %lu bytes...\n", outbuf[i].cbBuffer);
424 /* send handshake token to server */
425 code = Curl_write_plain(conn, conn->sock[sockindex],
426 outbuf[i].pvBuffer, outbuf[i].cbBuffer,
428 if((code != CURLE_OK) || (outbuf[i].cbBuffer != (size_t)written)) {
429 failf(data, "schannel: failed to send next handshake data: "
430 "sent %zd of %lu bytes", written, outbuf[i].cbBuffer);
431 return CURLE_SSL_CONNECT_ERROR;
435 /* free obsolete buffer */
436 if(outbuf[i].pvBuffer != NULL) {
437 s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
442 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
443 failf(data, "schannel: SNI or certificate check failed: %s",
444 Curl_sspi_strerror(conn, sspi_status));
446 failf(data, "schannel: next InitializeSecurityContext failed: %s",
447 Curl_sspi_strerror(conn, sspi_status));
448 return CURLE_SSL_CONNECT_ERROR;
451 /* check if there was additional remaining encrypted data */
452 if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
453 infof(data, "schannel: encrypted data length: %lu\n", inbuf[1].cbBuffer);
455 There are two cases where we could be getting extra data here:
456 1) If we're renegotiating a connection and the handshake is already
457 complete (from the server perspective), it can encrypted app data
458 (not handshake data) in an extra buffer at this point.
459 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a
460 connection and this extra data is part of the handshake.
461 We should process the data immediately; waiting for the socket to
462 be ready may fail since the server is done sending handshake data.
464 /* check if the remaining data is less than the total amount
465 and therefore begins after the already processed data */
466 if(connssl->encdata_offset > inbuf[1].cbBuffer) {
467 memmove(connssl->encdata_buffer,
468 (connssl->encdata_buffer + connssl->encdata_offset) -
469 inbuf[1].cbBuffer, inbuf[1].cbBuffer);
470 connssl->encdata_offset = inbuf[1].cbBuffer;
471 if(sspi_status == SEC_I_CONTINUE_NEEDED) {
478 connssl->encdata_offset = 0;
483 /* check if the handshake needs to be continued */
484 if(sspi_status == SEC_I_CONTINUE_NEEDED) {
485 connssl->connecting_state = ssl_connect_2_reading;
489 /* check if the handshake is complete */
490 if(sspi_status == SEC_E_OK) {
491 connssl->connecting_state = ssl_connect_3;
492 infof(data, "schannel: SSL/TLS handshake complete\n");
496 /* Windows CE doesn't do any server certificate validation.
497 We have to do it manually. */
498 if(data->set.ssl.verifypeer)
499 return verify_certificate(conn, sockindex);
506 schannel_connect_step3(struct connectdata *conn, int sockindex)
508 CURLcode result = CURLE_OK;
509 struct SessionHandle *data = conn->data;
510 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
511 struct curl_schannel_cred *old_cred = NULL;
514 DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
516 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
517 conn->host.name, conn->remote_port);
520 return CURLE_SSL_CONNECT_ERROR;
522 /* check if the required context attributes are met */
523 if(connssl->ret_flags != connssl->req_flags) {
524 if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
525 failf(data, "schannel: failed to setup sequence detection");
526 if(!(connssl->ret_flags & ISC_RET_REPLAY_DETECT))
527 failf(data, "schannel: failed to setup replay detection");
528 if(!(connssl->ret_flags & ISC_RET_CONFIDENTIALITY))
529 failf(data, "schannel: failed to setup confidentiality");
530 if(!(connssl->ret_flags & ISC_RET_ALLOCATED_MEMORY))
531 failf(data, "schannel: failed to setup memory allocation");
532 if(!(connssl->ret_flags & ISC_RET_STREAM))
533 failf(data, "schannel: failed to setup stream orientation");
534 return CURLE_SSL_CONNECT_ERROR;
537 /* increment the reference counter of the credential/session handle */
538 if(connssl->cred && connssl->ctxt) {
539 connssl->cred->refcount++;
540 infof(data, "schannel: incremented credential handle refcount = %d\n",
541 connssl->cred->refcount);
544 /* save the current session data for possible re-use */
545 incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL));
547 if(old_cred != connssl->cred) {
548 infof(data, "schannel: old credential handle is stale, removing\n");
549 Curl_ssl_delsessionid(conn, (void *)old_cred);
555 result = Curl_ssl_addsessionid(conn, (void *)connssl->cred,
556 sizeof(struct curl_schannel_cred));
558 failf(data, "schannel: failed to store credential handle");
562 connssl->cred->cached = TRUE;
563 infof(data, "schannel: stored credential handle in session cache\n");
567 connssl->connecting_state = ssl_connect_done;
573 schannel_connect_common(struct connectdata *conn, int sockindex,
574 bool nonblocking, bool *done)
577 struct SessionHandle *data = conn->data;
578 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
579 curl_socket_t sockfd = conn->sock[sockindex];
583 /* check if the connection has already been established */
584 if(ssl_connection_complete == connssl->state) {
589 if(ssl_connect_1 == connssl->connecting_state) {
590 /* check out how much more time we're allowed */
591 timeout_ms = Curl_timeleft(data, NULL, TRUE);
594 /* no need to continue if time already is up */
595 failf(data, "SSL/TLS connection timeout");
596 return CURLE_OPERATION_TIMEDOUT;
599 result = schannel_connect_step1(conn, sockindex);
604 while(ssl_connect_2 == connssl->connecting_state ||
605 ssl_connect_2_reading == connssl->connecting_state ||
606 ssl_connect_2_writing == connssl->connecting_state) {
608 /* check out how much more time we're allowed */
609 timeout_ms = Curl_timeleft(data, NULL, TRUE);
612 /* no need to continue if time already is up */
613 failf(data, "SSL/TLS connection timeout");
614 return CURLE_OPERATION_TIMEDOUT;
617 /* if ssl is expecting something, check if it's available. */
618 if(connssl->connecting_state == ssl_connect_2_reading
619 || connssl->connecting_state == ssl_connect_2_writing) {
621 curl_socket_t writefd = ssl_connect_2_writing ==
622 connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
623 curl_socket_t readfd = ssl_connect_2_reading ==
624 connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
626 what = Curl_socket_ready(readfd, writefd, nonblocking ? 0 : timeout_ms);
629 failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
630 return CURLE_SSL_CONNECT_ERROR;
639 failf(data, "SSL/TLS connection timeout");
640 return CURLE_OPERATION_TIMEDOUT;
643 /* socket is readable or writable */
646 /* Run transaction, and return to the caller if it failed or if
647 * this connection is part of a multi handle and this loop would
648 * execute again. This permits the owner of a multi handle to
649 * abort a connection attempt before step2 has completed while
650 * ensuring that a client using select() or epoll() will always
651 * have a valid fdset to wait on.
653 result = schannel_connect_step2(conn, sockindex);
654 if(result || (nonblocking &&
655 (ssl_connect_2 == connssl->connecting_state ||
656 ssl_connect_2_reading == connssl->connecting_state ||
657 ssl_connect_2_writing == connssl->connecting_state)))
660 } /* repeat step2 until all transactions are done. */
662 if(ssl_connect_3 == connssl->connecting_state) {
663 result = schannel_connect_step3(conn, sockindex);
668 if(ssl_connect_done == connssl->connecting_state) {
669 connssl->state = ssl_connection_complete;
670 conn->recv[sockindex] = schannel_recv;
671 conn->send[sockindex] = schannel_send;
677 /* reset our connection state machine */
678 connssl->connecting_state = ssl_connect_1;
684 schannel_send(struct connectdata *conn, int sockindex,
685 const void *buf, size_t len, CURLcode *err)
687 ssize_t written = -1;
689 unsigned char *data = NULL;
690 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
692 SecBufferDesc outbuf_desc;
693 SECURITY_STATUS sspi_status = SEC_E_OK;
696 /* check if the maximum stream sizes were queried */
697 if(connssl->stream_sizes.cbMaximumMessage == 0) {
698 sspi_status = s_pSecFn->QueryContextAttributes(
699 &connssl->ctxt->ctxt_handle,
700 SECPKG_ATTR_STREAM_SIZES,
701 &connssl->stream_sizes);
702 if(sspi_status != SEC_E_OK) {
703 *err = CURLE_SEND_ERROR;
708 /* check if the buffer is longer than the maximum message length */
709 if(len > connssl->stream_sizes.cbMaximumMessage) {
710 *err = CURLE_SEND_ERROR;
714 /* calculate the complete message length and allocate a buffer for it */
715 data_len = connssl->stream_sizes.cbHeader + len +
716 connssl->stream_sizes.cbTrailer;
717 data = (unsigned char *) malloc(data_len);
719 *err = CURLE_OUT_OF_MEMORY;
723 /* setup output buffers (header, data, trailer, empty) */
724 InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
725 data, connssl->stream_sizes.cbHeader);
726 InitSecBuffer(&outbuf[1], SECBUFFER_DATA,
727 data + connssl->stream_sizes.cbHeader, curlx_uztoul(len));
728 InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
729 data + connssl->stream_sizes.cbHeader + len,
730 connssl->stream_sizes.cbTrailer);
731 InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0);
732 InitSecBufferDesc(&outbuf_desc, outbuf, 4);
734 /* copy data into output buffer */
735 memcpy(outbuf[1].pvBuffer, buf, len);
737 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
738 sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt->ctxt_handle, 0,
741 /* check if the message was encrypted */
742 if(sspi_status == SEC_E_OK) {
745 /* send the encrypted message including header, data and trailer */
746 len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
749 It's important to send the full message which includes the header,
750 encrypted payload, and trailer. Until the client receives all the
751 data a coherent message has not been delivered and the client
752 can't read any of it.
754 If we wanted to buffer the unwritten encrypted bytes, we would
755 tell the client that all data it has requested to be sent has been
756 sent. The unwritten encrypted bytes would be the first bytes to
757 send on the next invocation.
758 Here's the catch with this - if we tell the client that all the
759 bytes have been sent, will the client call this method again to
760 send the buffered data? Looking at who calls this function, it
761 seems the answer is NO.
764 /* send entire message or fail */
765 while(len > (size_t)written) {
772 timeleft = Curl_timeleft(conn->data, NULL, FALSE);
774 /* we already got the timeout */
775 failf(conn->data, "schannel: timed out sending data "
776 "(bytes sent: %zd)", written);
777 *err = CURLE_OPERATION_TIMEDOUT;
782 what = Curl_socket_ready(CURL_SOCKET_BAD, conn->sock[sockindex],
786 failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
787 *err = CURLE_SEND_ERROR;
792 failf(conn->data, "schannel: timed out sending data "
793 "(bytes sent: %zd)", written);
794 *err = CURLE_OPERATION_TIMEDOUT;
798 /* socket is writable */
800 code = Curl_write_plain(conn, conn->sock[sockindex], data + written,
801 len - written, &this_write);
802 if(code == CURLE_AGAIN)
804 else if(code != CURLE_OK) {
810 written += this_write;
813 else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) {
814 *err = CURLE_OUT_OF_MEMORY;
817 *err = CURLE_SEND_ERROR;
822 if(len == (size_t)written)
823 /* Encrypted message including header, data and trailer entirely sent.
824 The return value is the number of unencrypted bytes that were sent. */
825 written = outbuf[1].cbBuffer;
831 schannel_recv(struct connectdata *conn, int sockindex,
832 char *buf, size_t len, CURLcode *err)
835 ssize_t nread = 0, ret = -1;
837 struct SessionHandle *data = conn->data;
838 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
839 unsigned char *reallocated_buffer;
840 size_t reallocated_length;
843 SecBufferDesc inbuf_desc;
844 SECURITY_STATUS sspi_status = SEC_E_OK;
846 infof(data, "schannel: client wants to read %zu bytes\n", len);
849 /* buffer to store previously received and decrypted data */
850 if(connssl->decdata_buffer == NULL) {
851 connssl->decdata_offset = 0;
852 connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
853 connssl->decdata_buffer = malloc(connssl->decdata_length);
854 if(connssl->decdata_buffer == NULL) {
855 failf(data, "schannel: unable to allocate memory");
856 *err = CURLE_OUT_OF_MEMORY;
861 /* increase buffer in order to fit the requested amount of data */
862 if(connssl->encdata_length - connssl->encdata_offset <
863 CURL_SCHANNEL_BUFFER_FREE_SIZE || connssl->encdata_length < len) {
864 /* increase internal encrypted data buffer */
865 reallocated_length = connssl->encdata_offset +
866 CURL_SCHANNEL_BUFFER_FREE_SIZE;
867 /* make sure that the requested amount of data fits */
868 if(reallocated_length < len) {
869 reallocated_length = len;
871 reallocated_buffer = realloc(connssl->encdata_buffer,
874 if(reallocated_buffer == NULL) {
875 failf(data, "schannel: unable to re-allocate memory");
876 *err = CURLE_OUT_OF_MEMORY;
880 connssl->encdata_buffer = reallocated_buffer;
881 connssl->encdata_length = reallocated_length;
885 /* read encrypted data from socket */
886 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
887 connssl->encdata_offset, connssl->encdata_length);
888 size = connssl->encdata_length - connssl->encdata_offset;
890 *err = Curl_read_plain(conn->sock[sockindex],
891 (char *) (connssl->encdata_buffer + connssl->encdata_offset),
893 /* check for received data */
898 /* increase encrypted data buffer offset */
899 connssl->encdata_offset += nread;
902 infof(data, "schannel: encrypted data got %zd\n", ret);
905 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
906 connssl->encdata_offset, connssl->encdata_length);
908 /* check if we still have some data in our buffers */
909 while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK &&
910 connssl->decdata_offset < len) {
911 /* prepare data buffer for DecryptMessage call */
912 InitSecBuffer(&inbuf[0], SECBUFFER_DATA, connssl->encdata_buffer,
913 curlx_uztoul(connssl->encdata_offset));
915 /* we need 3 more empty input buffers for possible output */
916 InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
917 InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0);
918 InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0);
920 InitSecBufferDesc(&inbuf_desc, inbuf, 4);
922 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */
923 sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
924 &inbuf_desc, 0, NULL);
926 /* check if we need more data */
927 if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
928 infof(data, "schannel: failed to decrypt data, need more data\n");
933 /* check if everything went fine (server may want to renegotiate
934 or shutdown the connection context) */
935 if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE ||
936 sspi_status == SEC_I_CONTEXT_EXPIRED) {
937 /* check for successfully decrypted data, even before actual
938 renegotiation or shutdown of the connection context */
939 if(inbuf[1].BufferType == SECBUFFER_DATA) {
940 infof(data, "schannel: decrypted data length: %lu\n",
943 /* increase buffer in order to fit the received amount of data */
944 size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ?
945 inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE;
946 if(connssl->decdata_length - connssl->decdata_offset < size ||
947 connssl->decdata_length < len) {
948 /* increase internal decrypted data buffer */
949 reallocated_length = connssl->decdata_offset + size;
950 /* make sure that the requested amount of data fits */
951 if(reallocated_length < len) {
952 reallocated_length = len;
954 reallocated_buffer = realloc(connssl->decdata_buffer,
957 if(reallocated_buffer == NULL) {
958 failf(data, "schannel: unable to re-allocate memory");
959 *err = CURLE_OUT_OF_MEMORY;
963 connssl->decdata_buffer = reallocated_buffer;
964 connssl->decdata_length = reallocated_length;
968 /* copy decrypted data to internal buffer */
969 size = inbuf[1].cbBuffer;
971 memcpy(connssl->decdata_buffer + connssl->decdata_offset,
972 inbuf[1].pvBuffer, size);
973 connssl->decdata_offset += size;
976 infof(data, "schannel: decrypted data added: %zu\n", size);
977 infof(data, "schannel: decrypted data cached: offset %zu length %zu\n",
978 connssl->decdata_offset, connssl->decdata_length);
981 /* check for remaining encrypted data */
982 if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
983 infof(data, "schannel: encrypted data length: %lu\n",
986 /* check if the remaining data is less than the total amount
987 * and therefore begins after the already processed data
989 if(connssl->encdata_offset > inbuf[3].cbBuffer) {
990 /* move remaining encrypted data forward to the beginning of
992 memmove(connssl->encdata_buffer,
993 (connssl->encdata_buffer + connssl->encdata_offset) -
994 inbuf[3].cbBuffer, inbuf[3].cbBuffer);
995 connssl->encdata_offset = inbuf[3].cbBuffer;
998 infof(data, "schannel: encrypted data cached: offset %zu length %zu\n",
999 connssl->encdata_offset, connssl->encdata_length);
1002 /* reset encrypted buffer offset, because there is no data remaining */
1003 connssl->encdata_offset = 0;
1007 /* check if server wants to renegotiate the connection context */
1008 if(sspi_status == SEC_I_RENEGOTIATE) {
1009 infof(data, "schannel: remote party requests SSL/TLS renegotiation\n");
1011 /* begin renegotiation */
1012 infof(data, "schannel: renegotiating SSL/TLS connection\n");
1013 connssl->state = ssl_connection_negotiating;
1014 connssl->connecting_state = ssl_connect_2_writing;
1015 result = schannel_connect_common(conn, sockindex, FALSE, &done);
1019 infof(data, "schannel: SSL/TLS connection renegotiated\n");
1020 /* now retry receiving data */
1021 return schannel_recv(conn, sockindex, buf, len, err);
1026 infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
1027 connssl->decdata_offset, connssl->decdata_length);
1029 /* copy requested decrypted data to supplied buffer */
1030 size = len < connssl->decdata_offset ? len : connssl->decdata_offset;
1032 memcpy(buf, connssl->decdata_buffer, size);
1035 /* move remaining decrypted data forward to the beginning of buffer */
1036 memmove(connssl->decdata_buffer, connssl->decdata_buffer + size,
1037 connssl->decdata_offset - size);
1038 connssl->decdata_offset -= size;
1040 infof(data, "schannel: decrypted data returned %zd\n", size);
1041 infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
1042 connssl->decdata_offset, connssl->decdata_length);
1047 /* check if the server closed the connection */
1048 if(ret <= 0 && ( /* special check for Windows 2000 Professional */
1049 sspi_status == SEC_I_CONTEXT_EXPIRED || (sspi_status == SEC_E_OK &&
1050 connssl->encdata_offset > 0 && connssl->encdata_buffer[0] == 0x15))) {
1051 infof(data, "schannel: server closed the connection\n");
1056 /* check if something went wrong and we need to return an error */
1057 if(ret < 0 && sspi_status != SEC_E_OK) {
1058 infof(data, "schannel: failed to read data from server: %s\n",
1059 Curl_sspi_strerror(conn, sspi_status));
1060 *err = CURLE_RECV_ERROR;
1068 Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex,
1071 return schannel_connect_common(conn, sockindex, TRUE, done);
1075 Curl_schannel_connect(struct connectdata *conn, int sockindex)
1080 result = schannel_connect_common(conn, sockindex, FALSE, &done);
1089 bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex)
1091 const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1093 if(connssl->use) /* SSL/TLS is in use */
1094 return (connssl->encdata_offset > 0 ||
1095 connssl->decdata_offset > 0 ) ? TRUE : FALSE;
1100 void Curl_schannel_close(struct connectdata *conn, int sockindex)
1102 if(conn->ssl[sockindex].use)
1103 /* if the SSL/TLS channel hasn't been shut down yet, do that now. */
1104 Curl_ssl_shutdown(conn, sockindex);
1107 int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
1109 /* See http://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
1110 * Shutting Down an Schannel Connection
1112 struct SessionHandle *data = conn->data;
1113 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1115 infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
1116 conn->host.name, conn->remote_port);
1118 if(connssl->cred && connssl->ctxt) {
1119 SecBufferDesc BuffDesc;
1121 SECURITY_STATUS sspi_status;
1123 SecBufferDesc outbuf_desc;
1126 DWORD dwshut = SCHANNEL_SHUTDOWN;
1128 InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
1129 InitSecBufferDesc(&BuffDesc, &Buffer, 1);
1131 sspi_status = s_pSecFn->ApplyControlToken(&connssl->ctxt->ctxt_handle,
1134 if(sspi_status != SEC_E_OK)
1135 failf(data, "schannel: ApplyControlToken failure: %s",
1136 Curl_sspi_strerror(conn, sspi_status));
1138 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
1140 return CURLE_OUT_OF_MEMORY;
1142 /* setup output buffer */
1143 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
1144 InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
1146 sspi_status = s_pSecFn->InitializeSecurityContext(
1147 &connssl->cred->cred_handle,
1148 &connssl->ctxt->ctxt_handle,
1155 &connssl->ctxt->ctxt_handle,
1157 &connssl->ret_flags,
1158 &connssl->ctxt->time_stamp);
1160 Curl_unicodefree(host_name);
1162 if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
1163 /* send close message which is in output buffer */
1165 code = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
1166 outbuf.cbBuffer, &written);
1168 s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
1169 if((code != CURLE_OK) || (outbuf.cbBuffer != (size_t)written)) {
1170 infof(data, "schannel: failed to send close msg: %s"
1171 " (bytes written: %zd)\n", curl_easy_strerror(code), written);
1176 /* free SSPI Schannel API security context handle */
1178 infof(data, "schannel: clear security context handle\n");
1179 s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
1180 Curl_safefree(connssl->ctxt);
1183 /* free SSPI Schannel API credential handle */
1185 /* decrement the reference counter of the credential/session handle */
1186 if(connssl->cred->refcount > 0) {
1187 connssl->cred->refcount--;
1188 infof(data, "schannel: decremented credential handle refcount = %d\n",
1189 connssl->cred->refcount);
1192 /* if the handle was not cached and the refcount is zero */
1193 if(!connssl->cred->cached && connssl->cred->refcount == 0) {
1194 infof(data, "schannel: clear credential handle\n");
1195 s_pSecFn->FreeCredentialsHandle(&connssl->cred->cred_handle);
1196 Curl_safefree(connssl->cred);
1200 /* free internal buffer for received encrypted data */
1201 if(connssl->encdata_buffer != NULL) {
1202 Curl_safefree(connssl->encdata_buffer);
1203 connssl->encdata_length = 0;
1204 connssl->encdata_offset = 0;
1207 /* free internal buffer for received decrypted data */
1208 if(connssl->decdata_buffer != NULL) {
1209 Curl_safefree(connssl->decdata_buffer);
1210 connssl->decdata_length = 0;
1211 connssl->decdata_offset = 0;
1217 void Curl_schannel_session_free(void *ptr)
1219 struct curl_schannel_cred *cred = ptr;
1221 if(cred && cred->cached && cred->refcount == 0) {
1222 s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
1223 Curl_safefree(cred);
1227 int Curl_schannel_init(void)
1229 return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
1232 void Curl_schannel_cleanup(void)
1234 Curl_sspi_global_cleanup();
1237 size_t Curl_schannel_version(char *buffer, size_t size)
1239 size = snprintf(buffer, size, "WinSSL");
1244 int Curl_schannel_random(unsigned char *entropy, size_t length)
1246 HCRYPTPROV hCryptProv = 0;
1248 if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
1249 CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
1252 if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
1253 CryptReleaseContext(hCryptProv, 0UL);
1257 CryptReleaseContext(hCryptProv, 0UL);
1262 static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
1264 SECURITY_STATUS status;
1265 struct SessionHandle *data = conn->data;
1266 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1267 CURLcode result = CURLE_OK;
1268 CERT_CONTEXT *pCertContextServer = NULL;
1269 const CERT_CHAIN_CONTEXT *pChainContext = NULL;
1271 status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
1272 SECPKG_ATTR_REMOTE_CERT_CONTEXT,
1273 &pCertContextServer);
1275 if((status != SEC_E_OK) || (pCertContextServer == NULL)) {
1276 failf(data, "schannel: Failed to read remote certificate context: %s",
1277 Curl_sspi_strerror(conn, status));
1278 result = CURLE_PEER_FAILED_VERIFICATION;
1281 if(result == CURLE_OK) {
1282 CERT_CHAIN_PARA ChainPara;
1283 memset(&ChainPara, 0, sizeof(ChainPara));
1284 ChainPara.cbSize = sizeof(ChainPara);
1286 if(!CertGetCertificateChain(NULL,
1289 pCertContextServer->hCertStore,
1294 failf(data, "schannel: CertGetCertificateChain failed: %s",
1295 Curl_sspi_strerror(conn, GetLastError()));
1296 pChainContext = NULL;
1297 result = CURLE_PEER_FAILED_VERIFICATION;
1300 if(result == CURLE_OK) {
1301 CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0];
1302 DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED|
1303 CERT_TRUST_REVOCATION_STATUS_UNKNOWN);
1304 dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
1305 if(dwTrustErrorMask) {
1306 if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
1307 failf(data, "schannel: CertGetCertificateChain trust error"
1308 " CERT_TRUST_IS_PARTIAL_CHAIN");
1309 if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
1310 failf(data, "schannel: CertGetCertificateChain trust error"
1311 " CERT_TRUST_IS_UNTRUSTED_ROOT");
1312 if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
1313 failf(data, "schannel: CertGetCertificateChain trust error"
1314 " CERT_TRUST_IS_NOT_TIME_VALID");
1315 failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
1317 result = CURLE_PEER_FAILED_VERIFICATION;
1322 if(result == CURLE_OK) {
1323 if(data->set.ssl.verifyhost) {
1324 TCHAR cert_hostname_buff[128];
1326 xcharp_u cert_hostname;
1329 cert_hostname.const_tchar_ptr = cert_hostname_buff;
1330 hostname.tchar_ptr = Curl_convert_UTF8_to_tchar(conn->host.name);
1332 len = CertGetNameString(pCertContextServer,
1336 cert_hostname.tchar_ptr,
1338 if(len > 0 && *cert_hostname.tchar_ptr == '*') {
1339 /* this is a wildcard cert. try matching the last len - 1 chars */
1340 int hostname_len = strlen(conn->host.name);
1341 cert_hostname.tchar_ptr++;
1342 if(_tcsicmp(cert_hostname.const_tchar_ptr,
1343 hostname.const_tchar_ptr + hostname_len - len + 2) != 0)
1344 result = CURLE_PEER_FAILED_VERIFICATION;
1346 else if(len == 0 || _tcsicmp(hostname.const_tchar_ptr,
1347 cert_hostname.const_tchar_ptr) != 0) {
1348 result = CURLE_PEER_FAILED_VERIFICATION;
1350 if(result == CURLE_PEER_FAILED_VERIFICATION) {
1351 char *_cert_hostname;
1352 _cert_hostname = Curl_convert_tchar_to_UTF8(cert_hostname.tchar_ptr);
1353 failf(data, "schannel: CertGetNameString() certificate hostname "
1354 "(%s) did not match connection (%s)",
1355 _cert_hostname, conn->host.name);
1356 Curl_unicodefree(_cert_hostname);
1358 Curl_unicodefree(hostname.tchar_ptr);
1363 CertFreeCertificateChain(pChainContext);
1365 if(pCertContextServer)
1366 CertFreeCertificateContext(pCertContextServer);
1370 #endif /* _WIN32_WCE */
1372 #endif /* USE_SCHANNEL */