aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2019-10-20 12:59:02 -0400
committerJesse Luehrs <doy@tozt.net>2019-10-20 12:59:02 -0400
commit1fd0154711eadcd77c36bf6fc55917be54949f6a (patch)
tree1702186dac1f7f4be3295fd9d53f7dd72477cee6
parentce48e85996f81a9a20846217bb5c150f7745b082 (diff)
downloadteleterm-1fd0154711eadcd77c36bf6fc55917be54949f6a.tar.gz
teleterm-1fd0154711eadcd77c36bf6fc55917be54949f6a.zip
drop root privileges during normal operation
it should only be necessary for reading the tls key and binding to low-numbered ports
-rw-r--r--Cargo.lock10
-rw-r--r--Cargo.toml1
-rw-r--r--src/cmd/server.rs25
-rw-r--r--src/config.rs119
-rw-r--r--src/error.rs18
-rw-r--r--src/main.rs1
6 files changed, 174 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6df8fb4..be1d369 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1605,6 +1605,7 @@ dependencies = [
"tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"twoway 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "users 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1974,6 +1975,14 @@ dependencies = [
]
[[package]]
+name = "users"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "uuid"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2298,6 +2307,7 @@ dependencies = [
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61"
+"checksum users 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c72f4267aea0c3ec6d07eaabea6ead7c5ddacfafc5e22bcf8d186706851fb4cf"
"checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a"
"checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
diff --git a/Cargo.toml b/Cargo.toml
index c696d13..1ab6844 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -30,6 +30,7 @@ tokio-signal = "0.2"
tokio-tls = "0.2"
twoway = "0.2"
url = "1"
+users = "0.9"
uuid = { version = "0.7", features = ["v4"] }
[[bin]]
diff --git a/src/cmd/server.rs b/src/cmd/server.rs
index 6e98fe5..a6b8cff 100644
--- a/src/cmd/server.rs
+++ b/src/cmd/server.rs
@@ -35,6 +35,8 @@ impl crate::config::Config for Config {
tls_identity_file,
self.server.allowed_login_methods.clone(),
self.oauth_configs.clone(),
+ self.server.uid,
+ self.server.gid,
)?
} else {
create_server(
@@ -43,6 +45,8 @@ impl crate::config::Config for Config {
self.server.read_timeout,
self.server.allowed_login_methods.clone(),
self.oauth_configs.clone(),
+ self.server.uid,
+ self.server.gid,
)?
};
tokio::run(futures::future::lazy(move || {
@@ -82,6 +86,8 @@ fn create_server(
crate::protocol::AuthType,
crate::oauth::Config,
>,
+ uid: Option<users::uid_t>,
+ gid: Option<users::gid_t>,
) -> Result<(
Box<dyn futures::future::Future<Item = (), Error = Error> + Send>,
Box<dyn futures::future::Future<Item = (), Error = Error> + Send>,
@@ -89,6 +95,7 @@ fn create_server(
let (mut sock_w, sock_r) = tokio::sync::mpsc::channel(100);
let listener = tokio::net::TcpListener::bind(&address)
.context(crate::error::Bind { address })?;
+ drop_privs(uid, gid)?;
let acceptor = listener
.incoming()
.context(crate::error::Acceptor)
@@ -119,6 +126,8 @@ fn create_server_tls(
crate::protocol::AuthType,
crate::oauth::Config,
>,
+ uid: Option<users::uid_t>,
+ gid: Option<users::gid_t>,
) -> Result<(
Box<dyn futures::future::Future<Item = (), Error = Error> + Send>,
Box<dyn futures::future::Future<Item = (), Error = Error> + Send>,
@@ -126,6 +135,7 @@ fn create_server_tls(
let (mut sock_w, sock_r) = tokio::sync::mpsc::channel(100);
let listener = tokio::net::TcpListener::bind(&address)
.context(crate::error::Bind { address })?;
+ drop_privs(uid, gid)?;
let mut file = std::fs::File::open(tls_identity_file).context(
crate::error::OpenFileSync {
@@ -159,3 +169,18 @@ fn create_server_tls(
);
Ok((Box::new(acceptor), Box::new(server)))
}
+
+fn drop_privs(
+ uid: Option<users::uid_t>,
+ gid: Option<users::gid_t>,
+) -> Result<()> {
+ if let Some(gid) = gid {
+ users::switch::set_effective_gid(gid)
+ .context(crate::error::SwitchGid)?;
+ }
+ if let Some(uid) = uid {
+ users::switch::set_effective_uid(uid)
+ .context(crate::error::SwitchUid)?;
+ }
+ Ok(())
+}
diff --git a/src/config.rs b/src/config.rs
index 3a22085..dcc44d7 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -222,6 +222,12 @@ pub struct Server {
)]
pub allowed_login_methods:
std::collections::HashSet<crate::protocol::AuthType>,
+
+ #[serde(deserialize_with = "uid", default)]
+ pub uid: Option<users::uid_t>,
+
+ #[serde(deserialize_with = "gid", default)]
+ pub gid: Option<users::gid_t>,
}
impl Server {
@@ -307,6 +313,8 @@ impl Default for Server {
read_timeout: default_read_timeout(),
tls_identity_file: None,
allowed_login_methods: default_allowed_login_methods(),
+ uid: None,
+ gid: None,
}
}
}
@@ -410,6 +418,117 @@ fn default_allowed_login_methods(
crate::protocol::AuthType::iter().collect()
}
+fn uid<'a, D>(
+ deserializer: D,
+) -> std::result::Result<Option<users::uid_t>, D::Error>
+where
+ D: serde::de::Deserializer<'a>,
+{
+ struct StringOrInt;
+
+ impl<'a> serde::de::Visitor<'a> for StringOrInt {
+ type Value = Option<u32>;
+
+ fn expecting(
+ &self,
+ formatter: &mut std::fmt::Formatter,
+ ) -> std::fmt::Result {
+ formatter.write_str("string or int")
+ }
+
+ fn visit_str<E>(
+ self,
+ value: &str,
+ ) -> std::result::Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(Some(
+ users::get_user_by_name(value)
+ .context(crate::error::UnknownUser { name: value })
+ .map_err(serde::de::Error::custom)?
+ .uid(),
+ ))
+ }
+
+ fn visit_u32<E>(
+ self,
+ value: u32,
+ ) -> std::result::Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ if users::get_user_by_uid(value).is_none() {
+ return Err(serde::de::Error::custom(Error::UnknownUid {
+ uid: value,
+ }));
+ }
+ Ok(Some(value))
+ }
+ }
+
+ deserializer.deserialize_any(StringOrInt)
+}
+
+fn gid<'a, D>(
+ deserializer: D,
+) -> std::result::Result<Option<users::gid_t>, D::Error>
+where
+ D: serde::de::Deserializer<'a>,
+{
+ struct StringOrInt;
+
+ impl<'a> serde::de::Visitor<'a> for StringOrInt {
+ type Value = Option<u32>;
+
+ fn expecting(
+ &self,
+ formatter: &mut std::fmt::Formatter,
+ ) -> std::fmt::Result {
+ formatter.write_str("string or int")
+ }
+
+ fn visit_none<E>(self) -> std::result::Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(None)
+ }
+
+ fn visit_str<E>(
+ self,
+ value: &str,
+ ) -> std::result::Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(Some(
+ users::get_group_by_name(value)
+ .context(crate::error::UnknownGroup { name: value })
+ .map_err(serde::de::Error::custom)?
+ .gid(),
+ ))
+ }
+
+ fn visit_u32<E>(
+ self,
+ value: u32,
+ ) -> std::result::Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ if users::get_group_by_gid(value).is_none() {
+ return Err(serde::de::Error::custom(Error::UnknownGid {
+ gid: value,
+ }));
+ }
+ Ok(Some(value))
+ }
+ }
+
+ deserializer.deserialize_any(StringOrInt)
+}
+
#[derive(serde::Deserialize, Debug)]
pub struct Command {
#[serde(default = "default_buffer_size")]
diff --git a/src/error.rs b/src/error.rs
index eeca1e8..a02b836 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -347,6 +347,12 @@ pub enum Error {
#[snafu(display("failed to spawn process for `{}`: {}", cmd, source))]
SpawnProcess { cmd: String, source: std::io::Error },
+ #[snafu(display("failed to switch gid: {}", source))]
+ SwitchGid { source: std::io::Error },
+
+ #[snafu(display("failed to switch uid: {}", source))]
+ SwitchUid { source: std::io::Error },
+
#[snafu(display(
"failed to spawn a background thread to read terminal input: {}",
source
@@ -386,6 +392,18 @@ pub enum Error {
#[snafu(display("unexpected message: {:?}", message))]
UnexpectedMessage { message: crate::protocol::Message },
+ #[snafu(display("failed to find group with gid {}", gid))]
+ UnknownGid { gid: users::gid_t },
+
+ #[snafu(display("failed to find group with group name {}", name))]
+ UnknownGroup { name: String },
+
+ #[snafu(display("failed to find user with uid {}", uid))]
+ UnknownUid { uid: users::uid_t },
+
+ #[snafu(display("failed to find user with username {}", name))]
+ UnknownUser { name: String },
+
#[snafu(display("failed to write to event channel: {}", source))]
WriteChannel {
source: tokio::sync::mpsc::error::UnboundedSendError,
diff --git a/src/main.rs b/src/main.rs
index c088d6d..ed241d8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,6 +5,7 @@
#![allow(clippy::similar_names)]
#![allow(clippy::single_match)]
#![allow(clippy::single_match_else)]
+#![allow(clippy::too_many_arguments)]
#![allow(clippy::type_complexity)]
mod prelude;