Skip to content

Commit 539c570

Browse files
Merge #1216
1216: Add UnixCredentials support on FreeBSD/DragonFly r=asomers a=myfreeweb This allows working with `SCM_CREDS` messages, which are like `SCM_CREDENTIALS` on Linux, but slightly different (always overwritten by the kernel, contain a bit more info — euid and groups). With this PR, it is possible to write portable code that would use the appropriate message for the platform, but one remaining quirk is that `PassCred` thing still has to be present and `cfg`'d to Linux. Adding the `SCM_CREDS` constant to libc: rust-lang/libc#1740 Co-authored-by: Greg V <greg@unrelenting.technology>
2 parents 3ae6b18 + b2a9c4f commit 539c570

File tree

4 files changed

+120
-20
lines changed

4 files changed

+120
-20
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
2525
receive offload (GRO) ([#1209](https://github.com/nix-rust/nix/pull/1209))
2626
- Added support for `sendmmsg` and `recvmmsg` calls
2727
(#[1208](https://github.com/nix-rust/nix/pull/1208))
28+
- Added support for `SCM_CREDS` messages (`UnixCredentials`) on FreeBSD/DragonFly
29+
(#[1216](https://github.com/nix-rust/nix/pull/1216))
2830

2931
### Changed
3032
- Changed `fallocate` return type from `c_int` to `()` (#[1201](https://github.com/nix-rust/nix/pull/1201))

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ exclude = [
1616
]
1717

1818
[dependencies]
19-
libc = { version = "0.2.69", features = [ "extra_traits" ] }
19+
libc = { git = "https://github.com/rust-lang/libc/", features = [ "extra_traits" ] }
2020
bitflags = "1.1"
2121
cfg-if = "0.1.10"
2222
void = "1.0.2"

src/sys/socket/mod.rs

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,12 +189,22 @@ cfg_if! {
189189
if #[cfg(any(target_os = "android", target_os = "linux"))] {
190190
/// Unix credentials of the sending process.
191191
///
192-
/// This struct is used with the `SO_PEERCRED` ancillary message for UNIX sockets.
192+
/// This struct is used with the `SO_PEERCRED` ancillary message
193+
/// and the `SCM_CREDENTIALS` control message for UNIX sockets.
193194
#[repr(transparent)]
194195
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
195196
pub struct UnixCredentials(libc::ucred);
196197

197198
impl UnixCredentials {
199+
/// Creates a new instance with the credentials of the current process
200+
pub fn new() -> Self {
201+
UnixCredentials(libc::ucred {
202+
pid: crate::unistd::getpid().as_raw(),
203+
uid: crate::unistd::getuid().as_raw(),
204+
gid: crate::unistd::getgid().as_raw(),
205+
})
206+
}
207+
198208
/// Returns the process identifier
199209
pub fn pid(&self) -> libc::pid_t {
200210
self.0.pid
@@ -222,6 +232,46 @@ cfg_if! {
222232
self.0
223233
}
224234
}
235+
} else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] {
236+
/// Unix credentials of the sending process.
237+
///
238+
/// This struct is used with the `SCM_CREDS` ancillary message for UNIX sockets.
239+
#[repr(transparent)]
240+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
241+
pub struct UnixCredentials(libc::cmsgcred);
242+
243+
impl UnixCredentials {
244+
/// Returns the process identifier
245+
pub fn pid(&self) -> libc::pid_t {
246+
self.0.cmcred_pid
247+
}
248+
249+
/// Returns the real user identifier
250+
pub fn uid(&self) -> libc::uid_t {
251+
self.0.cmcred_uid
252+
}
253+
254+
/// Returns the effective user identifier
255+
pub fn euid(&self) -> libc::uid_t {
256+
self.0.cmcred_euid
257+
}
258+
259+
/// Returns the real group identifier
260+
pub fn gid(&self) -> libc::gid_t {
261+
self.0.cmcred_gid
262+
}
263+
264+
/// Returns a list group identifiers (the first one being the effective GID)
265+
pub fn groups(&self) -> &[libc::gid_t] {
266+
unsafe { slice::from_raw_parts(self.0.cmcred_groups.as_ptr() as *const libc::gid_t, self.0.cmcred_ngroups as _) }
267+
}
268+
}
269+
270+
impl From<libc::cmsgcred> for UnixCredentials {
271+
fn from(cred: libc::cmsgcred) -> Self {
272+
UnixCredentials(cred)
273+
}
274+
}
225275
}
226276
}
227277

@@ -369,6 +419,10 @@ pub enum ControlMessageOwned {
369419
/// [`ControlMessage::ScmCredentials`][#enum.ControlMessage.html#variant.ScmCredentials]
370420
#[cfg(any(target_os = "android", target_os = "linux"))]
371421
ScmCredentials(UnixCredentials),
422+
/// Received version of
423+
/// [`ControlMessage::ScmCreds`][#enum.ControlMessage.html#variant.ScmCreds]
424+
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
425+
ScmCreds(UnixCredentials),
372426
/// A message of type `SCM_TIMESTAMP`, containing the time the
373427
/// packet was received by the kernel.
374428
///
@@ -510,6 +564,11 @@ impl ControlMessageOwned {
510564
let cred: libc::ucred = ptr::read_unaligned(p as *const _);
511565
ControlMessageOwned::ScmCredentials(cred.into())
512566
}
567+
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
568+
(libc::SOL_SOCKET, libc::SCM_CREDS) => {
569+
let cred: libc::cmsgcred = ptr::read_unaligned(p as *const _);
570+
ControlMessageOwned::ScmCreds(cred.into())
571+
}
513572
(libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => {
514573
let tv: libc::timeval = ptr::read_unaligned(p as *const _);
515574
ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
@@ -603,6 +662,20 @@ pub enum ControlMessage<'a> {
603662
/// [`unix(7)`](http://man7.org/linux/man-pages/man7/unix.7.html) man page.
604663
#[cfg(any(target_os = "android", target_os = "linux"))]
605664
ScmCredentials(&'a UnixCredentials),
665+
/// A message of type `SCM_CREDS`, containing the pid, uid, euid, gid and groups of
666+
/// a process connected to the socket.
667+
///
668+
/// This is similar to the socket options `LOCAL_CREDS` and `LOCAL_PEERCRED`, but
669+
/// requires a process to explicitly send its credentials.
670+
///
671+
/// Credentials are always overwritten by the kernel, so this variant does have
672+
/// any data, unlike the receive-side
673+
/// [`ControlMessageOwned::ScmCreds`][#enum.ControlMessageOwned.html#variant.ScmCreds].
674+
///
675+
/// For further information, please refer to the
676+
/// [`unix(4)`](https://www.freebsd.org/cgi/man.cgi?query=unix) man page.
677+
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
678+
ScmCreds,
606679

607680
/// Set IV for `AF_ALG` crypto API.
608681
///
@@ -682,6 +755,13 @@ impl<'a> ControlMessage<'a> {
682755
ControlMessage::ScmCredentials(creds) => {
683756
&creds.0 as *const libc::ucred as *const u8
684757
}
758+
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
759+
ControlMessage::ScmCreds => {
760+
// The kernel overwrites the data, we just zero it
761+
// to make sure it's not uninitialized memory
762+
unsafe { ptr::write_bytes(cmsg_data, 0, self.len()) };
763+
return
764+
}
685765
#[cfg(any(target_os = "android", target_os = "linux"))]
686766
ControlMessage::AlgSetIv(iv) => {
687767
let af_alg_iv = libc::af_alg_iv {
@@ -738,6 +818,10 @@ impl<'a> ControlMessage<'a> {
738818
ControlMessage::ScmCredentials(creds) => {
739819
mem::size_of_val(creds)
740820
}
821+
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
822+
ControlMessage::ScmCreds => {
823+
mem::size_of::<libc::cmsgcred>()
824+
}
741825
#[cfg(any(target_os = "android", target_os = "linux"))]
742826
ControlMessage::AlgSetIv(iv) => {
743827
mem::size_of::<libc::af_alg_iv>() + iv.len()
@@ -763,6 +847,8 @@ impl<'a> ControlMessage<'a> {
763847
ControlMessage::ScmRights(_) => libc::SOL_SOCKET,
764848
#[cfg(any(target_os = "android", target_os = "linux"))]
765849
ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET,
850+
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
851+
ControlMessage::ScmCreds => libc::SOL_SOCKET,
766852
#[cfg(any(target_os = "android", target_os = "linux"))]
767853
ControlMessage::AlgSetIv(_) | ControlMessage::AlgSetOp(_) |
768854
ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG,
@@ -777,6 +863,8 @@ impl<'a> ControlMessage<'a> {
777863
ControlMessage::ScmRights(_) => libc::SCM_RIGHTS,
778864
#[cfg(any(target_os = "android", target_os = "linux"))]
779865
ControlMessage::ScmCredentials(_) => libc::SCM_CREDENTIALS,
866+
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
867+
ControlMessage::ScmCreds => libc::SCM_CREDS,
780868
#[cfg(any(target_os = "android", target_os = "linux"))]
781869
ControlMessage::AlgSetIv(_) => {
782870
libc::ALG_SET_IV

test/sys/test_socket.rs

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -752,50 +752,60 @@ pub fn test_sendmsg_empty_cmsgs() {
752752
}
753753
}
754754

755-
#[cfg(any(target_os = "android", target_os = "linux"))]
755+
#[cfg(any(
756+
target_os = "android",
757+
target_os = "linux",
758+
target_os = "freebsd",
759+
target_os = "dragonfly",
760+
))]
756761
#[test]
757762
fn test_scm_credentials() {
758-
use libc;
759763
use nix::sys::uio::IoVec;
760764
use nix::unistd::{close, getpid, getuid, getgid};
761765
use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt,
762766
AddressFamily, SockType, SockFlag,
763-
ControlMessage, ControlMessageOwned, MsgFlags};
767+
ControlMessage, ControlMessageOwned, MsgFlags,
768+
UnixCredentials};
769+
#[cfg(any(target_os = "android", target_os = "linux"))]
764770
use nix::sys::socket::sockopt::PassCred;
765771

766772
let (send, recv) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
767773
.unwrap();
774+
#[cfg(any(target_os = "android", target_os = "linux"))]
768775
setsockopt(recv, PassCred, &true).unwrap();
769776

770777
{
771778
let iov = [IoVec::from_slice(b"hello")];
772-
let cred = libc::ucred {
773-
pid: getpid().as_raw(),
774-
uid: getuid().as_raw(),
775-
gid: getgid().as_raw(),
776-
}.into();
779+
#[cfg(any(target_os = "android", target_os = "linux"))]
780+
let cred = UnixCredentials::new();
781+
#[cfg(any(target_os = "android", target_os = "linux"))]
777782
let cmsg = ControlMessage::ScmCredentials(&cred);
783+
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
784+
let cmsg = ControlMessage::ScmCreds;
778785
assert_eq!(sendmsg(send, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(), 5);
779786
close(send).unwrap();
780787
}
781788

782789
{
783790
let mut buf = [0u8; 5];
784791
let iov = [IoVec::from_mut_slice(&mut buf[..])];
785-
let mut cmsgspace = cmsg_space!(libc::ucred);
792+
let mut cmsgspace = cmsg_space!(UnixCredentials);
786793
let msg = recvmsg(recv, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap();
787794
let mut received_cred = None;
788795

789796
for cmsg in msg.cmsgs() {
790-
if let ControlMessageOwned::ScmCredentials(cred) = cmsg {
791-
assert!(received_cred.is_none());
792-
assert_eq!(cred.pid(), getpid().as_raw());
793-
assert_eq!(cred.uid(), getuid().as_raw());
794-
assert_eq!(cred.gid(), getgid().as_raw());
795-
received_cred = Some(cred);
796-
} else {
797-
panic!("unexpected cmsg");
798-
}
797+
let cred = match cmsg {
798+
#[cfg(any(target_os = "android", target_os = "linux"))]
799+
ControlMessageOwned::ScmCredentials(cred) => cred,
800+
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
801+
ControlMessageOwned::ScmCreds(cred) => cred,
802+
other => panic!("unexpected cmsg {:?}", other),
803+
};
804+
assert!(received_cred.is_none());
805+
assert_eq!(cred.pid(), getpid().as_raw());
806+
assert_eq!(cred.uid(), getuid().as_raw());
807+
assert_eq!(cred.gid(), getgid().as_raw());
808+
received_cred = Some(cred);
799809
}
800810
received_cred.expect("no creds received");
801811
assert_eq!(msg.bytes, 5);

0 commit comments

Comments
 (0)