1use alloc::string::{String, ToString};
4use alloc::vec::Vec;
5
6use xcb_rust_protocol::proto::xproto::FamilyEnum as X11Family;
7use xcb_rust_protocol::XcbEnv;
8
9const MIT_MAGIC_COOKIE_1: &[u8] = b"MIT-MAGIC-COOKIE-1";
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct Family(u16);
17
18impl Family {
19 pub const INTERNET: Self = Self(0);
21 pub const DEC_NET: Self = Self(1);
23 pub const CHAOS: Self = Self(2);
25 pub const SERVER_INTERPRETED: Self = Self(5);
27 pub const INTERNET6: Self = Self(6);
29 pub const WILD: Self = Self(65535);
31 pub const LOCAL: Self = Self(256);
33 pub const NETNAME: Self = Self(254);
35 pub const KRB5_PRINCIPAL: Self = Self(253);
37 pub const LOCAL_HOST: Self = Self(252);
39}
40
41impl From<X11Family> for Family {
42 fn from(value: X11Family) -> Self {
43 Self(value.0.into())
44 }
45}
46
47impl From<u16> for Family {
48 fn from(value: u16) -> Self {
49 Self(value)
50 }
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
55pub(crate) struct AuthEntry {
56 family: Family,
58 address: Vec<u8>,
60 number: Vec<u8>,
62 name: Vec<u8>,
65 data: Vec<u8>,
67}
68
69mod file {
70 use alloc::string::String;
72 use alloc::vec;
73 use alloc::vec::Vec;
74 use rusl::string::unix_str::UnixStr;
75
76 use super::AuthEntry;
77
78 #[inline]
82 fn read_u16(read: &[u8], offset: usize) -> Option<u16> {
83 Some(u16::from_be_bytes(
84 read.get(offset..offset + 2)?.try_into().unwrap(),
85 ))
86 }
87
88 fn read_string(read: &[u8], offset: usize) -> Option<(Vec<u8>, usize)> {
93 let length = read_u16(read, offset)? as usize;
94 Some((
95 read.get(offset + 2..offset + 2 + length)?.to_vec(),
96 offset + 2 + length,
97 ))
98 }
99
100 fn read_entry(read: &[u8], base_offset: &mut usize) -> Option<AuthEntry> {
106 let family = read_u16(read, *base_offset)?.into();
107 *base_offset += 2;
108
109 let (address, offset) = read_string(read, *base_offset)?;
110 let (number, offset) = read_string(read, offset)?;
111 let (name, offset) = read_string(read, offset)?;
112 let (data, offset) = read_string(read, offset)?;
113 *base_offset = offset;
114 Some(AuthEntry {
115 family,
116 address,
117 number,
118 name,
119 data,
120 })
121 }
122
123 #[derive(Debug)]
125 pub(crate) struct XAuthorityEntries(Vec<u8>, usize);
126
127 impl XAuthorityEntries {
128 pub(crate) fn new(
134 auth_file: &UnixStr,
135 ) -> Result<Option<XAuthorityEntries>, tiny_std::Error> {
136 tiny_std::fs::read(auth_file)
137 .ok()
138 .map(|buf| Ok(XAuthorityEntries(buf, 0)))
139 .transpose()
140 }
141 }
142
143 impl Iterator for XAuthorityEntries {
144 type Item = AuthEntry;
145
146 fn next(&mut self) -> Option<Self::Item> {
147 read_entry(&self.0, &mut self.1)
148 }
149 }
150
151 #[cfg(test)]
152 mod test {
153 use alloc::vec;
154
155 use std::io::Cursor;
156
157 use super::super::{AuthEntry, Family};
158 use super::read_entry;
159
160 #[test]
161 fn test_read() {
162 let data = [
164 0x01, 0x00, 0x00, 0x07, 0x5a, 0x77, 0x65, 0x69, 0x4c, 0x45, 0x44, 0x00, 0x01, 0x31,
165 0x00, 0x03, 0x62, 0x61, 0x72, 0x00, 0x04, 0xde, 0xad, 0xbe, 0xef,
166 ];
167 let mut buf = data.to_vec();
168 let entry = read_entry(&buf, &mut 0).unwrap();
169 assert_eq!(
170 entry,
171 AuthEntry {
172 family: Family::LOCAL,
173 address: b"ZweiLED".to_vec(),
174 number: b"1".to_vec(),
175 name: b"bar".to_vec(),
176 data: u32::to_be_bytes(0xdead_beef).to_vec(),
177 }
178 );
179 }
180
181 #[test]
182 fn test_read_iterate() {
183 let data = [
187 0x01, 0x00, 0x00, 0x07, 0x5a, 0x77, 0x65, 0x69, 0x4c, 0x45, 0x44, 0x00, 0x01, 0x31,
188 0x00, 0x03, 0x62, 0x61, 0x72, 0x00, 0x04, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00,
189 0x04, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, 0x32, 0x00, 0x03, 0x62, 0x61, 0x7a, 0x00,
190 0x04, 0xaa, 0xbb, 0xcc, 0xdd,
191 ];
192 let mut buf = data.to_vec();
193 let mut offset = 0;
194 for expected in &[
195 AuthEntry {
196 family: Family::LOCAL,
197 address: b"ZweiLED".to_vec(),
198 number: b"1".to_vec(),
199 name: b"bar".to_vec(),
200 data: u32::to_be_bytes(0xdead_beef).to_vec(),
201 },
202 AuthEntry {
203 family: Family::INTERNET,
204 address: vec![1, 2, 3, 4],
205 number: b"2".to_vec(),
206 name: b"baz".to_vec(),
207 data: u32::to_be_bytes(0xaabb_ccdd).to_vec(),
208 },
209 ] {
210 let entry = read_entry(&buf, &mut offset).unwrap();
211 assert_eq!(&entry, expected);
212 }
213 let entry = read_entry(&buf, &mut offset);
214 assert_eq!(entry, None);
215 }
216 }
217}
218
219pub(crate) type AuthInfo = (Vec<u8>, Vec<u8>);
220
221pub fn get_auth(
231 xcb_env: XcbEnv,
232 family: Family,
233 address: &[u8],
234 display: u16,
235) -> Result<Option<AuthInfo>, tiny_std::Error> {
236 if let Some(xauth_file) = xcb_env.x_authority {
237 return match file::XAuthorityEntries::new(xauth_file)? {
238 None => Ok(None),
239 Some(entries) => Ok(get_auth_impl(entries, family, address, display)),
240 };
241 }
242 Ok(None)
243}
244
245fn get_auth_impl(
246 entries: impl Iterator<Item = AuthEntry>,
247 family: Family,
248 address: &[u8],
249 display: u16,
250) -> Option<AuthInfo> {
251 fn address_matches(
252 (family1, address1): (Family, &[u8]),
253 (family2, address2): (Family, &[u8]),
254 ) -> bool {
255 if family1 == Family::WILD || family2 == Family::WILD {
256 true
257 } else if family1 != family2 {
258 false
259 } else {
260 address1 == address2
261 }
262 }
263
264 fn display_number_matches(entry_number: &[u8], display_number: &[u8]) -> bool {
265 debug_assert!(!display_number.is_empty()); entry_number.is_empty() || entry_number == display_number
267 }
268
269 let display = display.to_string();
270 let display = display.as_bytes();
271
272 for entry in entries {
273 if address_matches((family, address), (entry.family, &entry.address))
274 && display_number_matches(&entry.number, display)
275 && entry.name == MIT_MAGIC_COOKIE_1
276 {
277 return Some((entry.name, entry.data));
278 }
279 }
280 None
281}
282
283#[cfg(test)]
284mod test {
285 use alloc::vec;
286
287 use super::{get_auth_impl, AuthEntry, Family, MIT_MAGIC_COOKIE_1};
288
289 fn expect_match<F>(f: F)
292 where
293 F: FnOnce(&mut AuthEntry),
294 {
295 let mut entry = AuthEntry {
296 family: Family::LOCAL,
297 address: b"whatever".to_vec(),
298 number: b"42".to_vec(),
299 name: MIT_MAGIC_COOKIE_1.to_vec(),
300 data: b"1234".to_vec(),
301 };
302 f(&mut entry);
303 let entries = vec![entry];
304 let res = get_auth_impl(entries.into_iter(), Family::LOCAL, b"whatever", 42).unwrap();
305 assert_eq!(res, (MIT_MAGIC_COOKIE_1.to_vec(), b"1234".to_vec()));
306 }
307
308 fn expect_mismatch<F>(f: F)
311 where
312 F: FnOnce(&mut AuthEntry),
313 {
314 let mut entry = AuthEntry {
315 family: Family::LOCAL,
316 address: b"whatever".to_vec(),
317 number: b"42".to_vec(),
318 name: MIT_MAGIC_COOKIE_1.to_vec(),
319 data: b"1234".to_vec(),
320 };
321 f(&mut entry);
322 let entries = vec![entry];
323 assert_eq!(
324 get_auth_impl(entries.into_iter(), Family::LOCAL, b"whatever", 42),
325 None
326 );
327 }
328
329 #[test]
330 fn direct_match() {
331 expect_match(|_| {});
333 }
334
335 #[test]
336 fn display_wildcard() {
337 expect_match(|entry| entry.number = vec![]);
338 }
339
340 #[test]
341 fn address_wildcard_match1() {
342 expect_match(|entry| entry.family = Family::WILD);
343 }
344
345 #[test]
346 fn address_wildcard_match2() {
347 let entry = AuthEntry {
348 family: Family::LOCAL,
349 address: b"whatever".to_vec(),
350 number: b"42".to_vec(),
351 name: MIT_MAGIC_COOKIE_1.to_vec(),
352 data: b"1234".to_vec(),
353 };
354 let entries = vec![entry];
355 assert_eq!(
356 get_auth_impl(entries.into_iter(), Family::WILD, &[], 42).unwrap(),
357 (MIT_MAGIC_COOKIE_1.to_vec(), b"1234".to_vec())
358 );
359 }
360
361 #[test]
362 fn family_mismatch() {
363 expect_mismatch(|entry| entry.family = Family::KRB5_PRINCIPAL);
364 }
365
366 #[test]
367 fn address_mismatch() {
368 expect_mismatch(|entry| entry.address = b"something else".to_vec());
369 }
370
371 #[test]
372 fn number_mismatch() {
373 expect_mismatch(|entry| entry.number = b"1337".to_vec());
374 }
375
376 #[test]
377 fn protocol_mismatch() {
378 expect_mismatch(|entry| entry.name = b"XDM-AUTHORIZATION-1".to_vec());
379 }
380}