use std::{
io::{Cursor, Write},
iter::FromIterator,
ops::Deref,
};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::{Error, Opaque};
const MAX_GIDS: usize = 16;
const MAX_MACHINE_NAME_LEN: u32 = 255;
#[derive(Clone, PartialEq, Default)]
struct Gids {
values: [u32; MAX_GIDS],
len: u8,
}
impl Deref for Gids {
type Target = [u32];
fn deref(&self) -> &Self::Target {
&self.values[..self.len as usize]
}
}
impl std::fmt::Debug for Gids {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.deref().fmt(f)
}
}
impl FromIterator<u32> for Gids {
fn from_iter<T: IntoIterator<Item = u32>>(iter: T) -> Self {
let mut values = [0; MAX_GIDS];
let mut len = 0;
for v in iter.into_iter() {
assert!(len < MAX_GIDS);
values[len] = v;
len += 1;
}
Self {
values,
len: len as u8,
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct AuthUnixParams<T>
where
T: AsRef<[u8]>,
{
stamp: u32,
machine_name: Opaque<T>,
uid: u32,
gid: u32,
gids: Gids,
}
impl<'a> AuthUnixParams<&'a [u8]> {
pub(crate) fn from_cursor(r: &mut Cursor<&'a [u8]>, expected_len: u32) -> Result<Self, Error> {
let start_pos = r.position();
let stamp = r.read_u32::<BigEndian>()?;
let machine_name = Opaque::from_wire(&mut *r, MAX_MACHINE_NAME_LEN as _)?;
let uid = r.read_u32::<BigEndian>()?;
let gid = r.read_u32::<BigEndian>()?;
let gids_count = r.read_u32::<BigEndian>()? as usize;
let gids = match gids_count {
0 => Gids::default(),
c if c <= 16 => (0..c)
.map(|_| r.read_u32::<BigEndian>())
.collect::<Result<Gids, _>>()?,
_ => return Err(Error::InvalidAuthData),
};
if (r.position() - start_pos) != expected_len as u64 {
return Err(Error::InvalidAuthData);
}
Ok(AuthUnixParams {
stamp,
machine_name,
uid,
gid,
gids,
})
}
}
impl<T> AuthUnixParams<T>
where
T: AsRef<[u8]>,
{
pub fn new(
stamp: u32,
machine_name: T,
uid: u32,
gid: u32,
gids: impl IntoIterator<Item = u32>,
) -> Self {
assert!(machine_name.as_ref().len() <= MAX_MACHINE_NAME_LEN as usize);
Self {
stamp,
machine_name: Opaque::from_user_payload(machine_name),
uid,
gid,
gids: gids.into_iter().collect::<Gids>(),
}
}
pub fn serialise_into<W: Write>(&self, mut buf: W) -> Result<(), std::io::Error> {
buf.write_u32::<BigEndian>(self.stamp)?;
self.machine_name.serialise_into(&mut buf)?;
buf.write_u32::<BigEndian>(self.uid)?;
buf.write_u32::<BigEndian>(self.gid)?;
buf.write_u32::<BigEndian>(self.gids.deref().len() as u32)?;
for g in &*self.gids {
buf.write_u32::<BigEndian>(*g)?;
}
Ok(())
}
pub fn stamp(&self) -> u32 {
self.stamp
}
pub fn machine_name(&self) -> &[u8] {
self.machine_name.as_ref()
}
pub fn machine_name_str(&self) -> &str {
std::str::from_utf8(self.machine_name.as_ref()).unwrap()
}
pub fn uid(&self) -> u32 {
self.uid
}
pub fn gid(&self) -> u32 {
self.gid
}
pub fn gids(&self) -> Option<&[u32]> {
if self.gids.len == 0 {
return None;
}
Some(&*self.gids)
}
pub fn serialised_len(&self) -> u32 {
let mut l = std::mem::size_of::<u32>() * 3;
l += self.machine_name.serialised_len() as usize;
l += (self.gids.deref().len() + 1) * std::mem::size_of::<u32>();
l as u32
}
pub(crate) fn associated_data_len(&self) -> u32 {
let mut l = std::mem::size_of::<u32>() * 3;
l += self.machine_name.len();
l += std::mem::size_of_val(self.gids.deref());
l as u32
}
}
#[cfg(feature = "bytes")]
impl TryFrom<crate::Bytes> for AuthUnixParams<crate::Bytes> {
type Error = Error;
fn try_from(mut v: crate::Bytes) -> Result<Self, Self::Error> {
use crate::bytes_ext::BytesReaderExt;
let stamp = v.try_u32()?;
let name = v.try_array(MAX_MACHINE_NAME_LEN as _)?;
let uid = v.try_u32()?;
let gid = v.try_u32()?;
let gids_count = v.try_u32()? as usize;
let gids = match gids_count {
0 => Gids::default(),
c if c <= 16 => (0..c).map(|_| v.try_u32()).collect::<Result<Gids, _>>()?,
_ => return Err(Error::InvalidAuthData),
};
Ok(Self {
stamp,
machine_name: Opaque::from_user_payload(name),
uid,
gid,
gids,
})
}
}
#[cfg(test)]
mod tests {
use hex_literal::hex;
#[cfg(feature = "bytes")]
use crate::Bytes;
use super::*;
#[test]
fn test_serialise_deserialise() {
let gids = [
501, 12, 20, 61, 79, 80, 81, 98, 701, 33, 100, 204, 250, 395, 398, 399,
];
let params = AuthUnixParams::new(0, b"".as_ref(), 501, 20, gids);
let mut buf = Cursor::new(Vec::new());
params
.serialise_into(&mut buf)
.expect("failed to serialise");
#[rustfmt::skip]
let want = hex!(
"0000000000000000000001f50000001400000010000001f50000000c0000001400
00003d0000004f000000500000005100000062000002bd000000210000006400000
0cc000000fa0000018b0000018e0000018f"
);
let buf = buf.into_inner();
assert_eq!(want.len(), buf.len());
assert_eq!(want.as_ref(), buf.as_slice());
let mut c = Cursor::new(want.as_ref());
let s = AuthUnixParams::from_cursor(&mut c, 84).expect("deserialise failed");
assert_eq!(s.serialised_len(), 84);
assert_eq!(params, s);
}
#[test]
fn test_empty() {
let want = hex!("000000000000000000000000000000000000000100000000");
let mut c = Cursor::new(want.as_ref());
let s = AuthUnixParams::from_cursor(&mut c, 24).expect("deserialise failed");
assert_eq!(s.stamp(), 0);
assert_eq!(s.machine_name_str(), "");
assert_eq!(s.uid(), 0);
assert_eq!(s.gid(), 0);
assert_eq!(s.gids(), Some([0].as_slice()));
assert_eq!(s.serialised_len(), 24);
let mut buf = Cursor::new(Vec::new());
s.serialise_into(&mut buf).expect("failed to serialise");
let buf = buf.into_inner();
assert_eq!(want.len(), buf.len());
assert_eq!(want.as_ref(), buf.as_slice());
}
#[test]
#[cfg(feature = "bytes")]
fn test_deserialise_bytes() {
#[rustfmt::skip]
let want = hex!(
"0000000000000000000001f50000001400000010000001f50000000c0000001400
00003d0000004f000000500000005100000062000002bd000000210000006400000
0cc000000fa0000018b0000018e0000018f"
);
let static_want: &'static [u8] = Box::leak(Box::new(want));
let got =
AuthUnixParams::try_from(Bytes::from(static_want)).expect("failed to deserialise");
assert_eq!(got.stamp(), 0);
assert_eq!(got.machine_name_str(), "");
assert_eq!(got.uid(), 501);
assert_eq!(got.gid(), 20);
assert_eq!(
got.gids(),
Some(
[501, 12, 20, 61, 79, 80, 81, 98, 701, 33, 100, 204, 250, 395, 398, 399].as_slice()
)
);
assert_eq!(got.serialised_len(), 84);
}
#[test]
#[cfg(feature = "bytes")]
fn test_empty_bytes() {
let want = hex!("000000000000000000000000000000000000000100000000");
let static_want: &'static [u8] = Box::leak(Box::new(want));
let s = AuthUnixParams::try_from(Bytes::from(static_want)).expect("deserialise failed");
assert_eq!(s.stamp(), 0);
assert_eq!(s.machine_name_str(), "");
assert_eq!(s.uid(), 0);
assert_eq!(s.gid(), 0);
assert_eq!(s.gids(), Some([0].as_slice()));
assert_eq!(s.serialised_len(), 24);
let mut buf = Cursor::new(Vec::new());
s.serialise_into(&mut buf).expect("failed to serialise");
let buf = buf.into_inner();
assert_eq!(want.len(), buf.len());
assert_eq!(want.as_ref(), buf.as_slice());
}
#[test]
fn test_max_machine_name() {
AuthUnixParams::new(42, [1_u8; 255], 42, 42, None);
}
#[test]
#[should_panic]
fn test_long_machine_name_panic() {
AuthUnixParams::new(42, [1_u8; 256], 42, 42, None);
}
#[test]
#[should_panic]
fn test_long_gids_panic() {
AuthUnixParams::new(
42,
Opaque::from_user_payload([].as_slice()),
42,
42,
[
1_u32, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
],
);
}
}