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