1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
24 * Source file for all CyaSSL-specific code for the TLS/SSL layer. No code
25 * but vtls.c should ever call or use these functions.
29 #include "curl_setup.h"
33 #define WOLFSSL_OPTIONS_IGNORE_SYS
34 /* CyaSSL's version.h, which should contain only the version, should come
35 before all other CyaSSL includes and be immediately followed by build config
36 aka options.h. https://curl.haxx.se/mail/lib-2015-04/0069.html */
37 #include <cyassl/version.h>
38 #if defined(HAVE_CYASSL_OPTIONS_H) && (LIBCYASSL_VERSION_HEX > 0x03004008)
39 #if defined(CYASSL_API) || defined(WOLFSSL_API)
40 /* Safety measure. If either is defined some API include was already included
41 and that's a problem since options.h hasn't been included yet. */
42 #error "CyaSSL API was included before the CyaSSL build options."
44 #include <cyassl/options.h>
53 #include "inet_pton.h"
56 #include "parsedate.h"
57 #include "connect.h" /* for the connect timeout */
61 #include "curl_printf.h"
63 #include <cyassl/ssl.h>
64 #ifdef HAVE_CYASSL_ERROR_SSL_H
65 #include <cyassl/error-ssl.h>
67 #include <cyassl/error.h>
69 #include <cyassl/ctaocrypt/random.h>
70 #include <cyassl/ctaocrypt/sha256.h>
72 /* The last #include files should be: */
73 #include "curl_memory.h"
76 #if LIBCYASSL_VERSION_HEX < 0x02007002 /* < 2.7.2 */
77 #define CYASSL_MAX_ERROR_SZ 80
80 static Curl_recv cyassl_recv;
81 static Curl_send cyassl_send;
84 static int do_file_type(const char *type)
87 return SSL_FILETYPE_PEM;
88 if(Curl_raw_equal(type, "PEM"))
89 return SSL_FILETYPE_PEM;
90 if(Curl_raw_equal(type, "DER"))
91 return SSL_FILETYPE_ASN1;
96 * This function loads all the client/CA certificates and CRLs. Setup the TLS
97 * layer and do all necessary magic.
100 cyassl_connect_step1(struct connectdata *conn,
103 char error_buffer[CYASSL_MAX_ERROR_SZ];
104 struct SessionHandle *data = conn->data;
105 struct ssl_connect_data* conssl = &conn->ssl[sockindex];
106 SSL_METHOD* req_method = NULL;
107 void* ssl_sessionid = NULL;
108 curl_socket_t sockfd = conn->sock[sockindex];
111 #define use_sni(x) sni = (x)
113 #define use_sni(x) Curl_nop_stmt
116 if(conssl->state == ssl_connection_complete)
119 /* check to see if we've been told to use an explicit SSL/TLS version */
120 switch(data->set.ssl.version) {
121 case CURL_SSLVERSION_DEFAULT:
122 case CURL_SSLVERSION_TLSv1:
123 #if LIBCYASSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */
124 /* minimum protocol version is set later after the CTX object is created */
125 req_method = SSLv23_client_method();
127 infof(data, "CyaSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, "
128 "TLS 1.0 is used exclusively\n");
129 req_method = TLSv1_client_method();
133 case CURL_SSLVERSION_TLSv1_0:
134 req_method = TLSv1_client_method();
137 case CURL_SSLVERSION_TLSv1_1:
138 req_method = TLSv1_1_client_method();
141 case CURL_SSLVERSION_TLSv1_2:
142 req_method = TLSv1_2_client_method();
145 case CURL_SSLVERSION_SSLv3:
146 /* before WolfSSL SSLv3 was enabled by default, and starting in WolfSSL
147 we check for its presence since it is built without it by default */
148 #if !defined(WOLFSSL_VERSION) || defined(HAVE_WOLFSSLV3_CLIENT_METHOD)
149 req_method = SSLv3_client_method();
152 failf(data, "No support for SSLv3");
153 return CURLE_NOT_BUILT_IN;
156 case CURL_SSLVERSION_SSLv2:
157 failf(data, "CyaSSL does not support SSLv2");
158 return CURLE_SSL_CONNECT_ERROR;
160 failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
161 return CURLE_SSL_CONNECT_ERROR;
165 failf(data, "SSL: couldn't create a method!");
166 return CURLE_OUT_OF_MEMORY;
170 SSL_CTX_free(conssl->ctx);
171 conssl->ctx = SSL_CTX_new(req_method);
174 failf(data, "SSL: couldn't create a context!");
175 return CURLE_OUT_OF_MEMORY;
178 switch(data->set.ssl.version) {
179 case CURL_SSLVERSION_DEFAULT:
180 case CURL_SSLVERSION_TLSv1:
181 #if LIBCYASSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */
182 /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is whatever
183 minimum version of TLS was built in and at least TLS 1.0. For later library
184 versions that could change (eg TLS 1.0 built in but defaults to TLS 1.1) so
185 we have this short circuit evaluation to find the minimum supported TLS
186 version. We use wolfSSL_CTX_SetMinVersion and not CyaSSL_SetMinVersion
187 because only the former will work before the user's CTX callback is called.
189 if((wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1) != 1) &&
190 (wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1_1) != 1) &&
191 (wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1_2) != 1)) {
192 failf(data, "SSL: couldn't set the minimum protocol version");
193 return CURLE_SSL_CONNECT_ERROR;
199 #ifndef NO_FILESYSTEM
200 /* load trusted cacert */
201 if(data->set.str[STRING_SSL_CAFILE]) {
202 if(1 != SSL_CTX_load_verify_locations(conssl->ctx,
203 data->set.str[STRING_SSL_CAFILE],
204 data->set.str[STRING_SSL_CAPATH])) {
205 if(data->set.ssl.verifypeer) {
206 /* Fail if we insist on successfully verifying the server. */
207 failf(data, "error setting certificate verify locations:\n"
208 " CAfile: %s\n CApath: %s",
209 data->set.str[STRING_SSL_CAFILE]?
210 data->set.str[STRING_SSL_CAFILE]: "none",
211 data->set.str[STRING_SSL_CAPATH]?
212 data->set.str[STRING_SSL_CAPATH] : "none");
213 return CURLE_SSL_CACERT_BADFILE;
216 /* Just continue with a warning if no strict certificate
217 verification is required. */
218 infof(data, "error setting certificate verify locations,"
219 " continuing anyway:\n");
223 /* Everything is fine. */
224 infof(data, "successfully set certificate verify locations:\n");
229 data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]:
231 data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
235 /* Load the client certificate, and private key */
236 if(data->set.str[STRING_CERT] && data->set.str[STRING_KEY]) {
237 int file_type = do_file_type(data->set.str[STRING_CERT_TYPE]);
239 if(SSL_CTX_use_certificate_file(conssl->ctx, data->set.str[STRING_CERT],
241 failf(data, "unable to use client certificate (no key or wrong pass"
243 return CURLE_SSL_CONNECT_ERROR;
246 file_type = do_file_type(data->set.str[STRING_KEY_TYPE]);
247 if(SSL_CTX_use_PrivateKey_file(conssl->ctx, data->set.str[STRING_KEY],
249 failf(data, "unable to set private key");
250 return CURLE_SSL_CONNECT_ERROR;
253 #endif /* !NO_FILESYSTEM */
255 /* SSL always tries to verify the peer, this only says whether it should
256 * fail to connect if the verification fails, or if it should continue
257 * anyway. In the latter case the result of the verification is checked with
258 * SSL_get_verify_result() below. */
259 SSL_CTX_set_verify(conssl->ctx,
260 data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
265 struct in_addr addr4;
267 struct in6_addr addr6;
269 size_t hostname_len = strlen(conn->host.name);
270 if((hostname_len < USHRT_MAX) &&
271 (0 == Curl_inet_pton(AF_INET, conn->host.name, &addr4)) &&
273 (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr6)) &&
275 (CyaSSL_CTX_UseSNI(conssl->ctx, CYASSL_SNI_HOST_NAME, conn->host.name,
276 (unsigned short)hostname_len) != 1)) {
277 infof(data, "WARNING: failed to configure server name indication (SNI) "
283 /* give application a chance to interfere with SSL set up. */
284 if(data->set.ssl.fsslctx) {
285 CURLcode result = CURLE_OK;
286 result = (*data->set.ssl.fsslctx)(data, conssl->ctx,
287 data->set.ssl.fsslctxp);
289 failf(data, "error signaled by ssl ctx callback");
294 else if(data->set.ssl.verifypeer) {
295 failf(data, "SSL: Certificates couldn't be loaded because CyaSSL was built"
296 " with \"no filesystem\". Either disable peer verification"
297 " (insecure) or if you are building an application with libcurl you"
298 " can load certificates via CURLOPT_SSL_CTX_FUNCTION.");
299 return CURLE_SSL_CONNECT_ERROR;
303 /* Let's make an SSL structure */
305 SSL_free(conssl->handle);
306 conssl->handle = SSL_new(conssl->ctx);
307 if(!conssl->handle) {
308 failf(data, "SSL: couldn't create a context (handle)!");
309 return CURLE_OUT_OF_MEMORY;
312 /* Check if there's a cached ID we can/should use here! */
313 if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
314 /* we got a session id, use it! */
315 if(!SSL_set_session(conssl->handle, ssl_sessionid)) {
316 failf(data, "SSL: SSL_set_session failed: %s",
317 ERR_error_string(SSL_get_error(conssl->handle, 0), error_buffer));
318 return CURLE_SSL_CONNECT_ERROR;
320 /* Informational message */
321 infof (data, "SSL re-using session ID\n");
324 /* pass the raw socket into the SSL layer */
325 if(!SSL_set_fd(conssl->handle, (int)sockfd)) {
326 failf(data, "SSL: SSL_set_fd failed");
327 return CURLE_SSL_CONNECT_ERROR;
330 conssl->connecting_state = ssl_connect_2;
336 cyassl_connect_step2(struct connectdata *conn,
340 struct SessionHandle *data = conn->data;
341 struct ssl_connect_data* conssl = &conn->ssl[sockindex];
343 conn->recv[sockindex] = cyassl_recv;
344 conn->send[sockindex] = cyassl_send;
346 /* Enable RFC2818 checks */
347 if(data->set.ssl.verifyhost) {
348 ret = CyaSSL_check_domain_name(conssl->handle, conn->host.name);
349 if(ret == SSL_FAILURE)
350 return CURLE_OUT_OF_MEMORY;
353 ret = SSL_connect(conssl->handle);
355 char error_buffer[CYASSL_MAX_ERROR_SZ];
356 int detail = SSL_get_error(conssl->handle, ret);
358 if(SSL_ERROR_WANT_READ == detail) {
359 conssl->connecting_state = ssl_connect_2_reading;
362 else if(SSL_ERROR_WANT_WRITE == detail) {
363 conssl->connecting_state = ssl_connect_2_writing;
366 /* There is no easy way to override only the CN matching.
367 * This will enable the override of both mismatching SubjectAltNames
368 * as also mismatching CN fields */
369 else if(DOMAIN_NAME_MISMATCH == detail) {
371 failf(data, "\tsubject alt name(s) or common name do not match \"%s\"\n",
372 conn->host.dispname);
373 return CURLE_PEER_FAILED_VERIFICATION;
375 /* When the CyaSSL_check_domain_name() is used and you desire to continue
376 * on a DOMAIN_NAME_MISMATCH, i.e. 'data->set.ssl.verifyhost == 0',
377 * CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA error. The only
378 * way to do this is currently to switch the CyaSSL_check_domain_name()
379 * in and out based on the 'data->set.ssl.verifyhost' value. */
380 if(data->set.ssl.verifyhost) {
382 "\tsubject alt name(s) or common name do not match \"%s\"\n",
383 conn->host.dispname);
384 return CURLE_PEER_FAILED_VERIFICATION;
388 "\tsubject alt name(s) and/or common name do not match \"%s\"\n",
389 conn->host.dispname);
394 #if LIBCYASSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */
395 else if(ASN_NO_SIGNER_E == detail) {
396 if(data->set.ssl.verifypeer) {
397 failf(data, "\tCA signer not available for verification\n");
398 return CURLE_SSL_CACERT_BADFILE;
401 /* Just continue with a warning if no strict certificate
402 verification is required. */
403 infof(data, "CA signer not available for verification, "
404 "continuing anyway\n");
409 failf(data, "SSL_connect failed with error %d: %s", detail,
410 ERR_error_string(detail, error_buffer));
411 return CURLE_SSL_CONNECT_ERROR;
415 if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) {
416 #if defined(HAVE_WOLFSSL_GET_PEER_CERTIFICATE) || \
417 defined(HAVE_CYASSL_GET_PEER_CERTIFICATE)
419 const char *x509_der;
421 curl_X509certificate x509_parsed;
422 curl_asn1Element *pubkey;
425 x509 = SSL_get_peer_certificate(conssl->handle);
427 failf(data, "SSL: failed retrieving server certificate");
428 return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
431 x509_der = (const char *)CyaSSL_X509_get_der(x509, &x509_der_len);
433 failf(data, "SSL: failed retrieving ASN.1 server certificate");
434 return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
437 memset(&x509_parsed, 0, sizeof x509_parsed);
438 Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len);
440 pubkey = &x509_parsed.subjectPublicKeyInfo;
441 if(!pubkey->header || pubkey->end <= pubkey->header) {
442 failf(data, "SSL: failed retrieving public key from server certificate");
443 return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
446 result = Curl_pin_peer_pubkey(data,
447 data->set.str[STRING_SSL_PINNEDPUBLICKEY],
448 (const unsigned char *)pubkey->header,
449 (size_t)(pubkey->end - pubkey->header));
451 failf(data, "SSL: public key does not match pinned public key!");
455 failf(data, "Library lacks pinning support built-in");
456 return CURLE_NOT_BUILT_IN;
460 conssl->connecting_state = ssl_connect_3;
461 infof(data, "SSL connected\n");
468 cyassl_connect_step3(struct connectdata *conn,
471 CURLcode result = CURLE_OK;
472 void *old_ssl_sessionid=NULL;
473 struct SessionHandle *data = conn->data;
474 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
476 SSL_SESSION *our_ssl_sessionid;
478 DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
480 our_ssl_sessionid = SSL_get_session(connssl->handle);
482 incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
484 if(old_ssl_sessionid != our_ssl_sessionid) {
485 infof(data, "old SSL session ID is stale, removing\n");
486 Curl_ssl_delsessionid(conn, old_ssl_sessionid);
492 result = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
493 0 /* unknown size */);
495 failf(data, "failed to store ssl session");
500 connssl->connecting_state = ssl_connect_done;
506 static ssize_t cyassl_send(struct connectdata *conn,
512 char error_buffer[CYASSL_MAX_ERROR_SZ];
513 int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
514 int rc = SSL_write(conn->ssl[sockindex].handle, mem, memlen);
517 int err = SSL_get_error(conn->ssl[sockindex].handle, rc);
520 case SSL_ERROR_WANT_READ:
521 case SSL_ERROR_WANT_WRITE:
522 /* there's data pending, re-invoke SSL_write() */
523 *curlcode = CURLE_AGAIN;
526 failf(conn->data, "SSL write: %s, errno %d",
527 ERR_error_string(err, error_buffer),
529 *curlcode = CURLE_SEND_ERROR;
536 void Curl_cyassl_close(struct connectdata *conn, int sockindex)
538 struct ssl_connect_data *conssl = &conn->ssl[sockindex];
541 (void)SSL_shutdown(conssl->handle);
542 SSL_free (conssl->handle);
543 conssl->handle = NULL;
546 SSL_CTX_free (conssl->ctx);
551 static ssize_t cyassl_recv(struct connectdata *conn,
557 char error_buffer[CYASSL_MAX_ERROR_SZ];
558 int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
559 int nread = SSL_read(conn->ssl[num].handle, buf, buffsize);
562 int err = SSL_get_error(conn->ssl[num].handle, nread);
565 case SSL_ERROR_ZERO_RETURN: /* no more data */
567 case SSL_ERROR_WANT_READ:
568 case SSL_ERROR_WANT_WRITE:
569 /* there's data pending, re-invoke SSL_read() */
570 *curlcode = CURLE_AGAIN;
573 failf(conn->data, "SSL read: %s, errno %d",
574 ERR_error_string(err, error_buffer),
576 *curlcode = CURLE_RECV_ERROR;
584 void Curl_cyassl_session_free(void *ptr)
587 /* CyaSSL reuses sessions on own, no free */
591 size_t Curl_cyassl_version(char *buffer, size_t size)
593 #ifdef WOLFSSL_VERSION
594 return snprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION);
595 #elif defined(CYASSL_VERSION)
596 return snprintf(buffer, size, "CyaSSL/%s", CYASSL_VERSION);
598 return snprintf(buffer, size, "CyaSSL/%s", "<1.8.8");
603 int Curl_cyassl_init(void)
605 return (CyaSSL_Init() == SSL_SUCCESS);
609 bool Curl_cyassl_data_pending(const struct connectdata* conn, int connindex)
611 if(conn->ssl[connindex].handle) /* SSL is in use */
612 return (0 != SSL_pending(conn->ssl[connindex].handle)) ? TRUE : FALSE;
619 * This function is called to shut down the SSL layer but keep the
620 * socket open (CCC - Clear Command Channel)
622 int Curl_cyassl_shutdown(struct connectdata *conn, int sockindex)
625 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
627 if(connssl->handle) {
628 SSL_free (connssl->handle);
629 connssl->handle = NULL;
636 cyassl_connect_common(struct connectdata *conn,
642 struct SessionHandle *data = conn->data;
643 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
644 curl_socket_t sockfd = conn->sock[sockindex];
648 /* check if the connection has already been established */
649 if(ssl_connection_complete == connssl->state) {
654 if(ssl_connect_1==connssl->connecting_state) {
655 /* Find out how much more time we're allowed */
656 timeout_ms = Curl_timeleft(data, NULL, TRUE);
659 /* no need to continue if time already is up */
660 failf(data, "SSL connection timeout");
661 return CURLE_OPERATION_TIMEDOUT;
664 result = cyassl_connect_step1(conn, sockindex);
669 while(ssl_connect_2 == connssl->connecting_state ||
670 ssl_connect_2_reading == connssl->connecting_state ||
671 ssl_connect_2_writing == connssl->connecting_state) {
673 /* check allowed time left */
674 timeout_ms = Curl_timeleft(data, NULL, TRUE);
677 /* no need to continue if time already is up */
678 failf(data, "SSL connection timeout");
679 return CURLE_OPERATION_TIMEDOUT;
682 /* if ssl is expecting something, check if it's available. */
683 if(connssl->connecting_state == ssl_connect_2_reading
684 || connssl->connecting_state == ssl_connect_2_writing) {
686 curl_socket_t writefd = ssl_connect_2_writing==
687 connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
688 curl_socket_t readfd = ssl_connect_2_reading==
689 connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
691 what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms);
694 failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
695 return CURLE_SSL_CONNECT_ERROR;
704 failf(data, "SSL connection timeout");
705 return CURLE_OPERATION_TIMEDOUT;
708 /* socket is readable or writable */
711 /* Run transaction, and return to the caller if it failed or if
712 * this connection is part of a multi handle and this loop would
713 * execute again. This permits the owner of a multi handle to
714 * abort a connection attempt before step2 has completed while
715 * ensuring that a client using select() or epoll() will always
716 * have a valid fdset to wait on.
718 result = cyassl_connect_step2(conn, sockindex);
719 if(result || (nonblocking &&
720 (ssl_connect_2 == connssl->connecting_state ||
721 ssl_connect_2_reading == connssl->connecting_state ||
722 ssl_connect_2_writing == connssl->connecting_state)))
724 } /* repeat step2 until all transactions are done. */
726 if(ssl_connect_3 == connssl->connecting_state) {
727 result = cyassl_connect_step3(conn, sockindex);
732 if(ssl_connect_done == connssl->connecting_state) {
733 connssl->state = ssl_connection_complete;
734 conn->recv[sockindex] = cyassl_recv;
735 conn->send[sockindex] = cyassl_send;
741 /* Reset our connect state machine */
742 connssl->connecting_state = ssl_connect_1;
749 Curl_cyassl_connect_nonblocking(struct connectdata *conn,
753 return cyassl_connect_common(conn, sockindex, TRUE, done);
758 Curl_cyassl_connect(struct connectdata *conn,
764 result = cyassl_connect_common(conn, sockindex, FALSE, &done);
773 int Curl_cyassl_random(struct SessionHandle *data,
774 unsigned char *entropy,
781 if(length > UINT_MAX)
783 if(RNG_GenerateBlock(&rng, entropy, (unsigned)length))
788 void Curl_cyassl_sha256sum(const unsigned char *tmp, /* input */
790 unsigned char *sha256sum /* output */,
795 InitSha256(&SHA256pw);
796 Sha256Update(&SHA256pw, tmp, (word32)tmplen);
797 Sha256Final(&SHA256pw, sha256sum);