Skip to content
This repository was archived by the owner on May 15, 2018. It is now read-only.

WIP Implementation of peer credentials. #13

Merged
merged 7 commits into from
Jun 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ use tokio_io::{IoStream, AsyncRead, AsyncWrite};

mod frame;
pub use frame::{UnixDatagramFramed, UnixDatagramCodec};
mod ucred;
pub use ucred::UCred;

fn would_block() -> io::Error {
io::Error::new(io::ErrorKind::WouldBlock, "would block")
Expand Down Expand Up @@ -250,6 +252,7 @@ impl UnixStream {
let (a, b) = try!(mio_uds::UnixStream::pair());
let a = try!(UnixStream::new(a, handle));
let b = try!(UnixStream::new(b, handle));

Ok((a, b))
}

Expand Down Expand Up @@ -279,6 +282,11 @@ impl UnixStream {
self.io.get_ref().peer_addr()
}

/// Returns effective credentials of the process which called `connect` or `socketpair`.
pub fn peer_cred(&self) -> io::Result<UCred> {
ucred::get_peer_cred(self)
}

/// Returns the value of the `SO_ERROR` option.
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.io.get_ref().take_error()
Expand Down
101 changes: 101 additions & 0 deletions src/ucred.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use libc::{uid_t, gid_t};

/// Credentials of a process
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct UCred {
/// UID (user ID) of the process
pub uid: uid_t,
/// GID (group ID) of the process
pub gid: gid_t,
}

#[cfg(target_os = "linux")]
pub use self::impl_linux::get_peer_cred;

#[cfg(target_os = "macos")]
pub use self::impl_macos::get_peer_cred;

#[cfg(target_os = "linux")]
pub mod impl_linux {
use libc::{getsockopt, SOL_SOCKET, SO_PEERCRED, c_void};
use std::{io, mem};
use UnixStream;
use std::os::unix::io::AsRawFd;

use libc::ucred;

pub fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
unsafe {
let raw_fd = sock.as_raw_fd();

let mut ucred = ucred { pid: 0, uid: 0, gid: 0 };

let ucred_size = mem::size_of::<ucred>();

// These paranoid checks should be optimized-out
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;

let ret = getsockopt(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(super::UCred {
uid: ucred.uid,
gid: ucred.gid,
})
} else {
Err(io::Error::last_os_error())
}
}
}
}

#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
pub mod impl_macos {
use libc::getpeereid;
use std::{io, mem};
use UnixStream;
use std::os::unix::io::AsRawFd;

pub fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
unsafe {
let raw_fd = sock.as_raw_fd();

let mut cred: super::UCred = mem::uninitialized();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this use zeroed? Seems like there's no reason to use the unsafer version here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be. But I see no point. My reasoning is that the code is already unsafe so I see no point in zeroing it. However, If you have good reason to do it, I'd probably go with explicit safe version (pid: 0, uid: 0, gid: 0).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use zeroed here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? Is that some kind of standard?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I see no reason to use uninitialized, please change it to zeroed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant zeroed instead of ucred { pid: 0, uid: 0, gid: 0 }.

I guess that syscall only overwrites it, so the reason would be performance. I agree it's negligible, so I accept zeroig. I'm just interested to know whether there is some difference between using zeroed and zeroing "manually".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah those are the same. With libc I tend to use zeroed because there's often random hidden fields or fields used for padding, but either way is fine in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, OK. I prefer "safe" version in this case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be udpated to using UCred { ... }?


let ret = getpeereid(raw_fd, &mut cred.uid, &mut cred.gid);

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

#[cfg(test)]
mod test {
use tokio_core::reactor::Core;
use UnixStream;
use libc::geteuid;
use libc::getegid;

#[test]
fn test_socket_pair() {
let core = Core::new().unwrap();
let handle = core.handle();

let (a, b) = UnixStream::pair(&handle).unwrap();
let cred_a = a.peer_cred().unwrap();
let cred_b = b.peer_cred().unwrap();
assert_eq!(cred_a, cred_b);

let uid = unsafe { geteuid() };
let gid = unsafe { getegid() };

assert_eq!(cred_a.uid, uid);
assert_eq!(cred_a.gid, gid);
}
}