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