diff options
author | Martin Robinson <mrobinson@igalia.com> | 2023-08-08 16:00:10 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-08 14:00:10 +0000 |
commit | bce7622cde4cd10f6b3edf852d97ae9a540a0076 (patch) | |
tree | e8c09178e875b63e64b32a290840c6ff80d2c4e0 /components/net/fetch/methods.rs | |
parent | ab0f48f8e8a72542269c9e563fad4fa03273d2f3 (diff) | |
download | servo-bce7622cde4cd10f6b3edf852d97ae9a540a0076.tar.gz servo-bce7622cde4cd10f6b3edf852d97ae9a540a0076.zip |
Switch to rustls and webpki-roots (#30025)
This change replaces OpenSSL with rustls and also the manually curated
CA certs file with webpki-roots (effectively the same thing, but as a
crate).
Generally speaking the design of the network stack is the same. Changes:
- Code around certificate overrides needed to be refactored to work with
rustls so the various thread-safe list of certificates is refactored
into `CertificateErrorOverrideManager`
- hyper-rustls takes care of setting ALPN protocols for HTTP requests,
so for WebSockets this is moved to the WebSocket code.
- The safe set of cypher suites is chosen, which seem to correspond to
the "Modern" configuration from [1]. This can be adjusted later.
- Instead of passing a string of PEM CA certificates around, an enum is
used that includes parsed Certificates (or the default which reads
them from webpki-roots).
- Code for starting up an SSL server for testing is cleaned up a little,
due to the fact that the certificates need to be overriden explicitly
now. This is due to the fact that the `webpki` crate is more stringent
with self-signed certificates than SSL (CA certificates cannot used as
end-entity certificates). [2]
1. https://wiki.mozilla.org/Security/Server_Side_TLS
2. https://github.com/briansmith/webpki/issues/114
Fixes #7888.
Fixes #13749.
Fixes #26835.
Fixes #29291.
Diffstat (limited to 'components/net/fetch/methods.rs')
-rw-r--r-- | components/net/fetch/methods.rs | 74 |
1 files changed, 47 insertions, 27 deletions
diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index fb40d40ac19..f11a203b3bc 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -9,7 +9,7 @@ use crate::filemanager_thread::{FileManager, FILE_CHUNK_SIZE}; use crate::http_loader::{determine_requests_referrer, http_fetch, HttpState}; use crate::http_loader::{set_default_accept, set_default_accept_language}; use crate::subresource_integrity::is_response_integrity_valid; -use base64::Engine; +use base64::{engine::general_purpose, Engine as _}; use content_security_policy as csp; use crossbeam_channel::Sender; use devtools_traits::DevtoolsControlMsg; @@ -31,11 +31,12 @@ use net_traits::request::{ use net_traits::response::{Response, ResponseBody, ResponseType}; use net_traits::{FetchTaskTarget, NetworkError, ReferrerPolicy, ResourceFetchTiming}; use net_traits::{ResourceAttribute, ResourceTimeValue, ResourceTimingType}; +use rustls::Certificate; use servo_arc::Arc as ServoArc; use servo_url::ServoUrl; use std::borrow::Cow; use std::fs::File; -use std::io::{BufReader, Seek, SeekFrom}; +use std::io::{self, BufReader, Seek, SeekFrom}; use std::mem; use std::ops::Bound; use std::str; @@ -627,6 +628,48 @@ fn create_blank_reply(url: ServoUrl, timing_type: ResourceTimingType) -> Respons response } +/// Handle a request from the user interface to ignore validation errors for a certificate. +fn handle_allowcert_request(request: &mut Request, context: &FetchContext) -> io::Result<()> { + let error = |string| Err(io::Error::new(io::ErrorKind::Other, string)); + + let body = match request.body.as_mut() { + Some(body) => body, + None => return error("No body found"), + }; + + let stream = body.take_stream(); + let stream = stream.lock().unwrap(); + let (body_chan, body_port) = ipc::channel().unwrap(); + let _ = stream.send(BodyChunkRequest::Connect(body_chan)); + let _ = stream.send(BodyChunkRequest::Chunk); + let body_bytes = match body_port.recv().ok() { + Some(BodyChunkResponse::Chunk(bytes)) => bytes, + _ => return error("Certificate not sent in a single chunk"), + }; + + let split_idx = match body_bytes.iter().position(|b| *b == b'&') { + Some(split_idx) => split_idx, + None => return error("Could not find ampersand in data"), + }; + let (secret, cert_base64) = body_bytes.split_at(split_idx); + + let secret = str::from_utf8(secret).ok().and_then(|s| s.parse().ok()); + if secret != Some(*net_traits::PRIVILEGED_SECRET) { + return error("Invalid secret sent. Ignoring request"); + } + + let cert_bytes = match general_purpose::STANDARD_NO_PAD.decode(&cert_base64[1..]) { + Ok(bytes) => bytes, + Err(_) => return error("Could not decode certificate base64"), + }; + + context + .state + .override_manager + .add_override(&Certificate(cert_bytes)); + Ok(()) +} + /// [Scheme fetch](https://fetch.spec.whatwg.org#scheme-fetch) async fn scheme_fetch( request: &mut Request, @@ -641,32 +684,9 @@ async fn scheme_fetch( "about" if url.path() == "blank" => create_blank_reply(url, request.timing_type()), "chrome" if url.path() == "allowcert" => { - let data = request.body.as_mut().and_then(|body| { - let stream = body.take_stream(); - let stream = stream.lock().unwrap(); - let (body_chan, body_port) = ipc::channel().unwrap(); - let _ = stream.send(BodyChunkRequest::Connect(body_chan)); - let _ = stream.send(BodyChunkRequest::Chunk); - match body_port.recv().ok() { - Some(BodyChunkResponse::Chunk(bytes)) => Some(bytes), - _ => panic!("cert should be sent in a single chunk."), - } - }); - let data = data.as_ref().and_then(|b| { - let idx = b.iter().position(|b| *b == b'&')?; - Some(b.split_at(idx)) - }); - - if let Some((secret, bytes)) = data { - let secret = str::from_utf8(secret).ok().and_then(|s| s.parse().ok()); - if secret == Some(*net_traits::PRIVILEGED_SECRET) { - if let Ok(bytes) = base64::engine::general_purpose::STANDARD.decode(&bytes[1..]) - { - context.state.extra_certs.add(bytes); - } - } + if let Err(error) = handle_allowcert_request(request, context) { + warn!("Could not handle allowcert request: {error}"); } - create_blank_reply(url, request.timing_type()) }, |