parsec_service/authenticators/unix_peer_credentials_authenticator/
mod.rs1use super::{AdminList, Application, ApplicationIdentity, Authenticate};
13use crate::front::listener::ConnectionMetadata;
14use crate::utils::config::Admin;
15use log::error;
16use parsec_interface::operations::list_authenticators;
17use parsec_interface::requests::request::RequestAuth;
18use parsec_interface::requests::AuthType;
19use parsec_interface::requests::{ResponseStatus, Result};
20use parsec_interface::secrecy::ExposeSecret;
21use std::convert::TryInto;
22
23#[derive(Clone, Debug)]
25pub struct UnixPeerCredentialsAuthenticator {
26 admins: AdminList,
27}
28
29impl UnixPeerCredentialsAuthenticator {
30 pub fn new(admins: Vec<Admin>) -> Self {
32 UnixPeerCredentialsAuthenticator {
33 admins: admins.into(),
34 }
35 }
36}
37
38impl Authenticate for UnixPeerCredentialsAuthenticator {
39 fn describe(&self) -> Result<list_authenticators::AuthenticatorInfo> {
40 Ok(list_authenticators::AuthenticatorInfo {
41 description: String::from(
42 "Uses Unix peer credentials to authenticate the client. Verifies that the self-declared \
43 Unix user identifier (UID) in the request's authentication header matches that which is \
44 found from the peer credentials."
45 ),
46 version_maj: 0,
47 version_min: 1,
48 version_rev: 0,
49 id: AuthType::UnixPeerCredentials,
50 })
51 }
52
53 fn authenticate(
54 &self,
55 auth: &RequestAuth,
56 meta: Option<ConnectionMetadata>,
57 ) -> Result<Application> {
58 let expected_uid_bytes = auth.buffer.expose_secret();
60
61 const EXPECTED_UID_SIZE_BYTES: usize = 4;
62 let expected_uid: [u8; EXPECTED_UID_SIZE_BYTES] =
63 expected_uid_bytes.as_slice().try_into().map_err(|_| {
64 error!(
65 "UID in authentication request is not the right size (expected: {}, got: {}).",
66 EXPECTED_UID_SIZE_BYTES,
67 expected_uid_bytes.len()
68 );
69 ResponseStatus::AuthenticationError
70 })?;
71 let expected_uid = u32::from_le_bytes(expected_uid);
72
73 let meta = meta.ok_or_else(|| {
74 error!("Authenticator did not receive any metadata; cannot perform authentication.");
75 ResponseStatus::AuthenticationError
76 })?;
77
78 #[allow(unreachable_patterns)]
79 let (uid, _gid, _pid) = match meta {
80 ConnectionMetadata::UnixPeerCredentials { uid, gid, pid } => (uid, gid, pid),
81 _ => {
82 error!("Wrong metadata type given to Unix peer credentials authenticator.");
83 return Err(ResponseStatus::AuthenticationError);
84 }
85 };
86
87 if uid == expected_uid {
90 let app_name = uid.to_string();
91 let is_admin = self.admins.is_admin(&app_name);
92 Ok(Application {
93 identity: ApplicationIdentity {
94 name: app_name,
95 auth: AuthType::UnixPeerCredentials.into(),
96 },
97 is_admin,
98 })
99 } else {
100 error!("Declared UID in authentication request does not match the process's UID.");
101 Err(ResponseStatus::AuthenticationError)
102 }
103 }
104}
105
106#[cfg(test)]
107mod test {
108 use super::super::Authenticate;
109 use super::UnixPeerCredentialsAuthenticator;
110 use crate::front::domain_socket::peer_credentials;
111 use crate::front::listener::ConnectionMetadata;
112 use libc::{getuid, uid_t};
113 use parsec_interface::requests::request::RequestAuth;
114 use parsec_interface::requests::ResponseStatus;
115 use rand::Rng;
116 use std::os::unix::net::UnixStream;
117
118 #[test]
119 fn successful_authentication() {
120 let (sock_a, _sock_b) = UnixStream::pair().unwrap();
125 let (cred_a, _cred_b) = (
126 peer_credentials::peer_cred(&sock_a).unwrap(),
127 peer_credentials::peer_cred(&_sock_b).unwrap(),
128 );
129
130 let authenticator = UnixPeerCredentialsAuthenticator {
131 admins: Default::default(),
132 };
133
134 let req_auth_data = cred_a.uid.to_le_bytes().to_vec();
135 let req_auth = RequestAuth::new(req_auth_data);
136 let conn_metadata = Some(ConnectionMetadata::UnixPeerCredentials {
137 uid: cred_a.uid,
138 gid: cred_a.gid,
139 pid: None,
140 });
141
142 let application = authenticator
143 .authenticate(&req_auth, conn_metadata)
144 .expect("Failed to authenticate");
145
146 let current_uid: uid_t = unsafe { getuid() };
147 assert_eq!(application.identity.name, current_uid.to_string());
148 assert!(!application.is_admin);
149 }
150
151 #[test]
152 fn unsuccessful_authentication_wrong_declared_uid() {
153 let (sock_a, _sock_b) = UnixStream::pair().unwrap();
158 let (cred_a, _cred_b) = (
159 peer_credentials::peer_cred(&sock_a).unwrap(),
160 peer_credentials::peer_cred(&_sock_b).unwrap(),
161 );
162
163 let authenticator = UnixPeerCredentialsAuthenticator {
164 admins: Default::default(),
165 };
166
167 let wrong_uid = cred_a.uid + 1;
168 let wrong_req_auth_data = wrong_uid.to_le_bytes().to_vec();
169 let req_auth = RequestAuth::new(wrong_req_auth_data);
170 let conn_metadata = Some(ConnectionMetadata::UnixPeerCredentials {
171 uid: cred_a.uid,
172 gid: cred_a.gid,
173 pid: cred_a.pid,
174 });
175
176 let auth_result = authenticator
177 .authenticate(&req_auth, conn_metadata)
178 .unwrap_err();
179 assert_eq!(auth_result, ResponseStatus::AuthenticationError);
180 }
181
182 #[test]
183 fn unsuccessful_authentication_garbage_data() {
184 let (sock_a, _sock_b) = UnixStream::pair().unwrap();
188 let (cred_a, _cred_b) = (
189 peer_credentials::peer_cred(&sock_a).unwrap(),
190 peer_credentials::peer_cred(&_sock_b).unwrap(),
191 );
192
193 let authenticator = UnixPeerCredentialsAuthenticator {
194 admins: Default::default(),
195 };
196
197 let garbage_data = rand::thread_rng().gen::<[u8; 32]>().to_vec();
198 let req_auth = RequestAuth::new(garbage_data);
199 let conn_metadata = Some(ConnectionMetadata::UnixPeerCredentials {
200 uid: cred_a.uid,
201 gid: cred_a.gid,
202 pid: cred_a.pid,
203 });
204
205 let auth_result = authenticator
206 .authenticate(&req_auth, conn_metadata)
207 .unwrap_err();
208 assert_eq!(auth_result, ResponseStatus::AuthenticationError);
209 }
210
211 #[test]
212 fn unsuccessful_authentication_no_metadata() {
213 let authenticator = UnixPeerCredentialsAuthenticator {
214 admins: Default::default(),
215 };
216 let req_auth = RequestAuth::new("secret".into());
217
218 let conn_metadata = None;
219 let auth_result = authenticator
220 .authenticate(&req_auth, conn_metadata)
221 .unwrap_err();
222 assert_eq!(auth_result, ResponseStatus::AuthenticationError);
223 }
224
225 #[test]
226 fn admin_check() {
227 let (sock_a, _sock_b) = UnixStream::pair().unwrap();
229 let (cred_a, _cred_b) = (
230 peer_credentials::peer_cred(&sock_a).unwrap(),
231 peer_credentials::peer_cred(&_sock_b).unwrap(),
232 );
233
234 let current_uid: uid_t = unsafe { getuid() };
235 let admin = toml::from_str(&format!("name = '{}'", current_uid)).unwrap();
236 let authenticator = UnixPeerCredentialsAuthenticator {
237 admins: vec![admin].into(),
238 };
239
240 let req_auth_data = cred_a.uid.to_le_bytes().to_vec();
241 let req_auth = RequestAuth::new(req_auth_data);
242 let conn_metadata = Some(ConnectionMetadata::UnixPeerCredentials {
243 uid: cred_a.uid,
244 gid: cred_a.gid,
245 pid: None,
246 });
247
248 let application = authenticator
249 .authenticate(&req_auth, conn_metadata)
250 .expect("Failed to authenticate");
251
252 assert_eq!(application.identity.name, current_uid.to_string());
253 assert!(application.is_admin);
254 }
255
256 #[test]
257 fn unsuccessful_authentication_wrong_metadata() {
258 }
262}