summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-08-11 00:39:47 +0000
committerGitHub <noreply@github.com>2021-08-11 00:39:47 +0000
commit06b5d3344a433c30059e165675107cf232f3e145 (patch)
treecd0648c649220bcc397c82ba8582bfa4e992ddaf
parenta7e86b2fda0109d0806b9159f9294cdc15b3eb21 (diff)
parent0d3bc089d53a126716b4ed8f6b629c5b8c76964a (diff)
downloadnix-06b5d3344a433c30059e165675107cf232f3e145.zip
Merge #1482
1482: Add support for LOCAL_PEER_CRED r=asomers a=asomers On FreeBSD and its derivatives, this socket option gets the credentials of the connected peer. Co-authored-by: Alan Somers <asomers@gmail.com>
-rw-r--r--CHANGELOG.md2
-rw-r--r--src/sys/socket/mod.rs32
-rw-r--r--src/sys/socket/sockopt.rs48
-rw-r--r--test/sys/test_sockopt.rs41
4 files changed, 103 insertions, 20 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1faf8f97..3e2c9680 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased] - ReleaseDate
### Added
+- Added the `LocalPeerCred` sockopt.
+ (#[1482](https://github.com/nix-rust/nix/pull/1482))
- Added `TimeSpec::from_duration` and `TimeSpec::from_timespec`
(#[1465](https://github.com/nix-rust/nix/pull/1465))
- Added `IPV6_V6ONLY` sockopt.
diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs
index 0f54ef0c..733bd660 100644
--- a/src/sys/socket/mod.rs
+++ b/src/sys/socket/mod.rs
@@ -358,6 +358,38 @@ cfg_if! {
}
}
+cfg_if!{
+ if #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"
+ ))] {
+ /// Return type of [`LocalPeerCred`](crate::sys::socket::sockopt::LocalPeerCred)
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ pub struct XuCred(libc::xucred);
+
+ impl XuCred {
+ /// Structure layout version
+ pub fn version(&self) -> u32 {
+ self.0.cr_version
+ }
+
+ /// Effective user ID
+ pub fn uid(&self) -> libc::uid_t {
+ self.0.cr_uid
+ }
+
+ /// Returns a list of group identifiers (the first one being the
+ /// effective GID)
+ pub fn groups(&self) -> &[libc::gid_t] {
+ &self.0.cr_groups
+ }
+ }
+ }
+}
+
/// Request for multicast socket operations
///
/// This is a wrapper type around `ip_mreq`.
diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs
index 69fe479a..82df7f87 100644
--- a/src/sys/socket/sockopt.rs
+++ b/src/sys/socket/sockopt.rs
@@ -30,7 +30,7 @@ const TCP_CA_NAME_MAX: usize = 16;
/// # Arguments
///
/// * `$name:ident`: name of the type you want to implement `SetSockOpt` for.
-/// * `$level:path` : socket layer, or a `protocol level`: could be *raw sockets*
+/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
/// and more. Please refer to your system manual for more options. Will be passed as the second
/// argument (`level`) to the `setsockopt` call.
@@ -41,7 +41,7 @@ const TCP_CA_NAME_MAX: usize = 16;
/// * Type that implements the `Set` trait for the type from the previous item (like `SetBool` for
/// `bool`, `SetUsize` for `usize`, etc.).
macro_rules! setsockopt_impl {
- ($name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => {
+ ($name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => {
impl SetSockOpt for $name {
type Val = $ty;
@@ -82,7 +82,7 @@ macro_rules! setsockopt_impl {
/// * Type that implements the `Get` trait for the type from the previous item (`GetBool` for
/// `bool`, `GetUsize` for `usize`, etc.).
macro_rules! getsockopt_impl {
- ($name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => {
+ ($name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => {
impl GetSockOpt for $name {
type Val = $ty;
@@ -117,7 +117,7 @@ macro_rules! getsockopt_impl {
/// * `GetOnly`, `SetOnly` or `Both`: whether you want to implement only getter, only setter or
/// both of them.
/// * `$name:ident`: name of type `GetSockOpt`/`SetSockOpt` will be implemented for.
-/// * `$level:path` : socket layer, or a `protocol level`: could be *raw sockets*
+/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
/// (`lic::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
/// and more. Please refer to your system manual for more options. Will be passed as the second
/// argument (`level`) to the `getsockopt`/`setsockopt` call.
@@ -128,43 +128,43 @@ macro_rules! getsockopt_impl {
/// * `$getter:ty`: `Get` implementation; optional; only for `GetOnly` and `Both`.
/// * `$setter:ty`: `Set` implementation; optional; only for `SetOnly` and `Both`.
macro_rules! sockopt_impl {
- (GetOnly, $name:ident, $level:path, $flag:path, bool) => {
+ (GetOnly, $name:ident, $level:expr, $flag:path, bool) => {
sockopt_impl!(GetOnly, $name, $level, $flag, bool, GetBool);
};
- (GetOnly, $name:ident, $level:path, $flag:path, u8) => {
+ (GetOnly, $name:ident, $level:expr, $flag:path, u8) => {
sockopt_impl!(GetOnly, $name, $level, $flag, u8, GetU8);
};
- (GetOnly, $name:ident, $level:path, $flag:path, usize) => {
+ (GetOnly, $name:ident, $level:expr, $flag:path, usize) => {
sockopt_impl!(GetOnly, $name, $level, $flag, usize, GetUsize);
};
- (SetOnly, $name:ident, $level:path, $flag:path, bool) => {
+ (SetOnly, $name:ident, $level:expr, $flag:path, bool) => {
sockopt_impl!(SetOnly, $name, $level, $flag, bool, SetBool);
};
- (SetOnly, $name:ident, $level:path, $flag:path, u8) => {
+ (SetOnly, $name:ident, $level:expr, $flag:path, u8) => {
sockopt_impl!(SetOnly, $name, $level, $flag, u8, SetU8);
};
- (SetOnly, $name:ident, $level:path, $flag:path, usize) => {
+ (SetOnly, $name:ident, $level:expr, $flag:path, usize) => {
sockopt_impl!(SetOnly, $name, $level, $flag, usize, SetUsize);
};
- (Both, $name:ident, $level:path, $flag:path, bool) => {
+ (Both, $name:ident, $level:expr, $flag:path, bool) => {
sockopt_impl!(Both, $name, $level, $flag, bool, GetBool, SetBool);
};
- (Both, $name:ident, $level:path, $flag:path, u8) => {
+ (Both, $name:ident, $level:expr, $flag:path, u8) => {
sockopt_impl!(Both, $name, $level, $flag, u8, GetU8, SetU8);
};
- (Both, $name:ident, $level:path, $flag:path, usize) => {
+ (Both, $name:ident, $level:expr, $flag:path, usize) => {
sockopt_impl!(Both, $name, $level, $flag, usize, GetUsize, SetUsize);
};
- (Both, $name:ident, $level:path, $flag:path, OsString<$array:ty>) => {
+ (Both, $name:ident, $level:expr, $flag:path, OsString<$array:ty>) => {
sockopt_impl!(Both, $name, $level, $flag, OsString, GetOsString<$array>, SetOsString);
};
@@ -172,29 +172,29 @@ macro_rules! sockopt_impl {
* Matchers with generic getter types must be placed at the end, so
* they'll only match _after_ specialized matchers fail
*/
- (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => {
+ (GetOnly, $name:ident, $level:expr, $flag:path, $ty:ty) => {
sockopt_impl!(GetOnly, $name, $level, $flag, $ty, GetStruct<$ty>);
};
- (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => {
+ (GetOnly, $name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct $name;
getsockopt_impl!($name, $level, $flag, $ty, $getter);
};
- (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => {
+ (SetOnly, $name:ident, $level:expr, $flag:path, $ty:ty) => {
sockopt_impl!(SetOnly, $name, $level, $flag, $ty, SetStruct<$ty>);
};
- (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => {
+ (SetOnly, $name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct $name;
setsockopt_impl!($name, $level, $flag, $ty, $setter);
};
- (Both, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty, $setter:ty) => {
+ (Both, $name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty, $setter:ty) => {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct $name;
@@ -202,7 +202,7 @@ macro_rules! sockopt_impl {
getsockopt_impl!($name, $level, $flag, $ty, $getter);
};
- (Both, $name:ident, $level:path, $flag:path, $ty:ty) => {
+ (Both, $name:ident, $level:expr, $flag:path, $ty:ty) => {
sockopt_impl!(Both, $name, $level, $flag, $ty, GetStruct<$ty>, SetStruct<$ty>);
};
}
@@ -246,6 +246,14 @@ sockopt_impl!(Both, Broadcast, libc::SOL_SOCKET, libc::SO_BROADCAST, bool);
sockopt_impl!(Both, OobInline, libc::SOL_SOCKET, libc::SO_OOBINLINE, bool);
sockopt_impl!(GetOnly, SocketError, libc::SOL_SOCKET, libc::SO_ERROR, i32);
sockopt_impl!(Both, KeepAlive, libc::SOL_SOCKET, libc::SO_KEEPALIVE, bool);
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"
+))]
+// Get the credentials of the peer process of a connected unix domain socket.
+sockopt_impl!(GetOnly, LocalPeerCred, 0, libc::LOCAL_PEERCRED, super::XuCred);
#[cfg(any(target_os = "android", target_os = "linux"))]
sockopt_impl!(GetOnly, PeerCredentials, libc::SOL_SOCKET, libc::SO_PEERCRED, super::UnixCredentials);
#[cfg(any(target_os = "ios",
diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs
index e0ed0f7c..4d2d7e51 100644
--- a/test/sys/test_sockopt.rs
+++ b/test/sys/test_sockopt.rs
@@ -3,6 +3,47 @@ use nix::sys::socket::{socket, sockopt, getsockopt, setsockopt, AddressFamily, S
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::*;
+// NB: FreeBSD supports LOCAL_PEERCRED for SOCK_SEQPACKET, but OSX does not.
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+))]
+#[test]
+pub fn test_local_peercred_seqpacket() {
+ use nix::{
+ unistd::{Gid, Uid},
+ sys::socket::socketpair
+ };
+
+ let (fd1, _fd2) = socketpair(AddressFamily::Unix, SockType::SeqPacket, None,
+ SockFlag::empty()).unwrap();
+ let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap();
+ assert_eq!(xucred.version(), 0);
+ assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
+ assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
+}
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"
+))]
+#[test]
+pub fn test_local_peercred_stream() {
+ use nix::{
+ unistd::{Gid, Uid},
+ sys::socket::socketpair
+ };
+
+ let (fd1, _fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None,
+ SockFlag::empty()).unwrap();
+ let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap();
+ assert_eq!(xucred.version(), 0);
+ assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
+ assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
+}
+
#[cfg(target_os = "linux")]
#[test]
fn is_so_mark_functional() {