Skip to content

Commit

Permalink
Implementation of peer credentials for Unix sockets
Browse files Browse the repository at this point in the history
The code in `ucred.rs` is based on the work done in PR 13 in the
tokio-uds repository on GitHub. Link below for reference:

    tokio-rs/tokio-uds#13

Credit to Martin Habovštiak (GitHub username Kixunil) and contributors
for this work!
  • Loading branch information
Joe Ellis committed Sep 14, 2020
1 parent 5bc8b18 commit ed20eff
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 0 deletions.
12 changes: 12 additions & 0 deletions library/std/src/sys/unix/ext/mod.rs
Expand Up @@ -37,6 +37,18 @@ pub mod process;
pub mod raw;
pub mod thread;

#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
#[cfg(any(
target_os = "android",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "openbsd"
))]
pub mod ucred;

/// A prelude for conveniently writing platform-specific code.
///
/// Includes all extension traits, and some important type definitions.
Expand Down
41 changes: 41 additions & 0 deletions library/std/src/sys/unix/ext/net.rs
Expand Up @@ -30,6 +30,29 @@ use crate::sys::{self, cvt};
use crate::sys_common::{self, AsInner, FromInner, IntoInner};
use crate::time::Duration;

#[cfg(any(
target_os = "android",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "openbsd"
))]
use crate::os::unix::ucred;

#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
#[cfg(any(
target_os = "android",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "openbsd"
))]
pub use ucred::UCred;

#[cfg(any(
target_os = "linux",
target_os = "android",
Expand Down Expand Up @@ -405,6 +428,24 @@ impl UnixStream {
SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) })
}

/// Gets the peer credentials for this Unix domain socket.
///
/// # Examples
///
/// ```no_run
/// use std::os::unix::net::UnixStream;
///
/// fn main() -> std::io::Result<()> {
/// let socket = UnixStream::connect("/tmp/sock")?;
/// let peer_cred = socket.peer_cred().expect("Couldn't get peer credentials");
/// Ok(())
/// }
/// ```
#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
pub fn peer_cred(&self) -> io::Result<UCred> {
ucred::peer_cred(self)
}

/// Sets the read timeout for the socket.
///
/// If the provided value is [`None`], then [`read`] calls will block
Expand Down
92 changes: 92 additions & 0 deletions library/std/src/sys/unix/ext/ucred.rs
@@ -0,0 +1,92 @@
//! Unix peer credentials.

// NOTE: Code in this file is heavily based on work done in PR 13 from the tokio-uds repository on
// GitHub.
//
// For reference, the link is here: https://github.com/tokio-rs/tokio-uds/pull/13
// Credit to Martin Habovštiak (GitHub username Kixunil) and contributors for this work.

use libc::{gid_t, uid_t};

/// Credentials for a UNIX process for credentials passing.
#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct UCred {
pub uid: uid_t,
pub gid: gid_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"))]
pub mod impl_linux {
use super::UCred;
use crate::mem::MaybeUninit;
use crate::os::unix::io::AsRawFd;
use crate::os::unix::net::UnixStream;
use crate::{io, mem};

pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
use libc::{c_void, 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_value() as usize);

let mut ucred_size = ucred_size as u32;

unsafe {
let mut ucred: ucred = MaybeUninit::uninit().assume_init();
let ret = libc::getsockopt(
socket.as_raw_fd(),
libc::SOL_SOCKET,
libc::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 })
} else {
Err(io::Error::last_os_error())
}
}
}
}

#[cfg(any(
target_os = "dragonfly",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "openbsd"
))]
pub mod impl_bsd {
use super::UCred;
use crate::io;
use crate::mem::MaybeUninit;
use crate::os::unix::io::AsRawFd;
use crate::os::unix::net::UnixStream;

pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
unsafe {
// Create `cred` and attempt to populate it.
let mut cred: UCred = MaybeUninit::uninit().assume_init();
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 ed20eff

Please sign in to comment.