From c75d2242ab5de5301c4340a97f96648a056cf208 Mon Sep 17 00:00:00 2001 From: Denis Kolodin Date: Mon, 18 Jul 2022 10:42:50 +0300 Subject: [PATCH] feat: read tor cookies from a file (#4317) Description --- The PR adds a capability to read a tor cookie from a file. To set a `cookie` value in the config of the `tari_base_node` we could use three ways: ```toml tor.control_auth = "cookie=HEX" tor.control_auth = "cookie=@" # read `/run/tor/control.authcookie` default cookie file tor.control_auth = "cookie=@/custom/path/to/cookie/file" ``` Motivation and Context --- Fixes https://github.com/tari-project/tari/issues/2808 How Has This Been Tested? --- CI and manually (Fedora 35, stardard `tor` service + added `ControlPort` to the config) --- base_layer/p2p/src/initialization.rs | 2 +- base_layer/p2p/src/tor_authentication.rs | 97 ++++++++++++++++++++---- base_layer/p2p/src/transport.rs | 7 +- base_layer/wallet_ffi/src/lib.rs | 2 +- 4 files changed, 90 insertions(+), 18 deletions(-) diff --git a/base_layer/p2p/src/initialization.rs b/base_layer/p2p/src/initialization.rs index e0e436440b..f51cbd9308 100644 --- a/base_layer/p2p/src/initialization.rs +++ b/base_layer/p2p/src/initialization.rs @@ -256,7 +256,7 @@ async fn initialize_hidden_service( .with_hs_flags(tor::HsFlags::DETACH) .with_port_mapping(config.to_port_mapping()?) .with_socks_authentication(config.to_socks_auth()) - .with_control_server_auth(config.to_control_auth()) + .with_control_server_auth(config.to_control_auth()?) .with_socks_address_override(config.socks_address_override) .with_control_server_address(config.control_address) .with_bypass_proxy_addresses(config.proxy_bypass_addresses.into()); diff --git a/base_layer/p2p/src/tor_authentication.rs b/base_layer/p2p/src/tor_authentication.rs index 79b891e5e9..b9cfa2f362 100644 --- a/base_layer/p2p/src/tor_authentication.rs +++ b/base_layer/p2p/src/tor_authentication.rs @@ -20,26 +20,45 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::{convert::TryFrom, fmt, fmt::Formatter, str::FromStr}; +use std::{convert::TryFrom, fmt, fmt::Formatter, fs, io::Error, str::FromStr}; use serde::{Deserialize, Serialize}; use tari_comms::tor; +use tari_utilities::hex::Hex; -#[derive(Clone, Serialize, Deserialize)] +const DEFAULT_TOR_COOKIE_PATH: &str = "/run/tor/control.authcookie"; + +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)] +pub enum TorCookie { + Hex(String), + FilePath(String), +} + +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(try_from = "String")] pub enum TorControlAuthentication { None, Password(String), /// Cookie authentication. The contents of the cookie file encoded as hex - Cookie(String), + Cookie(TorCookie), } -impl From for tor::Authentication { - fn from(auth: TorControlAuthentication) -> Self { - match auth { - TorControlAuthentication::None => tor::Authentication::None, - TorControlAuthentication::Password(passwd) => tor::Authentication::HashedPassword(passwd), - TorControlAuthentication::Cookie(cookie) => tor::Authentication::Cookie(cookie), +impl TorControlAuthentication { + pub fn hex(data: String) -> Self { + Self::Cookie(TorCookie::Hex(data)) + } + + pub fn make_tor_auth(self) -> Result { + match self { + TorControlAuthentication::None => Ok(tor::Authentication::None), + TorControlAuthentication::Password(passwd) => Ok(tor::Authentication::HashedPassword(passwd)), + TorControlAuthentication::Cookie(cookie) => match cookie { + TorCookie::Hex(hex) => Ok(tor::Authentication::Cookie(hex)), + TorCookie::FilePath(path) => { + let data = fs::read(path)?.to_hex(); + Ok(tor::Authentication::Cookie(data)) + }, + }, } } } @@ -79,11 +98,22 @@ impl FromStr for TorControlAuthentication { Ok(TorControlAuthentication::Password(password.to_string())) }, "cookie" => { - let password = maybe_value.ok_or_else(|| { - "Invalid format for 'cookie' tor authentication type. It should be in the format 'cookie=xxxxxx'." - .to_string() - })?; - Ok(TorControlAuthentication::Cookie(password.to_string())) + if let Some(value) = maybe_value { + if let Some(mut path) = value.strip_prefix('@') { + if path.is_empty() { + path = DEFAULT_TOR_COOKIE_PATH; + } + Ok(TorControlAuthentication::Cookie(TorCookie::FilePath(path.to_string()))) + } else { + Ok(TorControlAuthentication::Cookie(TorCookie::Hex(value.to_string()))) + } + } else { + Err( + "Invalid format for 'cookie' tor authentication type. It should be in the format \ + 'cookie=xxxxxx'." + .into(), + ) + } }, s => Err(format!("Invalid tor auth type '{}'", s)), } @@ -101,3 +131,42 @@ impl fmt::Debug for TorControlAuthentication { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn tor_parser_test() { + let auth = TorControlAuthentication::from_str("none"); + assert_eq!(auth, Ok(TorControlAuthentication::None)); + + let auth = TorControlAuthentication::from_str("password="); + assert_eq!(auth, Ok(TorControlAuthentication::Password("".into()))); + + let auth = TorControlAuthentication::from_str("password=123"); + assert_eq!(auth, Ok(TorControlAuthentication::Password("123".into()))); + + let auth = TorControlAuthentication::from_str("cookie="); + assert_eq!(auth, Ok(TorControlAuthentication::hex("".into()))); + + let auth = TorControlAuthentication::from_str("cookie=8b6f"); + assert_eq!(auth, Ok(TorControlAuthentication::hex("8b6f".into()))); + + let auth = TorControlAuthentication::from_str("cookie=@"); + assert_eq!( + auth, + Ok(TorControlAuthentication::Cookie(TorCookie::FilePath( + DEFAULT_TOR_COOKIE_PATH.into() + ))) + ); + + let auth = TorControlAuthentication::from_str("cookie=@/path/to/file"); + assert_eq!( + auth, + Ok(TorControlAuthentication::Cookie(TorCookie::FilePath( + "/path/to/file".into() + ))) + ); + } +} diff --git a/base_layer/p2p/src/transport.rs b/base_layer/p2p/src/transport.rs index a97c0234b0..16ab78fa9a 100644 --- a/base_layer/p2p/src/transport.rs +++ b/base_layer/p2p/src/transport.rs @@ -172,8 +172,11 @@ impl TorTransportConfig { Ok(tor::PortMapping::new(self.onion_port.get(), forward_addr)) } - pub fn to_control_auth(&self) -> tor::Authentication { - self.control_auth.clone().into() + pub fn to_control_auth(&self) -> Result { + self.control_auth + .clone() + .make_tor_auth() + .map_err(CommsInitializationError::from) } pub fn to_socks_auth(&self) -> socks::Authentication { diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index 897bcbb7ac..5165a77c79 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -3374,7 +3374,7 @@ pub unsafe extern "C" fn transport_tor_create( TorControlAuthentication::None } else { let cookie_hex = hex::to_hex((*tor_cookie).0.as_slice()); - TorControlAuthentication::Cookie(cookie_hex) + TorControlAuthentication::hex(cookie_hex) }; let onion_port = match NonZeroU16::new(tor_port) {