summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2023-09-28 10:41:31 +0200
committerDavid Lönnhager <david.l@mullvad.net>2023-10-09 14:40:15 +0200
commitccc5aaaff23291366b4c0074f57a5096b94cdfce (patch)
treedd30da0b9ac7b1650cb72bbdae6372924a7c18dd
parent998fd39aaebc1435065f1f4886f4bd40f5889e5a (diff)
downloadmullvadvpn-ccc5aaaff23291366b4c0074f57a5096b94cdfce.tar.xz
mullvadvpn-ccc5aaaff23291366b4c0074f57a5096b94cdfce.zip
Add authentication with username+password for SOCKS5 access method
Add the option to authenticate against remote SOCKS5 proxies with a username+password combination. It was an oversight that this was not added from the start.
-rw-r--r--mullvad-api/src/https_client_with_sni.rs47
-rw-r--r--mullvad-cli/src/cmds/api_access.rs75
-rw-r--r--mullvad-management-interface/proto/management_interface.proto9
-rw-r--r--mullvad-management-interface/src/types/conversions/access_method.rs63
-rw-r--r--mullvad-types/src/access_method.rs24
5 files changed, 176 insertions, 42 deletions
diff --git a/mullvad-api/src/https_client_with_sni.rs b/mullvad-api/src/https_client_with_sni.rs
index 47b6923d37..17d9f7f0d8 100644
--- a/mullvad-api/src/https_client_with_sni.rs
+++ b/mullvad-api/src/https_client_with_sni.rs
@@ -125,11 +125,21 @@ impl InnerConnectionMode {
InnerConnectionMode::Socks5(socks) => {
let first_hop = socks.peer;
let make_proxy_stream = |tcp_stream| async {
- tokio_socks::tcp::Socks5Stream::connect_with_socket(tcp_stream, addr)
- .await
- .map_err(|error| {
- io::Error::new(io::ErrorKind::Other, format!("SOCKS error: {error}"))
- })
+ match socks.authentication {
+ SocksAuth::None => {
+ tokio_socks::tcp::Socks5Stream::connect_with_socket(tcp_stream, addr)
+ .await
+ }
+ SocksAuth::Password { username, password } => {
+ tokio_socks::tcp::Socks5Stream::connect_with_password_and_socket(
+ tcp_stream, addr, &username, &password,
+ )
+ .await
+ }
+ }
+ .map_err(|error| {
+ io::Error::new(io::ErrorKind::Other, format!("SOCKS error: {error}"))
+ })
};
Self::connect_proxied(
first_hop,
@@ -207,6 +217,13 @@ impl From<ParsedShadowsocksConfig> for ServerConfig {
#[derive(Clone)]
struct SocksConfig {
peer: SocketAddr,
+ authentication: SocksAuth,
+}
+
+#[derive(Clone)]
+pub enum SocksAuth {
+ None,
+ Password { username: String, password: String },
}
#[derive(err_derive::Error, Debug)]
@@ -219,6 +236,8 @@ impl TryFrom<ApiConnectionMode> for InnerConnectionMode {
type Error = ProxyConfigError;
fn try_from(config: ApiConnectionMode) -> Result<Self, Self::Error> {
+ use mullvad_types::access_method;
+ use std::net::Ipv4Addr;
Ok(match config {
ApiConnectionMode::Direct => InnerConnectionMode::Direct,
ApiConnectionMode::Proxied(proxy_settings) => match proxy_settings {
@@ -234,13 +253,23 @@ impl TryFrom<ApiConnectionMode> for InnerConnectionMode {
})
}
ProxyConfig::Socks(config) => match config {
- mullvad_types::access_method::Socks5::Local(config) => {
+ access_method::Socks5::Local(config) => {
InnerConnectionMode::Socks5(SocksConfig {
- peer: SocketAddr::new("127.0.0.1".parse().unwrap(), config.port),
+ peer: SocketAddr::new(IpAddr::from(Ipv4Addr::LOCALHOST), config.port),
+ authentication: SocksAuth::None,
})
}
- mullvad_types::access_method::Socks5::Remote(config) => {
- InnerConnectionMode::Socks5(SocksConfig { peer: config.peer })
+ access_method::Socks5::Remote(config) => {
+ let authentication = match config.authentication {
+ Some(access_method::SocksAuth { username, password }) => {
+ SocksAuth::Password { username, password }
+ }
+ None => SocksAuth::None,
+ };
+ InnerConnectionMode::Socks5(SocksConfig {
+ peer: config.peer,
+ authentication,
+ })
}
},
},
diff --git a/mullvad-cli/src/cmds/api_access.rs b/mullvad-cli/src/cmds/api_access.rs
index a3b17aca0b..75fe7c44bf 100644
--- a/mullvad-cli/src/cmds/api_access.rs
+++ b/mullvad-cli/src/cmds/api_access.rs
@@ -121,8 +121,20 @@ impl ApiAccess {
mullvad_types::access_method::Socks5::Remote(remote) => {
let ip = cmd.params.ip.unwrap_or(remote.peer.ip()).to_string();
let port = cmd.params.port.unwrap_or(remote.peer.port());
- mullvad_types::access_method::Socks5Remote::from_args(ip, port)
- .map(AccessMethod::from)
+ match remote.authentication {
+ None => mullvad_types::access_method::Socks5Remote::from_args(ip, port),
+ Some(mullvad_types::access_method::SocksAuth {
+ username,
+ password,
+ }) => {
+ let username = cmd.params.username.unwrap_or(username);
+ let password = cmd.params.password.unwrap_or(password);
+ mullvad_types::access_method::Socks5Remote::from_args_with_password(
+ ip, port, username, password,
+ )
+ }
+ }
+ .map(AccessMethod::from)
}
},
},
@@ -241,6 +253,8 @@ pub enum AddSocks5Commands {
remote_ip: IpAddr,
/// The port of the remote proxy server
remote_port: u16,
+ #[clap(flatten)]
+ authentication: Option<SocksAuthentication>,
/// Disable the use of this custom access method. It has to be manually
/// enabled at a later stage to be used when accessing the Mullvad API.
#[arg(default_value_t = false, short, long)]
@@ -263,6 +277,16 @@ pub enum AddSocks5Commands {
},
}
+#[derive(Args, Debug, Clone)]
+pub struct SocksAuthentication {
+ /// Username for authentication against a remote SOCKS5 proxy
+ #[arg(short, long)]
+ username: String,
+ /// Password for authentication against a remote SOCKS5 proxy
+ #[arg(short, long)]
+ password: String,
+}
+
impl AddCustomCommands {
fn name(&self) -> &str {
match self {
@@ -319,7 +343,10 @@ pub struct EditParams {
/// Name of the API access method in the Mullvad client [All]
#[arg(long)]
name: Option<String>,
- /// Password for authentication [Shadowsocks]
+ /// Username for authentication [Socks5 (Remote proxy)]
+ #[arg(long)]
+ username: Option<String>,
+ /// Password for authentication [Socks5 (Remote proxy), Shadowsocks]
#[arg(long)]
password: Option<String>,
/// Cipher to use [Shadowsocks]
@@ -344,7 +371,7 @@ mod conversions {
use anyhow::{anyhow, Error};
use mullvad_types::access_method as daemon_types;
- use super::{AddCustomCommands, AddSocks5Commands};
+ use super::{AddCustomCommands, AddSocks5Commands, SocksAuthentication};
impl TryFrom<AddCustomCommands> for daemon_types::AccessMethod {
type Error = Error;
@@ -373,18 +400,31 @@ mod conversions {
AddSocks5Commands::Remote {
remote_ip,
remote_port,
+ authentication,
name: _,
disabled: _,
} => {
- println!("Adding SOCKS5-proxy: {remote_ip}:{remote_port}");
- let socks_proxy = daemon_types::Socks5::Remote(
- daemon_types::Socks5Remote::from_args(
- remote_ip.to_string(),
- remote_port,
- )
- .ok_or(anyhow!("Could not create a remote Socks5 api proxy"))?,
- );
- daemon_types::AccessMethod::from(socks_proxy)
+ match authentication {
+ Some(SocksAuthentication { username, password }) => {
+ println!("Adding SOCKS5-proxy: {username}:{password}@{remote_ip}:{remote_port}");
+ daemon_types::Socks5Remote::from_args_with_password(
+ remote_ip.to_string(),
+ remote_port,
+ username,
+ password
+ )
+ }
+ None => {
+ println!("Adding SOCKS5-proxy: {remote_ip}:{remote_port}");
+ daemon_types::Socks5Remote::from_args(
+ remote_ip.to_string(),
+ remote_port,
+ )
+ }
+ }
+ .map(daemon_types::Socks5::Remote)
+ .map(daemon_types::AccessMethod::from)
+ .ok_or(anyhow!("Could not create a remote Socks5 api proxy"))?
}
},
AddCustomCommands::Shadowsocks {
@@ -415,7 +455,7 @@ mod conversions {
/// Pretty printing of [`ApiAccessMethod`]s
mod pp {
use mullvad_types::access_method::{
- AccessMethod, AccessMethodSetting, CustomAccessMethod, Socks5,
+ AccessMethod, AccessMethodSetting, CustomAccessMethod, Socks5, SocksAuth,
};
pub struct ApiAccessMethodFormatter<'a> {
@@ -462,6 +502,13 @@ mod pp {
writeln!(f)?;
print_option!("Protocol", "Socks5");
print_option!("Peer", remote.peer);
+ match &remote.authentication {
+ Some(SocksAuth { username, password }) => {
+ print_option!("Username", username);
+ print_option!("Password", password);
+ }
+ None => (),
+ }
Ok(())
}
Socks5::Local(local) => {
diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto
index c9b9bc5138..1bbb7f5012 100644
--- a/mullvad-management-interface/proto/management_interface.proto
+++ b/mullvad-management-interface/proto/management_interface.proto
@@ -342,9 +342,14 @@ message AccessMethod {
uint32 port = 2;
uint32 local_port = 3;
}
+ message SocksAuth {
+ string username = 1;
+ string password = 2;
+ }
message Socks5Remote {
- string ip = 2;
- uint32 port = 3;
+ string ip = 1;
+ uint32 port = 2;
+ SocksAuth authentication = 3;
}
message Shadowsocks {
string ip = 1;
diff --git a/mullvad-management-interface/src/types/conversions/access_method.rs b/mullvad-management-interface/src/types/conversions/access_method.rs
index 0f39d34e9e..8907c4da29 100644
--- a/mullvad-management-interface/src/types/conversions/access_method.rs
+++ b/mullvad-management-interface/src/types/conversions/access_method.rs
@@ -45,7 +45,7 @@ mod data {
use crate::types::{proto, FromProtobufTypeError};
use mullvad_types::access_method::{
AccessMethod, AccessMethodSetting, BuiltInAccessMethod, CustomAccessMethod, Id,
- Shadowsocks, Socks5, Socks5Local, Socks5Remote,
+ Shadowsocks, Socks5, Socks5Local, Socks5Remote, SocksAuth,
};
impl TryFrom<proto::AccessMethodSetting> for AccessMethodSetting {
@@ -154,13 +154,24 @@ mod data {
type Error = FromProtobufTypeError;
fn try_from(value: proto::access_method::Socks5Remote) -> Result<Self, Self::Error> {
- Socks5Remote::from_args(value.ip, value.port as u16)
- .ok_or({
- FromProtobufTypeError::InvalidArgument(
- "Could not parse Socks5 (remote) message from protobuf",
- )
- })
- .map(AccessMethod::from)
+ let proto::access_method::Socks5Remote {
+ ip,
+ port,
+ authentication,
+ } = value;
+ let port = port as u16;
+ match authentication.map(SocksAuth::from) {
+ Some(SocksAuth { username, password }) => {
+ Socks5Remote::from_args_with_password(ip, port, username, password)
+ }
+ None => Socks5Remote::from_args(ip, port),
+ }
+ .ok_or({
+ FromProtobufTypeError::InvalidArgument(
+ "Could not parse Socks5 (remote) message from protobuf",
+ )
+ })
+ .map(AccessMethod::from)
}
}
@@ -214,14 +225,16 @@ mod data {
},
)
}
- CustomAccessMethod::Socks5(Socks5::Remote(Socks5Remote { peer })) => {
- proto::access_method::AccessMethod::Socks5remote(
- proto::access_method::Socks5Remote {
- ip: peer.ip().to_string(),
- port: peer.port() as u32,
- },
- )
- }
+ CustomAccessMethod::Socks5(Socks5::Remote(Socks5Remote {
+ peer,
+ authentication,
+ })) => proto::access_method::AccessMethod::Socks5remote(
+ proto::access_method::Socks5Remote {
+ ip: peer.ip().to_string(),
+ port: peer.port() as u32,
+ authentication: authentication.map(proto::access_method::SocksAuth::from),
+ },
+ ),
};
proto::AccessMethod {
@@ -248,6 +261,24 @@ mod data {
}
}
+ impl From<SocksAuth> for proto::access_method::SocksAuth {
+ fn from(value: SocksAuth) -> Self {
+ proto::access_method::SocksAuth {
+ username: value.username,
+ password: value.password,
+ }
+ }
+ }
+
+ impl From<proto::access_method::SocksAuth> for SocksAuth {
+ fn from(value: proto::access_method::SocksAuth) -> Self {
+ Self {
+ username: value.username,
+ password: value.password,
+ }
+ }
+ }
+
impl TryFrom<&proto::AccessMethodSetting> for AccessMethodSetting {
type Error = FromProtobufTypeError;
diff --git a/mullvad-types/src/access_method.rs b/mullvad-types/src/access_method.rs
index a300072c82..30c1f25192 100644
--- a/mullvad-types/src/access_method.rs
+++ b/mullvad-types/src/access_method.rs
@@ -209,6 +209,13 @@ pub struct Socks5Local {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct Socks5Remote {
pub peer: SocketAddr,
+ pub authentication: Option<SocksAuth>,
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
+pub struct SocksAuth {
+ pub username: String,
+ pub password: String,
}
impl AccessMethod {
@@ -262,7 +269,10 @@ impl Socks5Local {
impl Socks5Remote {
pub fn new(peer: SocketAddr) -> Self {
- Self { peer }
+ Self {
+ peer,
+ authentication: None,
+ }
}
/// Like [new()], but tries to parse `ip` and `port` into a [`std::net::SocketAddr`] for you.
@@ -272,6 +282,18 @@ impl Socks5Remote {
let peer = SocketAddr::new(peer_ip, port);
Some(Self::new(peer))
}
+
+ /// Like [from_args()], but with authentication.
+ pub fn from_args_with_password(
+ ip: String,
+ port: u16,
+ username: String,
+ password: String,
+ ) -> Option<Self> {
+ let mut socks = Self::from_args(ip, port)?;
+ socks.authentication = Some(SocksAuth { username, password });
+ Some(socks)
+ }
}
impl From<BuiltInAccessMethod> for AccessMethod {