1use std::{
2 convert::TryFrom,
3 io::{Cursor, Write},
4};
5
6use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
7
8use crate::{auth::AuthUnixParams, Error, Opaque};
9
10const AUTH_NONE: u32 = 0;
11const AUTH_UNIX: u32 = 1;
12const AUTH_SHORT: u32 = 2;
13
14#[non_exhaustive]
19#[derive(Debug, PartialEq, Clone)]
20pub enum AuthFlavor<T>
21where
22 T: AsRef<[u8]>,
23{
24 AuthNone(Option<T>),
29
30 AuthUnix(AuthUnixParams<T>),
32
33 AuthShort(T),
37
38 Unknown {
41 id: u32,
43
44 data: T,
48 },
49}
50
51impl<'a> AuthFlavor<&'a [u8]> {
52 pub(crate) fn from_cursor(r: &mut Cursor<&'a [u8]>) -> Result<Self, Error> {
53 let flavor = r.read_u32::<BigEndian>()?;
55
56 let flavor = match flavor {
57 AUTH_NONE => AuthFlavor::new_none(r)?,
58 AUTH_UNIX => AuthFlavor::new_unix(r)?,
59 AUTH_SHORT => AuthFlavor::new_short(r)?,
60 v => AuthFlavor::Unknown {
63 id: v,
64 data: Opaque::from_wire(r, 200)?.into_inner(),
65 },
66 };
67
68 Ok(flavor)
69 }
70
71 fn new_none(r: &mut Cursor<&'a [u8]>) -> Result<Self, Error> {
72 let payload = Opaque::from_wire(r, 200)?.into_inner();
73 if payload.is_empty() {
74 return Ok(AuthFlavor::AuthNone(None));
75 }
76
77 Ok(AuthFlavor::AuthNone(Some(payload)))
78 }
79
80 fn new_unix(r: &mut Cursor<&'a [u8]>) -> Result<Self, Error> {
81 let len = r.read_u32::<BigEndian>()?;
83 if len > 200 {
84 return Err(Error::InvalidLength);
85 }
86
87 Ok(AuthFlavor::AuthUnix(AuthUnixParams::from_cursor(r, len)?))
88 }
89
90 fn new_short(r: &mut Cursor<&'a [u8]>) -> Result<Self, Error> {
91 Ok(AuthFlavor::AuthShort(
92 Opaque::from_wire(r, 200)?.into_inner(),
93 ))
94 }
95}
96
97impl<T> AuthFlavor<T>
98where
99 T: AsRef<[u8]>,
100{
101 pub fn serialise_into<W: Write>(&self, mut buf: W) -> Result<(), std::io::Error> {
107 buf.write_u32::<BigEndian>(self.id())?;
108
109 assert!(self.associated_data_len() <= 200);
111
112 match self {
114 Self::AuthNone(Some(data)) | Self::AuthShort(data) | Self::Unknown { data, .. } => {
116 Opaque::from_user_payload(data).serialise_into(&mut buf)
117 }
118 Self::AuthNone(None) => {
120 buf.write_u32::<BigEndian>(0)?;
121 Ok(())
122 }
123 Self::AuthUnix(p) => {
125 buf.write_u32::<BigEndian>(p.serialised_len())?;
126 p.serialise_into(buf)
127 }
128 }
129 }
130
131 pub fn id(&self) -> u32 {
133 match self {
134 Self::AuthNone(_) => AUTH_NONE,
135 Self::AuthUnix(_) => AUTH_UNIX,
136 Self::AuthShort(_) => AUTH_SHORT,
137 Self::Unknown { id, data: _ } => *id,
138 }
139 }
140
141 pub fn associated_data_len(&self) -> u32 {
143 match self {
144 Self::AuthNone(Some(d)) => d.as_ref().len() as u32,
145 Self::AuthNone(None) => 0,
146 Self::AuthUnix(p) => p.associated_data_len(),
147 Self::AuthShort(d) => d.as_ref().len() as u32,
148 Self::Unknown { id: _id, data } => data.as_ref().len() as u32,
149 }
150 }
151
152 pub fn serialised_len(&self) -> u32 {
155 let mut l = 0;
156
157 l += 4;
159
160 l += match self {
162 #[allow(clippy::identity_op)]
163 Self::AuthNone(None) => {
164 4 + 0
166 }
167 Self::AuthUnix(ref p) => 4 + p.serialised_len(),
168 Self::Unknown { data, .. } | Self::AuthShort(data) | Self::AuthNone(Some(data)) => {
169 Opaque::from_user_payload(data).serialised_len()
170 }
171 };
172
173 l
174 }
175}
176
177impl<'a> TryFrom<&'a [u8]> for AuthFlavor<&'a [u8]> {
178 type Error = Error;
179
180 fn try_from(v: &'a [u8]) -> Result<Self, Self::Error> {
181 let mut c = Cursor::new(v);
182 AuthFlavor::from_cursor(&mut c)
183 }
184}
185
186#[cfg(feature = "bytes")]
187impl TryFrom<crate::Bytes> for AuthFlavor<crate::Bytes> {
188 type Error = Error;
189
190 fn try_from(mut v: crate::Bytes) -> Result<Self, Self::Error> {
191 use crate::bytes_ext::BytesReaderExt;
192
193 let flavor = v.try_u32()?;
194 let auth_data = v.try_array(200)?;
195
196 let flavor = match flavor {
197 AUTH_NONE if auth_data.is_empty() => Self::AuthNone(None),
198 AUTH_NONE => Self::AuthNone(Some(auth_data)),
199 AUTH_UNIX => {
200 let should_consume = auth_data.len();
205 let params = AuthUnixParams::try_from(auth_data)?;
206 if params.serialised_len() as usize != should_consume {
207 return Err(Error::InvalidAuthData);
208 }
209 Self::AuthUnix(params)
210 }
211 AUTH_SHORT => Self::AuthShort(auth_data),
212 id => Self::Unknown {
215 id,
216 data: auth_data,
217 },
218 };
219
220 Ok(flavor)
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use std::convert::TryInto;
227
228 use hex_literal::hex;
229
230 use super::*;
231
232 #[test]
233 fn test_auth_unix_unaligned_machinename<'a>() {
234 #[rustfmt::skip]
235 const RAW: [u8; 44] = hex!(
246 "0000000100000024000000000000000f4c4150544f502d315151425044474d00000000000000000000000000"
247 );
248
249 let f: AuthFlavor<&'a [u8]> = RAW.as_ref().try_into().expect("failed to parse message");
250 assert_eq!(f.serialised_len(), 44);
251 assert_eq!(f.id(), AUTH_UNIX);
252 assert_eq!(f.associated_data_len(), 27);
253
254 let params = match f {
255 AuthFlavor::AuthUnix(ref p) => p,
256 _ => panic!("wrong auth"),
257 };
258
259 assert_eq!(params.uid(), 0);
260
261 let mut c = Cursor::new(Vec::new());
262 f.serialise_into(&mut c).expect("serialise failed");
263
264 let buf = c.into_inner();
265 assert_eq!(buf.as_slice(), RAW.as_ref());
266 }
267
268 #[test]
269 fn test_auth_unix<'a>() {
270 #[rustfmt::skip]
271 const RAW: [u8; 92] = hex!(
298 "00000001000000540000000000000000000001f50000001400000010000001f500
299 00000c000000140000003d0000004f000000500000005100000062000002bd00000
300 02100000064000000cc000000fa0000018b0000018e0000018f"
301 );
302
303 let f: AuthFlavor<&'a [u8]> = RAW.as_ref().try_into().expect("failed to parse message");
304 assert_eq!(f.serialised_len(), 92);
305 assert_eq!(f.id(), AUTH_UNIX);
306 assert_eq!(f.associated_data_len(), 92 - 4 - 4 - 4 - 4); let params = match f {
309 AuthFlavor::AuthUnix(ref p) => p,
310 _ => panic!("wrong auth"),
311 };
312
313 assert_eq!(params.uid(), 501);
314
315 let mut c = Cursor::new(Vec::new());
316 f.serialise_into(&mut c).expect("serialise failed");
317
318 let buf = c.into_inner();
319 assert_eq!(buf.as_slice(), RAW.as_ref());
320 }
321
322 #[test]
323 fn test_auth_none<'a>() {
324 const RAW: [u8; 92] = hex!(
325 "
326 00 00 00 00
327 00 00 00 54
328 0000000000000000000001f50000001400000010000001f50000000c00000014000
329 0003d0000004f000000500000005100000062000002bd0000002100000064000000
330 cc000000fa0000018b0000018e0000018f"
331 );
332
333 let f: AuthFlavor<&'a [u8]> = RAW.as_ref().try_into().expect("failed to parse message");
334 assert_eq!(f.serialised_len(), 92);
335 assert_eq!(f.id(), AUTH_NONE);
336 assert_eq!(f.associated_data_len(), 92 - 4 - 4);
337
338 let data = match f {
339 AuthFlavor::AuthNone(Some(ref p)) => p,
340 _ => panic!("wrong auth"),
341 };
342
343 assert_eq!(data.len(), f.associated_data_len() as usize);
344 }
345
346 #[test]
347 fn test_auth_short<'a>() {
348 const RAW: [u8; 92] = hex!(
349 "
350 00 00 00 02
351 00 00 00 54
352 0000000000000000000001f50000001400000010000001f50000000c00000014000
353 0003d0000004f000000500000005100000062000002bd0000002100000064000000
354 cc000000fa0000018b0000018e0000018f"
355 );
356
357 let f: AuthFlavor<&'a [u8]> = RAW.as_ref().try_into().expect("failed to parse message");
358 assert_eq!(f.serialised_len(), 92);
359 assert_eq!(f.id(), AUTH_SHORT);
360 assert_eq!(f.associated_data_len(), 92 - 4 - 4);
361
362 let data = match f {
363 AuthFlavor::AuthShort(ref p) => p,
364 _ => panic!("wrong auth"),
365 };
366
367 assert_eq!(data.len(), f.associated_data_len() as usize);
368 }
369
370 #[test]
371 fn test_auth_unknown<'a>() {
372 const RAW: [u8; 92] = hex!(
373 "
374 00 00 00 FF
375 00 00 00 54
376 0000000000000000000001f50000001400000010000001f50000000c00000014000
377 0003d0000004f000000500000005100000062000002bd0000002100000064000000
378 cc000000fa0000018b0000018e0000018f"
379 );
380
381 let f: AuthFlavor<&'a [u8]> = RAW.as_ref().try_into().expect("failed to parse message");
382 assert_eq!(f.serialised_len(), 92);
383 assert_eq!(f.id(), 255);
384 assert_eq!(f.associated_data_len(), 92 - 4 - 4);
385
386 let (id, data) = match f {
387 AuthFlavor::Unknown { id, data } => (id, data),
388 _ => panic!("wrong auth"),
389 };
390
391 assert_eq!(id, f.id());
392 assert_eq!(data.len(), f.associated_data_len() as usize);
393 }
394}