summaryrefslogtreecommitdiff
path: root/subprojects/gstreamer/libs/gst/helpers/ptp/privileges.rs
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/gstreamer/libs/gst/helpers/ptp/privileges.rs')
-rw-r--r--subprojects/gstreamer/libs/gst/helpers/ptp/privileges.rs268
1 files changed, 172 insertions, 96 deletions
diff --git a/subprojects/gstreamer/libs/gst/helpers/ptp/privileges.rs b/subprojects/gstreamer/libs/gst/helpers/ptp/privileges.rs
index 66e5c954e1..7ab567714c 100644
--- a/subprojects/gstreamer/libs/gst/helpers/ptp/privileges.rs
+++ b/subprojects/gstreamer/libs/gst/helpers/ptp/privileges.rs
@@ -10,125 +10,184 @@
use crate::error::Error;
-/// Drop all additional permissions / capabilities the current process might have as they're not
-/// needed anymore.
-///
-/// This does nothing if no such mechanism is implemented / selected for the target platform.
-pub fn drop() -> Result<(), Error> {
- #[cfg(ptp_helper_permissions = "setcap")]
- {
- // Drop all current capabilities of the process.
+#[cfg(ptp_helper_permissions = "setcap")]
+mod setcap {
+ use super::*;
- use std::io;
+ use crate::{error::Context, ffi::unix::setcaps::*};
+ use std::io;
- use crate::{bail, ffi::unix::setcaps::*};
+ struct Cap(cap_t);
- struct Cap(cap_t);
- impl Drop for Cap {
- fn drop(&mut self) {
- // SAFETY: The capabilities are valid by construction and are only dropped
- // once here.
- unsafe {
- let _ = cap_free(self.0);
- }
+ impl Drop for Cap {
+ fn drop(&mut self) {
+ // SAFETY: The capabilities are valid by construction and are only dropped
+ // once here.
+ unsafe {
+ let _ = cap_free(self.0);
}
}
+ }
- // SAFETY: There are 3 steps here
- // 1. Get the current capabilities of the process. This
- // returns NULL on error or otherwise newly allocated capabilities that have to be
- // freed again in the end. For that purpose we wrap them in the Cap struct.
- //
- // 2. Clearing all current capabilities. This requires a valid capabilities pointer,
- // which we have at this point by construction.
- //
- // 3. Setting the current process's capabilities, which is only affecting the current
- // thread unfortunately. At this point, no other threads were started yet so this is
- // not a problem. Also the capabilities pointer is still valid by construction.
- //
- // On every return path, the capabilities are going to be freed.
- unsafe {
- let c = cap_get_proc();
- if c.is_null() {
- bail!(
- source: io::Error::last_os_error(),
- "Failed to get current process capabilities"
- );
+ impl Cap {
+ /// Get the current process' capabilities.
+ fn get_proc() -> io::Result<Self> {
+ // SAFETY: Get the current capabilities of the process. This returns NULL on error or
+ // otherwise newly allocated capabilities that have to be freed again in the end. For
+ // that purpose we wrap them in the Cap struct.
+ unsafe {
+ let c = cap_get_proc();
+ if c.is_null() {
+ return Err(io::Error::last_os_error());
+ }
+
+ Ok(Cap(c))
}
+ }
- let c = Cap(c);
- if cap_clear(c.0) != 0 {
- bail!(
- source: io::Error::last_os_error(),
- "Failed to clear capabilities"
- );
+ /// Clear all capabilities.
+ fn clear(&mut self) -> io::Result<()> {
+ // SAFETY: Clearing all current capabilities. This requires a valid capabilities
+ // pointer, which we have at this point by construction.
+ unsafe {
+ if cap_clear(self.0) != 0 {
+ return Err(io::Error::last_os_error());
+ }
}
- if cap_set_proc(c.0) != 0 {
- bail!(
- source: io::Error::last_os_error(),
- "Failed to set current process capabilities"
- );
+
+ Ok(())
+ }
+
+ /// Set current process' capabilities.
+ fn set_proc(&self) -> io::Result<()> {
+ // SAFETY: Setting the current process's capabilities, which is only affecting the
+ // current thread unfortunately. At this point, no other threads were started yet so
+ // this is not a problem. Also the capabilities pointer is still valid by construction.
+ unsafe {
+ if cap_set_proc(self.0) != 0 {
+ return Err(io::Error::last_os_error());
+ }
}
+ Ok(())
}
}
- #[cfg(ptp_helper_permissions = "setuid-root")]
- {
- // Drop the process's UID/GID from root to the configured user/group or the user "nobody".
-
- use std::{ffi::CString, io};
-
- use crate::{bail, error::Context, ffi::unix::setuid_root::*};
-
- fn get_uid_gid_for_user(name: &str) -> io::Result<(uid_t, gid_t)> {
- let name_cstr = CString::new(name).unwrap();
-
- loop {
- // SAFETY: getpwnam() requires a NUL-terminated user name string and
- // returns either the user information in static storage, or NULL on error.
- // In case of EINTR, getting the user information can be retried.
- //
- // The user information is stored in static storage so might change if something
- // else calls related functions. As this is the only thread up to this point and we
- // just extract two integers from it there is no such possibility.
- unsafe {
- let pw = getpwnam(name_cstr.as_ptr());
- if pw.is_null() {
- let err = io::Error::last_os_error();
- if err.kind() == io::ErrorKind::Interrupted {
- continue;
- }
- return Err(err);
+
+ /// Drop all current capabilities of the process.
+ pub fn drop() -> Result<(), Error> {
+ let mut c = Cap::get_proc().context("Failed to get current process capabilities")?;
+ c.clear().context("Failed to clear capabilities")?;
+ c.set_proc()
+ .context("Failed to set current process capabilities")?;
+
+ Ok(())
+ }
+
+ #[cfg(test)]
+ mod test {
+ #[test]
+ fn test_get_set_same_and_clear_cap() {
+ let mut c = super::Cap::get_proc().unwrap();
+ // Setting the same capabilities should always succeed
+ c.set_proc().unwrap();
+ c.clear().unwrap();
+ }
+ }
+}
+
+#[cfg(ptp_helper_permissions = "setuid-root")]
+mod setuid_root {
+ use super::*;
+
+ use crate::{bail, error::Context, ffi::unix::setuid_root::*};
+ use std::{ffi::CString, io};
+
+ /// Retrieve UID and GID for the given username.
+ fn get_uid_gid_for_user(name: &str) -> io::Result<(uid_t, gid_t)> {
+ let name_cstr = CString::new(name).unwrap();
+
+ loop {
+ // SAFETY: getpwnam() requires a NUL-terminated user name string and
+ // returns either the user information in static storage, or NULL on error.
+ // In case of EINTR, getting the user information can be retried.
+ //
+ // The user information is stored in static storage so might change if something
+ // else calls related functions. As this is the only thread up to this point and we
+ // just extract two integers from it there is no such possibility.
+ unsafe {
+ let pw = getpwnam(name_cstr.as_ptr());
+ if pw.is_null() {
+ let err = io::Error::last_os_error();
+ if err.kind() == io::ErrorKind::Interrupted {
+ continue;
+ }
+ if err.raw_os_error() == Some(0) {
+ return Err(io::Error::from(io::ErrorKind::NotFound));
}
- return Ok(((*pw).pw_uid, (*pw).pw_gid));
+
+ return Err(err);
}
+ return Ok(((*pw).pw_uid, (*pw).pw_gid));
}
}
+ }
- fn get_gid_for_group(name: &str) -> io::Result<gid_t> {
- let name_cstr = CString::new(name).unwrap();
- loop {
- // SAFETY: getgrnam() requires a NUL-terminated group name string and
- // returns either the group information in static storage, or NULL on error.
- // In case of EINTR, getting the group information can be retried.
- //
- // The user information is stored in static storage so might change if something
- // else calls related functions. As this is the only thread up to this point and we
- // just extract two integers from it there is no such possibility.
- unsafe {
- let grp = getgrnam(name_cstr.as_ptr());
- if grp.is_null() {
- let err = io::Error::last_os_error();
- if err.kind() == io::ErrorKind::Interrupted {
- continue;
- }
- return Err(err);
+ /// Retrieve GID for the given group name.
+ fn get_gid_for_group(name: &str) -> io::Result<gid_t> {
+ let name_cstr = CString::new(name).unwrap();
+ loop {
+ // SAFETY: getgrnam() requires a NUL-terminated group name string and
+ // returns either the group information in static storage, or NULL on error.
+ // In case of EINTR, getting the group information can be retried.
+ //
+ // The user information is stored in static storage so might change if something
+ // else calls related functions. As this is the only thread up to this point and we
+ // just extract two integers from it there is no such possibility.
+ unsafe {
+ let grp = getgrnam(name_cstr.as_ptr());
+ if grp.is_null() {
+ let err = io::Error::last_os_error();
+ if err.kind() == io::ErrorKind::Interrupted {
+ continue;
+ }
+ if err.raw_os_error() == Some(0) {
+ return Err(io::Error::from(io::ErrorKind::NotFound));
}
- return Ok((*grp).gr_gid);
+ return Err(err);
}
+
+ return Ok((*grp).gr_gid);
+ }
+ }
+ }
+
+ #[cfg(test)]
+ mod test {
+ #[test]
+ fn test_get_uid_gid_for_user() {
+ match super::get_uid_gid_for_user("root") {
+ Ok(_) => (),
+ Err(err) if err.kind() != std::io::ErrorKind::NotFound => {
+ panic!("{}", err);
+ }
+ _ => (),
}
}
+ #[test]
+ fn test_get_gid_for_group() {
+ match super::get_gid_for_group("root") {
+ Ok(_) => (),
+ Err(err) if err.kind() != std::io::ErrorKind::NotFound => {
+ panic!("{}", err);
+ }
+ _ => (),
+ }
+ }
+ }
+
+ /// Drop the process's UID/GID from root to the configured user/group or the user "nobody".
+ pub fn drop() -> Result<(), Error> {
let username = gst_ptp_helper_conf::PTP_HELPER_SETUID_USER.unwrap_or("nobody");
let (uid, gid) = get_uid_gid_for_user(username)
@@ -163,6 +222,23 @@ pub fn drop() -> Result<(), Error> {
bail!(source: err, "Failed to set user id {} for process", uid);
}
}
+
+ Ok(())
+ }
+}
+
+/// Drop all additional permissions / capabilities the current process might have as they're not
+/// needed anymore.
+///
+/// This does nothing if no such mechanism is implemented / selected for the target platform.
+pub fn drop() -> Result<(), Error> {
+ #[cfg(ptp_helper_permissions = "setcap")]
+ {
+ setcap::drop()?;
+ }
+ #[cfg(ptp_helper_permissions = "setuid-root")]
+ {
+ setuid_root::drop()?;
}
Ok(())