mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-04-28 07:55:22 +00:00
net/quic: Fix inconsistencies around secure connection handling
This set of changes implements the below fixes: - Allow certificates to be specified for client/quicsink - Secure connection being true on server/quicsrc and false on client/quicsink still resulted in a successful connection instead of server rejecting the connection - Using secure connection with ALPN was not working Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1036>
This commit is contained in:
parent
97d8a79d36
commit
18cf5292b7
2 changed files with 100 additions and 31 deletions
|
@ -16,6 +16,7 @@ use gst::{glib, prelude::*, subclass::prelude::*};
|
||||||
use gst_base::subclass::prelude::*;
|
use gst_base::subclass::prelude::*;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use quinn::{Connection, SendStream};
|
use quinn::{Connection, SendStream};
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
static DEFAULT_SERVER_NAME: &str = "localhost";
|
static DEFAULT_SERVER_NAME: &str = "localhost";
|
||||||
|
@ -67,6 +68,7 @@ struct Settings {
|
||||||
timeout: u32,
|
timeout: u32,
|
||||||
secure_conn: bool,
|
secure_conn: bool,
|
||||||
use_datagram: bool,
|
use_datagram: bool,
|
||||||
|
certificate_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Settings {
|
impl Default for Settings {
|
||||||
|
@ -81,6 +83,7 @@ impl Default for Settings {
|
||||||
timeout: DEFAULT_TIMEOUT,
|
timeout: DEFAULT_TIMEOUT,
|
||||||
secure_conn: DEFAULT_SECURE_CONNECTION,
|
secure_conn: DEFAULT_SECURE_CONNECTION,
|
||||||
use_datagram: false,
|
use_datagram: false,
|
||||||
|
certificate_path: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,6 +134,30 @@ impl ElementImpl for QuinnQuicSink {
|
||||||
|
|
||||||
PAD_TEMPLATES.as_ref()
|
PAD_TEMPLATES.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn change_state(
|
||||||
|
&self,
|
||||||
|
transition: gst::StateChange,
|
||||||
|
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||||
|
if transition == gst::StateChange::NullToReady {
|
||||||
|
let settings = self.settings.lock().unwrap();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fail the state change if a secure connection was requested but
|
||||||
|
* no certificate path was provided.
|
||||||
|
*/
|
||||||
|
if settings.secure_conn && settings.certificate_path.is_none() {
|
||||||
|
gst::error!(
|
||||||
|
CAT,
|
||||||
|
imp: self,
|
||||||
|
"Certificate path not provided for secure connection"
|
||||||
|
);
|
||||||
|
return Err(gst::StateChangeError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.parent_change_state(transition)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for QuinnQuicSink {
|
impl ObjectImpl for QuinnQuicSink {
|
||||||
|
@ -184,6 +211,10 @@ impl ObjectImpl for QuinnQuicSink {
|
||||||
.blurb("Use certificates for QUIC connection. False: Insecure connection, True: Secure connection.")
|
.blurb("Use certificates for QUIC connection. False: Insecure connection, True: Secure connection.")
|
||||||
.default_value(DEFAULT_SECURE_CONNECTION)
|
.default_value(DEFAULT_SECURE_CONNECTION)
|
||||||
.build(),
|
.build(),
|
||||||
|
glib::ParamSpecString::builder("certificate-path")
|
||||||
|
.nick("Certificate Path")
|
||||||
|
.blurb("Path where the certificate files cert.pem and privkey.pem are stored")
|
||||||
|
.build(),
|
||||||
glib::ParamSpecBoolean::builder("use-datagram")
|
glib::ParamSpecBoolean::builder("use-datagram")
|
||||||
.nick("Use datagram")
|
.nick("Use datagram")
|
||||||
.blurb("Use datagram for lower latency, unreliable messaging")
|
.blurb("Use datagram for lower latency, unreliable messaging")
|
||||||
|
@ -233,6 +264,10 @@ impl ObjectImpl for QuinnQuicSink {
|
||||||
"secure-connection" => {
|
"secure-connection" => {
|
||||||
settings.secure_conn = value.get().expect("type checked upstream");
|
settings.secure_conn = value.get().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
"certificate-path" => {
|
||||||
|
let value: String = value.get().unwrap();
|
||||||
|
settings.certificate_path = Some(value.into());
|
||||||
|
}
|
||||||
"use-datagram" => {
|
"use-datagram" => {
|
||||||
settings.use_datagram = value.get().expect("type checked upstream");
|
settings.use_datagram = value.get().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
@ -261,6 +296,10 @@ impl ObjectImpl for QuinnQuicSink {
|
||||||
}
|
}
|
||||||
"timeout" => settings.timeout.to_value(),
|
"timeout" => settings.timeout.to_value(),
|
||||||
"secure-connection" => settings.secure_conn.to_value(),
|
"secure-connection" => settings.secure_conn.to_value(),
|
||||||
|
"certificate-path" => {
|
||||||
|
let certpath = settings.certificate_path.as_ref();
|
||||||
|
certpath.and_then(|file| file.to_str()).to_value()
|
||||||
|
}
|
||||||
"use-datagram" => settings.use_datagram.to_value(),
|
"use-datagram" => settings.use_datagram.to_value(),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
|
@ -456,6 +495,7 @@ impl QuinnQuicSink {
|
||||||
let alpns;
|
let alpns;
|
||||||
let use_datagram;
|
let use_datagram;
|
||||||
let secure_conn;
|
let secure_conn;
|
||||||
|
let cert_path;
|
||||||
|
|
||||||
{
|
{
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
|
@ -471,14 +511,16 @@ impl QuinnQuicSink {
|
||||||
alpns = settings.alpns.clone();
|
alpns = settings.alpns.clone();
|
||||||
use_datagram = settings.use_datagram;
|
use_datagram = settings.use_datagram;
|
||||||
secure_conn = settings.secure_conn;
|
secure_conn = settings.secure_conn;
|
||||||
|
cert_path = settings.certificate_path.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let endpoint = client_endpoint(client_addr, secure_conn, alpns).map_err(|err| {
|
let endpoint =
|
||||||
WaitError::FutureError(gst::error_msg!(
|
client_endpoint(client_addr, secure_conn, alpns, cert_path).map_err(|err| {
|
||||||
gst::ResourceError::Failed,
|
WaitError::FutureError(gst::error_msg!(
|
||||||
["Failed to configure endpoint: {}", err]
|
gst::ResourceError::Failed,
|
||||||
))
|
["Failed to configure endpoint: {}", err]
|
||||||
})?;
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
let connection = endpoint
|
let connection = endpoint
|
||||||
.connect(server_addr, &server_name)
|
.connect(server_addr, &server_name)
|
||||||
|
|
|
@ -136,23 +136,38 @@ impl rustls::client::ServerCertVerifier for SkipServerVerification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure_client(secure_conn: bool, alpns: Vec<String>) -> Result<ClientConfig, Box<dyn Error>> {
|
fn configure_client(
|
||||||
if secure_conn {
|
secure_conn: bool,
|
||||||
Ok(ClientConfig::with_native_roots())
|
certificate_path: Option<PathBuf>,
|
||||||
|
alpns: Vec<String>,
|
||||||
|
) -> Result<ClientConfig, Box<dyn Error>> {
|
||||||
|
let mut crypto = if secure_conn {
|
||||||
|
let (certs, key) = read_certs_from_file(certificate_path)?;
|
||||||
|
let mut cert_store = rustls::RootCertStore::empty();
|
||||||
|
|
||||||
|
for cert in &certs {
|
||||||
|
cert_store.add(cert)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
rustls::ClientConfig::builder()
|
||||||
|
.with_safe_defaults()
|
||||||
|
.with_root_certificates(Arc::new(cert_store))
|
||||||
|
.with_client_auth_cert(certs, key)?
|
||||||
} else {
|
} else {
|
||||||
let mut crypto = rustls::ClientConfig::builder()
|
rustls::ClientConfig::builder()
|
||||||
.with_safe_defaults()
|
.with_safe_defaults()
|
||||||
.with_custom_certificate_verifier(SkipServerVerification::new())
|
.with_custom_certificate_verifier(SkipServerVerification::new())
|
||||||
.with_no_client_auth();
|
.with_no_client_auth()
|
||||||
let alpn_protocols: Vec<Vec<u8>> = alpns
|
};
|
||||||
.iter()
|
|
||||||
.map(|x| x.as_bytes().to_vec())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
crypto.alpn_protocols = alpn_protocols;
|
|
||||||
crypto.key_log = Arc::new(rustls::KeyLogFile::new());
|
|
||||||
|
|
||||||
Ok(ClientConfig::new(Arc::new(crypto)))
|
let alpn_protocols: Vec<Vec<u8>> = alpns
|
||||||
}
|
.iter()
|
||||||
|
.map(|x| x.as_bytes().to_vec())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
crypto.alpn_protocols = alpn_protocols;
|
||||||
|
crypto.key_log = Arc::new(rustls::KeyLogFile::new());
|
||||||
|
|
||||||
|
Ok(ClientConfig::new(Arc::new(crypto)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_certs_from_file(
|
fn read_certs_from_file(
|
||||||
|
@ -213,8 +228,8 @@ fn configure_server(
|
||||||
certificate_path: Option<PathBuf>,
|
certificate_path: Option<PathBuf>,
|
||||||
alpns: Vec<String>,
|
alpns: Vec<String>,
|
||||||
) -> Result<(ServerConfig, Vec<rustls::Certificate>), Box<dyn Error>> {
|
) -> Result<(ServerConfig, Vec<rustls::Certificate>), Box<dyn Error>> {
|
||||||
let (cert, key) = if secure_conn {
|
let (certs, key) = if secure_conn {
|
||||||
read_certs_from_file(certificate_path).unwrap()
|
read_certs_from_file(certificate_path)?
|
||||||
} else {
|
} else {
|
||||||
let cert = rcgen::generate_simple_self_signed(vec![server_name.into()]).unwrap();
|
let cert = rcgen::generate_simple_self_signed(vec![server_name.into()]).unwrap();
|
||||||
let cert_der = cert.serialize_der().unwrap();
|
let cert_der = cert.serialize_der().unwrap();
|
||||||
|
@ -225,13 +240,24 @@ fn configure_server(
|
||||||
(cert_chain, priv_key)
|
(cert_chain, priv_key)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut crypto = rustls::ServerConfig::builder()
|
let mut crypto = if secure_conn {
|
||||||
.with_safe_default_cipher_suites()
|
let mut cert_store = rustls::RootCertStore::empty();
|
||||||
.with_safe_default_kx_groups()
|
for cert in &certs {
|
||||||
.with_protocol_versions(&[&rustls::version::TLS13])
|
cert_store.add(cert)?;
|
||||||
.unwrap()
|
}
|
||||||
.with_no_client_auth()
|
|
||||||
.with_single_cert(cert.clone(), key)?;
|
let auth_client = rustls::server::AllowAnyAuthenticatedClient::new(cert_store);
|
||||||
|
rustls::ServerConfig::builder()
|
||||||
|
.with_safe_defaults()
|
||||||
|
.with_client_cert_verifier(Arc::new(auth_client))
|
||||||
|
.with_single_cert(certs.clone(), key)
|
||||||
|
} else {
|
||||||
|
rustls::ServerConfig::builder()
|
||||||
|
.with_safe_defaults()
|
||||||
|
.with_no_client_auth()
|
||||||
|
.with_single_cert(certs.clone(), key)
|
||||||
|
}?;
|
||||||
|
|
||||||
let alpn_protocols: Vec<Vec<u8>> = alpns
|
let alpn_protocols: Vec<Vec<u8>> = alpns
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| x.as_bytes().to_vec())
|
.map(|x| x.as_bytes().to_vec())
|
||||||
|
@ -245,7 +271,7 @@ fn configure_server(
|
||||||
.max_concurrent_bidi_streams(0_u8.into())
|
.max_concurrent_bidi_streams(0_u8.into())
|
||||||
.max_concurrent_uni_streams(1_u8.into());
|
.max_concurrent_uni_streams(1_u8.into());
|
||||||
|
|
||||||
Ok((server_config, cert))
|
Ok((server_config, certs))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn server_endpoint(
|
pub fn server_endpoint(
|
||||||
|
@ -264,9 +290,10 @@ pub fn server_endpoint(
|
||||||
pub fn client_endpoint(
|
pub fn client_endpoint(
|
||||||
client_addr: SocketAddr,
|
client_addr: SocketAddr,
|
||||||
secure_conn: bool,
|
secure_conn: bool,
|
||||||
alpn: Vec<String>,
|
alpns: Vec<String>,
|
||||||
|
certificate_path: Option<PathBuf>,
|
||||||
) -> Result<Endpoint, Box<dyn Error>> {
|
) -> Result<Endpoint, Box<dyn Error>> {
|
||||||
let client_cfg = configure_client(secure_conn, alpn)?;
|
let client_cfg = configure_client(secure_conn, certificate_path, alpns)?;
|
||||||
let mut endpoint = Endpoint::client(client_addr)?;
|
let mut endpoint = Endpoint::client(client_addr)?;
|
||||||
|
|
||||||
endpoint.set_default_client_config(client_cfg);
|
endpoint.set_default_client_config(client_cfg);
|
||||||
|
|
Loading…
Reference in a new issue