1#![cfg(feature = "std")]
4
5use alloc::string::ToString;
6use alloc::vec::Vec;
7use std::io::Error;
8
9use crate::protocol::xproto::Family as X11Family;
10
11const MIT_MAGIC_COOKIE_1: &[u8] = b"MIT-MAGIC-COOKIE-1";
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct Family(u16);
19
20impl Family {
21 pub const INTERNET: Self = Self(0);
23 pub const DEC_NET: Self = Self(1);
25 pub const CHAOS: Self = Self(2);
27 pub const SERVER_INTERPRETED: Self = Self(5);
29 pub const INTERNET6: Self = Self(6);
31 pub const WILD: Self = Self(65535);
33 pub const LOCAL: Self = Self(256);
35 pub const NETNAME: Self = Self(254);
37 pub const KRB5_PRINCIPAL: Self = Self(253);
39 pub const LOCAL_HOST: Self = Self(252);
41}
42
43impl From<X11Family> for Family {
44 fn from(value: X11Family) -> Self {
45 Self(value.into())
46 }
47}
48
49impl From<u16> for Family {
50 fn from(value: u16) -> Self {
51 Self(value)
52 }
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
57pub(crate) struct AuthEntry {
58 family: Family,
60 address: Vec<u8>,
62 number: Vec<u8>,
64 name: Vec<u8>,
67 data: Vec<u8>,
69}
70
71mod file {
72 use alloc::{vec, vec::Vec};
75 use std::env::var_os;
76 use std::fs::File;
77 use std::io::{BufReader, Error, ErrorKind, Read};
78 use std::path::PathBuf;
79
80 use super::AuthEntry;
81
82 fn read_u16<R: Read>(read: &mut R) -> Result<u16, Error> {
86 let mut buffer = [0; 2];
87 read.read_exact(&mut buffer)?;
88 Ok(u16::from_be_bytes(buffer))
89 }
90
91 fn read_string<R: Read>(read: &mut R) -> Result<Vec<u8>, Error> {
96 let length = read_u16(read)?;
97 let mut result = vec![0; length.into()];
98 read.read_exact(&mut result[..])?;
99 Ok(result)
100 }
101
102 fn read_entry<R: Read>(read: &mut R) -> Result<Option<AuthEntry>, Error> {
108 let family = match read_u16(read) {
109 Ok(family) => family,
110 Err(ref e) if e.kind() == ErrorKind::UnexpectedEof => return Ok(None),
111 Err(e) => return Err(e),
112 }
113 .into();
114 let address = read_string(read)?;
115 let number = read_string(read)?;
116 let name = read_string(read)?;
117 let data = read_string(read)?;
118 Ok(Some(AuthEntry {
119 family,
120 address,
121 number,
122 name,
123 data,
124 }))
125 }
126
127 fn get_xauthority_file_name() -> Option<PathBuf> {
132 if let Some(name) = var_os("XAUTHORITY") {
133 return Some(name.into());
134 }
135 var_os("HOME").map(|prefix| {
136 let mut result = PathBuf::new();
137 result.push(prefix);
138 result.push(".Xauthority");
139 result
140 })
141 }
142
143 #[derive(Debug)]
145 pub(crate) struct XAuthorityEntries(BufReader<File>);
146
147 impl XAuthorityEntries {
148 pub(crate) fn new() -> Result<Option<XAuthorityEntries>, Error> {
154 get_xauthority_file_name()
155 .map(File::open)
156 .transpose()?
157 .map(|file| Ok(XAuthorityEntries(BufReader::new(file))))
160 .transpose()
161 }
162 }
163
164 impl Iterator for XAuthorityEntries {
165 type Item = Result<AuthEntry, Error>;
166
167 fn next(&mut self) -> Option<Self::Item> {
168 read_entry(&mut self.0).transpose()
169 }
170 }
171
172 #[cfg(test)]
173 mod test {
174 use super::super::{AuthEntry, Family};
175 use super::read_entry;
176 use alloc::vec;
177 use std::io::Cursor;
178
179 #[test]
180 fn test_read() {
181 let data = [
183 0x01, 0x00, 0x00, 0x07, 0x5a, 0x77, 0x65, 0x69, 0x4c, 0x45, 0x44, 0x00, 0x01, 0x31,
184 0x00, 0x03, 0x62, 0x61, 0x72, 0x00, 0x04, 0xde, 0xad, 0xbe, 0xef,
185 ];
186 let mut cursor = Cursor::new(&data[..]);
187 let entry = read_entry(&mut cursor).unwrap();
188 assert_eq!(
189 entry,
190 Some(AuthEntry {
191 family: Family::LOCAL,
192 address: b"ZweiLED".to_vec(),
193 number: b"1".to_vec(),
194 name: b"bar".to_vec(),
195 data: u32::to_be_bytes(0xdead_beef).to_vec(),
196 })
197 );
198 }
199
200 #[test]
201 fn test_read_iterate() {
202 let data = [
206 0x01, 0x00, 0x00, 0x07, 0x5a, 0x77, 0x65, 0x69, 0x4c, 0x45, 0x44, 0x00, 0x01, 0x31,
207 0x00, 0x03, 0x62, 0x61, 0x72, 0x00, 0x04, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00,
208 0x04, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, 0x32, 0x00, 0x03, 0x62, 0x61, 0x7a, 0x00,
209 0x04, 0xaa, 0xbb, 0xcc, 0xdd,
210 ];
211 let mut cursor = Cursor::new(&data[..]);
212 for expected in &[
213 AuthEntry {
214 family: Family::LOCAL,
215 address: b"ZweiLED".to_vec(),
216 number: b"1".to_vec(),
217 name: b"bar".to_vec(),
218 data: u32::to_be_bytes(0xdead_beef).to_vec(),
219 },
220 AuthEntry {
221 family: Family::INTERNET,
222 address: vec![1, 2, 3, 4],
223 number: b"2".to_vec(),
224 name: b"baz".to_vec(),
225 data: u32::to_be_bytes(0xaabb_ccdd).to_vec(),
226 },
227 ] {
228 let entry = read_entry(&mut cursor).unwrap();
229 assert_eq!(entry.as_ref(), Some(expected));
230 }
231 let entry = read_entry(&mut cursor).unwrap();
232 assert_eq!(entry, None);
233 }
234 }
235}
236
237pub(crate) type AuthInfo = (Vec<u8>, Vec<u8>);
238
239pub fn get_auth(family: Family, address: &[u8], display: u16) -> Result<Option<AuthInfo>, Error> {
249 match file::XAuthorityEntries::new()? {
250 None => Ok(None),
251 Some(entries) => get_auth_impl(entries, family, address, display),
252 }
253}
254
255fn get_auth_impl(
256 entries: impl Iterator<Item = Result<AuthEntry, Error>>,
257 family: Family,
258 address: &[u8],
259 display: u16,
260) -> Result<Option<AuthInfo>, Error> {
261 fn address_matches(
262 (family1, address1): (Family, &[u8]),
263 (family2, address2): (Family, &[u8]),
264 ) -> bool {
265 if family1 == Family::WILD || family2 == Family::WILD {
266 true
267 } else if family1 != family2 {
268 false
269 } else {
270 address1 == address2
271 }
272 }
273
274 fn display_number_matches(entry_number: &[u8], display_number: &[u8]) -> bool {
275 debug_assert!(!display_number.is_empty()); entry_number.is_empty() || entry_number == display_number
277 }
278
279 let display = display.to_string();
280 let display = display.as_bytes();
281
282 for entry in entries {
283 let entry = entry?;
284
285 if address_matches((family, address), (entry.family, &entry.address))
286 && display_number_matches(&entry.number, display)
287 && entry.name == MIT_MAGIC_COOKIE_1
288 {
289 return Ok(Some((entry.name, entry.data)));
290 }
291 }
292 Ok(None)
293}
294
295#[cfg(test)]
296mod test {
297 use super::{get_auth_impl, AuthEntry, Family, MIT_MAGIC_COOKIE_1};
298 use alloc::vec;
299
300 fn expect_match<F>(f: F)
303 where
304 F: FnOnce(&mut AuthEntry),
305 {
306 let mut entry = AuthEntry {
307 family: Family::LOCAL,
308 address: b"whatever".to_vec(),
309 number: b"42".to_vec(),
310 name: MIT_MAGIC_COOKIE_1.to_vec(),
311 data: b"1234".to_vec(),
312 };
313 f(&mut entry);
314 let entries = vec![Ok(entry)];
315 assert_eq!(
316 get_auth_impl(entries.into_iter(), Family::LOCAL, b"whatever", 42)
317 .unwrap()
318 .unwrap(),
319 (MIT_MAGIC_COOKIE_1.to_vec(), b"1234".to_vec())
320 );
321 }
322
323 fn expect_mismatch<F>(f: F)
326 where
327 F: FnOnce(&mut AuthEntry),
328 {
329 let mut entry = AuthEntry {
330 family: Family::LOCAL,
331 address: b"whatever".to_vec(),
332 number: b"42".to_vec(),
333 name: MIT_MAGIC_COOKIE_1.to_vec(),
334 data: b"1234".to_vec(),
335 };
336 f(&mut entry);
337 let entries = vec![Ok(entry)];
338 assert_eq!(
339 get_auth_impl(entries.into_iter(), Family::LOCAL, b"whatever", 42).unwrap(),
340 None
341 );
342 }
343
344 #[test]
345 fn direct_match() {
346 expect_match(|_| {});
348 }
349
350 #[test]
351 fn display_wildcard() {
352 expect_match(|entry| entry.number = vec![]);
353 }
354
355 #[test]
356 fn address_wildcard_match1() {
357 expect_match(|entry| entry.family = Family::WILD);
358 }
359
360 #[test]
361 fn address_wildcard_match2() {
362 let entry = AuthEntry {
363 family: Family::LOCAL,
364 address: b"whatever".to_vec(),
365 number: b"42".to_vec(),
366 name: MIT_MAGIC_COOKIE_1.to_vec(),
367 data: b"1234".to_vec(),
368 };
369 let entries = vec![Ok(entry)];
370 assert_eq!(
371 get_auth_impl(entries.into_iter(), Family::WILD, &[], 42)
372 .unwrap()
373 .unwrap(),
374 (MIT_MAGIC_COOKIE_1.to_vec(), b"1234".to_vec())
375 );
376 }
377
378 #[test]
379 fn family_mismatch() {
380 expect_mismatch(|entry| entry.family = Family::KRB5_PRINCIPAL);
381 }
382
383 #[test]
384 fn address_mismatch() {
385 expect_mismatch(|entry| entry.address = b"something else".to_vec());
386 }
387
388 #[test]
389 fn number_mismatch() {
390 expect_mismatch(|entry| entry.number = b"1337".to_vec());
391 }
392
393 #[test]
394 fn protocol_mismatch() {
395 expect_mismatch(|entry| entry.name = b"XDM-AUTHORIZATION-1".to_vec());
396 }
397}