Skip to content

Commit 6023cff

Browse files
committed
ptfs: switch to new implementation of unix credentials
Switch to new implementation of unix credentials, with support of supplemental group ids. Signed-off-by: Jiang Liu <[email protected]>
1 parent cc0a191 commit 6023cff

File tree

5 files changed

+140
-166
lines changed

5 files changed

+140
-166
lines changed

src/passthrough/credentials.rs

+84-65
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// SPDX-License-Identifier: BSD-3-Clause
22

3-
use crate::oslib;
4-
use crate::passthrough::util::einval;
53
use std::io;
64

5+
use super::util::{dropsupgroups, seteffgid, seteffuid, setsupgroup};
6+
77
pub struct UnixCredentials {
88
uid: libc::uid_t,
99
gid: libc::gid_t,
@@ -24,6 +24,7 @@ impl UnixCredentials {
2424
/// Set a supplementary group. Set `supported_extension` to `false` to signal that a
2525
/// supplementary group maybe required, but the guest was not able to tell us which,
2626
/// so we have to rely on keeping the DAC_OVERRIDE capability.
27+
#[allow(dead_code)]
2728
pub fn supplementary_gid(self, supported_extension: bool, sup_gid: Option<u32>) -> Self {
2829
UnixCredentials {
2930
uid: self.uid,
@@ -33,8 +34,9 @@ impl UnixCredentials {
3334
}
3435
}
3536

36-
/// Changes the effective uid/gid of the current thread to `val`. Changes
37-
/// the thread's credentials back to root when the returned struct is dropped.
37+
/// Changes the effective uid/gid of the current thread to `val`.
38+
///
39+
/// Changes the thread's credentials back to root when the returned struct is dropped.
3840
pub fn set(self) -> io::Result<Option<UnixCredentialsGuard>> {
3941
let change_uid = self.uid != 0;
4042
let change_gid = self.gid != 0;
@@ -43,15 +45,15 @@ impl UnixCredentials {
4345
// change the uid first then we lose the capability to change the gid.
4446
// However changing back can happen in any order.
4547
if let Some(sup_gid) = self.sup_gid {
46-
oslib::setsupgroup(sup_gid)?;
48+
setsupgroup(sup_gid)?;
4749
}
4850

4951
if change_gid {
50-
oslib::seteffgid(self.gid)?;
52+
seteffgid(self.gid)?;
5153
}
5254

5355
if change_uid {
54-
oslib::seteffuid(self.uid)?;
56+
seteffuid(self.uid)?;
5557
}
5658

5759
if change_uid && self.keep_capability {
@@ -61,7 +63,7 @@ impl UnixCredentials {
6163
// user ID, so we still have the 'DAC_OVERRIDE' in the permitted set.
6264
// After switching back to root the permitted set is copied to the effective set,
6365
// so no additional steps are required.
64-
if let Err(e) = crate::util::add_cap_to_eff("DAC_OVERRIDE") {
66+
if let Err(e) = add_cap_to_eff(caps::Capability::CAP_DAC_OVERRIDE) {
6567
warn!("failed to add 'DAC_OVERRIDE' to the effective set of capabilities: {e}");
6668
}
6769
}
@@ -87,88 +89,105 @@ pub struct UnixCredentialsGuard {
8789
impl Drop for UnixCredentialsGuard {
8890
fn drop(&mut self) {
8991
if self.reset_uid {
90-
oslib::seteffuid(0).unwrap_or_else(|e| {
92+
seteffuid(0).unwrap_or_else(|e| {
9193
error!("failed to change uid back to root: {e}");
9294
});
9395
}
9496

9597
if self.reset_gid {
96-
oslib::seteffgid(0).unwrap_or_else(|e| {
98+
seteffgid(0).unwrap_or_else(|e| {
9799
error!("failed to change gid back to root: {e}");
98100
});
99101
}
100102

101103
if self.drop_sup_gid {
102-
oslib::dropsupgroups().unwrap_or_else(|e| {
104+
dropsupgroups().unwrap_or_else(|e| {
103105
error!("failed to drop supplementary groups: {e}");
104106
});
105107
}
106108
}
107109
}
108110

109111
pub struct ScopedCaps {
110-
cap: capng::Capability,
112+
capability: caps::Capability,
111113
}
112114

113-
impl ScopedCaps {
114-
fn new(cap_name: &str) -> io::Result<Option<Self>> {
115-
use capng::{Action, CUpdate, Set, Type};
116-
117-
let cap = capng::name_to_capability(cap_name).map_err(|_| {
118-
let err = io::Error::last_os_error();
119-
error!(
120-
"couldn't get the capability id for name {}: {:?}",
121-
cap_name, err
122-
);
123-
err
124-
})?;
125-
126-
if capng::have_capability(Type::EFFECTIVE, cap) {
127-
let req = vec![CUpdate {
128-
action: Action::DROP,
129-
cap_type: Type::EFFECTIVE,
130-
capability: cap,
131-
}];
132-
capng::update(req).map_err(|e| {
133-
error!("couldn't drop {} capability: {:?}", cap, e);
134-
einval()
135-
})?;
136-
capng::apply(Set::CAPS).map_err(|e| {
137-
error!(
138-
"couldn't apply capabilities after dropping {}: {:?}",
139-
cap, e
140-
);
141-
einval()
142-
})?;
143-
Ok(Some(Self { cap }))
144-
} else {
145-
Ok(None)
146-
}
115+
impl Drop for ScopedCaps {
116+
fn drop(&mut self) {
117+
if let Err(e) = caps::raise(None, caps::CapSet::Effective, self.capability) {
118+
error!("fail to restore thread cap_fsetid: {}", e);
119+
};
147120
}
148121
}
149122

150-
impl Drop for ScopedCaps {
151-
fn drop(&mut self) {
152-
use capng::{Action, CUpdate, Set, Type};
123+
pub fn scoped_drop_capability(capability: caps::Capability) -> io::Result<Option<ScopedCaps>> {
124+
if !caps::has_cap(None, caps::CapSet::Effective, capability)
125+
.map_err(|_e| io::Error::new(io::ErrorKind::PermissionDenied, "no capability"))?
126+
{
127+
return Ok(None);
128+
}
129+
caps::drop(None, caps::CapSet::Effective, capability).map_err(|_e| {
130+
io::Error::new(io::ErrorKind::PermissionDenied, "failed to drop capability")
131+
})?;
132+
Ok(Some(ScopedCaps { capability }))
133+
}
153134

154-
let req = vec![CUpdate {
155-
action: Action::ADD,
156-
cap_type: Type::EFFECTIVE,
157-
capability: self.cap,
158-
}];
135+
pub fn drop_cap_fssetid() -> io::Result<Option<ScopedCaps>> {
136+
scoped_drop_capability(caps::Capability::CAP_FSETID)
137+
}
159138

160-
if let Err(e) = capng::update(req) {
161-
panic!("couldn't restore {} capability: {:?}", self.cap, e);
162-
}
163-
if let Err(e) = capng::apply(Set::CAPS) {
164-
panic!(
165-
"couldn't apply capabilities after restoring {}: {:?}",
166-
self.cap, e
167-
);
139+
/// Add a capability to the effective set
140+
///
141+
/// # Errors
142+
/// An error variant will be returned:
143+
/// - if the input string does not match the name, without the 'CAP_' prefix,
144+
/// of any of the capability defined in `linux/capabiliy.h`.
145+
/// - if `capng::get_caps_process()` cannot get the capabilities and bounding set of the process.
146+
/// - if `capng::update()` fails to update the internal posix capabilities settings.
147+
/// - if `capng::apply()` fails to transfer the specified internal posix capabilities
148+
/// settings to the kernel.
149+
pub fn add_cap_to_eff(capability: caps::Capability) -> io::Result<()> {
150+
caps::raise(None, caps::CapSet::Effective, capability).map_err(|_e| {
151+
io::Error::new(
152+
io::ErrorKind::PermissionDenied,
153+
"failed to raise capability",
154+
)
155+
})
156+
}
157+
158+
#[cfg(test)]
159+
mod tests {
160+
use super::*;
161+
use nix::unistd::getuid;
162+
163+
#[test]
164+
fn test_unix_credentials_set() {
165+
if getuid() == 0 {
166+
let cred = UnixCredentials::new(0, 0).set().unwrap();
167+
assert!(cred.is_none());
168+
drop(cred);
169+
170+
let cred = UnixCredentials::new(1, 1);
171+
let cred = cred.supplementary_gid(false, Some(2));
172+
let guard = cred.set().unwrap();
173+
assert!(guard.is_some());
174+
drop(guard);
168175
}
169176
}
170-
}
171177

172-
pub fn drop_effective_cap(cap_name: &str) -> io::Result<Option<ScopedCaps>> {
173-
ScopedCaps::new(cap_name)
178+
#[test]
179+
fn test_drop_cap_fssetid() {
180+
let cap = drop_cap_fssetid().unwrap();
181+
let has_cap =
182+
caps::has_cap(None, caps::CapSet::Effective, caps::Capability::CAP_FSETID).unwrap();
183+
assert_eq!(has_cap, false);
184+
drop(cap);
185+
}
186+
187+
#[test]
188+
fn test_add_cap_to_eff() {
189+
if getuid() == 0 {
190+
add_cap_to_eff(caps::Capability::CAP_DAC_OVERRIDE).unwrap();
191+
}
192+
}
174193
}

src/passthrough/file_handle.rs

-2
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,7 @@ impl OpenableFileHandle {
320320
#[cfg(test)]
321321
mod tests {
322322
use super::*;
323-
use nix::unistd::getuid;
324323
use std::ffi::CString;
325-
use std::io::Read;
326324

327325
fn generate_c_file_handle(
328326
handle_bytes: usize,

src/passthrough/mod.rs

+1-87
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ use crate::api::{
4848
#[cfg(feature = "async-io")]
4949
mod async_io;
5050
mod config;
51+
mod credentials;
5152
mod file_handle;
5253
mod inode_store;
5354
mod mount_fd;
@@ -888,93 +889,6 @@ impl<S: BitmapSlice + Send + Sync + 'static> BackendFileSystem for PassthroughFs
888889
}
889890
}
890891

891-
macro_rules! scoped_cred {
892-
($name:ident, $ty:ty, $syscall_nr:expr) => {
893-
#[derive(Debug)]
894-
pub(crate) struct $name;
895-
896-
impl $name {
897-
// Changes the effective uid/gid of the current thread to `val`. Changes
898-
// the thread's credentials back to root when the returned struct is dropped.
899-
fn new(val: $ty) -> io::Result<Option<$name>> {
900-
if val == 0 {
901-
// Nothing to do since we are already uid 0.
902-
return Ok(None);
903-
}
904-
905-
// We want credential changes to be per-thread because otherwise
906-
// we might interfere with operations being carried out on other
907-
// threads with different uids/gids. However, posix requires that
908-
// all threads in a process share the same credentials. To do this
909-
// libc uses signals to ensure that when one thread changes its
910-
// credentials the other threads do the same thing.
911-
//
912-
// So instead we invoke the syscall directly in order to get around
913-
// this limitation. Another option is to use the setfsuid and
914-
// setfsgid systems calls. However since those calls have no way to
915-
// return an error, it's preferable to do this instead.
916-
917-
// This call is safe because it doesn't modify any memory and we
918-
// check the return value.
919-
let res = unsafe { libc::syscall($syscall_nr, -1, val, -1) };
920-
if res == 0 {
921-
Ok(Some($name))
922-
} else {
923-
Err(io::Error::last_os_error())
924-
}
925-
}
926-
}
927-
928-
impl Drop for $name {
929-
fn drop(&mut self) {
930-
let res = unsafe { libc::syscall($syscall_nr, -1, 0, -1) };
931-
if res < 0 {
932-
error!(
933-
"fuse: failed to change credentials back to root: {}",
934-
io::Error::last_os_error(),
935-
);
936-
}
937-
}
938-
}
939-
};
940-
}
941-
scoped_cred!(ScopedUid, libc::uid_t, libc::SYS_setresuid);
942-
scoped_cred!(ScopedGid, libc::gid_t, libc::SYS_setresgid);
943-
944-
fn set_creds(
945-
uid: libc::uid_t,
946-
gid: libc::gid_t,
947-
) -> io::Result<(Option<ScopedUid>, Option<ScopedGid>)> {
948-
// We have to change the gid before we change the uid because if we change the uid first then we
949-
// lose the capability to change the gid. However changing back can happen in any order.
950-
ScopedGid::new(gid).and_then(|gid| Ok((ScopedUid::new(uid)?, gid)))
951-
}
952-
953-
struct CapFsetid {}
954-
955-
impl Drop for CapFsetid {
956-
fn drop(&mut self) {
957-
if let Err(e) = caps::raise(None, caps::CapSet::Effective, caps::Capability::CAP_FSETID) {
958-
error!("fail to restore thread cap_fsetid: {}", e);
959-
};
960-
}
961-
}
962-
963-
fn drop_cap_fsetid() -> io::Result<Option<CapFsetid>> {
964-
if !caps::has_cap(None, caps::CapSet::Effective, caps::Capability::CAP_FSETID)
965-
.map_err(|_e| io::Error::new(io::ErrorKind::PermissionDenied, "no CAP_FSETID capability"))?
966-
{
967-
return Ok(None);
968-
}
969-
caps::drop(None, caps::CapSet::Effective, caps::Capability::CAP_FSETID).map_err(|_e| {
970-
io::Error::new(
971-
io::ErrorKind::PermissionDenied,
972-
"failed to drop CAP_FSETID capability",
973-
)
974-
})?;
975-
Ok(Some(CapFsetid {}))
976-
}
977-
978892
#[cfg(test)]
979893
mod tests {
980894
use super::*;

0 commit comments

Comments
 (0)