1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
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.
13 *
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.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23/*
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.
26 *
27 */
28
29#include "curl_setup.h"
30
31#ifdef USE_CYASSL
32
33#define WOLFSSL_OPTIONS_IGNORE_SYS
34/* CyaSSL's version.h, which should contain only the version, should come
35before all other CyaSSL includes and be immediately followed by build config
36aka 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
41and that's a problem since options.h hasn't been included yet. */
42#error "CyaSSL API was included before the CyaSSL build options."
43#endif
44#include <cyassl/options.h>
45#endif
46
47#ifdef HAVE_LIMITS_H
48#include <limits.h>
49#endif
50
51#include "urldata.h"
52#include "sendf.h"
53#include "inet_pton.h"
54#include "vtls.h"
55#include "parsedate.h"
56#include "connect.h" /* for the connect timeout */
57#include "select.h"
58#include "rawstr.h"
59#include "x509asn1.h"
60#include "curl_printf.h"
61
62#include <cyassl/ssl.h>
63#ifdef HAVE_CYASSL_ERROR_SSL_H
64#include <cyassl/error-ssl.h>
65#else
66#include <cyassl/error.h>
67#endif
68#include <cyassl/ctaocrypt/random.h>
69#include <cyassl/ctaocrypt/sha256.h>
70
71#include "cyassl.h"
72
73/* The last #include files should be: */
74#include "curl_memory.h"
75#include "memdebug.h"
76
77#if LIBCYASSL_VERSION_HEX < 0x02007002 /* < 2.7.2 */
78#define CYASSL_MAX_ERROR_SZ 80
79#endif
80
81/* To determine what functions are available we rely on one or both of:
82   - the user's options.h generated by CyaSSL/wolfSSL
83   - the symbols detected by curl's configure
84   Since they are markedly different from one another, and one or the other may
85   not be available, we do some checking below to bring things in sync. */
86
87/* HAVE_ALPN is wolfSSL's build time symbol for enabling ALPN in options.h. */
88#ifndef HAVE_ALPN
89#ifdef HAVE_WOLFSSL_USEALPN
90#define HAVE_ALPN
91#endif
92#endif
93
94/* WOLFSSL_ALLOW_SSLV3 is wolfSSL's build time symbol for enabling SSLv3 in
95   options.h, but is only seen in >= 3.6.6 since that's when they started
96   disabling SSLv3 by default. */
97#ifndef WOLFSSL_ALLOW_SSLV3
98#if (LIBCYASSL_VERSION_HEX < 0x03006006) || \
99    defined(HAVE_WOLFSSLV3_CLIENT_METHOD)
100#define WOLFSSL_ALLOW_SSLV3
101#endif
102#endif
103
104/* HAVE_SUPPORTED_CURVES is wolfSSL's build time symbol for enabling the ECC
105   supported curve extension in options.h. Note ECC is enabled separately. */
106#ifndef HAVE_SUPPORTED_CURVES
107#if defined(HAVE_CYASSL_CTX_USESUPPORTEDCURVE) || \
108    defined(HAVE_WOLFSSL_CTX_USESUPPORTEDCURVE)
109#define HAVE_SUPPORTED_CURVES
110#endif
111#endif
112
113static Curl_recv cyassl_recv;
114static Curl_send cyassl_send;
115
116
117static int do_file_type(const char *type)
118{
119  if(!type || !type[0])
120    return SSL_FILETYPE_PEM;
121  if(Curl_raw_equal(type, "PEM"))
122    return SSL_FILETYPE_PEM;
123  if(Curl_raw_equal(type, "DER"))
124    return SSL_FILETYPE_ASN1;
125  return -1;
126}
127
128/*
129 * This function loads all the client/CA certificates and CRLs. Setup the TLS
130 * layer and do all necessary magic.
131 */
132static CURLcode
133cyassl_connect_step1(struct connectdata *conn,
134                     int sockindex)
135{
136  char error_buffer[CYASSL_MAX_ERROR_SZ];
137  struct Curl_easy *data = conn->data;
138  struct ssl_connect_data* conssl = &conn->ssl[sockindex];
139  SSL_METHOD* req_method = NULL;
140  curl_socket_t sockfd = conn->sock[sockindex];
141#ifdef HAVE_SNI
142  bool sni = FALSE;
143#define use_sni(x)  sni = (x)
144#else
145#define use_sni(x)  Curl_nop_stmt
146#endif
147
148  if(conssl->state == ssl_connection_complete)
149    return CURLE_OK;
150
151  /* check to see if we've been told to use an explicit SSL/TLS version */
152  switch(data->set.ssl.version) {
153  case CURL_SSLVERSION_DEFAULT:
154  case CURL_SSLVERSION_TLSv1:
155#if LIBCYASSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */
156    /* minimum protocol version is set later after the CTX object is created */
157    req_method = SSLv23_client_method();
158#else
159    infof(data, "CyaSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, "
160          "TLS 1.0 is used exclusively\n");
161    req_method = TLSv1_client_method();
162#endif
163    use_sni(TRUE);
164    break;
165  case CURL_SSLVERSION_TLSv1_0:
166    req_method = TLSv1_client_method();
167    use_sni(TRUE);
168    break;
169  case CURL_SSLVERSION_TLSv1_1:
170    req_method = TLSv1_1_client_method();
171    use_sni(TRUE);
172    break;
173  case CURL_SSLVERSION_TLSv1_2:
174    req_method = TLSv1_2_client_method();
175    use_sni(TRUE);
176    break;
177  case CURL_SSLVERSION_SSLv3:
178#ifdef WOLFSSL_ALLOW_SSLV3
179    req_method = SSLv3_client_method();
180    use_sni(FALSE);
181#else
182    failf(data, "No support for SSLv3");
183    return CURLE_NOT_BUILT_IN;
184#endif
185    break;
186  case CURL_SSLVERSION_SSLv2:
187    failf(data, "CyaSSL does not support SSLv2");
188    return CURLE_SSL_CONNECT_ERROR;
189  default:
190    failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
191    return CURLE_SSL_CONNECT_ERROR;
192  }
193
194  if(!req_method) {
195    failf(data, "SSL: couldn't create a method!");
196    return CURLE_OUT_OF_MEMORY;
197  }
198
199  if(conssl->ctx)
200    SSL_CTX_free(conssl->ctx);
201  conssl->ctx = SSL_CTX_new(req_method);
202
203  if(!conssl->ctx) {
204    failf(data, "SSL: couldn't create a context!");
205    return CURLE_OUT_OF_MEMORY;
206  }
207
208  switch(data->set.ssl.version) {
209  case CURL_SSLVERSION_DEFAULT:
210  case CURL_SSLVERSION_TLSv1:
211#if LIBCYASSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */
212    /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is whatever
213    minimum version of TLS was built in and at least TLS 1.0. For later library
214    versions that could change (eg TLS 1.0 built in but defaults to TLS 1.1) so
215    we have this short circuit evaluation to find the minimum supported TLS
216    version. We use wolfSSL_CTX_SetMinVersion and not CyaSSL_SetMinVersion
217    because only the former will work before the user's CTX callback is called.
218    */
219    if((wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1) != 1) &&
220       (wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1_1) != 1) &&
221       (wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1_2) != 1)) {
222      failf(data, "SSL: couldn't set the minimum protocol version");
223      return CURLE_SSL_CONNECT_ERROR;
224    }
225#endif
226    break;
227  }
228
229#ifndef NO_FILESYSTEM
230  /* load trusted cacert */
231  if(data->set.str[STRING_SSL_CAFILE]) {
232    if(1 != SSL_CTX_load_verify_locations(conssl->ctx,
233                                          data->set.str[STRING_SSL_CAFILE],
234                                          data->set.str[STRING_SSL_CAPATH])) {
235      if(data->set.ssl.verifypeer) {
236        /* Fail if we insist on successfully verifying the server. */
237        failf(data, "error setting certificate verify locations:\n"
238              "  CAfile: %s\n  CApath: %s",
239              data->set.str[STRING_SSL_CAFILE]?
240              data->set.str[STRING_SSL_CAFILE]: "none",
241              data->set.str[STRING_SSL_CAPATH]?
242              data->set.str[STRING_SSL_CAPATH] : "none");
243        return CURLE_SSL_CACERT_BADFILE;
244      }
245      else {
246        /* Just continue with a warning if no strict certificate
247           verification is required. */
248        infof(data, "error setting certificate verify locations,"
249              " continuing anyway:\n");
250      }
251    }
252    else {
253      /* Everything is fine. */
254      infof(data, "successfully set certificate verify locations:\n");
255    }
256    infof(data,
257          "  CAfile: %s\n"
258          "  CApath: %s\n",
259          data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]:
260          "none",
261          data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
262          "none");
263  }
264
265  /* Load the client certificate, and private key */
266  if(data->set.str[STRING_CERT] && data->set.str[STRING_KEY]) {
267    int file_type = do_file_type(data->set.str[STRING_CERT_TYPE]);
268
269    if(SSL_CTX_use_certificate_file(conssl->ctx, data->set.str[STRING_CERT],
270                                     file_type) != 1) {
271      failf(data, "unable to use client certificate (no key or wrong pass"
272            " phrase?)");
273      return CURLE_SSL_CONNECT_ERROR;
274    }
275
276    file_type = do_file_type(data->set.str[STRING_KEY_TYPE]);
277    if(SSL_CTX_use_PrivateKey_file(conssl->ctx, data->set.str[STRING_KEY],
278                                    file_type) != 1) {
279      failf(data, "unable to set private key");
280      return CURLE_SSL_CONNECT_ERROR;
281    }
282  }
283#endif /* !NO_FILESYSTEM */
284
285  /* SSL always tries to verify the peer, this only says whether it should
286   * fail to connect if the verification fails, or if it should continue
287   * anyway. In the latter case the result of the verification is checked with
288   * SSL_get_verify_result() below. */
289  SSL_CTX_set_verify(conssl->ctx,
290                     data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
291                     NULL);
292
293#ifdef HAVE_SNI
294  if(sni) {
295    struct in_addr addr4;
296#ifdef ENABLE_IPV6
297    struct in6_addr addr6;
298#endif
299    size_t hostname_len = strlen(conn->host.name);
300    if((hostname_len < USHRT_MAX) &&
301       (0 == Curl_inet_pton(AF_INET, conn->host.name, &addr4)) &&
302#ifdef ENABLE_IPV6
303       (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr6)) &&
304#endif
305       (CyaSSL_CTX_UseSNI(conssl->ctx, CYASSL_SNI_HOST_NAME, conn->host.name,
306                          (unsigned short)hostname_len) != 1)) {
307      infof(data, "WARNING: failed to configure server name indication (SNI) "
308            "TLS extension\n");
309    }
310  }
311#endif
312
313#ifdef HAVE_SUPPORTED_CURVES
314  /* CyaSSL/wolfSSL does not send the supported ECC curves ext automatically:
315     https://github.com/wolfSSL/wolfssl/issues/366
316     The supported curves below are those also supported by OpenSSL 1.0.2 and
317     in the same order. */
318  CyaSSL_CTX_UseSupportedCurve(conssl->ctx, 0x17); /* secp256r1 */
319  CyaSSL_CTX_UseSupportedCurve(conssl->ctx, 0x19); /* secp521r1 */
320  CyaSSL_CTX_UseSupportedCurve(conssl->ctx, 0x18); /* secp384r1 */
321#endif
322
323  /* give application a chance to interfere with SSL set up. */
324  if(data->set.ssl.fsslctx) {
325    CURLcode result = CURLE_OK;
326    result = (*data->set.ssl.fsslctx)(data, conssl->ctx,
327                                      data->set.ssl.fsslctxp);
328    if(result) {
329      failf(data, "error signaled by ssl ctx callback");
330      return result;
331    }
332  }
333#ifdef NO_FILESYSTEM
334  else if(data->set.ssl.verifypeer) {
335    failf(data, "SSL: Certificates couldn't be loaded because CyaSSL was built"
336          " with \"no filesystem\". Either disable peer verification"
337          " (insecure) or if you are building an application with libcurl you"
338          " can load certificates via CURLOPT_SSL_CTX_FUNCTION.");
339    return CURLE_SSL_CONNECT_ERROR;
340  }
341#endif
342
343  /* Let's make an SSL structure */
344  if(conssl->handle)
345    SSL_free(conssl->handle);
346  conssl->handle = SSL_new(conssl->ctx);
347  if(!conssl->handle) {
348    failf(data, "SSL: couldn't create a context (handle)!");
349    return CURLE_OUT_OF_MEMORY;
350  }
351
352#ifdef HAVE_ALPN
353  if(conn->bits.tls_enable_alpn) {
354    char protocols[128];
355    *protocols = '\0';
356
357    /* wolfSSL's ALPN protocol name list format is a comma separated string of
358       protocols in descending order of preference, eg: "h2,http/1.1" */
359
360#ifdef USE_NGHTTP2
361    if(data->set.httpversion >= CURL_HTTP_VERSION_2) {
362      strcpy(protocols + strlen(protocols), NGHTTP2_PROTO_VERSION_ID ",");
363      infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
364    }
365#endif
366
367    strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1);
368    infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);
369
370    if(wolfSSL_UseALPN(conssl->handle, protocols,
371                       (unsigned)strlen(protocols),
372                       WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) {
373      failf(data, "SSL: failed setting ALPN protocols");
374      return CURLE_SSL_CONNECT_ERROR;
375    }
376  }
377#endif /* HAVE_ALPN */
378
379  /* Check if there's a cached ID we can/should use here! */
380  if(conn->ssl_config.sessionid) {
381    void *ssl_sessionid = NULL;
382
383    Curl_ssl_sessionid_lock(conn);
384    if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
385      /* we got a session id, use it! */
386      if(!SSL_set_session(conssl->handle, ssl_sessionid)) {
387        Curl_ssl_sessionid_unlock(conn);
388        failf(data, "SSL: SSL_set_session failed: %s",
389              ERR_error_string(SSL_get_error(conssl->handle, 0),
390              error_buffer));
391        return CURLE_SSL_CONNECT_ERROR;
392      }
393      /* Informational message */
394      infof (data, "SSL re-using session ID\n");
395    }
396    Curl_ssl_sessionid_unlock(conn);
397  }
398
399  /* pass the raw socket into the SSL layer */
400  if(!SSL_set_fd(conssl->handle, (int)sockfd)) {
401    failf(data, "SSL: SSL_set_fd failed");
402    return CURLE_SSL_CONNECT_ERROR;
403  }
404
405  conssl->connecting_state = ssl_connect_2;
406  return CURLE_OK;
407}
408
409
410static CURLcode
411cyassl_connect_step2(struct connectdata *conn,
412                     int sockindex)
413{
414  int ret = -1;
415  struct Curl_easy *data = conn->data;
416  struct ssl_connect_data* conssl = &conn->ssl[sockindex];
417
418  conn->recv[sockindex] = cyassl_recv;
419  conn->send[sockindex] = cyassl_send;
420
421  /* Enable RFC2818 checks */
422  if(data->set.ssl.verifyhost) {
423    ret = CyaSSL_check_domain_name(conssl->handle, conn->host.name);
424    if(ret == SSL_FAILURE)
425      return CURLE_OUT_OF_MEMORY;
426  }
427
428  ret = SSL_connect(conssl->handle);
429  if(ret != 1) {
430    char error_buffer[CYASSL_MAX_ERROR_SZ];
431    int  detail = SSL_get_error(conssl->handle, ret);
432
433    if(SSL_ERROR_WANT_READ == detail) {
434      conssl->connecting_state = ssl_connect_2_reading;
435      return CURLE_OK;
436    }
437    else if(SSL_ERROR_WANT_WRITE == detail) {
438      conssl->connecting_state = ssl_connect_2_writing;
439      return CURLE_OK;
440    }
441    /* There is no easy way to override only the CN matching.
442     * This will enable the override of both mismatching SubjectAltNames
443     * as also mismatching CN fields */
444    else if(DOMAIN_NAME_MISMATCH == detail) {
445#if 1
446      failf(data, "\tsubject alt name(s) or common name do not match \"%s\"\n",
447            conn->host.dispname);
448      return CURLE_PEER_FAILED_VERIFICATION;
449#else
450      /* When the CyaSSL_check_domain_name() is used and you desire to continue
451       * on a DOMAIN_NAME_MISMATCH, i.e. 'data->set.ssl.verifyhost == 0',
452       * CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA error. The only
453       * way to do this is currently to switch the CyaSSL_check_domain_name()
454       * in and out based on the 'data->set.ssl.verifyhost' value. */
455      if(data->set.ssl.verifyhost) {
456        failf(data,
457              "\tsubject alt name(s) or common name do not match \"%s\"\n",
458              conn->host.dispname);
459        return CURLE_PEER_FAILED_VERIFICATION;
460      }
461      else {
462        infof(data,
463              "\tsubject alt name(s) and/or common name do not match \"%s\"\n",
464              conn->host.dispname);
465        return CURLE_OK;
466      }
467#endif
468    }
469#if LIBCYASSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */
470    else if(ASN_NO_SIGNER_E == detail) {
471      if(data->set.ssl.verifypeer) {
472        failf(data, "\tCA signer not available for verification\n");
473        return CURLE_SSL_CACERT_BADFILE;
474      }
475      else {
476        /* Just continue with a warning if no strict certificate
477           verification is required. */
478        infof(data, "CA signer not available for verification, "
479                    "continuing anyway\n");
480      }
481    }
482#endif
483    else {
484      failf(data, "SSL_connect failed with error %d: %s", detail,
485          ERR_error_string(detail, error_buffer));
486      return CURLE_SSL_CONNECT_ERROR;
487    }
488  }
489
490  if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) {
491#ifdef KEEP_PEER_CERT
492    X509 *x509;
493    const char *x509_der;
494    int x509_der_len;
495    curl_X509certificate x509_parsed;
496    curl_asn1Element *pubkey;
497    CURLcode result;
498
499    x509 = SSL_get_peer_certificate(conssl->handle);
500    if(!x509) {
501      failf(data, "SSL: failed retrieving server certificate");
502      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
503    }
504
505    x509_der = (const char *)CyaSSL_X509_get_der(x509, &x509_der_len);
506    if(!x509_der) {
507      failf(data, "SSL: failed retrieving ASN.1 server certificate");
508      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
509    }
510
511    memset(&x509_parsed, 0, sizeof x509_parsed);
512    Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len);
513
514    pubkey = &x509_parsed.subjectPublicKeyInfo;
515    if(!pubkey->header || pubkey->end <= pubkey->header) {
516      failf(data, "SSL: failed retrieving public key from server certificate");
517      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
518    }
519
520    result = Curl_pin_peer_pubkey(data,
521                                  data->set.str[STRING_SSL_PINNEDPUBLICKEY],
522                                  (const unsigned char *)pubkey->header,
523                                  (size_t)(pubkey->end - pubkey->header));
524    if(result) {
525      failf(data, "SSL: public key does not match pinned public key!");
526      return result;
527    }
528#else
529    failf(data, "Library lacks pinning support built-in");
530    return CURLE_NOT_BUILT_IN;
531#endif
532  }
533
534#ifdef HAVE_ALPN
535  if(conn->bits.tls_enable_alpn) {
536    int rc;
537    char *protocol = NULL;
538    unsigned short protocol_len = 0;
539
540    rc = wolfSSL_ALPN_GetProtocol(conssl->handle, &protocol, &protocol_len);
541
542    if(rc == SSL_SUCCESS) {
543      infof(data, "ALPN, server accepted to use %.*s\n", protocol_len,
544            protocol);
545
546      if(protocol_len == ALPN_HTTP_1_1_LENGTH &&
547         !memcmp(protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH))
548        conn->negnpn = CURL_HTTP_VERSION_1_1;
549#ifdef USE_NGHTTP2
550      else if(data->set.httpversion >= CURL_HTTP_VERSION_2 &&
551              protocol_len == NGHTTP2_PROTO_VERSION_ID_LEN &&
552              !memcmp(protocol, NGHTTP2_PROTO_VERSION_ID,
553                      NGHTTP2_PROTO_VERSION_ID_LEN))
554        conn->negnpn = CURL_HTTP_VERSION_2;
555#endif
556      else
557        infof(data, "ALPN, unrecognized protocol %.*s\n", protocol_len,
558              protocol);
559    }
560    else if(rc == SSL_ALPN_NOT_FOUND)
561      infof(data, "ALPN, server did not agree to a protocol\n");
562    else {
563      failf(data, "ALPN, failure getting protocol, error %d", rc);
564      return CURLE_SSL_CONNECT_ERROR;
565    }
566  }
567#endif /* HAVE_ALPN */
568
569  conssl->connecting_state = ssl_connect_3;
570  infof(data, "SSL connected\n");
571
572  return CURLE_OK;
573}
574
575
576static CURLcode
577cyassl_connect_step3(struct connectdata *conn,
578                     int sockindex)
579{
580  CURLcode result = CURLE_OK;
581  struct Curl_easy *data = conn->data;
582  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
583
584  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
585
586  if(conn->ssl_config.sessionid) {
587    bool incache;
588    SSL_SESSION *our_ssl_sessionid;
589    void *old_ssl_sessionid = NULL;
590
591    our_ssl_sessionid = SSL_get_session(connssl->handle);
592
593    Curl_ssl_sessionid_lock(conn);
594    incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
595    if(incache) {
596      if(old_ssl_sessionid != our_ssl_sessionid) {
597        infof(data, "old SSL session ID is stale, removing\n");
598        Curl_ssl_delsessionid(conn, old_ssl_sessionid);
599        incache = FALSE;
600      }
601    }
602
603    if(!incache) {
604      result = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
605                                     0 /* unknown size */);
606      if(result) {
607        Curl_ssl_sessionid_unlock(conn);
608        failf(data, "failed to store ssl session");
609        return result;
610      }
611    }
612    Curl_ssl_sessionid_unlock(conn);
613  }
614
615  connssl->connecting_state = ssl_connect_done;
616
617  return result;
618}
619
620
621static ssize_t cyassl_send(struct connectdata *conn,
622                           int sockindex,
623                           const void *mem,
624                           size_t len,
625                           CURLcode *curlcode)
626{
627  char error_buffer[CYASSL_MAX_ERROR_SZ];
628  int  memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
629  int  rc     = SSL_write(conn->ssl[sockindex].handle, mem, memlen);
630
631  if(rc < 0) {
632    int err = SSL_get_error(conn->ssl[sockindex].handle, rc);
633
634    switch(err) {
635    case SSL_ERROR_WANT_READ:
636    case SSL_ERROR_WANT_WRITE:
637      /* there's data pending, re-invoke SSL_write() */
638      *curlcode = CURLE_AGAIN;
639      return -1;
640    default:
641      failf(conn->data, "SSL write: %s, errno %d",
642            ERR_error_string(err, error_buffer),
643            SOCKERRNO);
644      *curlcode = CURLE_SEND_ERROR;
645      return -1;
646    }
647  }
648  return rc;
649}
650
651void Curl_cyassl_close(struct connectdata *conn, int sockindex)
652{
653  struct ssl_connect_data *conssl = &conn->ssl[sockindex];
654
655  if(conssl->handle) {
656    (void)SSL_shutdown(conssl->handle);
657    SSL_free (conssl->handle);
658    conssl->handle = NULL;
659  }
660  if(conssl->ctx) {
661    SSL_CTX_free (conssl->ctx);
662    conssl->ctx = NULL;
663  }
664}
665
666static ssize_t cyassl_recv(struct connectdata *conn,
667                           int num,
668                           char *buf,
669                           size_t buffersize,
670                           CURLcode *curlcode)
671{
672  char error_buffer[CYASSL_MAX_ERROR_SZ];
673  int  buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
674  int  nread    = SSL_read(conn->ssl[num].handle, buf, buffsize);
675
676  if(nread < 0) {
677    int err = SSL_get_error(conn->ssl[num].handle, nread);
678
679    switch(err) {
680    case SSL_ERROR_ZERO_RETURN: /* no more data */
681      break;
682    case SSL_ERROR_WANT_READ:
683    case SSL_ERROR_WANT_WRITE:
684      /* there's data pending, re-invoke SSL_read() */
685      *curlcode = CURLE_AGAIN;
686      return -1;
687    default:
688      failf(conn->data, "SSL read: %s, errno %d",
689            ERR_error_string(err, error_buffer),
690            SOCKERRNO);
691      *curlcode = CURLE_RECV_ERROR;
692      return -1;
693    }
694  }
695  return nread;
696}
697
698
699void Curl_cyassl_session_free(void *ptr)
700{
701  (void)ptr;
702  /* CyaSSL reuses sessions on own, no free */
703}
704
705
706size_t Curl_cyassl_version(char *buffer, size_t size)
707{
708#ifdef WOLFSSL_VERSION
709  return snprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION);
710#elif defined(CYASSL_VERSION)
711  return snprintf(buffer, size, "CyaSSL/%s", CYASSL_VERSION);
712#else
713  return snprintf(buffer, size, "CyaSSL/%s", "<1.8.8");
714#endif
715}
716
717
718int Curl_cyassl_init(void)
719{
720  return (CyaSSL_Init() == SSL_SUCCESS);
721}
722
723
724bool Curl_cyassl_data_pending(const struct connectdata* conn, int connindex)
725{
726  if(conn->ssl[connindex].handle)   /* SSL is in use */
727    return (0 != SSL_pending(conn->ssl[connindex].handle)) ? TRUE : FALSE;
728  else
729    return FALSE;
730}
731
732
733/*
734 * This function is called to shut down the SSL layer but keep the
735 * socket open (CCC - Clear Command Channel)
736 */
737int Curl_cyassl_shutdown(struct connectdata *conn, int sockindex)
738{
739  int retval = 0;
740  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
741
742  if(connssl->handle) {
743    SSL_free (connssl->handle);
744    connssl->handle = NULL;
745  }
746  return retval;
747}
748
749
750static CURLcode
751cyassl_connect_common(struct connectdata *conn,
752                      int sockindex,
753                      bool nonblocking,
754                      bool *done)
755{
756  CURLcode result;
757  struct Curl_easy *data = conn->data;
758  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
759  curl_socket_t sockfd = conn->sock[sockindex];
760  long timeout_ms;
761  int what;
762
763  /* check if the connection has already been established */
764  if(ssl_connection_complete == connssl->state) {
765    *done = TRUE;
766    return CURLE_OK;
767  }
768
769  if(ssl_connect_1==connssl->connecting_state) {
770    /* Find out how much more time we're allowed */
771    timeout_ms = Curl_timeleft(data, NULL, TRUE);
772
773    if(timeout_ms < 0) {
774      /* no need to continue if time already is up */
775      failf(data, "SSL connection timeout");
776      return CURLE_OPERATION_TIMEDOUT;
777    }
778
779    result = cyassl_connect_step1(conn, sockindex);
780    if(result)
781      return result;
782  }
783
784  while(ssl_connect_2 == connssl->connecting_state ||
785        ssl_connect_2_reading == connssl->connecting_state ||
786        ssl_connect_2_writing == connssl->connecting_state) {
787
788    /* check allowed time left */
789    timeout_ms = Curl_timeleft(data, NULL, TRUE);
790
791    if(timeout_ms < 0) {
792      /* no need to continue if time already is up */
793      failf(data, "SSL connection timeout");
794      return CURLE_OPERATION_TIMEDOUT;
795    }
796
797    /* if ssl is expecting something, check if it's available. */
798    if(connssl->connecting_state == ssl_connect_2_reading
799       || connssl->connecting_state == ssl_connect_2_writing) {
800
801      curl_socket_t writefd = ssl_connect_2_writing==
802        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
803      curl_socket_t readfd = ssl_connect_2_reading==
804        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
805
806      what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms);
807      if(what < 0) {
808        /* fatal error */
809        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
810        return CURLE_SSL_CONNECT_ERROR;
811      }
812      else if(0 == what) {
813        if(nonblocking) {
814          *done = FALSE;
815          return CURLE_OK;
816        }
817        else {
818          /* timeout */
819          failf(data, "SSL connection timeout");
820          return CURLE_OPERATION_TIMEDOUT;
821        }
822      }
823      /* socket is readable or writable */
824    }
825
826    /* Run transaction, and return to the caller if it failed or if
827     * this connection is part of a multi handle and this loop would
828     * execute again. This permits the owner of a multi handle to
829     * abort a connection attempt before step2 has completed while
830     * ensuring that a client using select() or epoll() will always
831     * have a valid fdset to wait on.
832     */
833    result = cyassl_connect_step2(conn, sockindex);
834    if(result || (nonblocking &&
835                  (ssl_connect_2 == connssl->connecting_state ||
836                   ssl_connect_2_reading == connssl->connecting_state ||
837                   ssl_connect_2_writing == connssl->connecting_state)))
838      return result;
839  } /* repeat step2 until all transactions are done. */
840
841  if(ssl_connect_3 == connssl->connecting_state) {
842    result = cyassl_connect_step3(conn, sockindex);
843    if(result)
844      return result;
845  }
846
847  if(ssl_connect_done == connssl->connecting_state) {
848    connssl->state = ssl_connection_complete;
849    conn->recv[sockindex] = cyassl_recv;
850    conn->send[sockindex] = cyassl_send;
851    *done = TRUE;
852  }
853  else
854    *done = FALSE;
855
856  /* Reset our connect state machine */
857  connssl->connecting_state = ssl_connect_1;
858
859  return CURLE_OK;
860}
861
862
863CURLcode
864Curl_cyassl_connect_nonblocking(struct connectdata *conn,
865                                int sockindex,
866                                bool *done)
867{
868  return cyassl_connect_common(conn, sockindex, TRUE, done);
869}
870
871
872CURLcode
873Curl_cyassl_connect(struct connectdata *conn,
874                    int sockindex)
875{
876  CURLcode result;
877  bool done = FALSE;
878
879  result = cyassl_connect_common(conn, sockindex, FALSE, &done);
880  if(result)
881    return result;
882
883  DEBUGASSERT(done);
884
885  return CURLE_OK;
886}
887
888int Curl_cyassl_random(struct Curl_easy *data,
889                       unsigned char *entropy,
890                       size_t length)
891{
892  RNG rng;
893  (void)data;
894  if(InitRng(&rng))
895    return 1;
896  if(length > UINT_MAX)
897    return 1;
898  if(RNG_GenerateBlock(&rng, entropy, (unsigned)length))
899    return 1;
900  return 0;
901}
902
903void Curl_cyassl_sha256sum(const unsigned char *tmp, /* input */
904                      size_t tmplen,
905                      unsigned char *sha256sum /* output */,
906                      size_t unused)
907{
908  Sha256 SHA256pw;
909  (void)unused;
910  InitSha256(&SHA256pw);
911  Sha256Update(&SHA256pw, tmp, (word32)tmplen);
912  Sha256Final(&SHA256pw, sha256sum);
913}
914
915#endif
916