• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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