use crate::ffi::{self, KeyCtlOperation, KeySerialId};
use crate::utils::Vec;
use crate::{KeyError, KeyPermissions, Metadata};
use core::fmt;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Key(KeySerialId);
impl fmt::Display for Key {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let info = self.metadata().map_err(|_| fmt::Error)?;
write!(f, "Key({:?})", info)
}
}
impl Key {
pub fn from_id(id: KeySerialId) -> Self {
Self(id)
}
pub fn get_id(&self) -> KeySerialId {
self.0
}
pub fn metadata(&self) -> Result<Metadata, KeyError> {
Metadata::from_id(self.0)
}
pub fn read<T: AsMut<[u8]>>(&self, buffer: &mut T) -> Result<usize, KeyError> {
let len = ffi::keyctl!(
KeyCtlOperation::Read,
self.0.as_raw_id() as libc::c_ulong,
buffer.as_mut().as_mut_ptr() as _,
buffer.as_mut().len() as _
)? as usize;
Ok(len)
}
pub fn read_to_vec(&self) -> Result<Vec<u8>, KeyError> {
let mut buffer = Vec::with_capacity(65536);
let len = ffi::keyctl!(
KeyCtlOperation::Read,
self.0.as_raw_id() as libc::c_ulong,
buffer.as_mut_ptr() as _,
buffer.capacity() as _
)? as usize;
unsafe {
buffer.set_len(len);
}
Ok(buffer)
}
pub fn update<T: AsRef<[u8]>>(&self, update: &T) -> Result<(), KeyError> {
_ = ffi::keyctl!(
KeyCtlOperation::Update,
self.0.as_raw_id() as libc::c_ulong,
update.as_ref().as_ptr() as _,
update.as_ref().len() as _
)?;
Ok(())
}
pub fn set_perms(&self, perm: KeyPermissions) -> Result<(), KeyError> {
_ = ffi::keyctl!(
KeyCtlOperation::SetPerm,
self.0.as_raw_id() as libc::c_ulong,
perm.bits() as _
)?;
Ok(())
}
pub fn chown(&self, uid: Option<u32>, gid: Option<u32>) -> Result<(), KeyError> {
let uid_opt = uid.unwrap_or(u32::MAX);
let gid_opt = gid.unwrap_or(u32::MAX);
_ = ffi::keyctl!(
KeyCtlOperation::Chown,
self.0.as_raw_id() as libc::c_ulong,
uid_opt as _,
gid_opt as _
)?;
Ok(())
}
pub fn set_timeout(&self, seconds: usize) -> Result<(), KeyError> {
_ = ffi::keyctl!(
KeyCtlOperation::SetTimeout,
self.0.as_raw_id() as libc::c_ulong,
seconds as _
)?;
Ok(())
}
pub fn revoke(&self) -> Result<(), KeyError> {
_ = ffi::keyctl!(KeyCtlOperation::Revoke, self.0.as_raw_id() as libc::c_ulong)?;
Ok(())
}
pub fn reject(&self, seconds: usize) -> Result<(), KeyError> {
_ = ffi::keyctl!(
KeyCtlOperation::Reject,
self.0.as_raw_id() as libc::c_ulong,
seconds as _,
libc::EKEYREJECTED as _
)?;
Ok(())
}
pub fn invalidate(&self) -> Result<(), KeyError> {
ffi::keyctl!(
KeyCtlOperation::Invalidate,
self.0.as_raw_id() as libc::c_ulong
)?;
Ok(())
}
pub fn assume_authority(&self) -> Result<(), KeyError> {
ffi::keyctl!(
KeyCtlOperation::AssumeAuthority,
self.0.as_raw_id() as libc::c_ulong
)?;
Ok(())
}
pub fn instantiate<T: AsRef<[u8]> + ?Sized>(
&self,
payload: &T,
id: KeySerialId,
) -> Result<(), KeyError> {
let buffer = payload.as_ref();
let (payload, plen) = match buffer.len() {
0 => (core::ptr::null(), 0),
_ => (buffer.as_ptr(), buffer.len()),
};
_ = ffi::keyctl!(
KeyCtlOperation::Instantiate,
self.0.as_raw_id() as libc::c_ulong,
payload as _,
plen as _,
id.as_raw_id() as libc::c_ulong
)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{KeyRing, KeyRingIdentifier, KeyType, Permission};
use zeroize::Zeroizing;
#[test]
fn test_from_raw_id() {
let raw: i32 = 0x12345;
let _key = Key::from_id(raw.into());
}
#[test]
fn test_metadata() {
let secret = "Test Data";
let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap();
let key = ring.add_key("my-info-key", secret).unwrap();
let info = key.metadata().unwrap();
assert_eq!(info.get_type(), KeyType::User);
assert_eq!(info.get_uid(), unsafe { libc::geteuid() });
assert_eq!(info.get_gid(), unsafe { libc::getegid() });
assert_eq!(info.get_perms().bits(), 0x3F010000);
assert_eq!(info.get_description(), "my-info-key");
key.invalidate().unwrap()
}
#[test]
fn test_read_into_vec() {
let secret = "Test Data";
let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap();
let key = ring.add_key("vec-read-key", secret).unwrap();
let payload = key.read_to_vec().unwrap();
assert_eq!(secret.as_bytes(), &payload);
key.invalidate().unwrap();
}
#[test]
fn test_user_keyring_add_key() {
let secret = "Test Data";
let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap();
let key = ring.add_key("my-super-secret-test-key", secret).unwrap();
let mut buf = Zeroizing::new([0u8; 4096]);
let mut perms = KeyPermissions::new();
perms.set_posessor_perms(Permission::ALL);
perms.set_user_perms(Permission::ALL);
perms.set_group_perms(Permission::ALL);
key.set_perms(perms).unwrap();
let len = key.read(&mut buf).unwrap();
assert_eq!(secret.as_bytes(), &buf[..len]);
key.update(&"wow".as_bytes()).unwrap();
let len = key.read(&mut buf).unwrap();
assert_eq!("wow".as_bytes(), &buf[..len]);
key.invalidate().unwrap()
}
}