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