diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index 1e1d489..0ab070b 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -75,8 +75,7 @@ rustix = {workspace = true} [features] experiment_memfd_secret = ["rosenpass-wireguard-broker/experiment_memfd_secret"] -experiment_broker_api = ["rosenpass-wireguard-broker/experiment_broker_api", "command-fds"] experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux"] -experiment_api = ["hex-literal", "uds", "rosenpass-util/experiment_file_descriptor_passing"] +experiment_api = ["hex-literal", "uds", "command-fds", "rosenpass-util/experiment_file_descriptor_passing", "rosenpass-wireguard-broker/experiment_api"] internal_testing = [] internal_bin_gen_ipc_msg_types = ["hex", "heck"] diff --git a/rosenpass/src/api/api_handler.rs b/rosenpass/src/api/api_handler.rs index d3088b2..24f036b 100644 --- a/rosenpass/src/api/api_handler.rs +++ b/rosenpass/src/api/api_handler.rs @@ -3,11 +3,19 @@ use std::{borrow::BorrowMut, collections::VecDeque, os::fd::OwnedFd}; use anyhow::Context; use rosenpass_to::{ops::copy_slice, To}; use rosenpass_util::{ - fd::FdIo, functional::run, io::ReadExt, mem::DiscardResultExt, result::OkExt, + fd::FdIo, + functional::{run, ApplyExt}, + io::ReadExt, + mem::DiscardResultExt, + mio::UnixStreamExt, + result::OkExt, }; +use rosenpass_wireguard_broker::brokers::mio_client::MioBrokerClient; use crate::{ - api::add_listen_socket_response_status, app_server::AppServer, protocol::BuildCryptoServer, + api::{add_listen_socket_response_status, add_psk_broker_response_status}, + app_server::AppServer, + protocol::BuildCryptoServer, }; use super::{supply_keypair_response_status, Server as ApiServer}; @@ -225,4 +233,72 @@ where res.payload.status = add_listen_socket_response_status::OK; Ok(()) } + + fn add_psk_broker( + &mut self, + _req: &super::boilerplate::AddPskBrokerRequest, + req_fds: &mut VecDeque, + res: &mut super::boilerplate::AddPskBrokerResponse, + ) -> anyhow::Result<()> { + // Retrieve file descriptor + let sock_res = run(|| { + let sock = req_fds + .pop_front() + .context("Invalid request – socket missing.")?; + mio::net::UnixStream::from_fd(sock) + }); + + // Handle errors + let sock = match sock_res { + Ok(sock) => sock, + Err(e) => { + log::debug!( + "Request found to be invalid while processing AddPskBroker API request: {e:?}" + ); + res.payload.status = add_psk_broker_response_status::INVALID_REQUEST; + return Ok(()); + } + }; + + // Register Socket + let client = Box::new(MioBrokerClient::new(sock)); + + // Workaround: The broker code is currently impressively overcomplicated. Brokers are + // stored in a hash map but the hash map key used is just a counter so a vector could + // have been used. Broker configuration is abstracted, different peers can have different + // brokers but there is no facility to add multiple brokers in practice. The broker index + // uses a `Public` wrapper without actually holding any cryptographic data. Even the broker + // configuration uses a trait abstraction for no discernible reason and a lot of the code + // introduces pointless, single-field wrapper structs. + // We should use an implement-what-is-actually-needed strategy next time. + // The Broker code needs to be slimmed down, the right direction to go is probably to + // just add event and capability support to the API and use the API to deliver OSK events. + // + // For now, we just replace the latest broker. + let erase_ptr = { + use crate::app_server::BrokerStorePtr; + // + use rosenpass_secret_memory::Public; + use zerocopy::AsBytes; + (self.app_server().brokers.store.len() - 1) + .apply(|x| x as u64) + .apply(|x| Public::from_slice(x.as_bytes())) + .apply(BrokerStorePtr) + }; + + let register_result = run(|| { + let srv = self.app_server_mut(); + srv.unregister_broker(erase_ptr)?; + srv.register_broker(client) + }); + + if let Err(e) = register_result { + log::warn!("Internal error while processing AddPskBroker API request: {e:?}"); + res.payload.status = add_psk_broker_response_status::INTERNAL_ERROR; + return Ok(()); + } + + res.payload.status = add_psk_broker_response_status::OK; + Ok(()) + } } diff --git a/rosenpass/src/api/boilerplate/byte_slice_ext.rs b/rosenpass/src/api/boilerplate/byte_slice_ext.rs index de2ff31..81d3a70 100644 --- a/rosenpass/src/api/boilerplate/byte_slice_ext.rs +++ b/rosenpass/src/api/boilerplate/byte_slice_ext.rs @@ -181,6 +181,42 @@ pub trait ByteSliceRefExt: ByteSlice { ) -> anyhow::Result> { self.zk_parse_suffix() } + + fn add_psk_broker_request(self) -> anyhow::Result> { + self.zk_parse() + } + + fn add_psk_broker_request_from_prefix( + self, + ) -> anyhow::Result> { + self.zk_parse_prefix() + } + + fn add_psk_broker_request_from_suffix( + self, + ) -> anyhow::Result> { + self.zk_parse_suffix() + } + + fn add_psk_broker_response_maker(self) -> RefMaker { + self.zk_ref_maker() + } + + fn add_psk_broker_response(self) -> anyhow::Result> { + self.zk_parse() + } + + fn add_psk_broker_response_from_prefix( + self, + ) -> anyhow::Result> { + self.zk_parse_prefix() + } + + fn add_psk_broker_response_from_suffix( + self, + ) -> anyhow::Result> { + self.zk_parse_suffix() + } } impl ByteSliceRefExt for B {} diff --git a/rosenpass/src/api/boilerplate/message_type.rs b/rosenpass/src/api/boilerplate/message_type.rs index e76a54d..ebc0b85 100644 --- a/rosenpass/src/api/boilerplate/message_type.rs +++ b/rosenpass/src/api/boilerplate/message_type.rs @@ -28,6 +28,13 @@ const ADD_LISTEN_SOCKET_REQUEST: RawMsgType = const ADD_LISTEN_SOCKET_RESPONSE: RawMsgType = RawMsgType::from_le_bytes(hex!("45d5 0f0d 93f0 6105 98f2 9469 5dfd 5f36")); +// hash domain hash of: Rosenpass IPC API -> Rosenpass Protocol Server -> Add Psk Broker Request +const ADD_PSK_BROKER_REQUEST: RawMsgType = + RawMsgType::from_le_bytes(hex!("d798 b8dc bd61 5cab 8df1 c63d e4eb a2d1")); +// hash domain hash of: Rosenpass IPC API -> Rosenpass Protocol Server -> Add Psk Broker Response +const ADD_PSK_BROKER_RESPONSE: RawMsgType = + RawMsgType::from_le_bytes(hex!("bd25 e418 ffb0 6930 248b 217e 2fae e353")); + pub trait MessageAttributes { fn message_size(&self) -> usize; } @@ -37,6 +44,7 @@ pub enum RequestMsgType { Ping, SupplyKeypair, AddListenSocket, + AddPskBroker, } #[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] @@ -44,6 +52,7 @@ pub enum ResponseMsgType { Ping, SupplyKeypair, AddListenSocket, + AddPskBroker, } impl MessageAttributes for RequestMsgType { @@ -52,6 +61,7 @@ impl MessageAttributes for RequestMsgType { Self::Ping => std::mem::size_of::(), Self::SupplyKeypair => std::mem::size_of::(), Self::AddListenSocket => std::mem::size_of::(), + Self::AddPskBroker => std::mem::size_of::(), } } } @@ -62,6 +72,7 @@ impl MessageAttributes for ResponseMsgType { Self::Ping => std::mem::size_of::(), Self::SupplyKeypair => std::mem::size_of::(), Self::AddListenSocket => std::mem::size_of::(), + Self::AddPskBroker => std::mem::size_of::(), } } } @@ -75,6 +86,7 @@ impl TryFrom for RequestMsgType { self::PING_REQUEST => E::Ping, self::SUPPLY_KEYPAIR_REQUEST => E::SupplyKeypair, self::ADD_LISTEN_SOCKET_REQUEST => E::AddListenSocket, + self::ADD_PSK_BROKER_REQUEST => E::AddPskBroker, _ => return Err(InvalidApiMessageType(value)), }) } @@ -87,6 +99,7 @@ impl From for RawMsgType { E::Ping => self::PING_REQUEST, E::SupplyKeypair => self::SUPPLY_KEYPAIR_REQUEST, E::AddListenSocket => self::ADD_LISTEN_SOCKET_REQUEST, + E::AddPskBroker => self::ADD_PSK_BROKER_REQUEST, } } } @@ -100,6 +113,7 @@ impl TryFrom for ResponseMsgType { self::PING_RESPONSE => E::Ping, self::SUPPLY_KEYPAIR_RESPONSE => E::SupplyKeypair, self::ADD_LISTEN_SOCKET_RESPONSE => E::AddListenSocket, + self::ADD_PSK_BROKER_RESPONSE => E::AddPskBroker, _ => return Err(InvalidApiMessageType(value)), }) } @@ -112,6 +126,7 @@ impl From for RawMsgType { E::Ping => self::PING_RESPONSE, E::SupplyKeypair => self::SUPPLY_KEYPAIR_RESPONSE, E::AddListenSocket => self::ADD_LISTEN_SOCKET_RESPONSE, + E::AddPskBroker => self::ADD_PSK_BROKER_RESPONSE, } } } diff --git a/rosenpass/src/api/boilerplate/payload.rs b/rosenpass/src/api/boilerplate/payload.rs index a55fb8d..a2980fd 100644 --- a/rosenpass/src/api/boilerplate/payload.rs +++ b/rosenpass/src/api/boilerplate/payload.rs @@ -265,3 +265,87 @@ impl Message for AddListenSocketResponse { self.msg_type = Self::MESSAGE_TYPE.into(); } } + +#[repr(packed)] +#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)] +pub struct AddPskBrokerRequestPayload {} + +pub type AddPskBrokerRequest = RequestEnvelope; + +impl Default for AddPskBrokerRequest { + fn default() -> Self { + Self::new() + } +} + +impl AddPskBrokerRequest { + pub fn new() -> Self { + Self::from_payload(AddPskBrokerRequestPayload {}) + } +} + +impl Message for AddPskBrokerRequest { + type Payload = AddPskBrokerRequestPayload; + type MessageClass = RequestMsgType; + const MESSAGE_TYPE: Self::MessageClass = RequestMsgType::AddPskBroker; + + fn from_payload(payload: Self::Payload) -> Self { + Self { + msg_type: Self::MESSAGE_TYPE.into(), + payload, + } + } + + fn setup(buf: B) -> anyhow::Result> { + let mut r: Ref = buf.zk_zeroized()?; + r.init(); + Ok(r) + } + + fn init(&mut self) { + self.msg_type = Self::MESSAGE_TYPE.into(); + } +} + +pub mod add_psk_broker_response_status { + pub const OK: u128 = 0; + pub const INVALID_REQUEST: u128 = 1; + pub const INTERNAL_ERROR: u128 = 2; +} + +#[repr(packed)] +#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)] +pub struct AddPskBrokerResponsePayload { + pub status: u128, +} + +pub type AddPskBrokerResponse = ResponseEnvelope; + +impl AddPskBrokerResponse { + pub fn new(status: u128) -> Self { + Self::from_payload(AddPskBrokerResponsePayload { status }) + } +} + +impl Message for AddPskBrokerResponse { + type Payload = AddPskBrokerResponsePayload; + type MessageClass = ResponseMsgType; + const MESSAGE_TYPE: Self::MessageClass = ResponseMsgType::AddPskBroker; + + fn from_payload(payload: Self::Payload) -> Self { + Self { + msg_type: Self::MESSAGE_TYPE.into(), + payload, + } + } + + fn setup(buf: B) -> anyhow::Result> { + let mut r: Ref = buf.zk_zeroized()?; + r.init(); + Ok(r) + } + + fn init(&mut self) { + self.msg_type = Self::MESSAGE_TYPE.into(); + } +} diff --git a/rosenpass/src/api/boilerplate/request_ref.rs b/rosenpass/src/api/boilerplate/request_ref.rs index 3161e8c..ef7b84f 100644 --- a/rosenpass/src/api/boilerplate/request_ref.rs +++ b/rosenpass/src/api/boilerplate/request_ref.rs @@ -27,6 +27,7 @@ impl RequestRef { Self::Ping(_) => RequestMsgType::Ping, Self::SupplyKeypair(_) => RequestMsgType::SupplyKeypair, Self::AddListenSocket(_) => RequestMsgType::AddListenSocket, + Self::AddPskBroker(_) => RequestMsgType::AddPskBroker, } } } @@ -49,6 +50,12 @@ impl From> for RequestRef { } } +impl From> for RequestRef { + fn from(v: Ref) -> Self { + Self::AddPskBroker(v) + } +} + impl RequestRefMaker { fn new(buf: B) -> anyhow::Result { let msg_type = buf.deref().request_msg_type_from_prefix()?; @@ -68,6 +75,9 @@ impl RequestRefMaker { RequestMsgType::AddListenSocket => { RequestRef::AddListenSocket(self.buf.add_listen_socket_request()?) } + RequestMsgType::AddPskBroker => { + RequestRef::AddPskBroker(self.buf.add_psk_broker_request()?) + } }) } @@ -104,6 +114,7 @@ pub enum RequestRef { Ping(Ref), SupplyKeypair(Ref), AddListenSocket(Ref), + AddPskBroker(Ref), } impl RequestRef @@ -115,6 +126,7 @@ where Self::Ping(r) => r.bytes(), Self::SupplyKeypair(r) => r.bytes(), Self::AddListenSocket(r) => r.bytes(), + Self::AddPskBroker(r) => r.bytes(), } } } @@ -128,6 +140,7 @@ where Self::Ping(r) => r.bytes_mut(), Self::SupplyKeypair(r) => r.bytes_mut(), Self::AddListenSocket(r) => r.bytes_mut(), + Self::AddPskBroker(r) => r.bytes_mut(), } } } diff --git a/rosenpass/src/api/boilerplate/request_response.rs b/rosenpass/src/api/boilerplate/request_response.rs index 148c51d..63082ac 100644 --- a/rosenpass/src/api/boilerplate/request_response.rs +++ b/rosenpass/src/api/boilerplate/request_response.rs @@ -58,6 +58,14 @@ impl ResponseMsg for super::AddListenSocketResponse { type RequestMsg = super::AddListenSocketRequest; } +impl RequestMsg for super::AddPskBrokerRequest { + type ResponseMsg = super::AddPskBrokerResponse; +} + +impl ResponseMsg for super::AddPskBrokerResponse { + type RequestMsg = super::AddPskBrokerRequest; +} + pub type PingPair = (Ref, Ref); pub type SupplyKeypairPair = ( Ref, @@ -67,11 +75,16 @@ pub type AddListenSocketPair = ( Ref, Ref, ); +pub type AddPskBrokerPair = ( + Ref, + Ref, +); pub enum RequestResponsePair { Ping(PingPair), SupplyKeypair(SupplyKeypairPair), AddListenSocket(AddListenSocketPair), + AddPskBroker(AddPskBrokerPair), } impl From> for RequestResponsePair { @@ -92,6 +105,12 @@ impl From> for RequestResponsePair { } } +impl From> for RequestResponsePair { + fn from(v: AddPskBrokerPair) -> Self { + RequestResponsePair::AddPskBroker(v) + } +} + impl RequestResponsePair where B1: ByteSlice, @@ -114,6 +133,11 @@ where let res = ResponseRef::AddListenSocket(res.emancipate()); (req, res) } + Self::AddPskBroker((req, res)) => { + let req = RequestRef::AddPskBroker(req.emancipate()); + let res = ResponseRef::AddPskBroker(res.emancipate()); + (req, res) + } } } @@ -148,6 +172,11 @@ where let res = ResponseRef::AddListenSocket(res.emancipate_mut()); (req, res) } + Self::AddPskBroker((req, res)) => { + let req = RequestRef::AddPskBroker(req.emancipate_mut()); + let res = ResponseRef::AddPskBroker(res.emancipate_mut()); + (req, res) + } } } diff --git a/rosenpass/src/api/boilerplate/response_ref.rs b/rosenpass/src/api/boilerplate/response_ref.rs index 32b6307..b414aee 100644 --- a/rosenpass/src/api/boilerplate/response_ref.rs +++ b/rosenpass/src/api/boilerplate/response_ref.rs @@ -28,6 +28,7 @@ impl ResponseRef { Self::Ping(_) => ResponseMsgType::Ping, Self::SupplyKeypair(_) => ResponseMsgType::SupplyKeypair, Self::AddListenSocket(_) => ResponseMsgType::AddListenSocket, + Self::AddPskBroker(_) => ResponseMsgType::AddPskBroker, } } } @@ -50,6 +51,12 @@ impl From> for ResponseRef { } } +impl From> for ResponseRef { + fn from(v: Ref) -> Self { + Self::AddPskBroker(v) + } +} + impl ResponseRefMaker { fn new(buf: B) -> anyhow::Result { let msg_type = buf.deref().response_msg_type_from_prefix()?; @@ -69,6 +76,9 @@ impl ResponseRefMaker { ResponseMsgType::AddListenSocket => { ResponseRef::AddListenSocket(self.buf.add_listen_socket_response()?) } + ResponseMsgType::AddPskBroker => { + ResponseRef::AddPskBroker(self.buf.add_psk_broker_response()?) + } }) } @@ -105,6 +115,7 @@ pub enum ResponseRef { Ping(Ref), SupplyKeypair(Ref), AddListenSocket(Ref), + AddPskBroker(Ref), } impl ResponseRef @@ -116,6 +127,7 @@ where Self::Ping(r) => r.bytes(), Self::SupplyKeypair(r) => r.bytes(), Self::AddListenSocket(r) => r.bytes(), + Self::AddPskBroker(r) => r.bytes(), } } } @@ -129,6 +141,7 @@ where Self::Ping(r) => r.bytes_mut(), Self::SupplyKeypair(r) => r.bytes_mut(), Self::AddListenSocket(r) => r.bytes_mut(), + Self::AddPskBroker(r) => r.bytes_mut(), } } } diff --git a/rosenpass/src/api/boilerplate/server.rs b/rosenpass/src/api/boilerplate/server.rs index cb033c4..df6d398 100644 --- a/rosenpass/src/api/boilerplate/server.rs +++ b/rosenpass/src/api/boilerplate/server.rs @@ -24,6 +24,13 @@ pub trait Server { res: &mut super::AddListenSocketResponse, ) -> anyhow::Result<()>; + fn add_psk_broker( + &mut self, + req: &super::AddPskBrokerRequest, + req_fds: &mut VecDeque, + res: &mut super::AddPskBrokerResponse, + ) -> anyhow::Result<()>; + fn dispatch( &mut self, p: &mut RequestResponsePair, @@ -41,6 +48,7 @@ pub trait Server { RequestResponsePair::AddListenSocket((req, res)) => { self.add_listen_socket(req, req_fds, res) } + RequestResponsePair::AddPskBroker((req, res)) => self.add_psk_broker(req, req_fds, res), } } @@ -72,6 +80,11 @@ pub trait Server { res.init(); RequestResponsePair::AddListenSocket((req, res)) } + RequestRef::AddPskBroker(req) => { + let mut res = res.add_psk_broker_response_from_prefix()?; + res.init(); + RequestResponsePair::AddPskBroker((req, res)) + } }; self.dispatch(&mut pair, req_fds)?; diff --git a/rosenpass/src/api/mio/connection.rs b/rosenpass/src/api/mio/connection.rs index 1697d25..6753b76 100644 --- a/rosenpass/src/api/mio/connection.rs +++ b/rosenpass/src/api/mio/connection.rs @@ -130,6 +130,7 @@ pub trait MioConnectionContext { bufs.write_buffer .restart_write_with_new_message(response_len)?; bufs.read_buffer.zeroize(); // clear for new message to read + bufs.read_fd_buffer.clear(); Ok(Some(())) }) diff --git a/rosenpass/src/app_server.rs b/rosenpass/src/app_server.rs index 9b5a5e8..77526d8 100644 --- a/rosenpass/src/app_server.rs +++ b/rosenpass/src/app_server.rs @@ -77,7 +77,7 @@ impl MioTokenDispenser { #[derive(Debug, Default)] pub struct BrokerStore { - store: HashMap< + pub store: HashMap< Public, Box>, >, diff --git a/rosenpass/src/bin/gen-ipc-msg-types.rs b/rosenpass/src/bin/gen-ipc-msg-types.rs index 22eef47..161968f 100644 --- a/rosenpass/src/bin/gen-ipc-msg-types.rs +++ b/rosenpass/src/bin/gen-ipc-msg-types.rs @@ -80,6 +80,8 @@ fn main() -> Result<()> { Tree::Leaf("Supply Keypair Response".to_owned()), Tree::Leaf("Add Listen Socket Request".to_owned()), Tree::Leaf("Add Listen Socket Response".to_owned()), + Tree::Leaf("Add Psk Broker Request".to_owned()), + Tree::Leaf("Add Psk Broker Response".to_owned()), ], )], ); diff --git a/rosenpass/src/cli.rs b/rosenpass/src/cli.rs index f9dbb88..b698c79 100644 --- a/rosenpass/src/cli.rs +++ b/rosenpass/src/cli.rs @@ -16,7 +16,7 @@ use crate::protocol::{SPk, SSk, SymKey}; use super::config; -#[cfg(feature = "experiment_broker_api")] +#[cfg(feature = "experiment_api")] use { command_fds::{CommandFdExt, FdMapping}, log::{error, info}, @@ -60,7 +60,7 @@ pub struct CliArgs { api: crate::api::cli::ApiCli, /// path of the wireguard_psk broker socket to connect to - #[cfg(feature = "experiment_broker_api")] + #[cfg(feature = "experiment_api")] #[arg(long, group = "psk-broker-specs")] psk_broker_path: Option, @@ -70,12 +70,12 @@ pub struct CliArgs { /// Unix socket for the psk broker connection to use themselves, passing it to this process -- /// in Rust this can be achieved using the /// [command-fds](https://docs.rs/command-fds/latest/command_fds/) crate - #[cfg(feature = "experiment_broker_api")] + #[cfg(feature = "experiment_api")] #[arg(long, group = "psk-broker-specs")] psk_broker_fd: Option, /// spawn a psk broker locally using a socket pair - #[cfg(feature = "experiment_broker_api")] + #[cfg(feature = "experiment_api")] #[arg(short, long, group = "psk-broker-specs")] psk_broker_spawn: bool, @@ -109,9 +109,9 @@ impl CliArgs { None } - #[cfg(feature = "experiment_broker_api")] + #[cfg(feature = "experiment_api")] /// returns the broker interface set by CLI args - /// returns `None` if the `experiment_broker_api` feature isn't enabled + /// returns `None` if the `experiment_api` feature isn't enabled pub fn get_broker_interface(&self) -> Option { if let Some(path_ref) = self.psk_broker_path.as_ref() { Some(BrokerInterface::Socket(path_ref.to_path_buf())) @@ -124,9 +124,9 @@ impl CliArgs { } } - #[cfg(not(feature = "experiment_broker_api"))] + #[cfg(not(feature = "experiment_api"))] /// returns the broker interface set by CLI args - /// returns `None` if the `experiment_broker_api` feature isn't enabled + /// returns `None` if the `experiment_api` feature isn't enabled pub fn get_broker_interface(&self) -> Option { None } @@ -458,7 +458,7 @@ impl CliArgs { srv.event_loop() } - #[cfg(feature = "experiment_broker_api")] + #[cfg(feature = "experiment_api")] fn create_broker( broker_interface: Option, ) -> Result< @@ -473,14 +473,14 @@ impl CliArgs { } } - #[cfg(not(feature = "experiment_broker_api"))] + #[cfg(not(feature = "experiment_api"))] fn create_broker( _broker_interface: Option, ) -> Result, anyhow::Error> { Ok(Box::new(NativeUnixBroker::new())) } - #[cfg(feature = "experiment_broker_api")] + #[cfg(feature = "experiment_api")] fn get_broker_socket(broker_interface: BrokerInterface) -> Result { // Connect to the psk broker unix socket if one was specified // OR OTHERWISE spawn the psk broker and use socketpair(2) to connect with them diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index 861c83c..d06b8e1 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -21,7 +21,7 @@ use serde::{Deserialize, Serialize}; use crate::app_server::AppServer; -#[cfg(feature = "experiment_broker_api")] +#[cfg(feature = "experiment_api")] fn empty_api_config() -> crate::api::config::ApiConfig { crate::api::config::ApiConfig { listen_path: Vec::new(), diff --git a/rosenpass/tests/api-integration-tests-api-setup.rs b/rosenpass/tests/api-integration-tests-api-setup.rs index 97e7cfb..6c6dcc7 100644 --- a/rosenpass/tests/api-integration-tests-api-setup.rs +++ b/rosenpass/tests/api-integration-tests-api-setup.rs @@ -7,8 +7,13 @@ use std::{ }; use anyhow::{bail, Context}; -use rosenpass::api::{self, add_listen_socket_response_status, supply_keypair_response_status}; +use hex_literal::hex; +use rosenpass::api::{ + self, add_listen_socket_response_status, add_psk_broker_response_status, + supply_keypair_response_status, +}; use rosenpass_util::{ + b64::B64Display, file::LoadValueB64, length_prefix_encoding::{decoder::LengthPrefixDecoder, encoder::LengthPrefixEncoder}, mio::WriteWithFileDescriptors, @@ -35,15 +40,20 @@ fn api_integration_api_setup() -> anyhow::Result<()> { } let peer_a_endpoint = "[::1]:0"; - let peer_a_osk = tempfile!("a.osk"); - let peer_b_osk = tempfile!("b.osk"); - let peer_a_listen = std::net::UdpSocket::bind(peer_a_endpoint)?; let peer_a_endpoint = format!("{}", peer_a_listen.local_addr()?); + let peer_a_keypair = config::Keypair::new(tempfile!("a.pk"), tempfile!("a.sk")); + + let peer_b_osk = tempfile!("b.osk"); + let peer_b_wg_device = "mock_device"; + let peer_b_wg_peer_id = hex!( + " + 93 0f ee 77 0c 6b 54 7e 13 5f 13 92 21 97 26 53 + 7d 77 4a 6a 0f 6c eb 1a dd 6e 5b c4 1b 92 cd 99 + " + ); use rosenpass::config; - - let peer_a_keypair = config::Keypair::new(tempfile!("a.pk"), tempfile!("a.sk")); let peer_a = config::Rosenpass { config_file_path: tempfile!("a.config"), keypair: None, @@ -56,10 +66,14 @@ fn api_integration_api_setup() -> anyhow::Result<()> { }, peers: vec![config::RosenpassPeer { public_key: tempfile!("b.pk"), - key_out: Some(peer_a_osk.clone()), + key_out: None, endpoint: None, pre_shared_key: None, - wg: None, + wg: Some(config::WireGuard { + device: peer_b_wg_device.to_string(), + peer: format!("{}", peer_b_wg_peer_id.fmt_b64::<8129>()), + extra_params: vec![], + }), }], }; @@ -98,13 +112,13 @@ fn api_integration_api_setup() -> anyhow::Result<()> { peer_b.commit()?; // Start peer a - let proc_a = std::process::Command::new(env!("CARGO_BIN_EXE_rosenpass")) + let _proc_a = std::process::Command::new(env!("CARGO_BIN_EXE_rosenpass")) .args([ "exchange-config", peer_a.config_file_path.to_str().context("")?, ]) .stdin(Stdio::null()) - .stdout(Stdio::piped()) + .stdout(Stdio::null()) .spawn()?; // Start peer b @@ -118,7 +132,6 @@ fn api_integration_api_setup() -> anyhow::Result<()> { .spawn()?; // Acquire stdout - let mut out_a = BufReader::new(proc_a.stdout.context("")?).lines(); let mut out_b = BufReader::new(proc_b.stdout.context("")?).lines(); // Now connect to the peers @@ -135,6 +148,7 @@ fn api_integration_api_setup() -> anyhow::Result<()> { } let api = UnixStream::connect(api_path)?; + let (psk_broker_sock, psk_broker_server_sock) = UnixStream::pair()?; // Send AddListenSocket request { @@ -175,7 +189,7 @@ fn api_integration_api_setup() -> anyhow::Result<()> { // Read response { let mut decoder = LengthPrefixDecoder::new([0u8; api::MAX_RESPONSE_LEN]); - let res = decoder.read_all_from_stdio(api)?; + let res = decoder.read_all_from_stdio(&api)?; let res = res.zk_parse::()?; assert_eq!( *res, @@ -183,41 +197,83 @@ fn api_integration_api_setup() -> anyhow::Result<()> { ); } + // Send AddPskBroker request + { + let mut fds = vec![psk_broker_server_sock.as_fd()].into(); + let mut api = WriteWithFileDescriptors::::new(&api, &mut fds); + LengthPrefixEncoder::from_message(api::AddPskBrokerRequest::new().as_bytes()) + .write_all_to_stdio(&mut api)?; + assert!(fds.is_empty(), "Failed to write all file descriptors"); + } + + // Read response + { + let mut decoder = LengthPrefixDecoder::new([0u8; api::MAX_RESPONSE_LEN]); + let res = decoder.read_all_from_stdio(&api)?; + let res = res.zk_parse::()?; + assert_eq!( + *res, + api::AddPskBrokerResponse::new(add_psk_broker_response_status::OK) + ); + } + // Wait for the keys to successfully exchange a key let mut attempt = 0; loop { - let line_a = out_a.next().context("")??; - let line_b = out_b.next().context("")??; + // Read OSK generated by A + let osk_a = { + use rosenpass_wireguard_broker::api::msgs as M; + type SetPskReqPkg = M::Envelope; + type SetPskResPkg = M::Envelope; - let words_a = line_a.split(' ').collect::>(); - let words_b = line_b.split(' ').collect::>(); + // Receive request + let mut decoder = LengthPrefixDecoder::new([0u8; M::REQUEST_MSG_BUFFER_SIZE]); + let req = decoder.read_all_from_stdio(&psk_broker_sock)?; - // FIXED FIXED PEER-ID FIXED FILENAME STATUS - // output-key peer KZqXTZ4l2aNnkJtLPhs4D8JxHTGmRSL9w3Qr+X8JxFk= key-file "client-A-osk" exchanged - let peer_a_id = words_b - .get(2) - .with_context(|| format!("Bad rosenpass output: `{line_b}`"))?; - let peer_b_id = words_a - .get(2) - .with_context(|| format!("Bad rosenpass output: `{line_a}`"))?; - assert_eq!( - line_a, - format!( - "output-key peer {peer_b_id} key-file \"{}\" exchanged", - peer_a_osk.to_str().context("")? - ) - ); - assert_eq!( - line_b, - format!( - "output-key peer {peer_a_id} key-file \"{}\" exchanged", - peer_b_osk.to_str().context("")? - ) - ); + let req = req.zk_parse::()?; + assert_eq!(req.msg_type, M::MsgType::SetPsk as u8); + assert_eq!(req.payload.peer_id, peer_b_wg_peer_id); + assert_eq!(req.payload.iface()?, peer_b_wg_device); - // Read OSKs - let osk_a = SymKey::load_b64::<64, _>(peer_a_osk.clone())?; - let osk_b = SymKey::load_b64::<64, _>(peer_b_osk.clone())?; + // Send response + let res = SetPskResPkg { + msg_type: M::MsgType::SetPsk as u8, + reserved: [0u8; 3], + payload: M::SetPskResponse { + return_code: M::SetPskResponseReturnCode::Success as u8, + }, + }; + LengthPrefixEncoder::from_message(res.as_bytes()) + .write_all_to_stdio(&psk_broker_sock)?; + + SymKey::from_slice(&req.payload.psk) + }; + + // Read OSK generated by B + let osk_b = { + let line = out_b.next().context("")??; + let words = line.split(' ').collect::>(); + + // FIXED FIXED PEER-ID FIXED FILENAME STATUS + // output-key peer KZqXTZ4l2aNnkJtLPhs4D8JxHTGmRSL9w3Qr+X8JxFk= key-file "client-A-osk" exchanged + let peer_id = words + .get(2) + .with_context(|| format!("Bad rosenpass output: `{line}`"))?; + assert_eq!( + line, + format!( + "output-key peer {peer_id} key-file \"{}\" exchanged", + peer_b_osk.to_str().context("")? + ) + ); + + SymKey::load_b64::<64, _>(peer_b_osk.clone())? + }; + + // TODO: This may be flaky. Both rosenpass instances are not guaranteed to produce + // the same number of output events; they merely guarantee eventual consistency of OSK. + // Correctly, we should use tokio to read any number of generated OSKs and indicate + // success on consensus match osk_a.secret() == osk_b.secret() { true => break, false if attempt > 10 => bail!("Peers did not produce a matching key even after ten attempts. Something is wrong with the key exchange!"), diff --git a/util/src/fd.rs b/util/src/fd.rs index fd11083..7729288 100644 --- a/util/src/fd.rs +++ b/util/src/fd.rs @@ -203,7 +203,7 @@ where Ok(self.is_unix_socket()? && self.is_stream_socket()?) } - fn demand_unix_stream_socket(&self) -> anyhow::Result<()>{ + fn demand_unix_stream_socket(&self) -> anyhow::Result<()> { use rustix::net::AddressFamily as SA; use rustix::net::SocketType as ST; match (self.socket_domain()?, self.socket_type()?) { diff --git a/util/src/mio/mio.rs b/util/src/mio/mio.rs index f70de92..b1907e3 100644 --- a/util/src/mio/mio.rs +++ b/util/src/mio/mio.rs @@ -1,7 +1,10 @@ use mio::net::{UnixListener, UnixStream}; use rustix::fd::{OwnedFd, RawFd}; -use crate::{fd::claim_fd, result::OkExt}; +use crate::{ + fd::{claim_fd, claim_fd_inplace}, + result::OkExt, +}; pub mod interest { use mio::Interest; @@ -27,12 +30,14 @@ impl UnixListenerExt for UnixListener { pub trait UnixStreamExt: Sized { fn from_fd(fd: OwnedFd) -> anyhow::Result; fn claim_fd(fd: RawFd) -> anyhow::Result; + fn claim_fd_inplace(fd: RawFd) -> anyhow::Result; } impl UnixStreamExt for UnixStream { fn from_fd(fd: OwnedFd) -> anyhow::Result { use std::os::unix::net::UnixStream as StdUnixStream; - + #[cfg(target_os = "linux")] // TODO: We should support this on other plattforms + crate::fd::GetUnixSocketType::demand_unix_stream_socket(&fd)?; let sock = StdUnixStream::from(fd); sock.set_nonblocking(true)?; UnixStream::from_std(sock).ok() @@ -41,4 +46,8 @@ impl UnixStreamExt for UnixStream { fn claim_fd(fd: RawFd) -> anyhow::Result { Self::from_fd(claim_fd(fd)?) } + + fn claim_fd_inplace(fd: RawFd) -> anyhow::Result { + Self::from_fd(claim_fd_inplace(fd)?) + } } diff --git a/wireguard-broker/Cargo.toml b/wireguard-broker/Cargo.toml index 8d26832..3ccc150 100644 --- a/wireguard-broker/Cargo.toml +++ b/wireguard-broker/Cargo.toml @@ -40,7 +40,7 @@ rand = {workspace = true} procspawn = {workspace = true} [features] -experiment_broker_api = ["rustix", "libc"] +experiment_api = ["rustix", "libc"] experiment_memfd_secret = [] [[bin]] @@ -48,7 +48,7 @@ name = "rosenpass-wireguard-broker-privileged" path = "src/bin/priviledged.rs" test = false doc = false -required-features = ["experiment_broker_api"] +required-features = ["experiment_api"] cfg = { target_os = "linux" } [[bin]] @@ -56,5 +56,5 @@ name = "rosenpass-wireguard-broker-socket-handler" test = false path = "src/bin/socket_handler.rs" doc = false -required-features = ["experiment_broker_api"] +required-features = ["experiment_api"] cfg = { target_os = "linux" } diff --git a/wireguard-broker/src/api/msgs.rs b/wireguard-broker/src/api/msgs.rs index 4021c5b..a5e4f60 100644 --- a/wireguard-broker/src/api/msgs.rs +++ b/wireguard-broker/src/api/msgs.rs @@ -20,8 +20,8 @@ pub struct Envelope { #[repr(packed)] #[derive(AsBytes, FromBytes, FromZeroes)] pub struct SetPskRequest { - pub peer_id: [u8; 32], pub psk: [u8; 32], + pub peer_id: [u8; 32], pub iface_size: u8, // TODO: We should have variable length strings in lenses pub iface_buf: [u8; 255], } diff --git a/wireguard-broker/src/brokers/mod.rs b/wireguard-broker/src/brokers/mod.rs index 76975fc..203b05d 100644 --- a/wireguard-broker/src/brokers/mod.rs +++ b/wireguard-broker/src/brokers/mod.rs @@ -1,6 +1,6 @@ -#[cfg(feature = "experiment_broker_api")] +#[cfg(feature = "experiment_api")] pub mod mio_client; -#[cfg(all(feature = "experiment_broker_api", target_os = "linux"))] +#[cfg(all(feature = "experiment_api", target_os = "linux"))] pub mod netlink; pub mod native_unix; diff --git a/wireguard-broker/src/lib.rs b/wireguard-broker/src/lib.rs index 8f48470..3a206ba 100644 --- a/wireguard-broker/src/lib.rs +++ b/wireguard-broker/src/lib.rs @@ -34,7 +34,7 @@ pub trait WireguardBrokerMio: WireGuardBroker { fn unregister(&mut self, registry: &mio::Registry) -> Result<(), Self::MioError>; } -#[cfg(feature = "experiment_broker_api")] +#[cfg(feature = "experiment_api")] pub mod api; pub mod brokers;