summaryrefslogtreecommitdiff
path: root/subprojects/gstreamer/libs/gst
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/gstreamer/libs/gst')
-rw-r--r--subprojects/gstreamer/libs/gst/helpers/ptp/ffi.rs11
-rw-r--r--subprojects/gstreamer/libs/gst/helpers/ptp/io.rs350
-rw-r--r--subprojects/gstreamer/libs/gst/helpers/ptp/meson.build11
-rw-r--r--subprojects/gstreamer/libs/gst/helpers/ptp/net.rs31
-rw-r--r--subprojects/gstreamer/libs/gst/helpers/ptp/privileges.rs268
-rw-r--r--subprojects/gstreamer/libs/gst/helpers/ptp/rand.rs260
6 files changed, 723 insertions, 208 deletions
diff --git a/subprojects/gstreamer/libs/gst/helpers/ptp/ffi.rs b/subprojects/gstreamer/libs/gst/helpers/ptp/ffi.rs
index 7b3ca372db..785a964cc2 100644
--- a/subprojects/gstreamer/libs/gst/helpers/ptp/ffi.rs
+++ b/subprojects/gstreamer/libs/gst/helpers/ptp/ffi.rs
@@ -224,6 +224,9 @@ pub mod unix {
pub fn freeifaddrs(ifa: *mut ifaddrs);
pub fn setpriority(which: c_int, who: c_int, prio: c_int) -> c_int;
+
+ #[cfg(test)]
+ pub fn pipe(pipefd: *mut i32) -> i32;
}
#[cfg(any(target_os = "linux", target_os = "solaris", target_os = "illumos"))]
@@ -606,6 +609,14 @@ pub mod windows {
pub fn SetThreadPriority(pthread: HANDLE, npriority: i32) -> i32;
pub fn GetCurrentThread() -> HANDLE;
+
+ #[cfg(test)]
+ pub fn CreatePipe(
+ hreadpipe: *mut HANDLE,
+ hwritepipe: *mut HANDLE,
+ lppipeattributes: *mut c_void,
+ nsize: u32,
+ ) -> i32;
}
pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002;
diff --git a/subprojects/gstreamer/libs/gst/helpers/ptp/io.rs b/subprojects/gstreamer/libs/gst/helpers/ptp/io.rs
index 7b0e064d51..d46572437d 100644
--- a/subprojects/gstreamer/libs/gst/helpers/ptp/io.rs
+++ b/subprojects/gstreamer/libs/gst/helpers/ptp/io.rs
@@ -8,8 +8,23 @@
//
// SPDX-License-Identifier: MPL-2.0
+/// Result of polling the inputs of the `Poll`.
+///
+/// Any input that has data available for reading will be set to `true`, potentially multiple
+/// at once.
+///
+/// Note that reading from the sockets is non-blocking but reading from stdin is blocking so
+/// special care has to be taken to only read as much as is available.
+pub struct PollResult {
+ pub event_socket: bool,
+ pub general_socket: bool,
+ pub stdin: bool,
+}
+
#[cfg(unix)]
mod imp {
+ use super::PollResult;
+
use std::{
io::{self, Read, Write},
net::UdpSocket,
@@ -28,17 +43,81 @@ mod imp {
stdout: Stdout,
}
- /// Result of polling the inputs of the `Poll`.
- ///
- /// Any input that has data available for reading will be set to `true`, potentially multiple
- /// at once.
- ///
- /// Note that reading from the sockets is non-blocking but reading from stdin is blocking so
- /// special care has to be taken to only read as much as is available.
- pub struct PollResult {
- pub event_socket: bool,
- pub general_socket: bool,
- pub stdin: bool,
+ #[cfg(test)]
+ /// A file descriptor pair representing a pipe for testing purposes.
+ pub struct Pipe {
+ pub read: i32,
+ pub write: i32,
+ }
+
+ #[cfg(test)]
+ impl Pipe {
+ fn new() -> io::Result<Self> {
+ // SAFETY: Requires two integers to be passed in and creates the read
+ // and write end of a pipe.
+ unsafe {
+ let mut fds = std::mem::MaybeUninit::<[i32; 2]>::uninit();
+ let res = pipe(fds.as_mut_ptr() as *mut i32);
+ if res == 0 {
+ let fds = fds.assume_init();
+ Ok(Pipe {
+ read: fds[0],
+ write: fds[1],
+ })
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+ }
+
+ #[cfg(test)]
+ impl Drop for Pipe {
+ fn drop(&mut self) {
+ // SAFETY: Only ever created with valid fds
+ unsafe {
+ close(self.read);
+ close(self.write);
+ }
+ }
+ }
+
+ #[cfg(test)]
+ impl Read for Pipe {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ // SAFETY: read() requires a valid fd and a mutable buffer with the given size.
+ // The fd is valid by construction as is the buffer.
+ //
+ // read() will return the number of bytes read or a negative value on errors.
+ let res = unsafe { read(self.read, buf.as_mut_ptr(), buf.len()) };
+
+ if res < 0 {
+ Err(std::io::Error::last_os_error())
+ } else {
+ Ok(res as usize)
+ }
+ }
+ }
+
+ #[cfg(test)]
+ impl Write for Pipe {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ // SAFETY: write() requires a valid fd and a mutable buffer with the given size.
+ // The fd is valid by construction as is the buffer.
+ //
+ // write() will return the number of bytes written or a negative value on errors.
+ let res = unsafe { write(self.write, buf.as_ptr(), buf.len()) };
+
+ if res == -1 {
+ Err(std::io::Error::last_os_error())
+ } else {
+ Ok(res as usize)
+ }
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
}
impl Poll {
@@ -65,6 +144,29 @@ mod imp {
})
}
+ #[cfg(test)]
+ /// Create a new `Poll` instance for testing purposes.
+ ///
+ /// The returned `Pipe`s are for stdin and stdout.
+ pub fn new_test(
+ event_socket: UdpSocket,
+ general_socket: UdpSocket,
+ ) -> Result<(Self, Pipe, Pipe), Error> {
+ let stdin = Pipe::new().unwrap();
+ let stdout = Pipe::new().unwrap();
+
+ Ok((
+ Self {
+ event_socket,
+ general_socket,
+ stdin: Stdin(stdin.read),
+ stdout: Stdout(stdout.write),
+ },
+ stdin,
+ stdout,
+ ))
+ }
+
/// Mutable reference to the event socket.
pub fn event_socket(&mut self) -> &mut UdpSocket {
&mut self.event_socket
@@ -210,6 +312,8 @@ mod imp {
#[cfg(windows)]
mod imp {
+ use super::PollResult;
+
use std::{
cmp,
io::{self, Read, Write},
@@ -270,25 +374,111 @@ mod imp {
}
}
- /// Result of polling the inputs of the `Poll`.
- ///
- /// Any input that has data available for reading will be set to `true`, potentially multiple
- /// at once.
- ///
- /// Note that reading from the sockets is non-blocking but reading from stdin is blocking so
- /// special care has to be taken to only read as much as is available.
- pub struct PollResult {
- pub event_socket: bool,
- pub general_socket: bool,
- pub stdin: bool,
+ #[cfg(test)]
+ pub struct Pipe {
+ read: HANDLE,
+ write: HANDLE,
}
- impl Poll {
- /// Create a new `Poll` instance from the two sockets.
- pub fn new(event_socket: UdpSocket, general_socket: UdpSocket) -> Result<Self, Error> {
- let stdin = Stdin::acquire().context("Failure acquiring stdin handle")?;
- let stdout = Stdout::acquire().context("Failed acquiring stdout handle")?;
+ #[cfg(test)]
+ impl Drop for Pipe {
+ fn drop(&mut self) {
+ // SAFETY: Both handles are by construction valid up there.
+ unsafe {
+ CloseHandle(self.read);
+ CloseHandle(self.write);
+ }
+ }
+ }
+ #[cfg(test)]
+ impl Pipe {
+ fn new() -> io::Result<Self> {
+ // SAFETY: On success returns a non-zero integer and stores read/write handles in the
+ // two out pointers, which will have to be closed again later.
+ unsafe {
+ let mut readpipe = mem::MaybeUninit::uninit();
+ let mut writepipe = mem::MaybeUninit::uninit();
+
+ let res = CreatePipe(
+ readpipe.as_mut_ptr(),
+ writepipe.as_mut_ptr(),
+ ptr::null_mut(),
+ 0,
+ );
+
+ if res != 0 {
+ Ok(Self {
+ read: readpipe.assume_init(),
+ write: writepipe.assume_init(),
+ })
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+ }
+
+ #[cfg(test)]
+ impl Read for Pipe {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ // SAFETY: Reads the given number of bytes into the buffer from the stdin handle.
+ unsafe {
+ let mut lpnumberofbytesread = mem::MaybeUninit::uninit();
+ let res = ReadFile(
+ self.read,
+ buf.as_mut_ptr(),
+ cmp::min(buf.len() as u32, u32::MAX) as u32,
+ lpnumberofbytesread.as_mut_ptr(),
+ ptr::null_mut(),
+ );
+
+ if res == 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(lpnumberofbytesread.assume_init() as usize)
+ }
+ }
+ }
+ }
+
+ #[cfg(test)]
+ impl Write for Pipe {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ // SAFETY: Writes the given number of bytes to stdout or at most u32::MAX. On error
+ // zero is returned, otherwise the number of bytes written is set accordingly and
+ // returned.
+ unsafe {
+ let mut lpnumberofbyteswritten = mem::MaybeUninit::uninit();
+ let res = WriteFile(
+ self.write,
+ buf.as_ptr(),
+ cmp::min(buf.len() as u32, u32::MAX) as u32,
+ lpnumberofbyteswritten.as_mut_ptr(),
+ ptr::null_mut(),
+ );
+
+ if res == 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(lpnumberofbyteswritten.assume_init() as usize)
+ }
+ }
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+ }
+
+ impl Poll {
+ /// Internal constructor.
+ pub fn new_internal(
+ event_socket: UdpSocket,
+ general_socket: UdpSocket,
+ stdin: Stdin,
+ stdout: Stdout,
+ ) -> Result<Self, Error> {
// Create event objects for the readability of the sockets.
let event_socket_event = EventHandle::new().context("Failed creating WSA event")?;
let general_socket_event = EventHandle::new().context("Failed creating WSA event")?;
@@ -329,6 +519,35 @@ mod imp {
})
}
+ /// Create a new `Poll` instance from the two sockets.
+ pub fn new(event_socket: UdpSocket, general_socket: UdpSocket) -> Result<Self, Error> {
+ let stdin = Stdin::acquire().context("Failure acquiring stdin handle")?;
+ let stdout = Stdout::acquire().context("Failed acquiring stdout handle")?;
+
+ Self::new_internal(event_socket, general_socket, stdin, stdout)
+ }
+
+ #[cfg(test)]
+ /// Create a new `Poll` instance for testing purposes.
+ ///
+ /// The returned `Pipe`s are for stdin and stdout.
+ pub fn new_test(
+ event_socket: UdpSocket,
+ general_socket: UdpSocket,
+ ) -> Result<(Self, Pipe, Pipe), Error> {
+ let stdin_pipe = Pipe::new().unwrap();
+ let stdout_pipe = Pipe::new().unwrap();
+
+ let stdin =
+ Stdin::from_handle(stdin_pipe.read).context("Failure acquiring stdin handle")?;
+ let stdout =
+ Stdout::from_handle(stdout_pipe.write).context("Failed acquiring stdout handle")?;
+
+ let poll = Self::new_internal(event_socket, general_socket, stdin, stdout)?;
+
+ Ok((poll, stdin_pipe, stdout_pipe))
+ }
+
/// Mutable reference to the event socket.
pub fn event_socket(&mut self) -> &mut UdpSocket {
&mut self.event_socket
@@ -516,6 +735,11 @@ mod imp {
handle
};
+
+ Self::from_handle(handle)
+ }
+
+ fn from_handle(handle: HANDLE) -> Result<Self, Error> {
// SAFETY: GetFileType() is safe to call on any valid handle.
let type_ = unsafe { GetFileType(handle) };
@@ -717,6 +941,11 @@ mod imp {
handle
};
+
+ Self::from_handle(handle)
+ }
+
+ fn from_handle(handle: HANDLE) -> Result<Self, Error> {
// SAFETY: GetFileType() is safe to call on any valid handle.
let type_ = unsafe { GetFileType(handle) };
@@ -766,4 +995,69 @@ mod imp {
}
}
-pub use self::imp::{Poll, PollResult, Stdin, Stdout};
+pub use self::imp::{Poll, Stdin, Stdout};
+
+#[cfg(test)]
+mod test {
+ #[test]
+ fn test_poll() {
+ use std::io::prelude::*;
+
+ let event_socket = std::net::UdpSocket::bind(std::net::SocketAddr::from((
+ std::net::Ipv4Addr::LOCALHOST,
+ 0,
+ )))
+ .unwrap();
+ let event_port = event_socket.local_addr().unwrap().port();
+
+ let general_socket = std::net::UdpSocket::bind(std::net::SocketAddr::from((
+ std::net::Ipv4Addr::LOCALHOST,
+ 0,
+ )))
+ .unwrap();
+ let general_port = general_socket.local_addr().unwrap().port();
+
+ let send_socket = std::net::UdpSocket::bind(std::net::SocketAddr::from((
+ std::net::Ipv4Addr::LOCALHOST,
+ 0,
+ )))
+ .unwrap();
+
+ let (mut poll, mut stdin, _stdout) =
+ super::Poll::new_test(event_socket, general_socket).unwrap();
+
+ let mut buf = [0u8; 4];
+
+ for _ in 0..10 {
+ send_socket
+ .send_to(&[1, 2, 3, 4], (std::net::Ipv4Addr::LOCALHOST, event_port))
+ .unwrap();
+ let res = poll.poll().unwrap();
+ assert!(res.event_socket);
+ assert!(!res.general_socket);
+ assert!(!res.stdin);
+ assert_eq!(poll.event_socket().recv(&mut buf).unwrap(), 4);
+ assert_eq!(buf, [1, 2, 3, 4]);
+
+ send_socket
+ .send_to(&[1, 2, 3, 4], (std::net::Ipv4Addr::LOCALHOST, general_port))
+ .unwrap();
+ let res = poll.poll().unwrap();
+ assert!(!res.event_socket);
+ assert!(res.general_socket);
+ assert!(!res.stdin);
+ assert_eq!(poll.general_socket().recv(&mut buf).unwrap(), 4);
+ assert_eq!(buf, [1, 2, 3, 4]);
+
+ stdin.write_all(&[1, 2, 3, 4]).unwrap();
+ let res = poll.poll().unwrap();
+ assert!(!res.event_socket);
+ assert!(!res.general_socket);
+ assert!(res.stdin);
+ poll.stdin().read_exact(&mut buf).unwrap();
+ assert_eq!(buf, [1, 2, 3, 4]);
+ }
+
+ drop(poll);
+ }
+}
diff --git a/subprojects/gstreamer/libs/gst/helpers/ptp/meson.build b/subprojects/gstreamer/libs/gst/helpers/ptp/meson.build
index 7ebc2c8964..55f1c6a106 100644
--- a/subprojects/gstreamer/libs/gst/helpers/ptp/meson.build
+++ b/subprojects/gstreamer/libs/gst/helpers/ptp/meson.build
@@ -98,7 +98,6 @@ conf_lib_rs = configure_file(input : 'conf_lib.rs.in',
conf = static_library('gst_ptp_helper_conf', conf_lib_rs,
override_options : ['rust_std=2018'],
- rust_args : ['-Cpanic=abort'],
rust_crate_type : 'rlib')
exe = executable('gst-ptp-helper', 'main.rs',
@@ -109,6 +108,16 @@ exe = executable('gst-ptp-helper', 'main.rs',
install_dir : helpers_install_dir,
install : true)
+exe_test = executable('gst-ptp-helper-test', 'main.rs',
+ override_options : ['rust_std=2018'],
+ rust_args : ['--test', rust_args],
+ dependencies : [cap_dep],
+ link_with : conf,
+ install_dir : helpers_install_dir,
+ install : true)
+
+test('gst-ptp-helper-test', exe_test, protocol : 'rust')
+
if host_system != 'windows'
meson.add_install_script('ptp_helper_post_install.sh',
helpers_install_dir, with_ptp_helper_permissions,
diff --git a/subprojects/gstreamer/libs/gst/helpers/ptp/net.rs b/subprojects/gstreamer/libs/gst/helpers/ptp/net.rs
index e3b0bbddeb..fc9b263072 100644
--- a/subprojects/gstreamer/libs/gst/helpers/ptp/net.rs
+++ b/subprojects/gstreamer/libs/gst/helpers/ptp/net.rs
@@ -665,3 +665,34 @@ mod imp {
}
pub use imp::*;
+
+#[cfg(test)]
+mod test {
+ #[test]
+ fn test_query_interfaces() {
+ let ifaces = super::query_interfaces().unwrap();
+ for iface in ifaces {
+ assert!(!iface.name.is_empty());
+ assert_ne!(iface.index, 0);
+ assert!(!iface.ip_addr.is_unspecified());
+ }
+ }
+
+ #[test]
+ fn test_join_multicast() {
+ let ifaces = super::query_interfaces().unwrap();
+ let iface = if ifaces.is_empty() {
+ return;
+ } else {
+ &ifaces[0]
+ };
+
+ let socket = std::net::UdpSocket::bind(std::net::SocketAddr::from((
+ std::net::Ipv4Addr::UNSPECIFIED,
+ 0,
+ )))
+ .unwrap();
+ super::set_reuse(&socket);
+ super::join_multicast_v4(&socket, &std::net::Ipv4Addr::new(224, 0, 0, 1), iface).unwrap();
+ }
+}
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(())
diff --git a/subprojects/gstreamer/libs/gst/helpers/ptp/rand.rs b/subprojects/gstreamer/libs/gst/helpers/ptp/rand.rs
index 48cda11ec5..a89a371786 100644
--- a/subprojects/gstreamer/libs/gst/helpers/ptp/rand.rs
+++ b/subprojects/gstreamer/libs/gst/helpers/ptp/rand.rs
@@ -8,70 +8,31 @@
//
// SPDX-License-Identifier: MPL-2.0
-/// Returns a random'ish 64 bit value.
-pub fn rand() -> [u8; 8] {
- #[cfg(unix)]
- {
- // Try getrandom syscall or otherwise first on Linux
- #[cfg(target_os = "linux")]
- {
- use std::io::Read;
-
- use crate::ffi::unix::linux::*;
-
- // Depends on us knowing the syscall number
- if SYS_getrandom != 0 {
- struct GetRandom;
-
- impl Read for GetRandom {
- fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
- // SAFETY: `getrandom` syscall fills up to the requested amount of bytes of
- // the provided memory and returns the number of bytes or a negative value
- // on errors.
- unsafe {
- let res = syscall(SYS_getrandom, buf.as_mut_ptr(), buf.len(), 0u32);
- if res < 0 {
- Err(std::io::Error::last_os_error())
- } else {
- Ok(res as usize)
- }
- }
- }
- }
+#[cfg(unix)]
+mod unix {
+ use std::io;
- let mut r = [0u8; 8];
- if GetRandom.read_exact(&mut r).is_ok() {
- return r;
- }
- }
+ #[cfg(target_os = "linux")]
+ /// Try using the getrandom syscall on Linux.
+ pub fn getrandom() -> io::Result<[u8; 8]> {
+ use std::io::Read;
+
+ use crate::ffi::unix::linux::*;
+
+ // Depends on us knowing the syscall number
+ if SYS_getrandom == 0 {
+ return Err(io::Error::from(io::ErrorKind::Unsupported));
}
- // Otherwise try /dev/urandom
- {
- use crate::ffi::unix::*;
- use std::{io::Read, os::raw::c_int};
-
- struct Fd(c_int);
-
- impl Drop for Fd {
- fn drop(&mut self) {
- // SAFETY: The fd is valid by construction below and closed by this at
- // most once.
- unsafe {
- // Return value is intentionally ignored as there's nothing that
- // can be done on errors anyway.
- let _ = close(self.0);
- }
- }
- }
+ struct GetRandom;
- impl Read for Fd {
- fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
- // SAFETY: read() requires a valid fd and a mutable buffer with the given size.
- // The fd is valid by construction as is the buffer.
- //
- // read() will return the number of bytes read or a negative value on errors.
- let res = unsafe { read(self.0, buf.as_mut_ptr(), buf.len()) };
+ impl Read for GetRandom {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+ // SAFETY: `getrandom` syscall fills up to the requested amount of bytes of
+ // the provided memory and returns the number of bytes or a negative value
+ // on errors.
+ unsafe {
+ let res = syscall(SYS_getrandom, buf.as_mut_ptr(), buf.len(), 0u32);
if res < 0 {
Err(std::io::Error::last_os_error())
} else {
@@ -79,37 +40,108 @@ pub fn rand() -> [u8; 8] {
}
}
}
+ }
- let fd = loop {
- // SAFETY: open() requires a NUL-terminated file path and will
- // return an integer in any case. A negative value is an invalid fd
- // and signals an error. On EINTR, opening can be retried.
- let fd = unsafe { open(b"/dev/urandom\0".as_ptr(), O_RDONLY) };
- if fd < 0 {
- let err = std::io::Error::last_os_error();
- if err.kind() == std::io::ErrorKind::Interrupted {
- continue;
- }
+ let mut r = [0u8; 8];
+ GetRandom.read_exact(&mut r)?;
- break None;
+ Ok(r)
+ }
+
+ /// Try reading random numbers from /dev/urandom.
+ pub fn dev_urandom() -> io::Result<[u8; 8]> {
+ use crate::ffi::unix::*;
+ use std::{io::Read, os::raw::c_int};
+
+ struct Fd(c_int);
+
+ impl Drop for Fd {
+ fn drop(&mut self) {
+ // SAFETY: The fd is valid by construction below and closed by this at
+ // most once.
+ unsafe {
+ // Return value is intentionally ignored as there's nothing that
+ // can be done on errors anyway.
+ let _ = close(self.0);
+ }
+ }
+ }
+
+ impl Read for Fd {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+ // SAFETY: read() requires a valid fd and a mutable buffer with the given size.
+ // The fd is valid by construction as is the buffer.
+ //
+ // read() will return the number of bytes read or a negative value on errors.
+ let res = unsafe { read(self.0, buf.as_mut_ptr(), buf.len()) };
+ if res < 0 {
+ Err(std::io::Error::last_os_error())
+ } else {
+ Ok(res as usize)
+ }
+ }
+ }
+
+ let mut fd = loop {
+ // SAFETY: open() requires a NUL-terminated file path and will
+ // return an integer in any case. A negative value is an invalid fd
+ // and signals an error. On EINTR, opening can be retried.
+ let fd = unsafe { open(b"/dev/urandom\0".as_ptr(), O_RDONLY) };
+ if fd < 0 {
+ let err = std::io::Error::last_os_error();
+ if err.kind() == std::io::ErrorKind::Interrupted {
+ continue;
}
- break Some(Fd(fd));
- };
+ return Err(err);
+ }
+
+ break Fd(fd);
+ };
- if let Some(mut fd) = fd {
- let mut r = [0u8; 8];
+ let mut r = [0u8; 8];
+ fd.read_exact(&mut r)?;
+
+ Ok(r)
+ }
- if fd.read_exact(&mut r).is_ok() {
- return r;
+ #[cfg(test)]
+ mod test {
+ #[test]
+ fn test_dev_urandom() {
+ match super::dev_urandom() {
+ Ok(n) => {
+ assert_ne!(n, [0u8; 8]);
}
+ Err(err) if err.kind() != std::io::ErrorKind::NotFound => {
+ panic!("{}", err);
+ }
+ _ => (),
+ }
+ }
+
+ #[cfg(target_os = "linux")]
+ #[test]
+ fn test_getrandom() {
+ match super::getrandom() {
+ Ok(n) => {
+ assert_ne!(n, [0u8; 8]);
+ }
+ Err(err) if err.kind() != std::io::ErrorKind::Unsupported => {
+ panic!("{}", err);
+ }
+ _ => (),
}
}
}
- #[cfg(windows)]
- {
- // Try BCryptGenRandom(), which is available since Windows Vista
- //
+}
+
+#[cfg(windows)]
+mod windows {
+ use std::io;
+
+ /// Call BCryptGenRandom(), which is available since Windows Vista.
+ pub fn bcrypt_gen_random() -> io::Result<[u8; 8]> {
// SAFETY: BCryptGenRandom() fills the provided memory with the requested number of bytes
// and returns 0 on success. In that case, all memory was written and is initialized now.
unsafe {
@@ -125,12 +157,25 @@ pub fn rand() -> [u8; 8] {
BCRYPT_USE_SYSTEM_PREFERRED_RNG,
);
if res == 0 {
- return r.assume_init();
+ Ok(r.assume_init())
+ } else {
+ Err(io::Error::from_raw_os_error(res as i32))
}
}
}
- // As fallback use a combination of the process ID and the current system time
+ #[cfg(test)]
+ mod test {
+ #[test]
+ fn test_bcrypt_gen_random() {
+ let n = super::bcrypt_gen_random().unwrap();
+ assert_ne!(n, [0u8; 8]);
+ }
+ }
+}
+
+/// As fallback use a combination of the process ID and the current system time.
+fn fallback_rand() -> [u8; 8] {
let now = std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.unwrap()
@@ -148,3 +193,52 @@ pub fn rand() -> [u8; 8] {
now[7] ^ now[8] ^ pid[3],
]
}
+
+/// Returns a random'ish 64 bit value.
+pub fn rand() -> [u8; 8] {
+ #[cfg(unix)]
+ {
+ // Try getrandom syscall or otherwise first on Linux
+ #[cfg(target_os = "linux")]
+ {
+ if let Ok(r) = unix::getrandom() {
+ return r;
+ }
+ }
+
+ if let Ok(r) = unix::dev_urandom() {
+ return r;
+ }
+ }
+ #[cfg(windows)]
+ {
+ if let Ok(r) = windows::bcrypt_gen_random() {
+ return r;
+ }
+ }
+
+ fallback_rand()
+}
+
+#[cfg(test)]
+mod test {
+ // While not a very useful test for randomness, we're mostly interested here
+ // in whether the memory is initialized correctly and nothing crashes because
+ // of the usage of unsafe code above. If the memory was not initialized fully
+ // then this test would fail in e.g. valgrind.
+ //
+ // Technically, all zeroes could be returned as a valid random number but that's
+ // extremely unlikely and more likely a bug in the code above.
+
+ #[test]
+ fn test_rand() {
+ let n = super::rand();
+ assert_ne!(n, [0u8; 8]);
+ }
+
+ #[test]
+ fn test_fallback_rand() {
+ let n = super::fallback_rand();
+ assert_ne!(n, [0u8; 8]);
+ }
+}