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