Skip to content

Commit 58944a7

Browse files
server: Drop unnecessary caps
Closes #78
1 parent 4737a36 commit 58944a7

5 files changed

Lines changed: 141 additions & 7 deletions

File tree

Cargo.lock

Lines changed: 58 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ version.workspace = true
1212
[dependencies]
1313
ashpd = {workspace = true, features = ["backend", "tracing"]}
1414
base64 = "0.22"
15+
caps = "0.4"
1516
clap.workspace = true
1617
enumflags2 = "0.7"
1718
hkdf = { version = "0.12", optional = true }
19+
nix = { version = "0.24", default-features = false, features = ["user"]}
1820
num = "0.4.0"
1921
num-bigint-dig = { version = "0.8", features = ["zeroize"] }
2022
openssl = { version = "0.10", optional = true }

server/src/capability.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use caps::{CapSet, Capability, CapsHashSet};
2+
use nix::unistd::{getgid, getuid, setgid, setgroups, setuid};
3+
4+
#[derive(Debug)]
5+
pub enum Error {
6+
CapsReadError(caps::errors::CapsError),
7+
CapsUpdateError(caps::errors::CapsError),
8+
DropGroupsError(nix::Error),
9+
SetGidError(nix::Error),
10+
SetUidError(nix::Error),
11+
InsufficientCapabilities,
12+
NoCapabilities,
13+
}
14+
15+
impl std::fmt::Display for Error {
16+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17+
match self {
18+
Self::CapsReadError(e) => write!(f, "Failed to read process capabilities: {}", e),
19+
Self::CapsUpdateError(e) => write!(f, "Failed updating process capabilities: {}", e),
20+
Self::DropGroupsError(e) => write!(f, "Failed to drop supplementary groups: {}", e),
21+
Self::SetGidError(e) => write!(f, "Failed to setgid: {}", e),
22+
Self::SetUidError(e) => write!(f, "Failed to setuid: {}", e),
23+
Self::InsufficientCapabilities => write!(
24+
f,
25+
"Insufficient process capabilities, insecure memory might get used"
26+
),
27+
Self::NoCapabilities => {
28+
write!(f, "No process capabilities, insecure memory might get used")
29+
}
30+
}
31+
}
32+
}
33+
34+
impl std::error::Error for Error {}
35+
36+
pub fn drop_unnecessary_capabilities() -> Result<(), Error> {
37+
// Load current process capabilities
38+
let permitted_caps = caps::read(None, CapSet::Permitted).map_err(Error::CapsReadError)?;
39+
40+
if permitted_caps.contains(&Capability::CAP_IPC_LOCK) {
41+
// Check if CAP_SETPCAP is available (needed to drop bounding set and groups)
42+
let has_setpcap = caps::has_cap(None, CapSet::Permitted, Capability::CAP_SETPCAP)
43+
.map_err(Error::CapsReadError)?;
44+
45+
let mut drop_caps = CapsHashSet::new();
46+
drop_caps.insert(Capability::CAP_IPC_LOCK);
47+
48+
// Clear other capabilities and apply only CAP_IPC_LOCK
49+
caps::set(None, CapSet::Effective, &drop_caps).map_err(Error::CapsUpdateError)?;
50+
caps::set(None, CapSet::Permitted, &drop_caps).map_err(Error::CapsUpdateError)?;
51+
52+
// Drop supplementary groups and switch to real UID/GID.
53+
if has_setpcap {
54+
setgroups(&[]).map_err(Error::DropGroupsError)?;
55+
setgid(getgid()).map_err(Error::SetGidError)?;
56+
setuid(getuid()).map_err(Error::SetUidError)?;
57+
} else {
58+
return Err(Error::InsufficientCapabilities);
59+
}
60+
} else if permitted_caps.is_empty() {
61+
return Err(Error::NoCapabilities);
62+
} else {
63+
return Err(Error::InsufficientCapabilities);
64+
}
65+
66+
Ok(())
67+
}

server/src/error.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use std::fmt;
22

33
use oo7::dbus::ServiceError;
44

5+
use crate::capability;
6+
57
#[derive(Debug)]
68
pub enum Error {
79
// File backend error
@@ -14,6 +16,8 @@ pub enum Error {
1416
EmptyPassword,
1517
// Invalid item error
1618
InvalidItem(oo7::file::InvalidItemError),
19+
// Capability error
20+
Capability(capability::Error),
1721
}
1822

1923
impl From<zbus::Error> for Error {
@@ -34,6 +38,12 @@ impl From<std::io::Error> for Error {
3438
}
3539
}
3640

41+
impl From<capability::Error> for Error {
42+
fn from(err: capability::Error) -> Self {
43+
Self::Capability(err)
44+
}
45+
}
46+
3747
impl fmt::Display for Error {
3848
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3949
match self {
@@ -42,6 +52,7 @@ impl fmt::Display for Error {
4252
Self::IO(err) => write!(f, "IO error {err}"),
4353
Self::EmptyPassword => write!(f, "Login password can't be empty"),
4454
Self::InvalidItem(err) => write!(f, "Item cannot be decrypted {err}"),
55+
Self::Capability(err) => write!(f, "Capability error {err}"),
4556
}
4657
}
4758
}

server/src/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod capability;
12
mod collection;
23
mod error;
34
#[allow(unused)]
@@ -31,6 +32,8 @@ struct Args {
3132
#[tokio::main]
3233
async fn main() -> Result<(), Error> {
3334
tracing_subscriber::fmt::init();
35+
capability::drop_unnecessary_capabilities()?;
36+
3437
let args = Args::parse();
3538
let mut secret = None;
3639

0 commit comments

Comments
 (0)