summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--Cargo.toml2
-rw-r--r--src/sys/socket/mod.rs90
-rw-r--r--test/sys/test_socket.rs46
4 files changed, 120 insertions, 20 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4794c11c..a9c28b4a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
receive offload (GRO) ([#1209](https://github.com/nix-rust/nix/pull/1209))
- Added support for `sendmmsg` and `recvmmsg` calls
(#[1208](https://github.com/nix-rust/nix/pull/1208))
+- Added support for `SCM_CREDS` messages (`UnixCredentials`) on FreeBSD/DragonFly
+ (#[1216](https://github.com/nix-rust/nix/pull/1216))
### Changed
- Changed `fallocate` return type from `c_int` to `()` (#[1201](https://github.com/nix-rust/nix/pull/1201))
diff --git a/Cargo.toml b/Cargo.toml
index 746cefc8..1277fbc4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,7 +16,7 @@ exclude = [
]
[dependencies]
-libc = { version = "0.2.69", features = [ "extra_traits" ] }
+libc = { git = "https://github.com/rust-lang/libc/", features = [ "extra_traits" ] }
bitflags = "1.1"
cfg-if = "0.1.10"
void = "1.0.2"
diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs
index 9b770fa8..1f627614 100644
--- a/src/sys/socket/mod.rs
+++ b/src/sys/socket/mod.rs
@@ -189,12 +189,22 @@ cfg_if! {
if #[cfg(any(target_os = "android", target_os = "linux"))] {
/// Unix credentials of the sending process.
///
- /// This struct is used with the `SO_PEERCRED` ancillary message for UNIX sockets.
+ /// This struct is used with the `SO_PEERCRED` ancillary message
+ /// and the `SCM_CREDENTIALS` control message for UNIX sockets.
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct UnixCredentials(libc::ucred);
impl UnixCredentials {
+ /// Creates a new instance with the credentials of the current process
+ pub fn new() -> Self {
+ UnixCredentials(libc::ucred {
+ pid: crate::unistd::getpid().as_raw(),
+ uid: crate::unistd::getuid().as_raw(),
+ gid: crate::unistd::getgid().as_raw(),
+ })
+ }
+
/// Returns the process identifier
pub fn pid(&self) -> libc::pid_t {
self.0.pid
@@ -222,6 +232,46 @@ cfg_if! {
self.0
}
}
+ } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] {
+ /// Unix credentials of the sending process.
+ ///
+ /// This struct is used with the `SCM_CREDS` ancillary message for UNIX sockets.
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ pub struct UnixCredentials(libc::cmsgcred);
+
+ impl UnixCredentials {
+ /// Returns the process identifier
+ pub fn pid(&self) -> libc::pid_t {
+ self.0.cmcred_pid
+ }
+
+ /// Returns the real user identifier
+ pub fn uid(&self) -> libc::uid_t {
+ self.0.cmcred_uid
+ }
+
+ /// Returns the effective user identifier
+ pub fn euid(&self) -> libc::uid_t {
+ self.0.cmcred_euid
+ }
+
+ /// Returns the real group identifier
+ pub fn gid(&self) -> libc::gid_t {
+ self.0.cmcred_gid
+ }
+
+ /// Returns a list group identifiers (the first one being the effective GID)
+ pub fn groups(&self) -> &[libc::gid_t] {
+ unsafe { slice::from_raw_parts(self.0.cmcred_groups.as_ptr() as *const libc::gid_t, self.0.cmcred_ngroups as _) }
+ }
+ }
+
+ impl From<libc::cmsgcred> for UnixCredentials {
+ fn from(cred: libc::cmsgcred) -> Self {
+ UnixCredentials(cred)
+ }
+ }
}
}
@@ -369,6 +419,10 @@ pub enum ControlMessageOwned {
/// [`ControlMessage::ScmCredentials`][#enum.ControlMessage.html#variant.ScmCredentials]
#[cfg(any(target_os = "android", target_os = "linux"))]
ScmCredentials(UnixCredentials),
+ /// Received version of
+ /// [`ControlMessage::ScmCreds`][#enum.ControlMessage.html#variant.ScmCreds]
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ScmCreds(UnixCredentials),
/// A message of type `SCM_TIMESTAMP`, containing the time the
/// packet was received by the kernel.
///
@@ -510,6 +564,11 @@ impl ControlMessageOwned {
let cred: libc::ucred = ptr::read_unaligned(p as *const _);
ControlMessageOwned::ScmCredentials(cred.into())
}
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ (libc::SOL_SOCKET, libc::SCM_CREDS) => {
+ let cred: libc::cmsgcred = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmCreds(cred.into())
+ }
(libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => {
let tv: libc::timeval = ptr::read_unaligned(p as *const _);
ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
@@ -603,6 +662,20 @@ pub enum ControlMessage<'a> {
/// [`unix(7)`](http://man7.org/linux/man-pages/man7/unix.7.html) man page.
#[cfg(any(target_os = "android", target_os = "linux"))]
ScmCredentials(&'a UnixCredentials),
+ /// A message of type `SCM_CREDS`, containing the pid, uid, euid, gid and groups of
+ /// a process connected to the socket.
+ ///
+ /// This is similar to the socket options `LOCAL_CREDS` and `LOCAL_PEERCRED`, but
+ /// requires a process to explicitly send its credentials.
+ ///
+ /// Credentials are always overwritten by the kernel, so this variant does have
+ /// any data, unlike the receive-side
+ /// [`ControlMessageOwned::ScmCreds`][#enum.ControlMessageOwned.html#variant.ScmCreds].
+ ///
+ /// For further information, please refer to the
+ /// [`unix(4)`](https://www.freebsd.org/cgi/man.cgi?query=unix) man page.
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ScmCreds,
/// Set IV for `AF_ALG` crypto API.
///
@@ -682,6 +755,13 @@ impl<'a> ControlMessage<'a> {
ControlMessage::ScmCredentials(creds) => {
&creds.0 as *const libc::ucred as *const u8
}
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => {
+ // The kernel overwrites the data, we just zero it
+ // to make sure it's not uninitialized memory
+ unsafe { ptr::write_bytes(cmsg_data, 0, self.len()) };
+ return
+ }
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetIv(iv) => {
let af_alg_iv = libc::af_alg_iv {
@@ -738,6 +818,10 @@ impl<'a> ControlMessage<'a> {
ControlMessage::ScmCredentials(creds) => {
mem::size_of_val(creds)
}
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => {
+ mem::size_of::<libc::cmsgcred>()
+ }
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetIv(iv) => {
mem::size_of::<libc::af_alg_iv>() + iv.len()
@@ -763,6 +847,8 @@ impl<'a> ControlMessage<'a> {
ControlMessage::ScmRights(_) => libc::SOL_SOCKET,
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET,
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => libc::SOL_SOCKET,
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetIv(_) | ControlMessage::AlgSetOp(_) |
ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG,
@@ -777,6 +863,8 @@ impl<'a> ControlMessage<'a> {
ControlMessage::ScmRights(_) => libc::SCM_RIGHTS,
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::ScmCredentials(_) => libc::SCM_CREDENTIALS,
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => libc::SCM_CREDS,
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetIv(_) => {
libc::ALG_SET_IV
diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs
index ae5ac66c..20ff8d78 100644
--- a/test/sys/test_socket.rs
+++ b/test/sys/test_socket.rs
@@ -752,29 +752,36 @@ pub fn test_sendmsg_empty_cmsgs() {
}
}
-#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+))]
#[test]
fn test_scm_credentials() {
- use libc;
use nix::sys::uio::IoVec;
use nix::unistd::{close, getpid, getuid, getgid};
use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt,
AddressFamily, SockType, SockFlag,
- ControlMessage, ControlMessageOwned, MsgFlags};
+ ControlMessage, ControlMessageOwned, MsgFlags,
+ UnixCredentials};
+ #[cfg(any(target_os = "android", target_os = "linux"))]
use nix::sys::socket::sockopt::PassCred;
let (send, recv) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
.unwrap();
+ #[cfg(any(target_os = "android", target_os = "linux"))]
setsockopt(recv, PassCred, &true).unwrap();
{
let iov = [IoVec::from_slice(b"hello")];
- let cred = libc::ucred {
- pid: getpid().as_raw(),
- uid: getuid().as_raw(),
- gid: getgid().as_raw(),
- }.into();
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ let cred = UnixCredentials::new();
+ #[cfg(any(target_os = "android", target_os = "linux"))]
let cmsg = ControlMessage::ScmCredentials(&cred);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ let cmsg = ControlMessage::ScmCreds;
assert_eq!(sendmsg(send, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(), 5);
close(send).unwrap();
}
@@ -782,20 +789,23 @@ fn test_scm_credentials() {
{
let mut buf = [0u8; 5];
let iov = [IoVec::from_mut_slice(&mut buf[..])];
- let mut cmsgspace = cmsg_space!(libc::ucred);
+ let mut cmsgspace = cmsg_space!(UnixCredentials);
let msg = recvmsg(recv, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap();
let mut received_cred = None;
for cmsg in msg.cmsgs() {
- if let ControlMessageOwned::ScmCredentials(cred) = cmsg {
- assert!(received_cred.is_none());
- assert_eq!(cred.pid(), getpid().as_raw());
- assert_eq!(cred.uid(), getuid().as_raw());
- assert_eq!(cred.gid(), getgid().as_raw());
- received_cred = Some(cred);
- } else {
- panic!("unexpected cmsg");
- }
+ let cred = match cmsg {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessageOwned::ScmCredentials(cred) => cred,
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessageOwned::ScmCreds(cred) => cred,
+ other => panic!("unexpected cmsg {:?}", other),
+ };
+ assert!(received_cred.is_none());
+ assert_eq!(cred.pid(), getpid().as_raw());
+ assert_eq!(cred.uid(), getuid().as_raw());
+ assert_eq!(cred.gid(), getgid().as_raw());
+ received_cred = Some(cred);
}
received_cred.expect("no creds received");
assert_eq!(msg.bytes, 5);