1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2020 - 2021, Jacob Hoffman-Andrews,
9  * <github@hoffman-andrews.com>
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 #include "curl_setup.h"
24 
25 #ifdef USE_RUSTLS
26 
27 #include "curl_printf.h"
28 
29 #include <errno.h>
30 #include <crustls.h>
31 
32 #include "inet_pton.h"
33 #include "urldata.h"
34 #include "sendf.h"
35 #include "vtls.h"
36 #include "select.h"
37 #include "strerror.h"
38 #include "multiif.h"
39 
40 struct ssl_backend_data
41 {
42   const struct rustls_client_config *config;
43   struct rustls_connection *conn;
44   bool data_pending;
45 };
46 
47 /* For a given rustls_result error code, return the best-matching CURLcode. */
map_error(rustls_result r)48 static CURLcode map_error(rustls_result r)
49 {
50   if(rustls_result_is_cert_error(r)) {
51     return CURLE_PEER_FAILED_VERIFICATION;
52   }
53   switch(r) {
54     case RUSTLS_RESULT_OK:
55       return CURLE_OK;
56     case RUSTLS_RESULT_NULL_PARAMETER:
57       return CURLE_BAD_FUNCTION_ARGUMENT;
58     default:
59       return CURLE_READ_ERROR;
60   }
61 }
62 
63 static bool
cr_data_pending(const struct connectdata * conn,int sockindex)64 cr_data_pending(const struct connectdata *conn, int sockindex)
65 {
66   const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
67   struct ssl_backend_data *backend = connssl->backend;
68   return backend->data_pending;
69 }
70 
71 static CURLcode
cr_connect(struct Curl_easy * data UNUSED_PARAM,struct connectdata * conn UNUSED_PARAM,int sockindex UNUSED_PARAM)72 cr_connect(struct Curl_easy *data UNUSED_PARAM,
73                     struct connectdata *conn UNUSED_PARAM,
74                     int sockindex UNUSED_PARAM)
75 {
76   infof(data, "rustls_connect: unimplemented");
77   return CURLE_SSL_CONNECT_ERROR;
78 }
79 
80 static int
read_cb(void * userdata,uint8_t * buf,uintptr_t len,uintptr_t * out_n)81 read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
82 {
83   ssize_t n = sread(*(int *)userdata, buf, len);
84   if(n < 0) {
85     return SOCKERRNO;
86   }
87   *out_n = n;
88   return 0;
89 }
90 
91 static int
write_cb(void * userdata,const uint8_t * buf,uintptr_t len,uintptr_t * out_n)92 write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
93 {
94   ssize_t n = swrite(*(int *)userdata, buf, len);
95   if(n < 0) {
96     return SOCKERRNO;
97   }
98   *out_n = n;
99   return 0;
100 }
101 
102 /*
103  * On each run:
104  *  - Read a chunk of bytes from the socket into rustls' TLS input buffer.
105  *  - Tell rustls to process any new packets.
106  *  - Read out as many plaintext bytes from rustls as possible, until hitting
107  *    error, EOF, or EAGAIN/EWOULDBLOCK, or plainbuf/plainlen is filled up.
108  *
109  * It's okay to call this function with plainbuf == NULL and plainlen == 0.
110  * In that case, it will copy bytes from the socket into rustls' TLS input
111  * buffer, and process packets, but won't consume bytes from rustls' plaintext
112  * output buffer.
113  */
114 static ssize_t
cr_recv(struct Curl_easy * data,int sockindex,char * plainbuf,size_t plainlen,CURLcode * err)115 cr_recv(struct Curl_easy *data, int sockindex,
116             char *plainbuf, size_t plainlen, CURLcode *err)
117 {
118   struct connectdata *conn = data->conn;
119   struct ssl_connect_data *const connssl = &conn->ssl[sockindex];
120   struct ssl_backend_data *const backend = connssl->backend;
121   struct rustls_connection *const rconn = backend->conn;
122   size_t n = 0;
123   size_t tls_bytes_read = 0;
124   size_t plain_bytes_copied = 0;
125   rustls_result rresult = 0;
126   char errorbuf[255];
127   rustls_io_result io_error;
128 
129   io_error = rustls_connection_read_tls(rconn, read_cb,
130     &conn->sock[sockindex], &tls_bytes_read);
131   if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
132     infof(data, "sread: EAGAIN or EWOULDBLOCK");
133   }
134   else if(io_error) {
135     char buffer[STRERROR_LEN];
136     failf(data, "reading from socket: %s",
137           Curl_strerror(io_error, buffer, sizeof(buffer)));
138     *err = CURLE_READ_ERROR;
139     return -1;
140   }
141   else if(tls_bytes_read == 0) {
142     failf(data, "connection closed without TLS close_notify alert");
143     *err = CURLE_READ_ERROR;
144     return -1;
145   }
146 
147   infof(data, "cr_recv read %ld bytes from the network", tls_bytes_read);
148 
149   rresult = rustls_connection_process_new_packets(rconn);
150   if(rresult != RUSTLS_RESULT_OK) {
151     rustls_error(rresult, errorbuf, sizeof(errorbuf), &n);
152     failf(data, "%.*s", n, errorbuf);
153     *err = map_error(rresult);
154     return -1;
155   }
156 
157   backend->data_pending = TRUE;
158 
159   while(plain_bytes_copied < plainlen) {
160     rresult = rustls_connection_read(rconn,
161       (uint8_t *)plainbuf + plain_bytes_copied,
162       plainlen - plain_bytes_copied,
163       &n);
164     if(rresult == RUSTLS_RESULT_ALERT_CLOSE_NOTIFY) {
165       *err = CURLE_OK;
166       return 0;
167     }
168     else if(rresult != RUSTLS_RESULT_OK) {
169       failf(data, "error in rustls_connection_read");
170       *err = CURLE_READ_ERROR;
171       return -1;
172     }
173     else if(n == 0) {
174       /* rustls returns 0 from connection_read to mean "all currently
175         available data has been read." If we bring in more ciphertext with
176         read_tls, more plaintext will become available. So don't tell curl
177         this is an EOF. Instead, say "come back later." */
178       infof(data, "cr_recv got 0 bytes of plaintext");
179       backend->data_pending = FALSE;
180       break;
181     }
182     else {
183       infof(data, "cr_recv copied out %ld bytes of plaintext", n);
184       plain_bytes_copied += n;
185     }
186   }
187 
188   /* If we wrote out 0 plaintext bytes, it might just mean we haven't yet
189      read a full TLS record. Return CURLE_AGAIN so curl doesn't treat this
190      as EOF. */
191   if(plain_bytes_copied == 0) {
192     *err = CURLE_AGAIN;
193     return -1;
194   }
195 
196   return plain_bytes_copied;
197 }
198 
199 /*
200  * On each call:
201  *  - Copy `plainlen` bytes into rustls' plaintext input buffer (if > 0).
202  *  - Fully drain rustls' plaintext output buffer into the socket until
203  *    we get either an error or EAGAIN/EWOULDBLOCK.
204  *
205  * It's okay to call this function with plainbuf == NULL and plainlen == 0.
206  * In that case, it won't read anything into rustls' plaintext input buffer.
207  * It will only drain rustls' plaintext output buffer into the socket.
208  */
209 static ssize_t
cr_send(struct Curl_easy * data,int sockindex,const void * plainbuf,size_t plainlen,CURLcode * err)210 cr_send(struct Curl_easy *data, int sockindex,
211         const void *plainbuf, size_t plainlen, CURLcode *err)
212 {
213   struct connectdata *conn = data->conn;
214   struct ssl_connect_data *const connssl = &conn->ssl[sockindex];
215   struct ssl_backend_data *const backend = connssl->backend;
216   struct rustls_connection *const rconn = backend->conn;
217   size_t plainwritten = 0;
218   size_t tlswritten = 0;
219   size_t tlswritten_total = 0;
220   rustls_result rresult;
221   rustls_io_result io_error;
222 
223   infof(data, "cr_send %ld bytes of plaintext", plainlen);
224 
225   if(plainlen > 0) {
226     rresult = rustls_connection_write(rconn, plainbuf, plainlen,
227                                       &plainwritten);
228     if(rresult != RUSTLS_RESULT_OK) {
229       failf(data, "error in rustls_connection_write");
230       *err = CURLE_WRITE_ERROR;
231       return -1;
232     }
233     else if(plainwritten == 0) {
234       failf(data, "EOF in rustls_connection_write");
235       *err = CURLE_WRITE_ERROR;
236       return -1;
237     }
238   }
239 
240   while(rustls_connection_wants_write(rconn)) {
241     io_error = rustls_connection_write_tls(rconn, write_cb,
242       &conn->sock[sockindex], &tlswritten);
243     if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
244       infof(data, "swrite: EAGAIN after %ld bytes", tlswritten_total);
245       *err = CURLE_AGAIN;
246       return -1;
247     }
248     else if(io_error) {
249       char buffer[STRERROR_LEN];
250       failf(data, "writing to socket: %s",
251             Curl_strerror(io_error, buffer, sizeof(buffer)));
252       *err = CURLE_WRITE_ERROR;
253       return -1;
254     }
255     if(tlswritten == 0) {
256       failf(data, "EOF in swrite");
257       *err = CURLE_WRITE_ERROR;
258       return -1;
259     }
260     infof(data, "cr_send wrote %ld bytes to network", tlswritten);
261     tlswritten_total += tlswritten;
262   }
263 
264   return plainwritten;
265 }
266 
267 /* A server certificate verify callback for rustls that always returns
268    RUSTLS_RESULT_OK, or in other words disable certificate verification. */
269 static enum rustls_result
cr_verify_none(void * userdata UNUSED_PARAM,const rustls_verify_server_cert_params * params UNUSED_PARAM)270 cr_verify_none(void *userdata UNUSED_PARAM,
271                const rustls_verify_server_cert_params *params UNUSED_PARAM)
272 {
273   return RUSTLS_RESULT_OK;
274 }
275 
276 static bool
cr_hostname_is_ip(const char * hostname)277 cr_hostname_is_ip(const char *hostname)
278 {
279   struct in_addr in;
280 #ifdef ENABLE_IPV6
281   struct in6_addr in6;
282   if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) {
283     return true;
284   }
285 #endif /* ENABLE_IPV6 */
286   if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
287     return true;
288   }
289   return false;
290 }
291 
292 static CURLcode
cr_init_backend(struct Curl_easy * data,struct connectdata * conn,struct ssl_backend_data * const backend)293 cr_init_backend(struct Curl_easy *data, struct connectdata *conn,
294                 struct ssl_backend_data *const backend)
295 {
296   struct rustls_connection *rconn = backend->conn;
297   struct rustls_client_config_builder *config_builder = NULL;
298   const char *const ssl_cafile = SSL_CONN_CONFIG(CAfile);
299   const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
300   const char *hostname = conn->host.name;
301   char errorbuf[256];
302   size_t errorlen;
303   int result;
304   rustls_slice_bytes alpn[2] = {
305     { (const uint8_t *)ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH },
306     { (const uint8_t *)ALPN_H2, ALPN_H2_LENGTH },
307   };
308 
309   config_builder = rustls_client_config_builder_new();
310 #ifdef USE_HTTP2
311   infof(data, "offering ALPN for HTTP/1.1 and HTTP/2");
312   rustls_client_config_builder_set_protocols(config_builder, alpn, 2);
313 #else
314   infof(data, "offering ALPN for HTTP/1.1 only");
315   rustls_client_config_builder_set_protocols(config_builder, alpn, 1);
316 #endif
317   if(!verifypeer) {
318     rustls_client_config_builder_dangerous_set_certificate_verifier(
319       config_builder, cr_verify_none);
320     /* rustls doesn't support IP addresses (as of 0.19.0), and will reject
321      * connections created with an IP address, even when certificate
322      * verification is turned off. Set a placeholder hostname and disable
323      * SNI. */
324     if(cr_hostname_is_ip(hostname)) {
325       rustls_client_config_builder_set_enable_sni(config_builder, false);
326       hostname = "example.invalid";
327     }
328   }
329   else if(ssl_cafile) {
330     result = rustls_client_config_builder_load_roots_from_file(
331       config_builder, ssl_cafile);
332     if(result != RUSTLS_RESULT_OK) {
333       failf(data, "failed to load trusted certificates");
334       rustls_client_config_free(
335         rustls_client_config_builder_build(config_builder));
336       return CURLE_SSL_CACERT_BADFILE;
337     }
338   }
339 
340   backend->config = rustls_client_config_builder_build(config_builder);
341   DEBUGASSERT(rconn == NULL);
342   result = rustls_client_connection_new(backend->config, hostname, &rconn);
343   if(result != RUSTLS_RESULT_OK) {
344     rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen);
345     failf(data, "rustls_client_connection_new: %.*s", errorlen, errorbuf);
346     return CURLE_COULDNT_CONNECT;
347   }
348   rustls_connection_set_userdata(rconn, backend);
349   backend->conn = rconn;
350   return CURLE_OK;
351 }
352 
353 static void
cr_set_negotiated_alpn(struct Curl_easy * data,struct connectdata * conn,const struct rustls_connection * rconn)354 cr_set_negotiated_alpn(struct Curl_easy *data, struct connectdata *conn,
355   const struct rustls_connection *rconn)
356 {
357   const uint8_t *protocol = NULL;
358   size_t len = 0;
359 
360   rustls_connection_get_alpn_protocol(rconn, &protocol, &len);
361   if(NULL == protocol) {
362     infof(data, "ALPN, server did not agree to a protocol");
363     return;
364   }
365 
366 #ifdef USE_HTTP2
367   if(len == ALPN_H2_LENGTH && 0 == memcmp(ALPN_H2, protocol, len)) {
368     infof(data, "ALPN, negotiated h2");
369     conn->negnpn = CURL_HTTP_VERSION_2;
370   }
371   else
372 #endif
373   if(len == ALPN_HTTP_1_1_LENGTH &&
374       0 == memcmp(ALPN_HTTP_1_1, protocol, len)) {
375     infof(data, "ALPN, negotiated http/1.1");
376     conn->negnpn = CURL_HTTP_VERSION_1_1;
377   }
378   else {
379     infof(data, "ALPN, negotiated an unrecognized protocol");
380   }
381 
382   Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
383                       BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
384 }
385 
386 static CURLcode
cr_connect_nonblocking(struct Curl_easy * data,struct connectdata * conn,int sockindex,bool * done)387 cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
388                        int sockindex, bool *done)
389 {
390   struct ssl_connect_data *const connssl = &conn->ssl[sockindex];
391   curl_socket_t sockfd = conn->sock[sockindex];
392   struct ssl_backend_data *const backend = connssl->backend;
393   struct rustls_connection *rconn = NULL;
394   CURLcode tmperr = CURLE_OK;
395   int result;
396   int what;
397   bool wants_read;
398   bool wants_write;
399   curl_socket_t writefd;
400   curl_socket_t readfd;
401 
402   if(ssl_connection_none == connssl->state) {
403     result = cr_init_backend(data, conn, connssl->backend);
404     if(result != CURLE_OK) {
405       return result;
406     }
407     connssl->state = ssl_connection_negotiating;
408   }
409 
410   rconn = backend->conn;
411 
412   /* Read/write data until the handshake is done or the socket would block. */
413   for(;;) {
414     /*
415     * Connection has been established according to rustls. Set send/recv
416     * handlers, and update the state machine.
417     * This check has to come last because is_handshaking starts out false,
418     * then becomes true when we first write data, then becomes false again
419     * once the handshake is done.
420     */
421     if(!rustls_connection_is_handshaking(rconn)) {
422       infof(data, "Done handshaking");
423       /* Done with the handshake. Set up callbacks to send/receive data. */
424       connssl->state = ssl_connection_complete;
425 
426       cr_set_negotiated_alpn(data, conn, rconn);
427 
428       conn->recv[sockindex] = cr_recv;
429       conn->send[sockindex] = cr_send;
430       *done = TRUE;
431       return CURLE_OK;
432     }
433 
434     wants_read = rustls_connection_wants_read(rconn);
435     wants_write = rustls_connection_wants_write(rconn);
436     DEBUGASSERT(wants_read || wants_write);
437     writefd = wants_write?sockfd:CURL_SOCKET_BAD;
438     readfd = wants_read?sockfd:CURL_SOCKET_BAD;
439 
440     what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, 0);
441     if(what < 0) {
442       /* fatal error */
443       failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
444       return CURLE_SSL_CONNECT_ERROR;
445     }
446     if(0 == what) {
447       infof(data, "Curl_socket_check: %s would block",
448             wants_read&&wants_write ? "writing and reading" :
449             wants_write ? "writing" : "reading");
450       *done = FALSE;
451       return CURLE_OK;
452     }
453     /* socket is readable or writable */
454 
455     if(wants_write) {
456       infof(data, "rustls_connection wants us to write_tls.");
457       cr_send(data, sockindex, NULL, 0, &tmperr);
458       if(tmperr == CURLE_AGAIN) {
459         infof(data, "writing would block");
460         /* fall through */
461       }
462       else if(tmperr != CURLE_OK) {
463         return tmperr;
464       }
465     }
466 
467     if(wants_read) {
468       infof(data, "rustls_connection wants us to read_tls.");
469 
470       cr_recv(data, sockindex, NULL, 0, &tmperr);
471       if(tmperr == CURLE_AGAIN) {
472         infof(data, "reading would block");
473         /* fall through */
474       }
475       else if(tmperr != CURLE_OK) {
476         if(tmperr == CURLE_READ_ERROR) {
477           return CURLE_SSL_CONNECT_ERROR;
478         }
479         else {
480           return tmperr;
481         }
482       }
483     }
484   }
485 
486   /* We should never fall through the loop. We should return either because
487      the handshake is done or because we can't read/write without blocking. */
488   DEBUGASSERT(false);
489 }
490 
491 /* returns a bitmap of flags for this connection's first socket indicating
492    whether we want to read or write */
493 static int
cr_getsock(struct connectdata * conn,curl_socket_t * socks)494 cr_getsock(struct connectdata *conn, curl_socket_t *socks)
495 {
496   struct ssl_connect_data *const connssl = &conn->ssl[FIRSTSOCKET];
497   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
498   struct ssl_backend_data *const backend = connssl->backend;
499   struct rustls_connection *rconn = backend->conn;
500 
501   if(rustls_connection_wants_write(rconn)) {
502     socks[0] = sockfd;
503     return GETSOCK_WRITESOCK(0);
504   }
505   if(rustls_connection_wants_read(rconn)) {
506     socks[0] = sockfd;
507     return GETSOCK_READSOCK(0);
508   }
509 
510   return GETSOCK_BLANK;
511 }
512 
513 static void *
cr_get_internals(struct ssl_connect_data * connssl,CURLINFO info UNUSED_PARAM)514 cr_get_internals(struct ssl_connect_data *connssl,
515                  CURLINFO info UNUSED_PARAM)
516 {
517   struct ssl_backend_data *backend = connssl->backend;
518   return &backend->conn;
519 }
520 
521 static void
cr_close(struct Curl_easy * data,struct connectdata * conn,int sockindex)522 cr_close(struct Curl_easy *data, struct connectdata *conn,
523          int sockindex)
524 {
525   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
526   struct ssl_backend_data *backend = connssl->backend;
527   CURLcode tmperr = CURLE_OK;
528   ssize_t n = 0;
529 
530   if(backend->conn) {
531     rustls_connection_send_close_notify(backend->conn);
532     n = cr_send(data, sockindex, NULL, 0, &tmperr);
533     if(n < 0) {
534       failf(data, "error sending close notify: %d", tmperr);
535     }
536 
537     rustls_connection_free(backend->conn);
538     backend->conn = NULL;
539   }
540   if(backend->config) {
541     rustls_client_config_free(backend->config);
542     backend->config = NULL;
543   }
544 }
545 
546 const struct Curl_ssl Curl_ssl_rustls = {
547   { CURLSSLBACKEND_RUSTLS, "rustls" },
548   SSLSUPP_TLS13_CIPHERSUITES,      /* supports */
549   sizeof(struct ssl_backend_data),
550 
551   Curl_none_init,                  /* init */
552   Curl_none_cleanup,               /* cleanup */
553   rustls_version,                  /* version */
554   Curl_none_check_cxn,             /* check_cxn */
555   Curl_none_shutdown,              /* shutdown */
556   cr_data_pending,                 /* data_pending */
557   Curl_none_random,                /* random */
558   Curl_none_cert_status_request,   /* cert_status_request */
559   cr_connect,                      /* connect */
560   cr_connect_nonblocking,          /* connect_nonblocking */
561   cr_getsock,                      /* cr_getsock */
562   cr_get_internals,                /* get_internals */
563   cr_close,                        /* close_one */
564   Curl_none_close_all,             /* close_all */
565   Curl_none_session_free,          /* session_free */
566   Curl_none_set_engine,            /* set_engine */
567   Curl_none_set_engine_default,    /* set_engine_default */
568   Curl_none_engines_list,          /* engines_list */
569   Curl_none_false_start,           /* false_start */
570   NULL,                            /* sha256sum */
571   NULL,                            /* associate_connection */
572   NULL                             /* disassociate_connection */
573 };
574 
575 #endif /* USE_RUSTLS */
576