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 http://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. http://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 req_method = SSLv3_client_method();
149 case CURL_SSLVERSION_SSLv2:
150 failf(data, "CyaSSL does not support SSLv2");
151 return CURLE_SSL_CONNECT_ERROR;
153 failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
154 return CURLE_SSL_CONNECT_ERROR;
158 failf(data, "SSL: couldn't create a method!");
159 return CURLE_OUT_OF_MEMORY;
163 SSL_CTX_free(conssl->ctx);
164 conssl->ctx = SSL_CTX_new(req_method);
167 failf(data, "SSL: couldn't create a context!");
168 return CURLE_OUT_OF_MEMORY;
171 switch(data->set.ssl.version) {
172 case CURL_SSLVERSION_DEFAULT:
173 case CURL_SSLVERSION_TLSv1:
174 #if LIBCYASSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */
175 /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is whatever
176 minimum version of TLS was built in and at least TLS 1.0. For later library
177 versions that could change (eg TLS 1.0 built in but defaults to TLS 1.1) so
178 we have this short circuit evaluation to find the minimum supported TLS
179 version. We use wolfSSL_CTX_SetMinVersion and not CyaSSL_SetMinVersion
180 because only the former will work before the user's CTX callback is called.
182 if((wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1) != 1) &&
183 (wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1_1) != 1) &&
184 (wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1_2) != 1)) {
185 failf(data, "SSL: couldn't set the minimum protocol version");
186 return CURLE_SSL_CONNECT_ERROR;
192 #ifndef NO_FILESYSTEM
193 /* load trusted cacert */
194 if(data->set.str[STRING_SSL_CAFILE]) {
195 if(1 != SSL_CTX_load_verify_locations(conssl->ctx,
196 data->set.str[STRING_SSL_CAFILE],
197 data->set.str[STRING_SSL_CAPATH])) {
198 if(data->set.ssl.verifypeer) {
199 /* Fail if we insist on successfully verifying the server. */
200 failf(data, "error setting certificate verify locations:\n"
201 " CAfile: %s\n CApath: %s",
202 data->set.str[STRING_SSL_CAFILE]?
203 data->set.str[STRING_SSL_CAFILE]: "none",
204 data->set.str[STRING_SSL_CAPATH]?
205 data->set.str[STRING_SSL_CAPATH] : "none");
206 return CURLE_SSL_CACERT_BADFILE;
209 /* Just continue with a warning if no strict certificate
210 verification is required. */
211 infof(data, "error setting certificate verify locations,"
212 " continuing anyway:\n");
216 /* Everything is fine. */
217 infof(data, "successfully set certificate verify locations:\n");
222 data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]:
224 data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
228 /* Load the client certificate, and private key */
229 if(data->set.str[STRING_CERT] && data->set.str[STRING_KEY]) {
230 int file_type = do_file_type(data->set.str[STRING_CERT_TYPE]);
232 if(SSL_CTX_use_certificate_file(conssl->ctx, data->set.str[STRING_CERT],
234 failf(data, "unable to use client certificate (no key or wrong pass"
236 return CURLE_SSL_CONNECT_ERROR;
239 file_type = do_file_type(data->set.str[STRING_KEY_TYPE]);
240 if(SSL_CTX_use_PrivateKey_file(conssl->ctx, data->set.str[STRING_KEY],
242 failf(data, "unable to set private key");
243 return CURLE_SSL_CONNECT_ERROR;
246 #endif /* !NO_FILESYSTEM */
248 /* SSL always tries to verify the peer, this only says whether it should
249 * fail to connect if the verification fails, or if it should continue
250 * anyway. In the latter case the result of the verification is checked with
251 * SSL_get_verify_result() below. */
252 SSL_CTX_set_verify(conssl->ctx,
253 data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
258 struct in_addr addr4;
260 struct in6_addr addr6;
262 size_t hostname_len = strlen(conn->host.name);
263 if((hostname_len < USHRT_MAX) &&
264 (0 == Curl_inet_pton(AF_INET, conn->host.name, &addr4)) &&
266 (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr6)) &&
268 (CyaSSL_CTX_UseSNI(conssl->ctx, CYASSL_SNI_HOST_NAME, conn->host.name,
269 (unsigned short)hostname_len) != 1)) {
270 infof(data, "WARNING: failed to configure server name indication (SNI) "
276 /* give application a chance to interfere with SSL set up. */
277 if(data->set.ssl.fsslctx) {
278 CURLcode result = CURLE_OK;
279 result = (*data->set.ssl.fsslctx)(data, conssl->ctx,
280 data->set.ssl.fsslctxp);
282 failf(data, "error signaled by ssl ctx callback");
287 else if(data->set.ssl.verifypeer) {
288 failf(data, "SSL: Certificates couldn't be loaded because CyaSSL was built"
289 " with \"no filesystem\". Either disable peer verification"
290 " (insecure) or if you are building an application with libcurl you"
291 " can load certificates via CURLOPT_SSL_CTX_FUNCTION.");
292 return CURLE_SSL_CONNECT_ERROR;
296 /* Let's make an SSL structure */
298 SSL_free(conssl->handle);
299 conssl->handle = SSL_new(conssl->ctx);
300 if(!conssl->handle) {
301 failf(data, "SSL: couldn't create a context (handle)!");
302 return CURLE_OUT_OF_MEMORY;
305 /* Check if there's a cached ID we can/should use here! */
306 if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
307 /* we got a session id, use it! */
308 if(!SSL_set_session(conssl->handle, ssl_sessionid)) {
309 failf(data, "SSL: SSL_set_session failed: %s",
310 ERR_error_string(SSL_get_error(conssl->handle, 0), error_buffer));
311 return CURLE_SSL_CONNECT_ERROR;
313 /* Informational message */
314 infof (data, "SSL re-using session ID\n");
317 /* pass the raw socket into the SSL layer */
318 if(!SSL_set_fd(conssl->handle, (int)sockfd)) {
319 failf(data, "SSL: SSL_set_fd failed");
320 return CURLE_SSL_CONNECT_ERROR;
323 conssl->connecting_state = ssl_connect_2;
329 cyassl_connect_step2(struct connectdata *conn,
333 struct SessionHandle *data = conn->data;
334 struct ssl_connect_data* conssl = &conn->ssl[sockindex];
336 conn->recv[sockindex] = cyassl_recv;
337 conn->send[sockindex] = cyassl_send;
339 /* Enable RFC2818 checks */
340 if(data->set.ssl.verifyhost) {
341 ret = CyaSSL_check_domain_name(conssl->handle, conn->host.name);
342 if(ret == SSL_FAILURE)
343 return CURLE_OUT_OF_MEMORY;
346 ret = SSL_connect(conssl->handle);
348 char error_buffer[CYASSL_MAX_ERROR_SZ];
349 int detail = SSL_get_error(conssl->handle, ret);
351 if(SSL_ERROR_WANT_READ == detail) {
352 conssl->connecting_state = ssl_connect_2_reading;
355 else if(SSL_ERROR_WANT_WRITE == detail) {
356 conssl->connecting_state = ssl_connect_2_writing;
359 /* There is no easy way to override only the CN matching.
360 * This will enable the override of both mismatching SubjectAltNames
361 * as also mismatching CN fields */
362 else if(DOMAIN_NAME_MISMATCH == detail) {
364 failf(data, "\tsubject alt name(s) or common name do not match \"%s\"\n",
365 conn->host.dispname);
366 return CURLE_PEER_FAILED_VERIFICATION;
368 /* When the CyaSSL_check_domain_name() is used and you desire to continue
369 * on a DOMAIN_NAME_MISMATCH, i.e. 'data->set.ssl.verifyhost == 0',
370 * CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA error. The only
371 * way to do this is currently to switch the CyaSSL_check_domain_name()
372 * in and out based on the 'data->set.ssl.verifyhost' value. */
373 if(data->set.ssl.verifyhost) {
375 "\tsubject alt name(s) or common name do not match \"%s\"\n",
376 conn->host.dispname);
377 return CURLE_PEER_FAILED_VERIFICATION;
381 "\tsubject alt name(s) and/or common name do not match \"%s\"\n",
382 conn->host.dispname);
387 #if LIBCYASSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */
388 else if(ASN_NO_SIGNER_E == detail) {
389 if(data->set.ssl.verifypeer) {
390 failf(data, "\tCA signer not available for verification\n");
391 return CURLE_SSL_CACERT_BADFILE;
394 /* Just continue with a warning if no strict certificate
395 verification is required. */
396 infof(data, "CA signer not available for verification, "
397 "continuing anyway\n");
402 failf(data, "SSL_connect failed with error %d: %s", detail,
403 ERR_error_string(detail, error_buffer));
404 return CURLE_SSL_CONNECT_ERROR;
408 if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) {
410 const char *x509_der;
412 curl_X509certificate x509_parsed;
413 curl_asn1Element *pubkey;
416 x509 = SSL_get_peer_certificate(conssl->handle);
418 failf(data, "SSL: failed retrieving server certificate");
419 return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
422 x509_der = (const char *)CyaSSL_X509_get_der(x509, &x509_der_len);
424 failf(data, "SSL: failed retrieving ASN.1 server certificate");
425 return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
428 memset(&x509_parsed, 0, sizeof x509_parsed);
429 Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len);
431 pubkey = &x509_parsed.subjectPublicKeyInfo;
432 if(!pubkey->header || pubkey->end <= pubkey->header) {
433 failf(data, "SSL: failed retrieving public key from server certificate");
434 return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
437 result = Curl_pin_peer_pubkey(data->set.str[STRING_SSL_PINNEDPUBLICKEY],
438 (const unsigned char *)pubkey->header,
439 (size_t)(pubkey->end - pubkey->header));
441 failf(data, "SSL: public key does not match pinned public key!");
446 conssl->connecting_state = ssl_connect_3;
447 infof(data, "SSL connected\n");
454 cyassl_connect_step3(struct connectdata *conn,
457 CURLcode result = CURLE_OK;
458 void *old_ssl_sessionid=NULL;
459 struct SessionHandle *data = conn->data;
460 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
462 SSL_SESSION *our_ssl_sessionid;
464 DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
466 our_ssl_sessionid = SSL_get_session(connssl->handle);
468 incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
470 if(old_ssl_sessionid != our_ssl_sessionid) {
471 infof(data, "old SSL session ID is stale, removing\n");
472 Curl_ssl_delsessionid(conn, old_ssl_sessionid);
478 result = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
479 0 /* unknown size */);
481 failf(data, "failed to store ssl session");
486 connssl->connecting_state = ssl_connect_done;
492 static ssize_t cyassl_send(struct connectdata *conn,
498 char error_buffer[CYASSL_MAX_ERROR_SZ];
499 int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
500 int rc = SSL_write(conn->ssl[sockindex].handle, mem, memlen);
503 int err = SSL_get_error(conn->ssl[sockindex].handle, rc);
506 case SSL_ERROR_WANT_READ:
507 case SSL_ERROR_WANT_WRITE:
508 /* there's data pending, re-invoke SSL_write() */
509 *curlcode = CURLE_AGAIN;
512 failf(conn->data, "SSL write: %s, errno %d",
513 ERR_error_string(err, error_buffer),
515 *curlcode = CURLE_SEND_ERROR;
522 void Curl_cyassl_close(struct connectdata *conn, int sockindex)
524 struct ssl_connect_data *conssl = &conn->ssl[sockindex];
527 (void)SSL_shutdown(conssl->handle);
528 SSL_free (conssl->handle);
529 conssl->handle = NULL;
532 SSL_CTX_free (conssl->ctx);
537 static ssize_t cyassl_recv(struct connectdata *conn,
543 char error_buffer[CYASSL_MAX_ERROR_SZ];
544 int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
545 int nread = SSL_read(conn->ssl[num].handle, buf, buffsize);
548 int err = SSL_get_error(conn->ssl[num].handle, nread);
551 case SSL_ERROR_ZERO_RETURN: /* no more data */
553 case SSL_ERROR_WANT_READ:
554 case SSL_ERROR_WANT_WRITE:
555 /* there's data pending, re-invoke SSL_read() */
556 *curlcode = CURLE_AGAIN;
559 failf(conn->data, "SSL read: %s, errno %d",
560 ERR_error_string(err, error_buffer),
562 *curlcode = CURLE_RECV_ERROR;
570 void Curl_cyassl_session_free(void *ptr)
573 /* CyaSSL reuses sessions on own, no free */
577 size_t Curl_cyassl_version(char *buffer, size_t size)
579 #ifdef WOLFSSL_VERSION
580 return snprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION);
581 #elif defined(CYASSL_VERSION)
582 return snprintf(buffer, size, "CyaSSL/%s", CYASSL_VERSION);
584 return snprintf(buffer, size, "CyaSSL/%s", "<1.8.8");
589 int Curl_cyassl_init(void)
591 return (CyaSSL_Init() == SSL_SUCCESS);
595 bool Curl_cyassl_data_pending(const struct connectdata* conn, int connindex)
597 if(conn->ssl[connindex].handle) /* SSL is in use */
598 return (0 != SSL_pending(conn->ssl[connindex].handle)) ? TRUE : FALSE;
605 * This function is called to shut down the SSL layer but keep the
606 * socket open (CCC - Clear Command Channel)
608 int Curl_cyassl_shutdown(struct connectdata *conn, int sockindex)
611 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
613 if(connssl->handle) {
614 SSL_free (connssl->handle);
615 connssl->handle = NULL;
622 cyassl_connect_common(struct connectdata *conn,
628 struct SessionHandle *data = conn->data;
629 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
630 curl_socket_t sockfd = conn->sock[sockindex];
634 /* check if the connection has already been established */
635 if(ssl_connection_complete == connssl->state) {
640 if(ssl_connect_1==connssl->connecting_state) {
641 /* Find out how much more time we're allowed */
642 timeout_ms = Curl_timeleft(data, NULL, TRUE);
645 /* no need to continue if time already is up */
646 failf(data, "SSL connection timeout");
647 return CURLE_OPERATION_TIMEDOUT;
650 result = cyassl_connect_step1(conn, sockindex);
655 while(ssl_connect_2 == connssl->connecting_state ||
656 ssl_connect_2_reading == connssl->connecting_state ||
657 ssl_connect_2_writing == connssl->connecting_state) {
659 /* check allowed time left */
660 timeout_ms = Curl_timeleft(data, NULL, TRUE);
663 /* no need to continue if time already is up */
664 failf(data, "SSL connection timeout");
665 return CURLE_OPERATION_TIMEDOUT;
668 /* if ssl is expecting something, check if it's available. */
669 if(connssl->connecting_state == ssl_connect_2_reading
670 || connssl->connecting_state == ssl_connect_2_writing) {
672 curl_socket_t writefd = ssl_connect_2_writing==
673 connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
674 curl_socket_t readfd = ssl_connect_2_reading==
675 connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
677 what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms);
680 failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
681 return CURLE_SSL_CONNECT_ERROR;
690 failf(data, "SSL connection timeout");
691 return CURLE_OPERATION_TIMEDOUT;
694 /* socket is readable or writable */
697 /* Run transaction, and return to the caller if it failed or if
698 * this connection is part of a multi handle and this loop would
699 * execute again. This permits the owner of a multi handle to
700 * abort a connection attempt before step2 has completed while
701 * ensuring that a client using select() or epoll() will always
702 * have a valid fdset to wait on.
704 result = cyassl_connect_step2(conn, sockindex);
705 if(result || (nonblocking &&
706 (ssl_connect_2 == connssl->connecting_state ||
707 ssl_connect_2_reading == connssl->connecting_state ||
708 ssl_connect_2_writing == connssl->connecting_state)))
710 } /* repeat step2 until all transactions are done. */
712 if(ssl_connect_3 == connssl->connecting_state) {
713 result = cyassl_connect_step3(conn, sockindex);
718 if(ssl_connect_done == connssl->connecting_state) {
719 connssl->state = ssl_connection_complete;
720 conn->recv[sockindex] = cyassl_recv;
721 conn->send[sockindex] = cyassl_send;
727 /* Reset our connect state machine */
728 connssl->connecting_state = ssl_connect_1;
735 Curl_cyassl_connect_nonblocking(struct connectdata *conn,
739 return cyassl_connect_common(conn, sockindex, TRUE, done);
744 Curl_cyassl_connect(struct connectdata *conn,
750 result = cyassl_connect_common(conn, sockindex, FALSE, &done);
759 int Curl_cyassl_random(struct SessionHandle *data,
760 unsigned char *entropy,
767 if(length > UINT_MAX)
769 if(RNG_GenerateBlock(&rng, entropy, (unsigned)length))
774 void Curl_cyassl_sha256sum(const unsigned char *tmp, /* input */
776 unsigned char *sha256sum /* output */,
781 InitSha256(&SHA256pw);
782 Sha256Update(&SHA256pw, tmp, tmplen);
783 Sha256Final(&SHA256pw, sha256sum);