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