xref: /curl/lib/vtls/rustls.c (revision e101a7a8)
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Jacob Hoffman-Andrews,
9  * <github@hoffman-andrews.com>
10  * Copyright (C) kpcyrd, <kpcyrd@archlinux.org>
11  *
12  * This software is licensed as described in the file COPYING, which
13  * you should have received as part of this distribution. The terms
14  * are also available at https://curl.se/docs/copyright.html.
15  *
16  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
17  * copies of the Software, and permit persons to whom the Software is
18  * furnished to do so, under the terms of the COPYING file.
19  *
20  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21  * KIND, either express or implied.
22  *
23  * SPDX-License-Identifier: curl
24  *
25  ***************************************************************************/
26 #include "curl_setup.h"
27 
28 #ifdef USE_RUSTLS
29 
30 #include "curl_printf.h"
31 
32 #include <errno.h>
33 #include <rustls.h>
34 
35 #include "inet_pton.h"
36 #include "urldata.h"
37 #include "sendf.h"
38 #include "vtls.h"
39 #include "vtls_int.h"
40 #include "select.h"
41 #include "strerror.h"
42 #include "multiif.h"
43 #include "connect.h" /* for the connect timeout */
44 
45 struct rustls_ssl_backend_data
46 {
47   const struct rustls_client_config *config;
48   struct rustls_connection *conn;
49   size_t plain_out_buffered;
50   BIT(data_in_pending);
51 };
52 
53 /* For a given rustls_result error code, return the best-matching CURLcode. */
map_error(rustls_result r)54 static CURLcode map_error(rustls_result r)
55 {
56   if(rustls_result_is_cert_error(r)) {
57     return CURLE_PEER_FAILED_VERIFICATION;
58   }
59   switch(r) {
60     case RUSTLS_RESULT_OK:
61       return CURLE_OK;
62     case RUSTLS_RESULT_NULL_PARAMETER:
63       return CURLE_BAD_FUNCTION_ARGUMENT;
64     default:
65       return CURLE_RECV_ERROR;
66   }
67 }
68 
69 static bool
cr_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)70 cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)
71 {
72   struct ssl_connect_data *ctx = cf->ctx;
73   struct rustls_ssl_backend_data *backend;
74 
75   (void)data;
76   DEBUGASSERT(ctx && ctx->backend);
77   backend = (struct rustls_ssl_backend_data *)ctx->backend;
78   return backend->data_in_pending;
79 }
80 
81 struct io_ctx {
82   struct Curl_cfilter *cf;
83   struct Curl_easy *data;
84 };
85 
86 static int
read_cb(void * userdata,uint8_t * buf,uintptr_t len,uintptr_t * out_n)87 read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
88 {
89   struct io_ctx *io_ctx = userdata;
90   struct ssl_connect_data *const connssl = io_ctx->cf->ctx;
91   CURLcode result;
92   int ret = 0;
93   ssize_t nread = Curl_conn_cf_recv(io_ctx->cf->next, io_ctx->data,
94                                     (char *)buf, len, &result);
95   if(nread < 0) {
96     nread = 0;
97     if(CURLE_AGAIN == result)
98       ret = EAGAIN;
99     else
100       ret = EINVAL;
101   }
102   else if(nread == 0)
103     connssl->peer_closed = TRUE;
104   *out_n = (int)nread;
105   CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next recv(len=%zu) -> %zd, %d",
106               len, nread, result);
107   return ret;
108 }
109 
110 static int
write_cb(void * userdata,const uint8_t * buf,uintptr_t len,uintptr_t * out_n)111 write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
112 {
113   struct io_ctx *io_ctx = userdata;
114   CURLcode result;
115   int ret = 0;
116   ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data,
117                                        (const char *)buf, len, &result);
118   if(nwritten < 0) {
119     nwritten = 0;
120     if(CURLE_AGAIN == result)
121       ret = EAGAIN;
122     else
123       ret = EINVAL;
124   }
125   *out_n = (int)nwritten;
126   CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d",
127               len, nwritten, result);
128   return ret;
129 }
130 
tls_recv_more(struct Curl_cfilter * cf,struct Curl_easy * data,CURLcode * err)131 static ssize_t tls_recv_more(struct Curl_cfilter *cf,
132                              struct Curl_easy *data, CURLcode *err)
133 {
134   struct ssl_connect_data *const connssl = cf->ctx;
135   struct rustls_ssl_backend_data *const backend =
136     (struct rustls_ssl_backend_data *)connssl->backend;
137   struct io_ctx io_ctx;
138   size_t tls_bytes_read = 0;
139   rustls_io_result io_error;
140   rustls_result rresult = 0;
141 
142   io_ctx.cf = cf;
143   io_ctx.data = data;
144   io_error = rustls_connection_read_tls(backend->conn, read_cb, &io_ctx,
145                                         &tls_bytes_read);
146   if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
147     *err = CURLE_AGAIN;
148     return -1;
149   }
150   else if(io_error) {
151     char buffer[STRERROR_LEN];
152     failf(data, "reading from socket: %s",
153           Curl_strerror(io_error, buffer, sizeof(buffer)));
154     *err = CURLE_RECV_ERROR;
155     return -1;
156   }
157 
158   rresult = rustls_connection_process_new_packets(backend->conn);
159   if(rresult != RUSTLS_RESULT_OK) {
160     char errorbuf[255];
161     size_t errorlen;
162     rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
163     failf(data, "rustls_connection_process_new_packets: %.*s",
164       (int)errorlen, errorbuf);
165     *err = map_error(rresult);
166     return -1;
167   }
168 
169   backend->data_in_pending = TRUE;
170   *err = CURLE_OK;
171   return (ssize_t)tls_bytes_read;
172 }
173 
174 /*
175  * On each run:
176  *  - Read a chunk of bytes from the socket into rustls' TLS input buffer.
177  *  - Tell rustls to process any new packets.
178  *  - Read out as many plaintext bytes from rustls as possible, until hitting
179  *    error, EOF, or EAGAIN/EWOULDBLOCK, or plainbuf/plainlen is filled up.
180  *
181  * It's okay to call this function with plainbuf == NULL and plainlen == 0.
182  * In that case, it will copy bytes from the socket into rustls' TLS input
183  * buffer, and process packets, but won't consume bytes from rustls' plaintext
184  * output buffer.
185  */
186 static ssize_t
cr_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * plainbuf,size_t plainlen,CURLcode * err)187 cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
188             char *plainbuf, size_t plainlen, CURLcode *err)
189 {
190   struct ssl_connect_data *const connssl = cf->ctx;
191   struct rustls_ssl_backend_data *const backend =
192     (struct rustls_ssl_backend_data *)connssl->backend;
193   struct rustls_connection *rconn = NULL;
194   size_t n = 0;
195   size_t plain_bytes_copied = 0;
196   rustls_result rresult = 0;
197   ssize_t nread;
198   bool eof = FALSE;
199 
200   DEBUGASSERT(backend);
201   rconn = backend->conn;
202 
203   while(plain_bytes_copied < plainlen) {
204     if(!backend->data_in_pending) {
205       if(tls_recv_more(cf, data, err) < 0) {
206         if(*err != CURLE_AGAIN) {
207           nread = -1;
208           goto out;
209         }
210         break;
211       }
212     }
213 
214     rresult = rustls_connection_read(rconn,
215       (uint8_t *)plainbuf + plain_bytes_copied,
216       plainlen - plain_bytes_copied,
217       &n);
218     if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) {
219       backend->data_in_pending = FALSE;
220     }
221     else if(rresult == RUSTLS_RESULT_UNEXPECTED_EOF) {
222       failf(data, "rustls: peer closed TCP connection "
223         "without first closing TLS connection");
224       *err = CURLE_RECV_ERROR;
225       nread = -1;
226       goto out;
227     }
228     else if(rresult != RUSTLS_RESULT_OK) {
229       /* n always equals 0 in this case, don't need to check it */
230       char errorbuf[255];
231       size_t errorlen;
232       rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
233       failf(data, "rustls_connection_read: %.*s", (int)errorlen, errorbuf);
234       *err = CURLE_RECV_ERROR;
235       nread = -1;
236       goto out;
237     }
238     else if(n == 0) {
239       /* n == 0 indicates clean EOF, but we may have read some other
240          plaintext bytes before we reached this. Break out of the loop
241          so we can figure out whether to return success or EOF. */
242       eof = TRUE;
243       break;
244     }
245     else {
246       plain_bytes_copied += n;
247     }
248   }
249 
250   if(plain_bytes_copied) {
251     *err = CURLE_OK;
252     nread = (ssize_t)plain_bytes_copied;
253   }
254   else if(eof) {
255     *err = CURLE_OK;
256     nread = 0;
257   }
258   else {
259     *err = CURLE_AGAIN;
260     nread = -1;
261   }
262 
263 out:
264   CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d",
265               plainlen, nread, *err);
266   return nread;
267 }
268 
cr_flush_out(struct Curl_cfilter * cf,struct Curl_easy * data,struct rustls_connection * rconn)269 static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data,
270                              struct rustls_connection *rconn)
271 {
272   struct io_ctx io_ctx;
273   rustls_io_result io_error;
274   size_t tlswritten = 0;
275   size_t tlswritten_total = 0;
276   CURLcode result = CURLE_OK;
277 
278   io_ctx.cf = cf;
279   io_ctx.data = data;
280 
281   while(rustls_connection_wants_write(rconn)) {
282     io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx,
283                                            &tlswritten);
284     if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
285       CURL_TRC_CF(data, cf, "cf_send: EAGAIN after %zu bytes",
286                   tlswritten_total);
287       return CURLE_AGAIN;
288     }
289     else if(io_error) {
290       char buffer[STRERROR_LEN];
291       failf(data, "writing to socket: %s",
292             Curl_strerror(io_error, buffer, sizeof(buffer)));
293       return CURLE_SEND_ERROR;
294     }
295     if(tlswritten == 0) {
296       failf(data, "EOF in swrite");
297       return CURLE_SEND_ERROR;
298     }
299     CURL_TRC_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten);
300     tlswritten_total += tlswritten;
301   }
302   return result;
303 }
304 
305 /*
306  * On each call:
307  *  - Copy `plainlen` bytes into rustls' plaintext input buffer (if > 0).
308  *  - Fully drain rustls' plaintext output buffer into the socket until
309  *    we get either an error or EAGAIN/EWOULDBLOCK.
310  *
311  * It's okay to call this function with plainbuf == NULL and plainlen == 0.
312  * In that case, it won't read anything into rustls' plaintext input buffer.
313  * It will only drain rustls' plaintext output buffer into the socket.
314  */
315 static ssize_t
cr_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * plainbuf,size_t plainlen,CURLcode * err)316 cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
317         const void *plainbuf, size_t plainlen, CURLcode *err)
318 {
319   struct ssl_connect_data *const connssl = cf->ctx;
320   struct rustls_ssl_backend_data *const backend =
321     (struct rustls_ssl_backend_data *)connssl->backend;
322   struct rustls_connection *rconn = NULL;
323   size_t plainwritten = 0;
324   rustls_result rresult;
325   char errorbuf[256];
326   size_t errorlen;
327   const unsigned char *buf = plainbuf;
328   size_t blen = plainlen;
329   ssize_t nwritten = 0;
330 
331   DEBUGASSERT(backend);
332   rconn = backend->conn;
333   DEBUGASSERT(rconn);
334 
335   CURL_TRC_CF(data, cf, "cf_send(len=%zu)", plainlen);
336 
337   /* If a previous send blocked, we already added its plain bytes
338    * to rustsls and must not do that again. Flush the TLS bytes and,
339    * if successful, deduct the previous plain bytes from the current
340    * send. */
341   if(backend->plain_out_buffered) {
342     *err = cr_flush_out(cf, data, rconn);
343     CURL_TRC_CF(data, cf, "cf_send: flushing %zu previously added bytes -> %d",
344                 backend->plain_out_buffered, *err);
345     if(*err)
346       return -1;
347     if(blen > backend->plain_out_buffered) {
348       blen -= backend->plain_out_buffered;
349       buf += backend->plain_out_buffered;
350     }
351     else
352       blen = 0;
353     nwritten += (ssize_t)backend->plain_out_buffered;
354     backend->plain_out_buffered = 0;
355   }
356 
357   if(blen > 0) {
358     CURL_TRC_CF(data, cf, "cf_send: adding %zu plain bytes to rustls", blen);
359     rresult = rustls_connection_write(rconn, buf, blen, &plainwritten);
360     if(rresult != RUSTLS_RESULT_OK) {
361       rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
362       failf(data, "rustls_connection_write: %.*s", (int)errorlen, errorbuf);
363       *err = CURLE_WRITE_ERROR;
364       return -1;
365     }
366     else if(plainwritten == 0) {
367       failf(data, "rustls_connection_write: EOF");
368       *err = CURLE_WRITE_ERROR;
369       return -1;
370     }
371   }
372 
373   *err = cr_flush_out(cf, data, rconn);
374   if(*err) {
375     if(CURLE_AGAIN == *err) {
376       /* The TLS bytes may have been partially written, but we fail the
377        * complete send() and remember how much we already added to rustls. */
378       CURL_TRC_CF(data, cf, "cf_send: EAGAIN, remember we added %zu plain"
379                   " bytes already to rustls", blen);
380       backend->plain_out_buffered = plainwritten;
381       if(nwritten) {
382         *err = CURLE_OK;
383         return (ssize_t)nwritten;
384       }
385     }
386     return -1;
387   }
388   else
389     nwritten += (ssize_t)plainwritten;
390 
391   CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %d, %zd",
392               plainlen, *err, nwritten);
393   return nwritten;
394 }
395 
396 /* A server certificate verify callback for rustls that always returns
397    RUSTLS_RESULT_OK, or in other words disable certificate verification. */
398 static uint32_t
cr_verify_none(void * userdata UNUSED_PARAM,const rustls_verify_server_cert_params * params UNUSED_PARAM)399 cr_verify_none(void *userdata UNUSED_PARAM,
400                const rustls_verify_server_cert_params *params UNUSED_PARAM)
401 {
402   return RUSTLS_RESULT_OK;
403 }
404 
405 static bool
cr_hostname_is_ip(const char * hostname)406 cr_hostname_is_ip(const char *hostname)
407 {
408   struct in_addr in;
409 #ifdef USE_IPV6
410   struct in6_addr in6;
411   if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) {
412     return true;
413   }
414 #endif /* USE_IPV6 */
415   if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
416     return true;
417   }
418   return false;
419 }
420 
421 static CURLcode
cr_init_backend(struct Curl_cfilter * cf,struct Curl_easy * data,struct rustls_ssl_backend_data * const backend)422 cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
423                 struct rustls_ssl_backend_data *const backend)
424 {
425   struct ssl_connect_data *connssl = cf->ctx;
426   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
427   struct rustls_connection *rconn = NULL;
428   struct rustls_client_config_builder *config_builder = NULL;
429   const struct rustls_root_cert_store *roots = NULL;
430   struct rustls_root_cert_store_builder *roots_builder = NULL;
431   struct rustls_web_pki_server_cert_verifier_builder *verifier_builder = NULL;
432   struct rustls_server_cert_verifier *server_cert_verifier = NULL;
433   const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
434   const char * const ssl_cafile =
435     /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
436     (ca_info_blob ? NULL : conn_config->CAfile);
437   const bool verifypeer = conn_config->verifypeer;
438   const char *hostname = connssl->peer.hostname;
439   char errorbuf[256];
440   size_t errorlen;
441   int result;
442 
443   DEBUGASSERT(backend);
444   rconn = backend->conn;
445 
446   config_builder = rustls_client_config_builder_new();
447   if(connssl->alpn) {
448     struct alpn_proto_buf proto;
449     rustls_slice_bytes alpn[ALPN_ENTRIES_MAX];
450     size_t i;
451 
452     for(i = 0; i < connssl->alpn->count; ++i) {
453       alpn[i].data = (const uint8_t *)connssl->alpn->entries[i];
454       alpn[i].len = strlen(connssl->alpn->entries[i]);
455     }
456     rustls_client_config_builder_set_alpn_protocols(config_builder, alpn,
457                                                     connssl->alpn->count);
458     Curl_alpn_to_proto_str(&proto, connssl->alpn);
459     infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
460   }
461   if(!verifypeer) {
462     rustls_client_config_builder_dangerous_set_certificate_verifier(
463       config_builder, cr_verify_none);
464     /* rustls doesn't support IP addresses (as of 0.19.0), and will reject
465      * connections created with an IP address, even when certificate
466      * verification is turned off. Set a placeholder hostname and disable
467      * SNI. */
468     if(cr_hostname_is_ip(hostname)) {
469       rustls_client_config_builder_set_enable_sni(config_builder, false);
470       hostname = "example.invalid";
471     }
472   }
473   else if(ca_info_blob || ssl_cafile) {
474     roots_builder = rustls_root_cert_store_builder_new();
475 
476     if(ca_info_blob) {
477       /* Enable strict parsing only if verification isn't disabled. */
478       result = rustls_root_cert_store_builder_add_pem(roots_builder,
479                                                       ca_info_blob->data,
480                                                       ca_info_blob->len,
481                                                       verifypeer);
482       if(result != RUSTLS_RESULT_OK) {
483         failf(data, "rustls: failed to parse trusted certificates from blob");
484         rustls_root_cert_store_builder_free(roots_builder);
485         rustls_client_config_free(
486           rustls_client_config_builder_build(config_builder));
487         return CURLE_SSL_CACERT_BADFILE;
488       }
489     }
490     else if(ssl_cafile) {
491       /* Enable strict parsing only if verification isn't disabled. */
492       result = rustls_root_cert_store_builder_load_roots_from_file(
493         roots_builder, ssl_cafile, verifypeer);
494       if(result != RUSTLS_RESULT_OK) {
495         failf(data, "rustls: failed to load trusted certificates");
496         rustls_root_cert_store_builder_free(roots_builder);
497         rustls_client_config_free(
498           rustls_client_config_builder_build(config_builder));
499         return CURLE_SSL_CACERT_BADFILE;
500       }
501     }
502 
503     result = rustls_root_cert_store_builder_build(roots_builder, &roots);
504     rustls_root_cert_store_builder_free(roots_builder);
505     if(result != RUSTLS_RESULT_OK) {
506       failf(data, "rustls: failed to load trusted certificates");
507       rustls_client_config_free(
508         rustls_client_config_builder_build(config_builder));
509       return CURLE_SSL_CACERT_BADFILE;
510     }
511 
512     verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots);
513 
514     result = rustls_web_pki_server_cert_verifier_builder_build(
515       verifier_builder, &server_cert_verifier);
516     rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
517     if(result != RUSTLS_RESULT_OK) {
518       failf(data, "rustls: failed to load trusted certificates");
519       rustls_server_cert_verifier_free(server_cert_verifier);
520       rustls_client_config_free(
521         rustls_client_config_builder_build(config_builder));
522       return CURLE_SSL_CACERT_BADFILE;
523     }
524 
525     rustls_client_config_builder_set_server_verifier(config_builder,
526                                                      server_cert_verifier);
527   }
528 
529   backend->config = rustls_client_config_builder_build(config_builder);
530   DEBUGASSERT(rconn == NULL);
531   result = rustls_client_connection_new(backend->config,
532                                         connssl->peer.hostname, &rconn);
533   if(result != RUSTLS_RESULT_OK) {
534     rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen);
535     failf(data, "rustls_client_connection_new: %.*s", (int)errorlen, errorbuf);
536     return CURLE_COULDNT_CONNECT;
537   }
538   DEBUGASSERT(rconn);
539   rustls_connection_set_userdata(rconn, backend);
540   backend->conn = rconn;
541   return CURLE_OK;
542 }
543 
544 static void
cr_set_negotiated_alpn(struct Curl_cfilter * cf,struct Curl_easy * data,const struct rustls_connection * rconn)545 cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data,
546   const struct rustls_connection *rconn)
547 {
548   const uint8_t *protocol = NULL;
549   size_t len = 0;
550 
551   rustls_connection_get_alpn_protocol(rconn, &protocol, &len);
552   Curl_alpn_set_negotiated(cf, data, protocol, len);
553 }
554 
555 /* Given an established network connection, do a TLS handshake.
556  *
557  * If `blocking` is true, this function will block until the handshake is
558  * complete. Otherwise it will return as soon as I/O would block.
559  *
560  * For the non-blocking I/O case, this function will set `*done` to true
561  * once the handshake is complete. This function never reads the value of
562  * `*done*`.
563  */
564 static CURLcode
cr_connect_common(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)565 cr_connect_common(struct Curl_cfilter *cf,
566                   struct Curl_easy *data,
567                   bool blocking,
568                   bool *done)
569 {
570   struct ssl_connect_data *const connssl = cf->ctx;
571   curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
572   struct rustls_ssl_backend_data *const backend =
573     (struct rustls_ssl_backend_data *)connssl->backend;
574   struct rustls_connection *rconn = NULL;
575   CURLcode tmperr = CURLE_OK;
576   int result;
577   int what;
578   bool wants_read;
579   bool wants_write;
580   curl_socket_t writefd;
581   curl_socket_t readfd;
582   timediff_t timeout_ms;
583   timediff_t socket_check_timeout;
584 
585   DEBUGASSERT(backend);
586 
587   CURL_TRC_CF(data, cf, "cr_connect_common, state=%d", connssl->state);
588   *done = FALSE;
589   if(!backend->conn) {
590     result = cr_init_backend(cf, data,
591                (struct rustls_ssl_backend_data *)connssl->backend);
592     CURL_TRC_CF(data, cf, "cr_connect_common, init backend -> %d", result);
593     if(result != CURLE_OK) {
594       return result;
595     }
596     connssl->state = ssl_connection_negotiating;
597   }
598 
599   rconn = backend->conn;
600 
601   /* Read/write data until the handshake is done or the socket would block. */
602   for(;;) {
603     /*
604     * Connection has been established according to rustls. Set send/recv
605     * handlers, and update the state machine.
606     */
607     if(!rustls_connection_is_handshaking(rconn)) {
608       infof(data, "Done handshaking");
609       /* rustls claims it is no longer handshaking *before* it has
610        * send its FINISHED message off. We attempt to let it write
611        * one more time. Oh my.
612        */
613       cr_set_negotiated_alpn(cf, data, rconn);
614       cr_send(cf, data, NULL, 0, &tmperr);
615       if(tmperr == CURLE_AGAIN) {
616         connssl->connecting_state = ssl_connect_2_writing;
617         return CURLE_OK;
618       }
619       else if(tmperr != CURLE_OK) {
620         return tmperr;
621       }
622       /* REALLY Done with the handshake. */
623       connssl->state = ssl_connection_complete;
624       *done = TRUE;
625       return CURLE_OK;
626     }
627 
628     wants_read = rustls_connection_wants_read(rconn);
629     wants_write = rustls_connection_wants_write(rconn) ||
630                   backend->plain_out_buffered;
631     DEBUGASSERT(wants_read || wants_write);
632     writefd = wants_write?sockfd:CURL_SOCKET_BAD;
633     readfd = wants_read?sockfd:CURL_SOCKET_BAD;
634 
635     connssl->connecting_state = wants_write?
636       ssl_connect_2_writing : ssl_connect_2_reading;
637     /* check allowed time left */
638     timeout_ms = Curl_timeleft(data, NULL, TRUE);
639 
640     if(timeout_ms < 0) {
641       /* no need to continue if time already is up */
642       failf(data, "rustls: operation timed out before socket check");
643       return CURLE_OPERATION_TIMEDOUT;
644     }
645 
646     socket_check_timeout = blocking?timeout_ms:0;
647 
648     what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
649                              socket_check_timeout);
650     if(what < 0) {
651       /* fatal error */
652       failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
653       return CURLE_SSL_CONNECT_ERROR;
654     }
655     if(blocking && 0 == what) {
656       failf(data, "rustls connection timeout after %"
657         CURL_FORMAT_TIMEDIFF_T " ms", socket_check_timeout);
658       return CURLE_OPERATION_TIMEDOUT;
659     }
660     if(0 == what) {
661       CURL_TRC_CF(data, cf, "Curl_socket_check: %s would block",
662             wants_read&&wants_write ? "writing and reading" :
663             wants_write ? "writing" : "reading");
664       return CURLE_OK;
665     }
666     /* socket is readable or writable */
667 
668     if(wants_write) {
669       CURL_TRC_CF(data, cf, "rustls_connection wants us to write_tls.");
670       cr_send(cf, data, NULL, 0, &tmperr);
671       if(tmperr == CURLE_AGAIN) {
672         CURL_TRC_CF(data, cf, "writing would block");
673         /* fall through */
674       }
675       else if(tmperr != CURLE_OK) {
676         return tmperr;
677       }
678     }
679 
680     if(wants_read) {
681       CURL_TRC_CF(data, cf, "rustls_connection wants us to read_tls.");
682       if(tls_recv_more(cf, data, &tmperr) < 0) {
683         if(tmperr == CURLE_AGAIN) {
684           CURL_TRC_CF(data, cf, "reading would block");
685           /* fall through */
686         }
687         else if(tmperr == CURLE_RECV_ERROR) {
688           return CURLE_SSL_CONNECT_ERROR;
689         }
690         else {
691           return tmperr;
692         }
693       }
694     }
695   }
696 
697   /* We should never fall through the loop. We should return either because
698      the handshake is done or because we can't read/write without blocking. */
699   DEBUGASSERT(false);
700 }
701 
702 static CURLcode
cr_connect_nonblocking(struct Curl_cfilter * cf,struct Curl_easy * data,bool * done)703 cr_connect_nonblocking(struct Curl_cfilter *cf,
704                        struct Curl_easy *data, bool *done)
705 {
706   return cr_connect_common(cf, data, false, done);
707 }
708 
709 static CURLcode
cr_connect_blocking(struct Curl_cfilter * cf,struct Curl_easy * data)710 cr_connect_blocking(struct Curl_cfilter *cf, struct Curl_easy *data)
711 {
712   bool done; /* unused */
713   return cr_connect_common(cf, data, true, &done);
714 }
715 
716 static void *
cr_get_internals(struct ssl_connect_data * connssl,CURLINFO info UNUSED_PARAM)717 cr_get_internals(struct ssl_connect_data *connssl,
718                  CURLINFO info UNUSED_PARAM)
719 {
720   struct rustls_ssl_backend_data *backend =
721     (struct rustls_ssl_backend_data *)connssl->backend;
722   DEBUGASSERT(backend);
723   return &backend->conn;
724 }
725 
726 static void
cr_close(struct Curl_cfilter * cf,struct Curl_easy * data)727 cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
728 {
729   struct ssl_connect_data *connssl = cf->ctx;
730   struct rustls_ssl_backend_data *backend =
731     (struct rustls_ssl_backend_data *)connssl->backend;
732   CURLcode tmperr = CURLE_OK;
733   ssize_t n = 0;
734 
735   DEBUGASSERT(backend);
736   if(backend->conn && !connssl->peer_closed) {
737     CURL_TRC_CF(data, cf, "closing connection, send notify");
738     rustls_connection_send_close_notify(backend->conn);
739     n = cr_send(cf, data, NULL, 0, &tmperr);
740     if(n < 0) {
741       failf(data, "rustls: error sending close_notify: %d", tmperr);
742     }
743 
744     rustls_connection_free(backend->conn);
745     backend->conn = NULL;
746   }
747   if(backend->config) {
748     rustls_client_config_free(backend->config);
749     backend->config = NULL;
750   }
751 }
752 
cr_version(char * buffer,size_t size)753 static size_t cr_version(char *buffer, size_t size)
754 {
755   struct rustls_str ver = rustls_version();
756   return msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data);
757 }
758 
759 const struct Curl_ssl Curl_ssl_rustls = {
760   { CURLSSLBACKEND_RUSTLS, "rustls" },
761   SSLSUPP_CAINFO_BLOB |            /* supports */
762   SSLSUPP_HTTPS_PROXY,
763   sizeof(struct rustls_ssl_backend_data),
764 
765   Curl_none_init,                  /* init */
766   Curl_none_cleanup,               /* cleanup */
767   cr_version,                      /* version */
768   Curl_none_check_cxn,             /* check_cxn */
769   Curl_none_shutdown,              /* shutdown */
770   cr_data_pending,                 /* data_pending */
771   Curl_none_random,                /* random */
772   Curl_none_cert_status_request,   /* cert_status_request */
773   cr_connect_blocking,             /* connect */
774   cr_connect_nonblocking,          /* connect_nonblocking */
775   Curl_ssl_adjust_pollset,         /* adjust_pollset */
776   cr_get_internals,                /* get_internals */
777   cr_close,                        /* close_one */
778   Curl_none_close_all,             /* close_all */
779   Curl_none_set_engine,            /* set_engine */
780   Curl_none_set_engine_default,    /* set_engine_default */
781   Curl_none_engines_list,          /* engines_list */
782   Curl_none_false_start,           /* false_start */
783   NULL,                            /* sha256sum */
784   NULL,                            /* associate_connection */
785   NULL,                            /* disassociate_connection */
786   cr_recv,                         /* recv decrypted data */
787   cr_send,                         /* send data to encrypt */
788 };
789 
790 #endif /* USE_RUSTLS */
791