1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2017 - 2018, Yiming Jing, <jingyiming@baidu.com>
9 * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
10 *
11 * This software is licensed as described in the file COPYING, which
12 * you should have received as part of this distribution. The terms
13 * are also available at https://curl.se/docs/copyright.html.
14 *
15 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
16 * copies of the Software, and permit persons to whom the Software is
17 * furnished to do so, under the terms of the COPYING file.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ***************************************************************************/
23
24/*
25 * Source file for all MesaLink-specific code for the TLS/SSL layer. No code
26 * but vtls.c should ever call or use these functions.
27 *
28 */
29
30/*
31 * Based upon the CyaSSL implementation in cyassl.c and cyassl.h:
32 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
33 *
34 * Thanks for code and inspiration!
35 */
36
37#include "curl_setup.h"
38
39#ifdef USE_MESALINK
40
41#include <mesalink/options.h>
42#include <mesalink/version.h>
43
44#include "urldata.h"
45#include "sendf.h"
46#include "inet_pton.h"
47#include "vtls.h"
48#include "parsedate.h"
49#include "connect.h" /* for the connect timeout */
50#include "select.h"
51#include "strcase.h"
52#include "x509asn1.h"
53#include "curl_printf.h"
54
55#include "mesalink.h"
56#include <mesalink/openssl/ssl.h>
57#include <mesalink/openssl/err.h>
58
59/* The last #include files should be: */
60#include "curl_memory.h"
61#include "memdebug.h"
62
63#define MESALINK_MAX_ERROR_SZ 80
64
65struct ssl_backend_data
66{
67 SSL_CTX *ctx;
68 SSL *handle;
69};
70
71#define BACKEND connssl->backend
72
73static Curl_recv mesalink_recv;
74static Curl_send mesalink_send;
75
76static int do_file_type(const char *type)
77{
78 if(!type || !type[0])
79 return SSL_FILETYPE_PEM;
80 if(strcasecompare(type, "PEM"))
81 return SSL_FILETYPE_PEM;
82 if(strcasecompare(type, "DER"))
83 return SSL_FILETYPE_ASN1;
84 return -1;
85}
86
87/*
88 * This function loads all the client/CA certificates and CRLs. Setup the TLS
89 * layer and do all necessary magic.
90 */
91static CURLcode
92mesalink_connect_step1(struct Curl_easy *data,
93 struct connectdata *conn, int sockindex)
94{
95 char *ciphers;
96 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
97 struct in_addr addr4;
98#ifdef ENABLE_IPV6
99 struct in6_addr addr6;
100#endif
101 const char * const hostname = SSL_HOST_NAME();
102 size_t hostname_len = strlen(hostname);
103
104 SSL_METHOD *req_method = NULL;
105 curl_socket_t sockfd = conn->sock[sockindex];
106
107 if(connssl->state == ssl_connection_complete)
108 return CURLE_OK;
109
110 if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) {
111 failf(data, "MesaLink does not support to set maximum SSL/TLS version");
112 return CURLE_SSL_CONNECT_ERROR;
113 }
114
115 switch(SSL_CONN_CONFIG(version)) {
116 case CURL_SSLVERSION_SSLv3:
117 case CURL_SSLVERSION_TLSv1:
118 case CURL_SSLVERSION_TLSv1_0:
119 case CURL_SSLVERSION_TLSv1_1:
120 failf(data, "MesaLink does not support SSL 3.0, TLS 1.0, or TLS 1.1");
121 return CURLE_NOT_BUILT_IN;
122 case CURL_SSLVERSION_DEFAULT:
123 case CURL_SSLVERSION_TLSv1_2:
124 req_method = TLSv1_2_client_method();
125 break;
126 case CURL_SSLVERSION_TLSv1_3:
127 req_method = TLSv1_3_client_method();
128 break;
129 case CURL_SSLVERSION_SSLv2:
130 failf(data, "MesaLink does not support SSLv2");
131 return CURLE_SSL_CONNECT_ERROR;
132 default:
133 failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
134 return CURLE_SSL_CONNECT_ERROR;
135 }
136
137 if(!req_method) {
138 failf(data, "SSL: couldn't create a method!");
139 return CURLE_OUT_OF_MEMORY;
140 }
141
142 if(BACKEND->ctx)
143 SSL_CTX_free(BACKEND->ctx);
144 BACKEND->ctx = SSL_CTX_new(req_method);
145
146 if(!BACKEND->ctx) {
147 failf(data, "SSL: couldn't create a context!");
148 return CURLE_OUT_OF_MEMORY;
149 }
150
151 SSL_CTX_set_verify(
152 BACKEND->ctx, SSL_CONN_CONFIG(verifypeer) ?
153 SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
154
155 if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(CApath)) {
156 if(!SSL_CTX_load_verify_locations(BACKEND->ctx, SSL_CONN_CONFIG(CAfile),
157 SSL_CONN_CONFIG(CApath))) {
158 if(SSL_CONN_CONFIG(verifypeer)) {
159 failf(data,
160 "error setting certificate verify locations: "
161 " CAfile: %s CApath: %s",
162 SSL_CONN_CONFIG(CAfile) ?
163 SSL_CONN_CONFIG(CAfile) : "none",
164 SSL_CONN_CONFIG(CApath) ?
165 SSL_CONN_CONFIG(CApath) : "none");
166 return CURLE_SSL_CACERT_BADFILE;
167 }
168 infof(data,
169 "error setting certificate verify locations,"
170 " continuing anyway:");
171 }
172 else {
173 infof(data, "successfully set certificate verify locations:");
174 }
175 infof(data, " CAfile: %s",
176 SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile): "none");
177 infof(data, " CApath: %s",
178 SSL_CONN_CONFIG(CApath) ? SSL_CONN_CONFIG(CApath): "none");
179 }
180
181 if(SSL_SET_OPTION(primary.clientcert) && SSL_SET_OPTION(key)) {
182 int file_type = do_file_type(SSL_SET_OPTION(cert_type));
183
184 if(SSL_CTX_use_certificate_chain_file(BACKEND->ctx,
185 SSL_SET_OPTION(primary.clientcert),
186 file_type) != 1) {
187 failf(data, "unable to use client certificate (no key or wrong pass"
188 " phrase?)");
189 return CURLE_SSL_CONNECT_ERROR;
190 }
191
192 file_type = do_file_type(SSL_SET_OPTION(key_type));
193 if(SSL_CTX_use_PrivateKey_file(BACKEND->ctx, SSL_SET_OPTION(key),
194 file_type) != 1) {
195 failf(data, "unable to set private key");
196 return CURLE_SSL_CONNECT_ERROR;
197 }
198 infof(data,
199 "client cert: %s",
200 SSL_CONN_CONFIG(clientcert)?
201 SSL_CONN_CONFIG(clientcert): "none");
202 }
203
204 ciphers = SSL_CONN_CONFIG(cipher_list);
205 if(ciphers) {
206#ifdef MESALINK_HAVE_CIPHER
207 if(!SSL_CTX_set_cipher_list(BACKEND->ctx, ciphers)) {
208 failf(data, "failed setting cipher list: %s", ciphers);
209 return CURLE_SSL_CIPHER;
210 }
211#endif
212 infof(data, "Cipher selection: %s", ciphers);
213 }
214
215 if(BACKEND->handle)
216 SSL_free(BACKEND->handle);
217 BACKEND->handle = SSL_new(BACKEND->ctx);
218 if(!BACKEND->handle) {
219 failf(data, "SSL: couldn't create a context (handle)!");
220 return CURLE_OUT_OF_MEMORY;
221 }
222
223 if((hostname_len < USHRT_MAX) &&
224 (0 == Curl_inet_pton(AF_INET, hostname, &addr4))
225#ifdef ENABLE_IPV6
226 && (0 == Curl_inet_pton(AF_INET6, hostname, &addr6))
227#endif
228 ) {
229 /* hostname is not a valid IP address */
230 if(SSL_set_tlsext_host_name(BACKEND->handle, hostname) != SSL_SUCCESS) {
231 failf(data,
232 "WARNING: failed to configure server name indication (SNI) "
233 "TLS extension\n");
234 return CURLE_SSL_CONNECT_ERROR;
235 }
236 }
237 else {
238#ifdef CURLDEBUG
239 /* Check if the hostname is 127.0.0.1 or [::1];
240 * otherwise reject because MesaLink always wants a valid DNS Name
241 * specified in RFC 5280 Section 7.2 */
242 if(strncmp(hostname, "127.0.0.1", 9) == 0
243#ifdef ENABLE_IPV6
244 || strncmp(hostname, "[::1]", 5) == 0
245#endif
246 ) {
247 SSL_set_tlsext_host_name(BACKEND->handle, "localhost");
248 }
249 else
250#endif
251 {
252 failf(data,
253 "ERROR: MesaLink does not accept an IP address as a hostname\n");
254 return CURLE_SSL_CONNECT_ERROR;
255 }
256 }
257
258#ifdef MESALINK_HAVE_SESSION
259 if(SSL_SET_OPTION(primary.sessionid)) {
260 void *ssl_sessionid = NULL;
261
262 Curl_ssl_sessionid_lock(data);
263 if(!Curl_ssl_getsessionid(data, conn,
264 SSL_IS_PROXY() ? TRUE : FALSE,
265 &ssl_sessionid, NULL, sockindex)) {
266 /* we got a session id, use it! */
267 if(!SSL_set_session(BACKEND->handle, ssl_sessionid)) {
268 Curl_ssl_sessionid_unlock(data);
269 failf(
270 data,
271 "SSL: SSL_set_session failed: %s",
272 ERR_error_string(SSL_get_error(BACKEND->handle, 0), error_buffer));
273 return CURLE_SSL_CONNECT_ERROR;
274 }
275 /* Informational message */
276 infof(data, "SSL re-using session ID");
277 }
278 Curl_ssl_sessionid_unlock(data);
279 }
280#endif /* MESALINK_HAVE_SESSION */
281
282 if(SSL_set_fd(BACKEND->handle, (int)sockfd) != SSL_SUCCESS) {
283 failf(data, "SSL: SSL_set_fd failed");
284 return CURLE_SSL_CONNECT_ERROR;
285 }
286
287 connssl->connecting_state = ssl_connect_2;
288 return CURLE_OK;
289}
290
291static CURLcode
292mesalink_connect_step2(struct Curl_easy *data,
293 struct connectdata *conn, int sockindex)
294{
295 int ret = -1;
296 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
297
298 conn->recv[sockindex] = mesalink_recv;
299 conn->send[sockindex] = mesalink_send;
300
301 ret = SSL_connect(BACKEND->handle);
302 if(ret != SSL_SUCCESS) {
303 int detail = SSL_get_error(BACKEND->handle, ret);
304
305 if(SSL_ERROR_WANT_CONNECT == detail || SSL_ERROR_WANT_READ == detail) {
306 connssl->connecting_state = ssl_connect_2_reading;
307 return CURLE_OK;
308 }
309 else {
310 char error_buffer[MESALINK_MAX_ERROR_SZ];
311 failf(data,
312 "SSL_connect failed with error %d: %s",
313 detail,
314 ERR_error_string_n(detail, error_buffer, sizeof(error_buffer)));
315 ERR_print_errors_fp(stderr);
316 if(detail && SSL_CONN_CONFIG(verifypeer)) {
317 detail &= ~0xFF;
318 if(detail == TLS_ERROR_WEBPKI_ERRORS) {
319 failf(data, "Cert verify failed");
320 return CURLE_PEER_FAILED_VERIFICATION;
321 }
322 }
323 return CURLE_SSL_CONNECT_ERROR;
324 }
325 }
326
327 connssl->connecting_state = ssl_connect_3;
328 infof(data,
329 "SSL connection using %s / %s",
330 SSL_get_version(BACKEND->handle),
331 SSL_get_cipher_name(BACKEND->handle));
332
333 return CURLE_OK;
334}
335
336static CURLcode
337mesalink_connect_step3(struct connectdata *conn, int sockindex)
338{
339 CURLcode result = CURLE_OK;
340 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
341
342 DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
343
344#ifdef MESALINK_HAVE_SESSION
345 if(SSL_SET_OPTION(primary.sessionid)) {
346 bool incache;
347 SSL_SESSION *our_ssl_sessionid;
348 void *old_ssl_sessionid = NULL;
349 bool isproxy = SSL_IS_PROXY() ? TRUE : FALSE;
350
351 our_ssl_sessionid = SSL_get_session(BACKEND->handle);
352
353 Curl_ssl_sessionid_lock(data);
354 incache =
355 !(Curl_ssl_getsessionid(data, conn, isproxy, &old_ssl_sessionid, NULL,
356 sockindex));
357 if(incache) {
358 if(old_ssl_sessionid != our_ssl_sessionid) {
359 infof(data, "old SSL session ID is stale, removing");
360 Curl_ssl_delsessionid(data, old_ssl_sessionid);
361 incache = FALSE;
362 }
363 }
364
365 if(!incache) {
366 result =
367 Curl_ssl_addsessionid(data, conn, isproxy, our_ssl_sessionid, 0,
368 sockindex);
369 if(result) {
370 Curl_ssl_sessionid_unlock(data);
371 failf(data, "failed to store ssl session");
372 return result;
373 }
374 }
375 Curl_ssl_sessionid_unlock(data);
376 }
377#endif /* MESALINK_HAVE_SESSION */
378
379 connssl->connecting_state = ssl_connect_done;
380
381 return result;
382}
383
384static ssize_t
385mesalink_send(struct Curl_easy *data, int sockindex, const void *mem,
386 size_t len, CURLcode *curlcode)
387{
388 struct connectdata *conn = data->conn;
389 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
390 char error_buffer[MESALINK_MAX_ERROR_SZ];
391 int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
392 int rc = SSL_write(BACKEND->handle, mem, memlen);
393
394 if(rc < 0) {
395 int err = SSL_get_error(BACKEND->handle, rc);
396 switch(err) {
397 case SSL_ERROR_WANT_READ:
398 case SSL_ERROR_WANT_WRITE:
399 /* there's data pending, re-invoke SSL_write() */
400 *curlcode = CURLE_AGAIN;
401 return -1;
402 default:
403 failf(data,
404 "SSL write: %s, errno %d",
405 ERR_error_string_n(err, error_buffer, sizeof(error_buffer)),
406 SOCKERRNO);
407 *curlcode = CURLE_SEND_ERROR;
408 return -1;
409 }
410 }
411 return rc;
412}
413
414static void
415mesalink_close(struct Curl_easy *data, struct connectdata *conn, int sockindex)
416{
417 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
418
419 (void) data;
420
421 if(BACKEND->handle) {
422 (void)SSL_shutdown(BACKEND->handle);
423 SSL_free(BACKEND->handle);
424 BACKEND->handle = NULL;
425 }
426 if(BACKEND->ctx) {
427 SSL_CTX_free(BACKEND->ctx);
428 BACKEND->ctx = NULL;
429 }
430}
431
432static ssize_t
433mesalink_recv(struct Curl_easy *data, int num, char *buf, size_t buffersize,
434 CURLcode *curlcode)
435{
436 struct connectdata *conn = data->conn;
437 struct ssl_connect_data *connssl = &conn->ssl[num];
438 char error_buffer[MESALINK_MAX_ERROR_SZ];
439 int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
440 int nread = SSL_read(BACKEND->handle, buf, buffsize);
441
442 if(nread <= 0) {
443 int err = SSL_get_error(BACKEND->handle, nread);
444
445 switch(err) {
446 case SSL_ERROR_ZERO_RETURN: /* no more data */
447 case IO_ERROR_CONNECTION_ABORTED:
448 break;
449 case SSL_ERROR_WANT_READ:
450 case SSL_ERROR_WANT_WRITE:
451 /* there's data pending, re-invoke SSL_read() */
452 *curlcode = CURLE_AGAIN;
453 return -1;
454 default:
455 failf(data,
456 "SSL read: %s, errno %d",
457 ERR_error_string_n(err, error_buffer, sizeof(error_buffer)),
458 SOCKERRNO);
459 *curlcode = CURLE_RECV_ERROR;
460 return -1;
461 }
462 }
463 return nread;
464}
465
466static size_t
467mesalink_version(char *buffer, size_t size)
468{
469 return msnprintf(buffer, size, "MesaLink/%s", MESALINK_VERSION_STRING);
470}
471
472static int
473mesalink_init(void)
474{
475 return (SSL_library_init() == SSL_SUCCESS);
476}
477
478/*
479 * This function is called to shut down the SSL layer but keep the
480 * socket open (CCC - Clear Command Channel)
481 */
482static int
483mesalink_shutdown(struct Curl_easy *data,
484 struct connectdata *conn, int sockindex)
485{
486 int retval = 0;
487 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
488
489 (void) data;
490
491 if(BACKEND->handle) {
492 SSL_free(BACKEND->handle);
493 BACKEND->handle = NULL;
494 }
495 return retval;
496}
497
498static CURLcode
499mesalink_connect_common(struct Curl_easy *data, struct connectdata *conn,
500 int sockindex, bool nonblocking, bool *done)
501{
502 CURLcode result;
503 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
504 curl_socket_t sockfd = conn->sock[sockindex];
505 timediff_t timeout_ms;
506 int what;
507
508 /* check if the connection has already been established */
509 if(ssl_connection_complete == connssl->state) {
510 *done = TRUE;
511 return CURLE_OK;
512 }
513
514 if(ssl_connect_1 == connssl->connecting_state) {
515 /* Find out how much more time we're allowed */
516 timeout_ms = Curl_timeleft(data, NULL, TRUE);
517
518 if(timeout_ms < 0) {
519 /* no need to continue if time already is up */
520 failf(data, "SSL connection timeout");
521 return CURLE_OPERATION_TIMEDOUT;
522 }
523
524 result = mesalink_connect_step1(data, conn, sockindex);
525 if(result)
526 return result;
527 }
528
529 while(ssl_connect_2 == connssl->connecting_state ||
530 ssl_connect_2_reading == connssl->connecting_state ||
531 ssl_connect_2_writing == connssl->connecting_state) {
532
533 /* check allowed time left */
534 timeout_ms = Curl_timeleft(data, NULL, TRUE);
535
536 if(timeout_ms < 0) {
537 /* no need to continue if time already is up */
538 failf(data, "SSL connection timeout");
539 return CURLE_OPERATION_TIMEDOUT;
540 }
541
542 /* if ssl is expecting something, check if it's available. */
543 if(connssl->connecting_state == ssl_connect_2_reading ||
544 connssl->connecting_state == ssl_connect_2_writing) {
545
546 curl_socket_t writefd =
547 ssl_connect_2_writing == connssl->connecting_state ? sockfd
548 : CURL_SOCKET_BAD;
549 curl_socket_t readfd = ssl_connect_2_reading == connssl->connecting_state
550 ? sockfd
551 : CURL_SOCKET_BAD;
552
553 what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
554 nonblocking ? 0 : timeout_ms);
555 if(what < 0) {
556 /* fatal error */
557 failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
558 return CURLE_SSL_CONNECT_ERROR;
559 }
560 else if(0 == what) {
561 if(nonblocking) {
562 *done = FALSE;
563 return CURLE_OK;
564 }
565 else {
566 /* timeout */
567 failf(data, "SSL connection timeout");
568 return CURLE_OPERATION_TIMEDOUT;
569 }
570 }
571 /* socket is readable or writable */
572 }
573
574 /* Run transaction, and return to the caller if it failed or if
575 * this connection is part of a multi handle and this loop would
576 * execute again. This permits the owner of a multi handle to
577 * abort a connection attempt before step2 has completed while
578 * ensuring that a client using select() or epoll() will always
579 * have a valid fdset to wait on.
580 */
581 result = mesalink_connect_step2(data, conn, sockindex);
582
583 if(result ||
584 (nonblocking && (ssl_connect_2 == connssl->connecting_state ||
585 ssl_connect_2_reading == connssl->connecting_state ||
586 ssl_connect_2_writing == connssl->connecting_state))) {
587 return result;
588 }
589 } /* repeat step2 until all transactions are done. */
590
591 if(ssl_connect_3 == connssl->connecting_state) {
592 result = mesalink_connect_step3(conn, sockindex);
593 if(result)
594 return result;
595 }
596
597 if(ssl_connect_done == connssl->connecting_state) {
598 connssl->state = ssl_connection_complete;
599 conn->recv[sockindex] = mesalink_recv;
600 conn->send[sockindex] = mesalink_send;
601 *done = TRUE;
602 }
603 else
604 *done = FALSE;
605
606 /* Reset our connect state machine */
607 connssl->connecting_state = ssl_connect_1;
608
609 return CURLE_OK;
610}
611
612static CURLcode
613mesalink_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
614 int sockindex, bool *done)
615{
616 return mesalink_connect_common(data, conn, sockindex, TRUE, done);
617}
618
619static CURLcode
620mesalink_connect(struct Curl_easy *data, struct connectdata *conn,
621 int sockindex)
622{
623 CURLcode result;
624 bool done = FALSE;
625
626 result = mesalink_connect_common(data, conn, sockindex, FALSE, &done);
627 if(result)
628 return result;
629
630 DEBUGASSERT(done);
631
632 return CURLE_OK;
633}
634
635static void *
636mesalink_get_internals(struct ssl_connect_data *connssl,
637 CURLINFO info UNUSED_PARAM)
638{
639 (void)info;
640 return BACKEND->handle;
641}
642
643const struct Curl_ssl Curl_ssl_mesalink = {
644 { CURLSSLBACKEND_MESALINK, "MesaLink" }, /* info */
645
646 SSLSUPP_SSL_CTX,
647
648 sizeof(struct ssl_backend_data),
649
650 mesalink_init, /* init */
651 Curl_none_cleanup, /* cleanup */
652 mesalink_version, /* version */
653 Curl_none_check_cxn, /* check_cxn */
654 mesalink_shutdown, /* shutdown */
655 Curl_none_data_pending, /* data_pending */
656 Curl_none_random, /* random */
657 Curl_none_cert_status_request, /* cert_status_request */
658 mesalink_connect, /* connect */
659 mesalink_connect_nonblocking, /* connect_nonblocking */
660 Curl_ssl_getsock, /* getsock */
661 mesalink_get_internals, /* get_internals */
662 mesalink_close, /* close_one */
663 Curl_none_close_all, /* close_all */
664 Curl_none_session_free, /* session_free */
665 Curl_none_set_engine, /* set_engine */
666 Curl_none_set_engine_default, /* set_engine_default */
667 Curl_none_engines_list, /* engines_list */
668 Curl_none_false_start, /* false_start */
669 NULL, /* sha256sum */
670 NULL, /* associate_connection */
671 NULL /* disassociate_connection */
672};
673
674#endif
675