Skip to content

Commit

Permalink
Merge pull request #418 from nworbnhoj/chat
Browse files Browse the repository at this point in the history
integrate tls trusted root_store into mtxchat
  • Loading branch information
bunnie authored Sep 26, 2023
2 parents e6c6145 + a89a344 commit 7e20460
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 16 deletions.
3 changes: 0 additions & 3 deletions apps/mtxchat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ Use another device such as a mobile or PC to:
* https://spec.matrix.org/latest/#room-structure
* Join the room

On Precursor, make sure to trust a tls certificate offered by the matrix server.
* shellchat `net tls probe matrix.org` and then `sleep coldboot`


## Functionality

Expand Down
5 changes: 2 additions & 3 deletions apps/mtxchat/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::fmt::Write as _;
use std::io::{Error, ErrorKind, Read, Write as StdWrite};
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use tls::Tls;
use tls::xtls::TlsConnector;
use trng::*;
use ureq::Agent;
use url::Url;
Expand Down Expand Up @@ -91,7 +91,6 @@ impl<'a> MtxChat<'a> {
let trng = Trng::new(&xns).unwrap();
let pddb = pddb::Pddb::new();
pddb.try_mount();
let tls = Tls::new();
MtxChat {
chat: chat,
trng: trng,
Expand All @@ -101,7 +100,7 @@ impl<'a> MtxChat<'a> {
user_name: None,
user_domain: Some(DOMAIN_MATRIX.to_string()),
agent: ureq::builder()
.tls_config(Arc::new(tls.client_config()))
.tls_connector(Arc::new(TlsConnector{}))
.build(),
token: None,
logged_in: false,
Expand Down
5 changes: 2 additions & 3 deletions apps/mtxchat/src/listen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{get_username, web, MTX_LONG_TIMEOUT};
use chat::ChatOp;
use modals::Modals;
use std::sync::Arc;
use tls::Tls;
use tls::xtls::TlsConnector;
use url::Url;
use xous::CID;
use xous_ipc::Buffer;
Expand All @@ -20,9 +20,8 @@ pub fn listen(
let modals = Modals::new(&xns).expect("can't connect to Modals server");
log::info!("client_sync for {} ms...", MTX_LONG_TIMEOUT);

let tls = Tls::new();
let mut agent = ureq::builder()
.tls_config(Arc::new(tls.client_config()))
.tls_connector(Arc::new(TlsConnector{}))
.build();
if let Some((_since, events)) = web::client_sync(
url,
Expand Down
12 changes: 7 additions & 5 deletions apps/mtxchat/src/web.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::Msg;
use serde::{Deserialize, Serialize};
use ureq::serde_json::{Map, Value};
use ureq::Agent;
use ureq::{Agent, ErrorKind};
use url::Url;

const ACCEPT: &str = "Accept";
Expand Down Expand Up @@ -45,14 +45,16 @@ pub fn handle_response(maybe_response: Result<ureq::Response, ureq::Error>) -> O
}
}
Err(ureq::Error::Status(code, response)) => {
/* the server returned an unexpected status
code (such as 400, 500 etc) */
// the server returned an unexpected status code (such as 400, 500 etc)
let err_body = response.into_string().unwrap();
log::info!("ERROR code {} err_body = {}", code, err_body);
None
}
Err(e) => {
log::info!("ERROR in handle_response: {:?}", e);
Err(ureq::Error::Transport(kind)) => {
match kind.kind() {
ErrorKind::ConnectionFailed => log::warn!("TLS failure"),
_ => log::info!("Transport error: {:?}", kind),
};
None
}
}
Expand Down
1 change: 1 addition & 0 deletions libs/tls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ sha2 = "0.9.8"

# note requirement for patch to xous-ring in workspace Cargo.toml
rustls = { version = "0.21.7", features = ["dangerous_configuration"] }
ureq = "2.7.1"
webpki = { package = "rustls-webpki", version = "0.101.4" }
webpki-roots = {version = "0.24.0", optional = true}
x509-parser = "0.15.0"
Expand Down
5 changes: 3 additions & 2 deletions libs/tls/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod cmd;
mod danger;
pub mod rota;
pub mod xtls;

use crate::rota::RustlsOwnedTrustAnchor;
use locales::t;
Expand All @@ -13,9 +14,9 @@ use rkyv::{
use rustls::{Certificate, ClientConfig, RootCertStore};
use sha2::Digest;
use std::convert::{Into, TryFrom, TryInto};
use std::io::{Error, Read, Write, ErrorKind};
use std::sync::Arc;
use std::io::{Error, ErrorKind, Read, Write};
use std::net::TcpStream;
use std::sync::Arc;
use x509_parser::prelude::{FromDer, X509Certificate};
use xous_names::XousNames;

Expand Down
117 changes: 117 additions & 0 deletions libs/tls/src/xtls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use crate::Tls;
use rustls::{ClientConnection, StreamOwned};
use std::{convert::TryInto, fmt::Debug, io, net::TcpStream, result::Result, sync::Arc};
use ureq::{ReadWrite, Response};

pub struct TlsConnector {}

/// Set up tls with rustls::ClientConnection,
/// BUT - on Error::InvalidCertificate - then
/// probe the host for the untrusted certificate chain and prompt the user
/// to perhaps trust one of the certificates in the chain - then try again.
impl ureq::TlsConnector for TlsConnector {
fn connect(
&self,
dns_name: &str,
mut io: Box<dyn ReadWrite>,
) -> Result<Box<dyn ReadWrite>, ureq::Error> {
log::info!("Commencing tls connection setup");
loop {
// refresh rustls client config with current root_store
let tls = Tls::new();
let config = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(tls.root_store())
.with_no_client_auth();
match rustls::ClientConnection::new(Arc::new(config), dns_name.try_into().unwrap()) {
Ok(mut connection) => {
log::info!("tls handshake started");
match connection.complete_io(&mut io) {
Ok(_) => {
if connection.peer_certificates().is_some() {
return Ok(Box::new(TlsStream(StreamOwned::new(connection, io))));
}
}
// errors generated late in the tls handshake
Err(e) => {
if let Some(inner) = e.get_ref() {
if let Some(rustls_error) = inner.downcast_ref::<rustls::Error>() {
if let rustls::Error::InvalidCertificate(_) = rustls_error {
if let Ok(n) = tls.probe(dns_name) {
if n > 0 {
log::info!("try again with new trusted certs");
continue;
}
}
}
}
}
// non certificate chain errors
log::warn!("{e}");
break;
}
}
}
// errors generated early in the tls handshake
Err(rustls::Error::InvalidCertificate(_)) => {
if let Ok(n) = tls.probe(dns_name) {
if n > 0 {
log::info!("try again with new trusted certs");
continue;
}
}
}
// non certificate chain errors
Err(e) => {
log::warn!("{e}");
break;
}
}
}
log::warn!("failed to establish tls connection");
// this would be better as a ureq:Error::Transport but they are hard to build
Err(ureq::Error::Status(
526,
Response::new(526, "tls", "untrusted certificate chain").unwrap(),
))
}
}

// TlsStream wraps StreamOwned and implements ReadWrite for use in TlsConnect::connect()
#[derive(Debug)]
pub struct TlsStream(StreamOwned<ClientConnection, Box<dyn ReadWrite>>);

impl TlsStream {
/// Returns a shared reference to the inner stream.
pub fn get_ref(&self) -> &Box<dyn ReadWrite> {
self.0.get_ref()
}

/// Returns a mutable reference to the inner stream.
pub fn get_mut(&mut self) -> &mut Box<dyn ReadWrite> {
self.0.get_mut()
}
}

impl io::Read for TlsStream {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
Ok(self.0.read(buf)?)
}
}

impl io::Write for TlsStream {
fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
Ok(self.0.write(buf)?)
}

fn flush(&mut self) -> Result<(), std::io::Error> {
Ok(self.0.flush()?)
}
}

// required for the return type in TlsConnect::connect()
impl ReadWrite for TlsStream {
fn socket(&self) -> Option<&TcpStream> {
self.get_ref().socket()
}
}

0 comments on commit 7e20460

Please sign in to comment.