vissue_control/
peercred.rs1use std::io;
4use std::os::fd::AsFd;
5
6use nix::unistd::Uid;
7
8#[must_use]
10pub fn accept_peer(uid: u32) -> bool {
11 uid == current_uid()
12}
13
14#[must_use]
16pub fn current_uid() -> u32 {
17 Uid::current().as_raw()
18}
19
20pub fn peer_uid<F: AsFd>(sock: &F) -> io::Result<u32> {
22 peer_uid_impl(sock)
23}
24
25#[must_use]
31pub fn accept_from_result(result: io::Result<u32>) -> bool {
32 match result {
33 Ok(uid) => accept_peer(uid),
34 Err(err) if err.kind() == io::ErrorKind::Unsupported => true,
35 Err(_) => false,
36 }
37}
38
39#[must_use]
41pub fn accept_socket<F: AsFd>(sock: &F) -> bool {
42 accept_from_result(peer_uid(sock))
43}
44
45#[cfg(any(target_os = "linux", target_os = "android"))]
46fn peer_uid_impl<F: AsFd>(sock: &F) -> io::Result<u32> {
47 use nix::sys::socket::{getsockopt, sockopt};
48 let creds = getsockopt(sock, sockopt::PeerCredentials)?;
49 Ok(creds.uid())
50}
51
52#[cfg(any(
53 target_os = "macos",
54 target_os = "ios",
55 target_os = "freebsd",
56 target_os = "dragonfly",
57 target_os = "openbsd",
58 target_os = "netbsd"
59))]
60fn peer_uid_impl<F: AsFd>(sock: &F) -> io::Result<u32> {
61 let (uid, _) = nix::unistd::getpeereid(sock)?;
62 Ok(uid.as_raw())
63}
64
65#[cfg(not(any(
66 target_os = "linux",
67 target_os = "android",
68 target_os = "macos",
69 target_os = "ios",
70 target_os = "freebsd",
71 target_os = "dragonfly",
72 target_os = "openbsd",
73 target_os = "netbsd"
74)))]
75fn peer_uid_impl<F: AsFd>(_sock: &F) -> io::Result<u32> {
76 Err(io::Error::new(
77 io::ErrorKind::Unsupported,
78 "peer credentials are unavailable; rely on dir 0700 / sock 0600",
79 ))
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn same_uid_is_accepted() {
88 assert!(accept_peer(current_uid()));
89 }
90
91 #[test]
92 fn other_uid_is_rejected() {
93 let other = current_uid().wrapping_add(1);
94 assert!(!accept_peer(other));
95 assert!(!accept_from_result(Ok(other)));
96 }
97
98 #[test]
99 fn unsupported_peercred_falls_back_to_mode_bits() {
100 let err = io::Error::new(io::ErrorKind::Unsupported, "no SO_PEERCRED");
101 assert!(accept_from_result(Err(err)));
102 let err = io::Error::other("getsockopt failed");
103 assert!(!accept_from_result(Err(err)));
104 assert!(accept_from_result(Ok(current_uid())));
105 }
106
107 #[test]
108 fn same_process_peer_is_accepted() {
109 use std::os::unix::net::{UnixListener, UnixStream};
110
111 let dir = tempfile::tempdir().unwrap();
112 let path = dir.path().join("peer.sock");
113 let listener = UnixListener::bind(&path).unwrap();
114 let client = UnixStream::connect(&path).unwrap();
115 let (server, _) = listener.accept().unwrap();
116 let uid = peer_uid(&server).unwrap();
117 assert_eq!(uid, current_uid());
118 assert!(accept_peer(uid));
119 assert!(accept_socket(&server));
120 assert!(accept_socket(&client));
121 }
122}