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