Skip to content

Commit

Permalink
Cherry-pick Rust standard library peer credentials feature into Parsec
Browse files Browse the repository at this point in the history
The code introduced in this patch has been cherry-picked from the
following PR:

    rust-lang/rust#75148

At the time of writing (16/09/20), this patch is in the nightly Rust
channel. To avoid needing to use the nightly compiler to build Parsec,
this patch includes the change from the standard library to allow us to
use this feature 'early'.

Once the feature hits the stable branch, it should be safe to revert
this commit with `git revert`.

Signed-off-by: Joe Ellis <joe.ellis@arm.com>
  • Loading branch information
Joe Ellis committed Sep 17, 2020
1 parent 72adeef commit 6ea9a16
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 5 deletions.
16 changes: 13 additions & 3 deletions src/authenticators/unix_peer_credentials_authenticator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ impl Authenticate for UnixPeerCredentialsAuthenticator {
mod test {
use super::super::Authenticate;
use super::UnixPeerCredentialsAuthenticator;
use crate::front::domain_socket::peer_credentials;
use crate::front::listener::ConnectionMetadata;
use parsec_interface::requests::request::RequestAuth;
use parsec_interface::requests::ResponseStatus;
Expand All @@ -98,7 +99,10 @@ mod test {

// Create two connected sockets.
let (sock_a, _sock_b) = UnixStream::pair().unwrap();
let (cred_a, _cred_b) = (sock_a.peer_cred().unwrap(), _sock_b.peer_cred().unwrap());
let (cred_a, _cred_b) = (
peer_credentials::peer_cred(&sock_a).unwrap(),
peer_credentials::peer_cred(&_sock_b).unwrap(),
);

let authenticator = UnixPeerCredentialsAuthenticator {};

Expand All @@ -123,7 +127,10 @@ mod test {

// Create two connected sockets.
let (sock_a, _sock_b) = UnixStream::pair().unwrap();
let (cred_a, _cred_b) = (sock_a.peer_cred().unwrap(), _sock_b.peer_cred().unwrap());
let (cred_a, _cred_b) = (
peer_credentials::peer_cred(&sock_a).unwrap(),
peer_credentials::peer_cred(&_sock_b).unwrap(),
);

let authenticator = UnixPeerCredentialsAuthenticator {};

Expand All @@ -145,7 +152,10 @@ mod test {

// Create two connected sockets.
let (sock_a, _sock_b) = UnixStream::pair().unwrap();
let (cred_a, _cred_b) = (sock_a.peer_cred().unwrap(), _sock_b.peer_cred().unwrap());
let (cred_a, _cred_b) = (
peer_credentials::peer_cred(&sock_a).unwrap(),
peer_credentials::peer_cred(&_sock_b).unwrap(),
);

let authenticator = UnixPeerCredentialsAuthenticator {};

Expand Down
128 changes: 126 additions & 2 deletions src/front/domain_socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,7 @@ impl Listen for DomainSocketListener {
format_error!("Failed to set stream as blocking", err);
None
} else {
let ucred = stream
.peer_cred()
let ucred = peer_credentials::peer_cred(&stream)
.map_err(|err| {
format_error!(
"Failed to grab peer credentials metadata from UnixStream",
Expand Down Expand Up @@ -259,3 +258,128 @@ impl DomainSocketListenerBuilder {
})?)
}
}

// == IMPORTANT NOTE ==
//
// The code below has been cherry-picked from the following PR:
//
// https://github.com/rust-lang/rust/pull/75148
//
// At the time of writing (16/09/20), this patch is in the nightly Rust channel. To avoid needing
// to use the nightly compiler to build Parsec, we have instead opted to cherry-pick the change
// from the patch to allow us to use this feature 'early'.
//
// Once the feature hits stable, it should be safe to revert the commit that introduced the changes
// below with `git revert`. You can find the stabilizing Rust issue here:
//
// https://github.com/rust-lang/rust/issues/42839

/// Implementation of peer credentials fetching for Unix domain socket.
pub mod peer_credentials {
use libc::{gid_t, pid_t, uid_t};

/// Credentials for a UNIX process for credentials passing.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct UCred {
/// The UID part of the peer credential. This is the effective UID of the process at the domain
/// socket's endpoint.
pub uid: uid_t,
/// The GID part of the peer credential. This is the effective GID of the process at the domain
/// socket's endpoint.
pub gid: gid_t,
/// The PID part of the peer credential. This field is optional because the PID part of the
/// peer credentials is not supported on every platform. On platforms where the mechanism to
/// discover the PID exists, this field will be populated to the PID of the process at the
/// domain socket's endpoint. Otherwise, it will be set to None.
pub pid: Option<pid_t>,
}

#[cfg(any(target_os = "android", target_os = "linux"))]
pub use self::impl_linux::peer_cred;

#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "openbsd"
))]
pub use self::impl_bsd::peer_cred;

#[cfg(any(target_os = "linux", target_os = "android"))]
#[allow(missing_docs, trivial_casts)] // docs not required; only used for selective compilation.
pub mod impl_linux {
use super::UCred;
use libc::{c_void, getsockopt, socklen_t, ucred, SOL_SOCKET, SO_PEERCRED};
use std::os::unix::io::AsRawFd;
use std::os::unix::net::UnixStream;
use std::{io, mem};

pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
let ucred_size = mem::size_of::<ucred>();

// Trivial sanity checks.
assert!(mem::size_of::<u32>() <= mem::size_of::<usize>());
assert!(ucred_size <= u32::MAX as usize);

let mut ucred_size = ucred_size as socklen_t;
let mut ucred: ucred = ucred {
pid: 1,
uid: 1,
gid: 1,
};

unsafe {
let ret = getsockopt(
socket.as_raw_fd(),
SOL_SOCKET,
SO_PEERCRED,
&mut ucred as *mut ucred as *mut c_void,
&mut ucred_size,
);

if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
Ok(UCred {
uid: ucred.uid,
gid: ucred.gid,
pid: Some(ucred.pid),
})
} else {
Err(io::Error::last_os_error())
}
}
}
}

#[cfg(any(
target_os = "dragonfly",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "openbsd"
))]
#[allow(missing_docs)] // docs not required; only used for selective compilation.
pub mod impl_bsd {
use super::UCred;
use std::io;
use std::os::unix::io::AsRawFd;
use std::os::unix::net::UnixStream;

pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
let mut cred = UCred {
uid: 1,
gid: 1,
pid: None,
};
unsafe {
let ret = libc::getpeereid(socket.as_raw_fd(), &mut cred.uid, &mut cred.gid);

if ret == 0 {
Ok(cred)
} else {
Err(io::Error::last_os_error())
}
}
}
}
}

0 comments on commit 6ea9a16

Please sign in to comment.