Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 2 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rumqttc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
### Changed
- Migrated from deprecated `rustls-pemfile` to `rustls-pki-types` PEM parsing API
### Deprecated
### Removed
### Fixed
Expand Down
3 changes: 1 addition & 2 deletions rumqttc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[features]
default = ["use-rustls"]
use-rustls = ["use-rustls-no-provider", "tokio-rustls/default"]
use-rustls-no-provider = ["dep:tokio-rustls", "dep:rustls-webpki", "dep:rustls-pemfile", "dep:rustls-native-certs"]
use-rustls-no-provider = ["dep:tokio-rustls", "dep:rustls-webpki", "dep:rustls-native-certs"]
use-native-tls = ["dep:tokio-native-tls", "dep:native-tls"]
websocket = ["dep:async-tungstenite", "dep:ws_stream_tungstenite", "dep:http"]
proxy = ["dep:async-http-proxy"]
Expand All @@ -36,7 +36,6 @@ thiserror = "2.0.8"
# rustls
tokio-rustls = { version = "0.26.0", optional = true, default-features = false }
rustls-webpki = { version = "0.102.8", optional = true }
rustls-pemfile = { version = "2.2.0", optional = true }
rustls-native-certs = { version = "0.8.1", optional = true }
# websockets
async-tungstenite = { version = "0.29.0", default-features = false, features = ["tokio-rustls-native-certs"], optional = true }
Expand Down
42 changes: 11 additions & 31 deletions rumqttc/src/tls.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
#[cfg(feature = "use-rustls-no-provider")]
use rustls_pemfile::Item;
#[cfg(feature = "use-rustls-no-provider")]
use tokio_rustls::rustls::{
self,
pki_types::{InvalidDnsNameError, ServerName},
pki_types::{pem::PemObject, CertificateDer, InvalidDnsNameError, PrivateKeyDer, ServerName},
ClientConfig, RootCertStore,
};
#[cfg(feature = "use-rustls-no-provider")]
Expand All @@ -12,8 +10,6 @@ use tokio_rustls::TlsConnector as RustlsConnector;
#[cfg(feature = "use-rustls-no-provider")]
use std::convert::TryFrom;
#[cfg(feature = "use-rustls-no-provider")]
use std::io::{BufReader, Cursor};
#[cfg(feature = "use-rustls-no-provider")]
use std::sync::Arc;

use crate::framed::AsyncReadWrite;
Expand Down Expand Up @@ -60,6 +56,10 @@ pub enum Error {
/// No valid key found
#[error("No valid key in chain")]
NoValidKeyInChain,
#[cfg(feature = "use-rustls-no-provider")]
/// PEM parsing error
#[error("PEM parsing error: {0}")]
Pem(#[from] tokio_rustls::rustls::pki_types::pem::Error),
#[cfg(feature = "use-native-tls")]
#[error("Native TLS error {0}")]
NativeTls(#[from] NativeTlsError),
Expand All @@ -75,8 +75,8 @@ pub async fn rustls_connector(tls_config: &TlsConfiguration) -> Result<RustlsCon
} => {
// Add ca to root store if the connection is TLS
let mut root_cert_store = RootCertStore::empty();
let certs = rustls_pemfile::certs(&mut BufReader::new(Cursor::new(ca)))
.collect::<Result<Vec<_>, _>>()?;
let certs: Vec<CertificateDer> =
CertificateDer::pem_slice_iter(ca).collect::<Result<Vec<_>, _>>()?;

root_cert_store.add_parsable_certificates(certs);

Expand All @@ -88,34 +88,14 @@ pub async fn rustls_connector(tls_config: &TlsConfiguration) -> Result<RustlsCon

// Add der encoded client cert and key
let mut config = if let Some(client) = client_auth.as_ref() {
let certs =
rustls_pemfile::certs(&mut BufReader::new(Cursor::new(client.0.clone())))
.collect::<Result<Vec<_>, _>>()?;
let certs: Vec<CertificateDer> =
CertificateDer::pem_slice_iter(&client.0).collect::<Result<Vec<_>, _>>()?;

if certs.is_empty() {
return Err(Error::NoValidClientCertInChain);
}

// Create buffer for key file
let mut key_buffer = BufReader::new(Cursor::new(client.1.clone()));

// Read PEM items until we find a valid key.
let key = loop {
let item = rustls_pemfile::read_one(&mut key_buffer)?;
match item {
Some(Item::Sec1Key(key)) => {
break key.into();
}
Some(Item::Pkcs1Key(key)) => {
break key.into();
}
Some(Item::Pkcs8Key(key)) => {
break key.into();
}
None => return Err(Error::NoValidKeyInChain),
_ => {}
}
};

let key = PrivateKeyDer::from_pem_slice(&client.1)?;
config.with_client_auth_cert(certs, key)?
} else {
config.with_no_client_auth()
Expand Down
1 change: 1 addition & 0 deletions rumqttd/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
### Changed
- Migrated from deprecated `rustls-pemfile` to `rustls-pki-types` PEM parsing API
### Deprecated
### Removed
### Fixed
Expand Down
3 changes: 1 addition & 2 deletions rumqttd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ tokio-util = { version = "0.7", features = ["codec"], optional = true }
tokio-rustls = { version = "0.25.0", optional = true }
rustls-webpki = { version = "0.102.2", optional = true }
tokio-native-tls = { version = "0.3.1", optional = true }
rustls-pemfile = { version = "2.1.0", optional = true }
async-tungstenite = { version = "0.25", default-features = false, features = ["tokio-runtime"], optional = true }
ws_stream_tungstenite = { version= "0.13", default-features = false, features = ["tokio_io"], optional = true }
x509-parser = {version= "0.15.1", optional = true}
Expand All @@ -42,7 +41,7 @@ subtle = "2.5"

[features]
default = ["use-rustls", "websocket"]
use-rustls = ["dep:tokio-rustls", "dep:rustls-webpki", "dep:rustls-pemfile", "dep:x509-parser"]
use-rustls = ["dep:tokio-rustls", "dep:rustls-webpki", "dep:x509-parser"]
use-native-tls = ["dep:tokio-native-tls", "dep:x509-parser"]
websocket = ["dep:async-tungstenite", "dep:tokio-util", "dep:futures-util", "dep:ws_stream_tungstenite"]
verify-client-cert = []
Expand Down
43 changes: 18 additions & 25 deletions rumqttd/src/link/bridge.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
use flume::Sender;

#[cfg(feature = "use-rustls")]
use std::{
fs,
io::{BufReader, Cursor},
path::Path,
sync::Arc,
};
use std::{fs, path::Path, sync::Arc};

use std::{io, net::AddrParseError, time::Duration};

Expand All @@ -15,13 +10,12 @@ use tokio::{
time::{sleep, sleep_until, Instant},
};

#[cfg(feature = "use-rustls")]
use rustls_pemfile::Item;

#[cfg(feature = "use-rustls")]
use tokio_rustls::{
rustls::{
pki_types::{InvalidDnsNameError, ServerName},
pki_types::{
pem::PemObject, CertificateDer, InvalidDnsNameError, PrivateKeyDer, ServerName,
},
ClientConfig, Error as TLSError, RootCertStore,
},
TlsConnector,
Expand Down Expand Up @@ -203,9 +197,11 @@ pub async fn tls_connect<P: AsRef<Path>>(
) -> Result<Box<dyn N>, BridgeError> {
let mut root_cert_store = RootCertStore::empty();

for cert in rustls_pemfile::certs(&mut BufReader::new(Cursor::new(fs::read(ca_file)?))) {
root_cert_store.add(cert?)?;
}
let ca_pem = fs::read(ca_file)?;
let certs: Vec<CertificateDer> =
CertificateDer::pem_slice_iter(&ca_pem).collect::<Result<Vec<_>, _>>()?;

root_cert_store.add_parsable_certificates(certs);

if root_cert_store.is_empty() {
return Err(BridgeError::NoValidCertInChain);
Expand All @@ -218,18 +214,12 @@ pub async fn tls_connect<P: AsRef<Path>>(
key: key_path,
}) = client_auth_opt
{
let certs = rustls_pemfile::certs(&mut BufReader::new(Cursor::new(fs::read(certs_path)?)))
.collect::<Result<Vec<_>, _>>()?;

let key = loop {
match rustls_pemfile::read_one(&mut BufReader::new(Cursor::new(fs::read(key_path)?)))? {
Some(Item::Pkcs1Key(key)) => break key.into(),
Some(Item::Pkcs8Key(key)) => break key.into(),
Some(Item::Sec1Key(key)) => break key.into(),
None => return Err(BridgeError::NoValidCertInChain),
_ => {}
};
};
let certs_pem = fs::read(certs_path)?;
let certs: Vec<CertificateDer> =
CertificateDer::pem_slice_iter(&certs_pem).collect::<Result<Vec<_>, _>>()?;

let key_pem = fs::read(key_path)?;
let key = PrivateKeyDer::from_pem_slice(&key_pem)?;

config.with_client_auth_cert(certs, key)?
} else {
Expand Down Expand Up @@ -319,4 +309,7 @@ pub enum BridgeError {
#[cfg(feature = "use-rustls")]
#[error("Invalid trust_anchor")]
NoValidCertInChain,
#[cfg(feature = "use-rustls")]
#[error("PEM parsing error: {0}")]
Pem(#[from] tokio_rustls::rustls::pki_types::pem::Error),
}
75 changes: 24 additions & 51 deletions rumqttd/src/server/tls.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use std::fs::File;
use tokio::net::TcpStream;

#[cfg(feature = "use-native-tls")]
use {
std::io::Read, tokio_native_tls::native_tls,
std::fs::File, std::io::Read, tokio_native_tls::native_tls,
tokio_native_tls::native_tls::Error as NativeTlsError,
};

Expand All @@ -12,9 +11,11 @@ use crate::TlsConfig;
use tokio_rustls::rustls::{server::WebPkiClientVerifier, RootCertStore};
#[cfg(feature = "use-rustls")]
use {
rustls_pemfile::Item,
std::{io::BufReader, sync::Arc},
tokio_rustls::rustls::{pki_types::PrivateKeyDer, Error as RustlsError, ServerConfig},
std::sync::Arc,
tokio_rustls::rustls::{
pki_types::{pem::PemObject, CertificateDer, PrivateKeyDer},
Error as RustlsError, ServerConfig,
},
tracing::error,
};

Expand Down Expand Up @@ -57,6 +58,9 @@ pub enum Error {
MissingTenantId,
#[error("Tenant id missing in certificate")]
CertificateParse,
#[cfg(feature = "use-rustls")]
#[error("PEM parsing error: {0}")]
Pem(#[from] tokio_rustls::rustls::pki_types::pem::Error),
}

#[cfg(feature = "verify-client-cert")]
Expand Down Expand Up @@ -198,14 +202,15 @@ impl TLSAcceptor {

let (certs, key) = {
// Get certificates
let cert_file = File::open(cert_path);
let cert_file = cert_file.map_err(|_| Error::ServerCertNotFound(cert_path.clone()))?;
let certs = rustls_pemfile::certs(&mut BufReader::new(cert_file))
.collect::<Result<Vec<_>, _>>()
.map_err(|_| Error::InvalidServerCert(cert_path.to_string()))?;
let cert_pem = std::fs::read(cert_path)
.map_err(|_| Error::ServerCertNotFound(cert_path.clone()))?;
let certs: Vec<CertificateDer> =
CertificateDer::pem_slice_iter(&cert_pem).collect::<Result<Vec<_>, _>>()?;

// Get private key
let key = first_private_key_in_pemfile(key_path)?;
let key_pem =
std::fs::read(key_path).map_err(|_| Error::ServerKeyNotFound(key_path.clone()))?;
let key = PrivateKeyDer::from_pem_slice(&key_pem)?;

(certs, key)
};
Expand All @@ -215,12 +220,15 @@ impl TLSAcceptor {
// client authentication with a CA. CA isn't required otherwise
#[cfg(feature = "verify-client-cert")]
let builder = {
let ca_file = File::open(ca_path);
let ca_file = ca_file.map_err(|_| Error::CaFileNotFound(ca_path.clone()))?;
let ca_file = &mut BufReader::new(ca_file);
let ca_cert = rustls_pemfile::certs(ca_file)
let ca_pem =
std::fs::read(ca_path).map_err(|_| Error::CaFileNotFound(ca_path.clone()))?;
let ca_certs: Vec<CertificateDer> =
CertificateDer::pem_slice_iter(&ca_pem).collect::<Result<Vec<_>, _>>()?;

let ca_cert = ca_certs
.into_iter()
.next()
.ok_or_else(|| Error::InvalidCACert(ca_path.to_string()))??;
.ok_or_else(|| Error::InvalidCACert(ca_path.to_string()))?;

let mut store = RootCertStore::empty();
store
Expand All @@ -245,38 +253,3 @@ impl TLSAcceptor {
Ok(TLSAcceptor::Rustls { acceptor })
}
}

#[cfg(feature = "use-rustls")]
/// Get the first private key in a PEM file
fn first_private_key_in_pemfile(key_path: &String) -> Result<PrivateKeyDer<'static>, Error> {
// Get private key
let key_file = File::open(key_path);
let key_file = key_file.map_err(|_| Error::ServerKeyNotFound(key_path.clone()))?;

let rd = &mut BufReader::new(key_file);

// keep reading Items one by one to find a Key, return error if none found.
loop {
let item = rustls_pemfile::read_one(rd).map_err(|err| {
error!("Error reading key file: {:?}", err);
Error::InvalidServerKey(key_path.clone())
})?;

match item {
Some(Item::Sec1Key(key)) => {
return Ok(key.into());
}
Some(Item::Pkcs1Key(key)) => {
return Ok(key.into());
}
Some(Item::Pkcs8Key(key)) => {
return Ok(key.into());
}
None => {
error!("No private key found in {:?}", key_path);
return Err(Error::InvalidServerKey(key_path.clone()));
}
_ => {}
}
}
}