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