• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2015, 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 http://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
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."
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 "cyassl.h"
55 #include "vtls.h"
56 #include "parsedate.h"
57 #include "connect.h" /* for the connect timeout */
58 #include "select.h"
59 #include "rawstr.h"
60 #include "x509asn1.h"
61 #include "curl_printf.h"
62 
63 #include <cyassl/ssl.h>
64 #ifdef HAVE_CYASSL_ERROR_SSL_H
65 #include <cyassl/error-ssl.h>
66 #else
67 #include <cyassl/error.h>
68 #endif
69 #include <cyassl/ctaocrypt/random.h>
70 
71 /* The last #include files should be: */
72 #include "curl_memory.h"
73 #include "memdebug.h"
74 
75 #if LIBCYASSL_VERSION_HEX < 0x02007002 /* < 2.7.2 */
76 #define CYASSL_MAX_ERROR_SZ 80
77 #endif
78 
79 static Curl_recv cyassl_recv;
80 static Curl_send cyassl_send;
81 
82 
do_file_type(const char * type)83 static int do_file_type(const char *type)
84 {
85   if(!type || !type[0])
86     return SSL_FILETYPE_PEM;
87   if(Curl_raw_equal(type, "PEM"))
88     return SSL_FILETYPE_PEM;
89   if(Curl_raw_equal(type, "DER"))
90     return SSL_FILETYPE_ASN1;
91   return -1;
92 }
93 
94 /*
95  * This function loads all the client/CA certificates and CRLs. Setup the TLS
96  * layer and do all necessary magic.
97  */
98 static CURLcode
cyassl_connect_step1(struct connectdata * conn,int sockindex)99 cyassl_connect_step1(struct connectdata *conn,
100                      int sockindex)
101 {
102   char error_buffer[CYASSL_MAX_ERROR_SZ];
103   struct SessionHandle *data = conn->data;
104   struct ssl_connect_data* conssl = &conn->ssl[sockindex];
105   SSL_METHOD* req_method = NULL;
106   void* ssl_sessionid = NULL;
107   curl_socket_t sockfd = conn->sock[sockindex];
108 #ifdef HAVE_SNI
109   bool sni = FALSE;
110 #define use_sni(x)  sni = (x)
111 #else
112 #define use_sni(x)  Curl_nop_stmt
113 #endif
114 
115   if(conssl->state == ssl_connection_complete)
116     return CURLE_OK;
117 
118   /* check to see if we've been told to use an explicit SSL/TLS version */
119   switch(data->set.ssl.version) {
120   case CURL_SSLVERSION_DEFAULT:
121   case CURL_SSLVERSION_TLSv1:
122 #if LIBCYASSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */
123     /* minimum protocol version is set later after the CTX object is created */
124     req_method = SSLv23_client_method();
125 #else
126     infof(data, "CyaSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, "
127           "TLS 1.0 is used exclusively\n");
128     req_method = TLSv1_client_method();
129 #endif
130     use_sni(TRUE);
131     break;
132   case CURL_SSLVERSION_TLSv1_0:
133     req_method = TLSv1_client_method();
134     use_sni(TRUE);
135     break;
136   case CURL_SSLVERSION_TLSv1_1:
137     req_method = TLSv1_1_client_method();
138     use_sni(TRUE);
139     break;
140   case CURL_SSLVERSION_TLSv1_2:
141     req_method = TLSv1_2_client_method();
142     use_sni(TRUE);
143     break;
144   case CURL_SSLVERSION_SSLv3:
145     req_method = SSLv3_client_method();
146     use_sni(FALSE);
147     break;
148   case CURL_SSLVERSION_SSLv2:
149     failf(data, "CyaSSL does not support SSLv2");
150     return CURLE_SSL_CONNECT_ERROR;
151   default:
152     failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
153     return CURLE_SSL_CONNECT_ERROR;
154   }
155 
156   if(!req_method) {
157     failf(data, "SSL: couldn't create a method!");
158     return CURLE_OUT_OF_MEMORY;
159   }
160 
161   if(conssl->ctx)
162     SSL_CTX_free(conssl->ctx);
163   conssl->ctx = SSL_CTX_new(req_method);
164 
165   if(!conssl->ctx) {
166     failf(data, "SSL: couldn't create a context!");
167     return CURLE_OUT_OF_MEMORY;
168   }
169 
170   switch(data->set.ssl.version) {
171   case CURL_SSLVERSION_DEFAULT:
172   case CURL_SSLVERSION_TLSv1:
173 #if LIBCYASSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */
174     /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is whatever
175     minimum version of TLS was built in and at least TLS 1.0. For later library
176     versions that could change (eg TLS 1.0 built in but defaults to TLS 1.1) so
177     we have this short circuit evaluation to find the minimum supported TLS
178     version. We use wolfSSL_CTX_SetMinVersion and not CyaSSL_SetMinVersion
179     because only the former will work before the user's CTX callback is called.
180     */
181     if((wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1) != 1) &&
182        (wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1_1) != 1) &&
183        (wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1_2) != 1)) {
184       failf(data, "SSL: couldn't set the minimum protocol version");
185       return CURLE_SSL_CONNECT_ERROR;
186     }
187 #endif
188     break;
189   }
190 
191 #ifndef NO_FILESYSTEM
192   /* load trusted cacert */
193   if(data->set.str[STRING_SSL_CAFILE]) {
194     if(1 != SSL_CTX_load_verify_locations(conssl->ctx,
195                                           data->set.str[STRING_SSL_CAFILE],
196                                           data->set.str[STRING_SSL_CAPATH])) {
197       if(data->set.ssl.verifypeer) {
198         /* Fail if we insist on successfully verifying the server. */
199         failf(data, "error setting certificate verify locations:\n"
200               "  CAfile: %s\n  CApath: %s",
201               data->set.str[STRING_SSL_CAFILE]?
202               data->set.str[STRING_SSL_CAFILE]: "none",
203               data->set.str[STRING_SSL_CAPATH]?
204               data->set.str[STRING_SSL_CAPATH] : "none");
205         return CURLE_SSL_CACERT_BADFILE;
206       }
207       else {
208         /* Just continue with a warning if no strict certificate
209            verification is required. */
210         infof(data, "error setting certificate verify locations,"
211               " continuing anyway:\n");
212       }
213     }
214     else {
215       /* Everything is fine. */
216       infof(data, "successfully set certificate verify locations:\n");
217     }
218     infof(data,
219           "  CAfile: %s\n"
220           "  CApath: %s\n",
221           data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]:
222           "none",
223           data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
224           "none");
225   }
226 
227   /* Load the client certificate, and private key */
228   if(data->set.str[STRING_CERT] && data->set.str[STRING_KEY]) {
229     int file_type = do_file_type(data->set.str[STRING_CERT_TYPE]);
230 
231     if(SSL_CTX_use_certificate_file(conssl->ctx, data->set.str[STRING_CERT],
232                                      file_type) != 1) {
233       failf(data, "unable to use client certificate (no key or wrong pass"
234             " phrase?)");
235       return CURLE_SSL_CONNECT_ERROR;
236     }
237 
238     file_type = do_file_type(data->set.str[STRING_KEY_TYPE]);
239     if(SSL_CTX_use_PrivateKey_file(conssl->ctx, data->set.str[STRING_KEY],
240                                     file_type) != 1) {
241       failf(data, "unable to set private key");
242       return CURLE_SSL_CONNECT_ERROR;
243     }
244   }
245 #endif /* !NO_FILESYSTEM */
246 
247   /* SSL always tries to verify the peer, this only says whether it should
248    * fail to connect if the verification fails, or if it should continue
249    * anyway. In the latter case the result of the verification is checked with
250    * SSL_get_verify_result() below. */
251   SSL_CTX_set_verify(conssl->ctx,
252                      data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
253                      NULL);
254 
255 #ifdef HAVE_SNI
256   if(sni) {
257     struct in_addr addr4;
258 #ifdef ENABLE_IPV6
259     struct in6_addr addr6;
260 #endif
261     size_t hostname_len = strlen(conn->host.name);
262     if((hostname_len < USHRT_MAX) &&
263        (0 == Curl_inet_pton(AF_INET, conn->host.name, &addr4)) &&
264 #ifdef ENABLE_IPV6
265        (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr6)) &&
266 #endif
267        (CyaSSL_CTX_UseSNI(conssl->ctx, CYASSL_SNI_HOST_NAME, conn->host.name,
268                           (unsigned short)hostname_len) != 1)) {
269       infof(data, "WARNING: failed to configure server name indication (SNI) "
270             "TLS extension\n");
271     }
272   }
273 #endif
274 
275   /* give application a chance to interfere with SSL set up. */
276   if(data->set.ssl.fsslctx) {
277     CURLcode result = CURLE_OK;
278     result = (*data->set.ssl.fsslctx)(data, conssl->ctx,
279                                       data->set.ssl.fsslctxp);
280     if(result) {
281       failf(data, "error signaled by ssl ctx callback");
282       return result;
283     }
284   }
285 #ifdef NO_FILESYSTEM
286   else if(data->set.ssl.verifypeer) {
287     failf(data, "SSL: Certificates couldn't be loaded because CyaSSL was built"
288           " with \"no filesystem\". Either disable peer verification"
289           " (insecure) or if you are building an application with libcurl you"
290           " can load certificates via CURLOPT_SSL_CTX_FUNCTION.");
291     return CURLE_SSL_CONNECT_ERROR;
292   }
293 #endif
294 
295   /* Let's make an SSL structure */
296   if(conssl->handle)
297     SSL_free(conssl->handle);
298   conssl->handle = SSL_new(conssl->ctx);
299   if(!conssl->handle) {
300     failf(data, "SSL: couldn't create a context (handle)!");
301     return CURLE_OUT_OF_MEMORY;
302   }
303 
304   /* Check if there's a cached ID we can/should use here! */
305   if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
306     /* we got a session id, use it! */
307     if(!SSL_set_session(conssl->handle, ssl_sessionid)) {
308       failf(data, "SSL: SSL_set_session failed: %s",
309             ERR_error_string(SSL_get_error(conssl->handle, 0), error_buffer));
310       return CURLE_SSL_CONNECT_ERROR;
311     }
312     /* Informational message */
313     infof (data, "SSL re-using session ID\n");
314   }
315 
316   /* pass the raw socket into the SSL layer */
317   if(!SSL_set_fd(conssl->handle, (int)sockfd)) {
318     failf(data, "SSL: SSL_set_fd failed");
319     return CURLE_SSL_CONNECT_ERROR;
320   }
321 
322   conssl->connecting_state = ssl_connect_2;
323   return CURLE_OK;
324 }
325 
326 
327 static CURLcode
cyassl_connect_step2(struct connectdata * conn,int sockindex)328 cyassl_connect_step2(struct connectdata *conn,
329                      int sockindex)
330 {
331   int ret = -1;
332   struct SessionHandle *data = conn->data;
333   struct ssl_connect_data* conssl = &conn->ssl[sockindex];
334 
335   conn->recv[sockindex] = cyassl_recv;
336   conn->send[sockindex] = cyassl_send;
337 
338   /* Enable RFC2818 checks */
339   if(data->set.ssl.verifyhost) {
340     ret = CyaSSL_check_domain_name(conssl->handle, conn->host.name);
341     if(ret == SSL_FAILURE)
342       return CURLE_OUT_OF_MEMORY;
343   }
344 
345   ret = SSL_connect(conssl->handle);
346   if(ret != 1) {
347     char error_buffer[CYASSL_MAX_ERROR_SZ];
348     int  detail = SSL_get_error(conssl->handle, ret);
349 
350     if(SSL_ERROR_WANT_READ == detail) {
351       conssl->connecting_state = ssl_connect_2_reading;
352       return CURLE_OK;
353     }
354     else if(SSL_ERROR_WANT_WRITE == detail) {
355       conssl->connecting_state = ssl_connect_2_writing;
356       return CURLE_OK;
357     }
358     /* There is no easy way to override only the CN matching.
359      * This will enable the override of both mismatching SubjectAltNames
360      * as also mismatching CN fields */
361     else if(DOMAIN_NAME_MISMATCH == detail) {
362 #if 1
363       failf(data, "\tsubject alt name(s) or common name do not match \"%s\"\n",
364             conn->host.dispname);
365       return CURLE_PEER_FAILED_VERIFICATION;
366 #else
367       /* When the CyaSSL_check_domain_name() is used and you desire to continue
368        * on a DOMAIN_NAME_MISMATCH, i.e. 'data->set.ssl.verifyhost == 0',
369        * CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA error. The only
370        * way to do this is currently to switch the CyaSSL_check_domain_name()
371        * in and out based on the 'data->set.ssl.verifyhost' value. */
372       if(data->set.ssl.verifyhost) {
373         failf(data,
374               "\tsubject alt name(s) or common name do not match \"%s\"\n",
375               conn->host.dispname);
376         return CURLE_PEER_FAILED_VERIFICATION;
377       }
378       else {
379         infof(data,
380               "\tsubject alt name(s) and/or common name do not match \"%s\"\n",
381               conn->host.dispname);
382         return CURLE_OK;
383       }
384 #endif
385     }
386 #if LIBCYASSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */
387     else if(ASN_NO_SIGNER_E == detail) {
388       if(data->set.ssl.verifypeer) {
389         failf(data, "\tCA signer not available for verification\n");
390         return CURLE_SSL_CACERT_BADFILE;
391       }
392       else {
393         /* Just continue with a warning if no strict certificate
394            verification is required. */
395         infof(data, "CA signer not available for verification, "
396                     "continuing anyway\n");
397       }
398     }
399 #endif
400     else {
401       failf(data, "SSL_connect failed with error %d: %s", detail,
402           ERR_error_string(detail, error_buffer));
403       return CURLE_SSL_CONNECT_ERROR;
404     }
405   }
406 
407   if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) {
408     X509 *x509;
409     const char *x509_der;
410     int x509_der_len;
411     curl_X509certificate x509_parsed;
412     curl_asn1Element *pubkey;
413     CURLcode result;
414 
415     x509 = SSL_get_peer_certificate(conssl->handle);
416     if(!x509) {
417       failf(data, "SSL: failed retrieving server certificate");
418       return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
419     }
420 
421     x509_der = (const char *)CyaSSL_X509_get_der(x509, &x509_der_len);
422     if(!x509_der) {
423       failf(data, "SSL: failed retrieving ASN.1 server certificate");
424       return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
425     }
426 
427     memset(&x509_parsed, 0, sizeof x509_parsed);
428     Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len);
429 
430     pubkey = &x509_parsed.subjectPublicKeyInfo;
431     if(!pubkey->header || pubkey->end <= pubkey->header) {
432       failf(data, "SSL: failed retrieving public key from server certificate");
433       return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
434     }
435 
436     result = Curl_pin_peer_pubkey(data->set.str[STRING_SSL_PINNEDPUBLICKEY],
437                                   (const unsigned char *)pubkey->header,
438                                   (size_t)(pubkey->end - pubkey->header));
439     if(result) {
440       failf(data, "SSL: public key does not match pinned public key!");
441       return result;
442     }
443   }
444 
445   conssl->connecting_state = ssl_connect_3;
446   infof(data, "SSL connected\n");
447 
448   return CURLE_OK;
449 }
450 
451 
452 static CURLcode
cyassl_connect_step3(struct connectdata * conn,int sockindex)453 cyassl_connect_step3(struct connectdata *conn,
454                      int sockindex)
455 {
456   CURLcode result = CURLE_OK;
457   void *old_ssl_sessionid=NULL;
458   struct SessionHandle *data = conn->data;
459   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
460   bool incache;
461   SSL_SESSION *our_ssl_sessionid;
462 
463   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
464 
465   our_ssl_sessionid = SSL_get_session(connssl->handle);
466 
467   incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
468   if(incache) {
469     if(old_ssl_sessionid != our_ssl_sessionid) {
470       infof(data, "old SSL session ID is stale, removing\n");
471       Curl_ssl_delsessionid(conn, old_ssl_sessionid);
472       incache = FALSE;
473     }
474   }
475 
476   if(!incache) {
477     result = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
478                                    0 /* unknown size */);
479     if(result) {
480       failf(data, "failed to store ssl session");
481       return result;
482     }
483   }
484 
485   connssl->connecting_state = ssl_connect_done;
486 
487   return result;
488 }
489 
490 
cyassl_send(struct connectdata * conn,int sockindex,const void * mem,size_t len,CURLcode * curlcode)491 static ssize_t cyassl_send(struct connectdata *conn,
492                            int sockindex,
493                            const void *mem,
494                            size_t len,
495                            CURLcode *curlcode)
496 {
497   char error_buffer[CYASSL_MAX_ERROR_SZ];
498   int  memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
499   int  rc     = SSL_write(conn->ssl[sockindex].handle, mem, memlen);
500 
501   if(rc < 0) {
502     int err = SSL_get_error(conn->ssl[sockindex].handle, rc);
503 
504     switch(err) {
505     case SSL_ERROR_WANT_READ:
506     case SSL_ERROR_WANT_WRITE:
507       /* there's data pending, re-invoke SSL_write() */
508       *curlcode = CURLE_AGAIN;
509       return -1;
510     default:
511       failf(conn->data, "SSL write: %s, errno %d",
512             ERR_error_string(err, error_buffer),
513             SOCKERRNO);
514       *curlcode = CURLE_SEND_ERROR;
515       return -1;
516     }
517   }
518   return rc;
519 }
520 
Curl_cyassl_close(struct connectdata * conn,int sockindex)521 void Curl_cyassl_close(struct connectdata *conn, int sockindex)
522 {
523   struct ssl_connect_data *conssl = &conn->ssl[sockindex];
524 
525   if(conssl->handle) {
526     (void)SSL_shutdown(conssl->handle);
527     SSL_free (conssl->handle);
528     conssl->handle = NULL;
529   }
530   if(conssl->ctx) {
531     SSL_CTX_free (conssl->ctx);
532     conssl->ctx = NULL;
533   }
534 }
535 
cyassl_recv(struct connectdata * conn,int num,char * buf,size_t buffersize,CURLcode * curlcode)536 static ssize_t cyassl_recv(struct connectdata *conn,
537                            int num,
538                            char *buf,
539                            size_t buffersize,
540                            CURLcode *curlcode)
541 {
542   char error_buffer[CYASSL_MAX_ERROR_SZ];
543   int  buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
544   int  nread    = SSL_read(conn->ssl[num].handle, buf, buffsize);
545 
546   if(nread < 0) {
547     int err = SSL_get_error(conn->ssl[num].handle, nread);
548 
549     switch(err) {
550     case SSL_ERROR_ZERO_RETURN: /* no more data */
551       break;
552     case SSL_ERROR_WANT_READ:
553     case SSL_ERROR_WANT_WRITE:
554       /* there's data pending, re-invoke SSL_read() */
555       *curlcode = CURLE_AGAIN;
556       return -1;
557     default:
558       failf(conn->data, "SSL read: %s, errno %d",
559             ERR_error_string(err, error_buffer),
560             SOCKERRNO);
561       *curlcode = CURLE_RECV_ERROR;
562       return -1;
563     }
564   }
565   return nread;
566 }
567 
568 
Curl_cyassl_session_free(void * ptr)569 void Curl_cyassl_session_free(void *ptr)
570 {
571   (void)ptr;
572   /* CyaSSL reuses sessions on own, no free */
573 }
574 
575 
Curl_cyassl_version(char * buffer,size_t size)576 size_t Curl_cyassl_version(char *buffer, size_t size)
577 {
578 #ifdef WOLFSSL_VERSION
579   return snprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION);
580 #elif defined(CYASSL_VERSION)
581   return snprintf(buffer, size, "CyaSSL/%s", CYASSL_VERSION);
582 #else
583   return snprintf(buffer, size, "CyaSSL/%s", "<1.8.8");
584 #endif
585 }
586 
587 
Curl_cyassl_init(void)588 int Curl_cyassl_init(void)
589 {
590   return (CyaSSL_Init() == SSL_SUCCESS);
591 }
592 
593 
Curl_cyassl_data_pending(const struct connectdata * conn,int connindex)594 bool Curl_cyassl_data_pending(const struct connectdata* conn, int connindex)
595 {
596   if(conn->ssl[connindex].handle)   /* SSL is in use */
597     return (0 != SSL_pending(conn->ssl[connindex].handle)) ? TRUE : FALSE;
598   else
599     return FALSE;
600 }
601 
602 
603 /*
604  * This function is called to shut down the SSL layer but keep the
605  * socket open (CCC - Clear Command Channel)
606  */
Curl_cyassl_shutdown(struct connectdata * conn,int sockindex)607 int Curl_cyassl_shutdown(struct connectdata *conn, int sockindex)
608 {
609   int retval = 0;
610   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
611 
612   if(connssl->handle) {
613     SSL_free (connssl->handle);
614     connssl->handle = NULL;
615   }
616   return retval;
617 }
618 
619 
620 static CURLcode
cyassl_connect_common(struct connectdata * conn,int sockindex,bool nonblocking,bool * done)621 cyassl_connect_common(struct connectdata *conn,
622                       int sockindex,
623                       bool nonblocking,
624                       bool *done)
625 {
626   CURLcode result;
627   struct SessionHandle *data = conn->data;
628   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
629   curl_socket_t sockfd = conn->sock[sockindex];
630   long timeout_ms;
631   int what;
632 
633   /* check if the connection has already been established */
634   if(ssl_connection_complete == connssl->state) {
635     *done = TRUE;
636     return CURLE_OK;
637   }
638 
639   if(ssl_connect_1==connssl->connecting_state) {
640     /* Find out how much more time we're allowed */
641     timeout_ms = Curl_timeleft(data, NULL, TRUE);
642 
643     if(timeout_ms < 0) {
644       /* no need to continue if time already is up */
645       failf(data, "SSL connection timeout");
646       return CURLE_OPERATION_TIMEDOUT;
647     }
648 
649     result = cyassl_connect_step1(conn, sockindex);
650     if(result)
651       return result;
652   }
653 
654   while(ssl_connect_2 == connssl->connecting_state ||
655         ssl_connect_2_reading == connssl->connecting_state ||
656         ssl_connect_2_writing == connssl->connecting_state) {
657 
658     /* check allowed time left */
659     timeout_ms = Curl_timeleft(data, NULL, TRUE);
660 
661     if(timeout_ms < 0) {
662       /* no need to continue if time already is up */
663       failf(data, "SSL connection timeout");
664       return CURLE_OPERATION_TIMEDOUT;
665     }
666 
667     /* if ssl is expecting something, check if it's available. */
668     if(connssl->connecting_state == ssl_connect_2_reading
669        || connssl->connecting_state == ssl_connect_2_writing) {
670 
671       curl_socket_t writefd = ssl_connect_2_writing==
672         connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
673       curl_socket_t readfd = ssl_connect_2_reading==
674         connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
675 
676       what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms);
677       if(what < 0) {
678         /* fatal error */
679         failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
680         return CURLE_SSL_CONNECT_ERROR;
681       }
682       else if(0 == what) {
683         if(nonblocking) {
684           *done = FALSE;
685           return CURLE_OK;
686         }
687         else {
688           /* timeout */
689           failf(data, "SSL connection timeout");
690           return CURLE_OPERATION_TIMEDOUT;
691         }
692       }
693       /* socket is readable or writable */
694     }
695 
696     /* Run transaction, and return to the caller if it failed or if
697      * this connection is part of a multi handle and this loop would
698      * execute again. This permits the owner of a multi handle to
699      * abort a connection attempt before step2 has completed while
700      * ensuring that a client using select() or epoll() will always
701      * have a valid fdset to wait on.
702      */
703     result = cyassl_connect_step2(conn, sockindex);
704     if(result || (nonblocking &&
705                   (ssl_connect_2 == connssl->connecting_state ||
706                    ssl_connect_2_reading == connssl->connecting_state ||
707                    ssl_connect_2_writing == connssl->connecting_state)))
708       return result;
709   } /* repeat step2 until all transactions are done. */
710 
711   if(ssl_connect_3 == connssl->connecting_state) {
712     result = cyassl_connect_step3(conn, sockindex);
713     if(result)
714       return result;
715   }
716 
717   if(ssl_connect_done == connssl->connecting_state) {
718     connssl->state = ssl_connection_complete;
719     conn->recv[sockindex] = cyassl_recv;
720     conn->send[sockindex] = cyassl_send;
721     *done = TRUE;
722   }
723   else
724     *done = FALSE;
725 
726   /* Reset our connect state machine */
727   connssl->connecting_state = ssl_connect_1;
728 
729   return CURLE_OK;
730 }
731 
732 
733 CURLcode
Curl_cyassl_connect_nonblocking(struct connectdata * conn,int sockindex,bool * done)734 Curl_cyassl_connect_nonblocking(struct connectdata *conn,
735                                 int sockindex,
736                                 bool *done)
737 {
738   return cyassl_connect_common(conn, sockindex, TRUE, done);
739 }
740 
741 
742 CURLcode
Curl_cyassl_connect(struct connectdata * conn,int sockindex)743 Curl_cyassl_connect(struct connectdata *conn,
744                     int sockindex)
745 {
746   CURLcode result;
747   bool done = FALSE;
748 
749   result = cyassl_connect_common(conn, sockindex, FALSE, &done);
750   if(result)
751     return result;
752 
753   DEBUGASSERT(done);
754 
755   return CURLE_OK;
756 }
757 
Curl_cyassl_random(struct SessionHandle * data,unsigned char * entropy,size_t length)758 int Curl_cyassl_random(struct SessionHandle *data,
759                        unsigned char *entropy,
760                        size_t length)
761 {
762   RNG rng;
763   (void)data;
764   if(InitRng(&rng))
765     return 1;
766   if(length > UINT_MAX)
767     return 1;
768   if(RNG_GenerateBlock(&rng, entropy, (unsigned)length))
769     return 1;
770   return 0;
771 }
772 
773 #endif
774