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