diff --git a/.ci/tarpaulin.sh b/.ci/tarpaulin.sh new file mode 100755 index 0000000..e146b75 --- /dev/null +++ b/.ci/tarpaulin.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +set -e + +readonly version="0.10.0" +readonly sha256sum="6843be8384bf14385b36a3118efc1ed2d25d531acb8df954cd3f93d44018b09e" +readonly filename="cargo-tarpaulin-$version-travis" +readonly tarball="$filename.tar.gz" + +cd .ci + +echo "$sha256sum $tarball" > tarpaulin.sha256sum +curl -OL "https://github.com/xd009642/tarpaulin/releases/download/$version/$tarball" +sha256sum --check tarpaulin.sha256sum +tar xf "$tarball" diff --git a/.cirrus.yml b/.cirrus.yml index 25df65e..2314e9b 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -19,7 +19,7 @@ rustfmt_task: linux_task: matrix: - container: - image: rust:1.34.0 + image: rust:1.35.0 - container: image: rust:latest - allow_failures: true @@ -31,7 +31,7 @@ linux_task: folder: $CARGO_HOME/registry fingerprint_script: cat Cargo.lock build_script: cargo build - test_script: cargo test + test_script: cargo test -- --test-threads 1 before_cache_script: rm -rf $CARGO_HOME/registry/index minimal_version_task: @@ -52,13 +52,13 @@ coverage_task: CODECOV_TOKEN: ENCRYPTED[1e221ef78a37c960613ff80db7141f3158e3218031934395466f4720f450b7acfd74e587819435ce9be0b13fa1b68f1b] keyutils_script: apt-get update && apt-get install libkeyutils-dev tarpaulin_cache: - folder: $CARGO_HOME/bin - populate_script: cargo install --version 0.8.7 cargo-tarpaulin - fingerprint_script: cargo install --list + folder: .ci + populate_script: .ci/tarpaulin.sh + fingerprint_script: cat .ci/tarpaulin.sh lockfile_script: cargo generate-lockfile cargo_cache: folder: $CARGO_HOME/registry fingerprint_script: cat Cargo.lock - coverage_script: cargo tarpaulin --out Xml + coverage_script: PATH=$PATH:$PWD/.ci cargo tarpaulin --out Xml upload_script: bash <(curl -s https://codecov.io/bash) -X gcov before_cache_script: rm -rf $CARGO_HOME/registry/index diff --git a/Cargo.toml b/Cargo.toml index 35ce3c6..51decba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,9 @@ members = ["keyutils-raw"] [dev-dependencies] lazy_static = "1" regex = "1" +serial_test = "*" +serial_test_derive = "*" +semver = "*" [dependencies] bitflags = "1.0.4" diff --git a/keyutils-raw/src/functions.rs b/keyutils-raw/src/functions.rs index 2a29a29..cd8bf07 100644 --- a/keyutils-raw/src/functions.rs +++ b/keyutils-raw/src/functions.rs @@ -89,7 +89,7 @@ extern "C" { ringid: KeyringSerial, type_: *const libc::c_char, description: *const libc::c_char, - destringid: KeyringSerial) + destringid: Option<KeyringSerial>) -> libc::c_long; pub fn keyctl_read( id: KeyringSerial, @@ -100,12 +100,12 @@ extern "C" { id: KeyringSerial, payload: *const libc::c_void, plen: libc::size_t, - ringid: KeyringSerial) + ringid: Option<KeyringSerial>) -> libc::c_long; pub fn keyctl_negate( id: KeyringSerial, timeout: TimeoutSeconds, - ringid: KeyringSerial) + ringid: Option<KeyringSerial>) -> libc::c_long; pub fn keyctl_set_reqkey_keyring( reqkey_defl: libc::c_int) @@ -128,7 +128,7 @@ extern "C" { id: KeyringSerial, timeout: TimeoutSeconds, error: libc::c_uint, - ringid: KeyringSerial) + ringid: Option<KeyringSerial>) -> libc::c_long; pub fn keyctl_invalidate( id: KeyringSerial) diff --git a/src/api.rs b/src/api.rs index 4ddcd7c..314f5e4 100644 --- a/src/api.rs +++ b/src/api.rs @@ -66,6 +66,7 @@ fn into_serial(res: libc::c_long) -> KeyringSerial { KeyringSerial::new(res as i32).unwrap() } +/// Request a key from the kernel. fn request_impl<K: KeyType>( description: &str, info: Option<&str>, @@ -89,6 +90,10 @@ impl Keyring { /// Instantiate a keyring from an ID. /// /// This is unsafe because no keyring is known to exist with the given ID. + /// + /// # Safety + /// + /// This method assumes that the given serial is a valid keyring ID at the kernel level. pub unsafe fn new(id: KeyringSerial) -> Self { Keyring { id, @@ -101,6 +106,11 @@ impl Keyring { } } + #[cfg(test)] + pub(crate) fn serial(&self) -> KeyringSerial { + self.id + } + /// Set the default keyring to use when implicit requests on the current thread. /// /// Returns the old default keyring. @@ -117,31 +127,23 @@ impl Keyring { /// Requests a keyring with the given description by searching the thread, process, and session /// keyrings. - pub fn request<D>(description: D) -> Result<Self> + /// + /// If it is not found, the `info` string (if provided) will be handed off to + /// `/sbin/request-key` to generate the key. + /// + /// If `target` is given, the found keyring will be linked into it. If `target` is not given + /// and a new key is constructed due to the request, it will be linked into the default + /// keyring (see `Keyring::set_default`). + pub fn request<'s, 'a, D, I, T>(description: D, info: I, target: T) -> Result<Self> where D: AsRef<str>, + I: Into<Option<&'s str>>, + T: Into<Option<TargetKeyring<'a>>>, { check_call_keyring(request_impl::<keytypes::Keyring>( description.as_ref(), - None, - None, - )) - } - - /// Requests a keyring with the given description by searching the thread, process, and session - /// keyrings. - /// - /// If it is not found, the `info` string will be handed off to `/sbin/request-key` to generate - /// the key. - pub fn request_with_fallback<D, I>(description: D, info: I) -> Result<Self> - where - D: Borrow<<keytypes::Keyring as KeyType>::Description>, - I: AsRef<str>, - { - check_call_keyring(request_impl::<keytypes::Keyring>( - &description.borrow().description(), - Some(info.as_ref()), - None, + info.into().as_ref().copied(), + target.into().map(TargetKeyring::serial), )) } @@ -213,14 +215,19 @@ impl Keyring { check_call(unsafe { keyctl_unlink(keyring.id, self.id) }) } - fn search_impl<K>(&self, description: &str) -> KeyringSerial + fn search_impl<K>(&self, description: &str, destination: Option<&mut Keyring>) -> KeyringSerial where K: KeyType, { let type_cstr = CString::new(K::name()).unwrap(); let desc_cstr = CString::new(description).unwrap(); into_serial(unsafe { - keyctl_search(self.id, type_cstr.as_ptr(), desc_cstr.as_ptr(), self.id) + keyctl_search( + self.id, + type_cstr.as_ptr(), + desc_cstr.as_ptr(), + destination.map(|dest| dest.id), + ) }) } @@ -229,12 +236,15 @@ impl Keyring { /// If it is found, it is attached to the keyring (if `write` permission to the keyring and /// `link` permission on the key exist) and return it. Requires the `search` permission on the /// keyring. Any children keyrings without the `search` permission are ignored. - pub fn search_for_key<K, D>(&self, description: D) -> Result<Key> + pub fn search_for_key<'a, K, D, DK>(&self, description: D, destination: DK) -> Result<Key> where K: KeyType, D: Borrow<K::Description>, + DK: Into<Option<&'a mut Keyring>>, { - check_call_key(self.search_impl::<K>(&description.borrow().description())) + check_call_key( + self.search_impl::<K>(&description.borrow().description(), destination.into()), + ) } /// Recursively search the keyring for a keyring with the matching description. @@ -243,19 +253,33 @@ impl Keyring { /// `link` permission on the found keyring exist) and return it. Requires the `search` /// permission on the keyring. Any children keyrings without the `search` permission are /// ignored. - pub fn search_for_keyring<D>(&self, description: D) -> Result<Self> + pub fn search_for_keyring<'a, D, DK>(&self, description: D, destination: DK) -> Result<Self> where D: Borrow<<keytypes::Keyring as KeyType>::Description>, + DK: Into<Option<&'a mut Keyring>>, { - check_call_keyring( - self.search_impl::<keytypes::Keyring>(&description.borrow().description()), - ) + check_call_keyring(self.search_impl::<keytypes::Keyring>( + &description.borrow().description(), + destination.into(), + )) } /// Return all immediate children of the keyring. /// /// Requires `read` permission on the keyring. pub fn read(&self) -> Result<(Vec<Key>, Vec<Keyring>)> { + // The `description` check below hides this error code from the kernel. + if self.id.get() == 0 { + return Err(errno::Errno(libc::ENOKEY)); + } + + // Avoid a panic in the code below be ensuring that we actually have a keyring. Parsing + // a key's payload as a keyring payload. + let desc = self.description()?; + if desc.type_ != keytypes::Keyring::name() { + return Err(errno::Errno(libc::ENOTDIR)); + } + let sz = unsafe { keyctl_read(self.id, ptr::null_mut(), 0) }; check_call(sz)?; let mut buffer = Vec::with_capacity((sz as usize) / mem::size_of::<KeyringSerial>()); @@ -344,74 +368,6 @@ impl Keyring { check_call_keyring(self.add_key_impl::<keytypes::Keyring>(description.borrow(), &())) } - /// Requests a key with the given description by searching the thread, process, and session - /// keyrings. - /// - /// If it is found, it is attached to the keyring. - pub fn request_key<K, D>(&self, description: D) -> Result<Key> - where - K: KeyType, - D: Borrow<K::Description>, - { - check_call_key(request_impl::<K>( - &description.borrow().description(), - None, - Some(self.id), - )) - } - - /// Requests a keyring with the given description by searching the thread, process, and session - /// keyrings. - /// - /// If it is found, it is attached to the keyring. - pub fn request_keyring<D>(&self, description: D) -> Result<Self> - where - D: Borrow<<keytypes::Keyring as KeyType>::Description>, - { - check_call_keyring(request_impl::<keytypes::Keyring>( - &description.borrow().description(), - None, - Some(self.id), - )) - } - - /// Requests a key with the given description by searching the thread, process, and session - /// keyrings. - /// - /// If it is not found, the `info` string will be handed off to `/sbin/request-key` to generate - /// the key. If found, it will be attached to the current keyring. Requires `write` permission - /// to the keyring. - pub fn request_key_with_fallback<K, D, I>(&self, description: D, info: I) -> Result<Key> - where - K: KeyType, - D: Borrow<K::Description>, - I: AsRef<str>, - { - check_call_key(request_impl::<K>( - &description.borrow().description(), - Some(info.as_ref()), - Some(self.id), - )) - } - - /// Requests a keyring with the given description by searching the thread, process, and session - /// keyrings. - /// - /// If it is not found, the `info` string will be handed off to `/sbin/request-key` to generate - /// the key. If found, it will be attached to the current keyring. Requires `write` permission - /// to the keyring. - pub fn request_keyring_with_fallback<D, I>(&self, description: D, info: I) -> Result<Self> - where - D: Borrow<<keytypes::Keyring as KeyType>::Description>, - I: AsRef<str>, - { - check_call_keyring(request_impl::<keytypes::Keyring>( - &description.borrow().description(), - Some(info.as_ref()), - Some(self.id), - )) - } - /// Revokes the keyring. /// /// Requires `write` permission on the keyring. @@ -443,6 +399,11 @@ impl Keyring { check_call(unsafe { keyctl_setperm(self.id, perms.bits()) }) } + #[cfg(test)] + pub(crate) fn set_permissions_raw(&mut self, perms: KeyPermissions) -> Result<()> { + check_call(unsafe { keyctl_setperm(self.id, perms) }) + } + fn description_raw(&self) -> Result<String> { let sz = unsafe { keyctl_describe(self.id, ptr::null_mut(), 0) }; check_call(sz)?; @@ -514,6 +475,10 @@ impl Key { /// Instantiate a key from an ID. /// /// This is unsafe because no key is known to exist with the given ID. + /// + /// # Safety + /// + /// This method assumes that the given serial is a valid key ID at the kernel level. pub unsafe fn new(id: KeyringSerial) -> Self { Self::new_impl(id) } @@ -524,35 +489,31 @@ impl Key { } } - /// Requests a key with the given description by searching the thread, process, and session - /// keyrings. - pub fn request<K, D>(description: D) -> Result<Self> - where - K: KeyType, - D: Borrow<K::Description>, - { - check_call_key(request_impl::<K>( - &description.borrow().description(), - None, - None, - )) + #[cfg(test)] + pub(crate) fn serial(&self) -> KeyringSerial { + self.id } - /// Requests a key with the given description by searching the thread, process, and session - /// keyrings. + /// Requests a key with the given type and description by searching the thread, process, and + /// session keyrings. + /// + /// If it is not found, the `info` string (if provided) will be handed off to + /// `/sbin/request-key` to generate the key. /// - /// If it is not found, the `info` string will be handed off to `/sbin/request-key` to generate - /// the key. - pub fn request_with_fallback<K, D, I>(description: D, info: I) -> Result<Self> + /// If `target` is given, the found keyring will be linked into it. If `target` is not given + /// and a new key is constructed due to the request, it will be linked into the default + /// keyring (see `Keyring::set_default`). + pub fn request<'s, 'a, K, D, I, T>(description: D, info: I, target: T) -> Result<Self> where K: KeyType, D: Borrow<K::Description>, - I: AsRef<str>, + I: Into<Option<&'s str>>, + T: Into<Option<TargetKeyring<'a>>>, { - check_call_key(request_impl::<keytypes::Keyring>( + check_call_key(request_impl::<K>( &description.borrow().description(), - Some(info.as_ref()), - None, + info.into().as_ref().copied(), + target.into().map(TargetKeyring::serial), )) } @@ -596,6 +557,11 @@ impl Key { Keyring::new_impl(self.id).set_permissions(perms) } + #[cfg(test)] + pub(crate) fn set_permissions_raw(&mut self, perms: KeyPermissions) -> Result<()> { + Keyring::new_impl(self.id).set_permissions_raw(perms) + } + /// Retrieve metadata about the key. /// /// # Panics @@ -738,6 +704,48 @@ impl Description { } } +/// The destination keyring of an instantiation request. +#[derive(Debug)] +pub enum TargetKeyring<'a> { + /// A special keyring. + Special(SpecialKeyring), + /// A specific keyring. + Keyring(&'a mut Keyring), +} + +impl<'a> TargetKeyring<'a> { + fn serial(self) -> KeyringSerial { + match self { + TargetKeyring::Special(special) => special.serial(), + TargetKeyring::Keyring(keyring) => keyring.id, + } + } +} + +impl<'a> From<SpecialKeyring> for TargetKeyring<'a> { + fn from(special: SpecialKeyring) -> Self { + TargetKeyring::Special(special) + } +} + +impl<'a> From<&'a mut Keyring> for TargetKeyring<'a> { + fn from(keyring: &'a mut Keyring) -> Self { + TargetKeyring::Keyring(keyring) + } +} + +impl<'a> From<SpecialKeyring> for Option<TargetKeyring<'a>> { + fn from(special: SpecialKeyring) -> Self { + Some(special.into()) + } +} + +impl<'a> From<&'a mut Keyring> for Option<TargetKeyring<'a>> { + fn from(keyring: &'a mut Keyring) -> Self { + Some(keyring.into()) + } +} + /// A manager for a key to respond to instantiate a key request by the kernel. #[derive(Debug, PartialEq, Eq)] pub struct KeyManager { @@ -751,6 +759,11 @@ impl KeyManager { } } + #[cfg(test)] + pub(crate) fn test_new(key: Key) -> Self { + Self::new(key) + } + /// Requests the authorization key created by `request_key`. /// /// This key must be present in an available keyring before `Key::manage` may be called. @@ -766,8 +779,9 @@ impl KeyManager { } /// Instantiate the key with the given payload. - pub fn instantiate<P>(self, keyring: &Keyring, payload: P) -> Result<()> + pub fn instantiate<'a, T, P>(self, keyring: T, payload: P) -> Result<()> where + T: Into<Option<TargetKeyring<'a>>>, P: AsRef<[u8]>, { let payload = payload.as_ref(); @@ -776,7 +790,7 @@ impl KeyManager { self.key.id, payload.as_ptr() as *const libc::c_void, payload.len(), - keyring.id, + keyring.into().map(TargetKeyring::serial), ) }) } @@ -787,14 +801,17 @@ impl KeyManager { /// seconds are ignored). This is to prevent a denial-of-service by /// requesting a non-existant key repeatedly. The requester must have /// `write` permission on the keyring. - pub fn reject(self, keyring: &Keyring, timeout: Duration, error: errno::Errno) -> Result<()> { + pub fn reject<'a, T>(self, keyring: T, timeout: Duration, error: errno::Errno) -> Result<()> + where + T: Into<Option<TargetKeyring<'a>>>, + { let errno::Errno(errval) = error; check_call(unsafe { keyctl_reject( self.key.id, timeout.as_secs() as TimeoutSeconds, errval as u32, - keyring.id, + keyring.into().map(TargetKeyring::serial), ) }) } @@ -805,446 +822,16 @@ impl KeyManager { /// seconds are ignored). This is to prevent a denial-of-service by /// requesting a non-existant key repeatedly. The requester must have /// `write` permission on the keyring. - pub fn negate(self, keyring: &Keyring, timeout: Duration) -> Result<()> { + pub fn negate<'a, T>(self, keyring: T, timeout: Duration) -> Result<()> + where + T: Into<Option<TargetKeyring<'a>>>, + { check_call(unsafe { - keyctl_negate(self.key.id, timeout.as_secs() as TimeoutSeconds, keyring.id) - }) - } -} - -#[cfg(test)] -mod tests { - use std::sync::atomic; - use std::thread; - - use super::*; - - // For testing, each test gets a new keyring attached to the Thread keyring. - // This makes sure tests don't interfere with eachother, and keys are not - // prematurely garbage collected. - fn new_test_keyring() -> Result<Keyring> { - let mut thread_keyring = Keyring::attach_or_create(SpecialKeyring::Thread)?; - - static KEYRING_COUNT: atomic::AtomicUsize = atomic::AtomicUsize::new(0); - let num = KEYRING_COUNT.fetch_add(1, atomic::Ordering::SeqCst); - thread_keyring.add_keyring(format!("test:rust-keyutils{}", num)) - } - - #[test] - fn test_add_key() { - let mut keyring = new_test_keyring().unwrap(); - - // Create the key. - let description = "test:rust-keyutils:add_key"; - let payload = "payload"; - let key = keyring - .add_key::<keytypes::User, _, _>(description, payload.as_bytes()) - .unwrap(); - assert_eq!( - key.read().unwrap(), - payload.as_bytes().iter().cloned().collect::<Vec<_>>() - ); - - // Clean up. - keyring.unlink_key(&key).unwrap(); - keyring.invalidate().unwrap(); - } - - #[test] - fn test_clear_keyring() { - let mut keyring = new_test_keyring().unwrap(); - - let (keys, keyrings) = keyring.read().unwrap(); - assert_eq!(keys.len(), 0); - assert_eq!(keyrings.len(), 0); - - // Create a key. - keyring - .add_key::<keytypes::User, _, _>( - "test:rust-keyutils:clear_keyring", - "payload".as_bytes(), - ) - .unwrap(); - keyring.add_keyring("description").unwrap(); - - let (keys, keyrings) = keyring.read().unwrap(); - assert_eq!(keys.len(), 1); - assert_eq!(keyrings.len(), 1); - - // Clear the keyring. - keyring.clear().unwrap(); - - let (keys, keyrings) = keyring.read().unwrap(); - assert_eq!(keys.len(), 0); - assert_eq!(keyrings.len(), 0); - - // Clean up. - keyring.invalidate().unwrap(); - } - - #[test] - fn test_describe_key() { - let mut keyring = new_test_keyring().unwrap(); - - // Create the key. - let desc = "test:rust-keyutils:describe_key"; - let payload = "payload"; - let key = keyring - .add_key::<keytypes::User, _, _>(desc, payload.as_bytes()) - .unwrap(); - - // Check its description. - let description = key.description().unwrap(); - assert_eq!(&description.type_, keytypes::User::name()); - assert_eq!(&description.description, desc); - - // Clean up. - keyring.unlink_key(&key).unwrap(); - keyring.invalidate().unwrap(); - } - - #[test] - fn test_invalidate_key() { - let mut keyring = new_test_keyring().unwrap(); - - // Create the key. - let description = "test:rust-keyutils:invalidate_key"; - let payload = "payload"; - let key = keyring - .add_key::<keytypes::User, _, _>(description, payload.as_bytes()) - .unwrap(); - key.invalidate().unwrap(); - - let (keys, keyrings) = keyring.read().unwrap(); - assert_eq!(keys.len(), 0); - assert_eq!(keyrings.len(), 0); - - // Clean up. - keyring.invalidate().unwrap(); - } - - #[test] - fn test_link_key() { - let mut keyring = new_test_keyring().unwrap(); - let mut new_keyring = keyring.add_keyring("new_keyring").unwrap(); - - // Create the key. - let description = "test:rust-keyutils:link_key"; - let payload = "payload"; - let key = keyring - .add_key::<keytypes::User, _, _>(description, payload.as_bytes()) - .unwrap(); - - new_keyring.link_key(&key).unwrap(); - - let (keys, keyrings) = new_keyring.read().unwrap(); - assert_eq!(keys.len(), 1); - assert_eq!(keys[0], key); - assert_eq!(keyrings.len(), 0); - - // Clean up. - key.invalidate().unwrap(); - new_keyring.invalidate().unwrap(); - keyring.invalidate().unwrap(); - } - - #[test] - fn test_link_keyring() { - let mut keyring = new_test_keyring().unwrap(); - let mut new_keyring = keyring.add_keyring("new_keyring").unwrap(); - let new_inner_keyring = keyring.add_keyring("new_inner_keyring").unwrap(); - - new_keyring.link_keyring(&new_inner_keyring).unwrap(); - - let (keys, keyrings) = new_keyring.read().unwrap(); - assert_eq!(keys.len(), 0); - assert_eq!(keyrings.len(), 1); - assert_eq!(keyrings[0], new_inner_keyring); - - // Clean up. - new_inner_keyring.invalidate().unwrap(); - new_keyring.invalidate().unwrap(); - keyring.invalidate().unwrap(); - } - - #[test] - fn test_read_keyring() { - let mut keyring = new_test_keyring().unwrap(); - - let (keys, keyrings) = keyring.read().unwrap(); - assert_eq!(keys.len(), 0); - assert_eq!(keyrings.len(), 0); - - let key = keyring - .add_key::<keytypes::User, _, _>( - "test:rust-keyutils:read_keyring", - "payload".as_bytes(), + keyctl_negate( + self.key.id, + timeout.as_secs() as TimeoutSeconds, + keyring.into().map(TargetKeyring::serial), ) - .unwrap(); - - let (keys, keyrings) = keyring.read().unwrap(); - assert_eq!(keys.len(), 1); - assert_eq!(keys[0], key); - assert_eq!(keyrings.len(), 0); - - // Clean up. - keyring.unlink_key(&key).unwrap(); - keyring.invalidate().unwrap(); - } - - #[test] - fn test_read_key() { - let mut keyring = new_test_keyring().unwrap(); - - // Create the key. - let description = "test:rust-keyutils:read_key"; - let payload = "payload"; - let key = keyring - .add_key::<keytypes::User, _, _>(description, payload.as_bytes()) - .unwrap(); - assert_eq!( - key.read().unwrap(), - payload.as_bytes().iter().cloned().collect::<Vec<_>>() - ); - - // Clean up. - keyring.unlink_key(&key).unwrap(); - keyring.invalidate().unwrap(); - } - - #[test] - fn test_create_keyring() { - let mut keyring = new_test_keyring().unwrap(); - let new_keyring = keyring.add_keyring("new_keyring").unwrap(); - - let (keys, keyrings) = keyring.read().unwrap(); - assert_eq!(keys.len(), 0); - assert_eq!(keyrings.len(), 1); - assert_eq!(keyrings[0], new_keyring); - - // Clean up. - new_keyring.invalidate().unwrap(); - keyring.invalidate().unwrap(); - } - - #[test] - fn test_chmod_keyring() { - let mut keyring = new_test_keyring().unwrap(); - let description = keyring.description().unwrap(); - let perms = description.perms; - let new_perms = { - let mut tmp_perms = perms.clone(); - let write_bits = Permission::POSSESSOR_WRITE - | Permission::USER_WRITE - | Permission::GROUP_WRITE - | Permission::OTHER_WRITE; - tmp_perms.remove(write_bits); - tmp_perms - }; - keyring.set_permissions(new_perms).unwrap(); - let err = keyring.add_keyring("new_keyring").unwrap_err(); - assert_eq!(err.0, libc::EACCES); - - keyring.set_permissions(perms).unwrap(); - let new_keyring = keyring.add_keyring("new_keyring").unwrap(); - - // Clean up. - new_keyring.invalidate().unwrap(); - keyring.invalidate().unwrap(); - } - - #[test] - fn test_request_key() { - let mut keyring = new_test_keyring().unwrap(); - - // Create the key. - let description = "test:rust-keyutils:request_key"; - let payload = "payload"; - let key = keyring - .add_key::<keytypes::User, _, _>(description, payload.as_bytes()) - .unwrap(); - - let found_key = keyring - .request_key::<keytypes::User, _>(description) - .unwrap(); - assert_eq!(found_key, key); - - // Clean up. - keyring.unlink_key(&key).unwrap(); - keyring.invalidate().unwrap(); - } - - #[test] - fn test_revoke_key() { - let mut keyring = new_test_keyring().unwrap(); - - // Create the key. - let description = "test:rust-keyutils:revoke_key"; - let payload = "payload"; - let key = keyring - .add_key::<keytypes::User, _, _>(description, payload.as_bytes()) - .unwrap(); - let key_copy = key.clone(); - - key.revoke().unwrap(); - - let err = key_copy.read().unwrap_err(); - assert_eq!(err.0, libc::EKEYREVOKED); - - // Clean up. - keyring.unlink_key(&key_copy).unwrap(); - keyring.invalidate().unwrap(); - } - - #[test] - fn test_search_key() { - let mut keyring = new_test_keyring().unwrap(); - let mut new_keyring = keyring.add_keyring("new_keyring").unwrap(); - let mut new_inner_keyring = new_keyring.add_keyring("new_inner_keyring").unwrap(); - - // Create the key. - let description = "test:rust-keyutils:search_key"; - let payload = "payload"; - let key = new_inner_keyring - .add_key::<keytypes::User, _, _>(description, payload.as_bytes()) - .unwrap(); - - let found_key = keyring - .search_for_key::<keytypes::User, _>(description) - .unwrap(); - assert_eq!(found_key, key); - - // Clean up. - new_inner_keyring.unlink_key(&key).unwrap(); - new_inner_keyring.invalidate().unwrap(); - new_keyring.invalidate().unwrap(); - keyring.invalidate().unwrap(); - } - - #[test] - fn test_search_keyring() { - let mut keyring = new_test_keyring().unwrap(); - let mut new_keyring = keyring.add_keyring("new_keyring").unwrap(); - let new_inner_keyring = new_keyring.add_keyring("new_inner_keyring").unwrap(); - - let found_keyring = keyring.search_for_keyring("new_inner_keyring").unwrap(); - assert_eq!(found_keyring, new_inner_keyring); - - // Clean up. - new_inner_keyring.invalidate().unwrap(); - new_keyring.invalidate().unwrap(); - keyring.invalidate().unwrap(); - } - - #[test] - fn test_key_timeout() { - let mut keyring = new_test_keyring().unwrap(); - - // Create the key. - let description = "test:rust-keyutils:key_timeout"; - let payload = "payload"; - let mut key = keyring - .add_key::<keytypes::User, _, _>(description, payload.as_bytes()) - .unwrap(); - - // Set the timeout on the key. - let duration = Duration::from_secs(1); - let timeout_duration = duration + duration; - key.set_timeout(duration).unwrap(); - - // Sleep the timeout away. - thread::sleep(timeout_duration); - - // Try to read the key. - let err = key.read().unwrap_err(); - assert_eq!(err.0, libc::EKEYEXPIRED); - - // Clean up. - keyring.unlink_key(&key).unwrap(); - keyring.invalidate().unwrap(); - } - - #[test] - fn test_unlink_keyring() { - let mut keyring = new_test_keyring().unwrap(); - - // Create the keyring. - let description = "test:rust-keyutils:unlink_keyring"; - let new_keyring = keyring.add_keyring(description).unwrap(); - - let (keys, keyrings) = keyring.read().unwrap(); - assert_eq!(keys.len(), 0); - assert_eq!(keyrings.len(), 1); - - // Unlink the key. - keyring.unlink_keyring(&new_keyring).unwrap(); - - // Use the keyring. - let err = new_keyring.read().unwrap_err(); - assert_eq!(err.0, libc::EACCES); - - let (keys, keyrings) = keyring.read().unwrap(); - assert_eq!(keys.len(), 0); - assert_eq!(keyrings.len(), 0); - - // Clean up. - keyring.invalidate().unwrap(); - } - - #[test] - fn test_unlink_key() { - let mut keyring = new_test_keyring().unwrap(); - - // Create the key. - let description = "test:rust-keyutils:unlink_key"; - let payload = "payload"; - let key = keyring - .add_key::<keytypes::User, _, _>(description, payload.as_bytes()) - .unwrap(); - - let (keys, keyrings) = keyring.read().unwrap(); - assert_eq!(keys.len(), 1); - assert_eq!(keyrings.len(), 0); - - // Unlink the key. - keyring.unlink_key(&key).unwrap(); - - // Use the key. - let err = key.read().unwrap_err(); - assert_eq!(err.0, libc::EACCES); - - let (keys, keyrings) = keyring.read().unwrap(); - assert_eq!(keys.len(), 0); - assert_eq!(keyrings.len(), 0); - - // Clean up. - keyring.invalidate().unwrap(); - } - - #[test] - fn test_update_key() { - let mut keyring = new_test_keyring().unwrap(); - - // Create the key. - let description = "test:rust-keyutils:update_key"; - let payload = "payload"; - let key = keyring - .add_key::<keytypes::User, _, _>(description, payload.as_bytes()) - .unwrap(); - - // Update the key. - let new_payload = "new_payload"; - let updated_key = keyring - .add_key::<keytypes::User, _, _>(description, new_payload.as_bytes()) - .unwrap(); - assert_eq!(key, updated_key); - assert_eq!( - updated_key.read().unwrap(), - new_payload.as_bytes().iter().cloned().collect::<Vec<_>>() - ); - - // Clean up. - keyring.unlink_key(&key).unwrap(); - keyring.invalidate().unwrap(); + }) } } diff --git a/src/constants.rs b/src/constants.rs index 6b43190..272bbae 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -28,6 +28,7 @@ use bitflags::bitflags; use keyutils_raw::*; /// Special keyrings predefined for a process. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum SpecialKeyring { /// A thread-specific keyring. Thread, @@ -65,7 +66,7 @@ bitflags! { /// keyring, and a third set which is used when neither of the other two match. /// /// The fourth set is combined with the permission set used above (priority to user, then - /// group, finaly other) where either set granting a permission allows it. This set is, + /// group, finally other) where either set granting a permission allows it. This set is, /// however, only used if the caller is a "possessor" of they key or keyring. Generally, /// "possession" requires the `search` permission, association from the calling thread /// (the session, process, and thread keyrings), or is linked to from a possessed keyring. See diff --git a/src/keytypes/encrypted.rs b/src/keytypes/encrypted.rs index 46234ad..fef2674 100644 --- a/src/keytypes/encrypted.rs +++ b/src/keytypes/encrypted.rs @@ -60,6 +60,11 @@ pub enum Format { /// Keys of this format must have a description of exactly 16 hexadecimal characters. The /// keylength must also be 64. Ecryptfs, + /// Encrypted keys with a payload size of 32 bytes. + /// + /// Intended for nvdimm security, but may be used for other 32-byte payload use cases in the + /// future. + Enc32, } impl Format { @@ -68,6 +73,7 @@ impl Format { match *self { Format::Default => "default", Format::Ecryptfs => "ecryptfs", + Format::Enc32 => "enc32", } } } diff --git a/src/lib.rs b/src/lib.rs index 303ddd1..6641af3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,10 +30,6 @@ #![warn(missing_docs)] -#[cfg(test)] -#[macro_use] -extern crate lazy_static; - mod api; mod constants; mod keytype; diff --git a/src/tests/add.rs b/src/tests/add.rs index a145f5b..6182a36 100644 --- a/src/tests/add.rs +++ b/src/tests/add.rs @@ -27,49 +27,49 @@ use std::iter; use crate::keytypes::User; -use crate::{Keyring, KeyringSerial, SpecialKeyring}; +use super::utils; use super::utils::kernel::*; use super::utils::keys::*; #[test] fn empty_key_type() { - let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap(); + let mut keyring = utils::new_test_keyring(); let err = keyring.add_key::<EmptyKey, _, _>("", ()).unwrap_err(); assert_eq!(err, errno::Errno(libc::EINVAL)); } #[test] fn unsupported_key_type() { - let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap(); + let mut keyring = utils::new_test_keyring(); let err = keyring.add_key::<UnsupportedKey, _, _>("", ()).unwrap_err(); assert_eq!(err, errno::Errno(libc::ENODEV)); } #[test] fn invalid_key_type() { - let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap(); + let mut keyring = utils::new_test_keyring(); let err = keyring.add_key::<InvalidKey, _, _>("", ()).unwrap_err(); assert_eq!(err, errno::Errno(libc::EPERM)); } #[test] fn maxlen_key_type() { - let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap(); + let mut keyring = utils::new_test_keyring(); let err = keyring.add_key::<MaxLenKey, _, _>("", ()).unwrap_err(); assert_eq!(err, errno::Errno(libc::ENODEV)); } #[test] fn overlong_key_type() { - let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap(); + let mut keyring = utils::new_test_keyring(); let err = keyring.add_key::<OverlongKey, _, _>("", ()).unwrap_err(); assert_eq!(err, errno::Errno(libc::EINVAL)); } #[test] fn keyring_with_payload() { - let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap(); + let mut keyring = utils::new_test_keyring(); let err = keyring .add_key::<KeyringShadow, _, _>("", "payload") .unwrap_err(); @@ -78,7 +78,7 @@ fn keyring_with_payload() { #[test] fn max_user_description() { - let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap(); + let mut keyring = utils::new_test_keyring(); // Subtract one because the NUL is added in the kernel API. let maxdesc: String = iter::repeat('a').take(*PAGE_SIZE - 1).collect(); let res = keyring.add_key::<User, _, _>(maxdesc.as_ref(), "payload".as_bytes()); @@ -94,7 +94,7 @@ fn max_user_description() { #[test] fn overlong_user_description() { - let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap(); + let mut keyring = utils::new_test_keyring(); // On MIPS with < 3.19, there is a bug where this is allowed. 3.19 was released in Feb 2015, // so this is being ignored here. let toolarge: String = iter::repeat('a').take(*PAGE_SIZE).collect(); @@ -106,26 +106,94 @@ fn overlong_user_description() { #[test] fn invalid_keyring() { - // Yes, we're explicitly breaking the NonZeroI32 rules here. However, it is not passing - // through any bits which care (e.g., `Option`), so this is purely to test that using an - // invalid keyring ID gives back `EINVAL` as expected. - let mut keyring = unsafe { Keyring::new(KeyringSerial::new_unchecked(0)) }; + let mut keyring = utils::invalid_keyring(); let err = keyring - .add_key::<User, _, _>("desc", "payload".as_bytes()) + .add_key::<User, _, _>("invalid_keyring", "payload".as_bytes()) .unwrap_err(); assert_eq!(err, errno::Errno(libc::EINVAL)); } #[test] -fn add_key_to_session() { - let mut keyring = Keyring::attach_or_create(SpecialKeyring::Session).unwrap(); +fn add_key_to_non_keyring() { + let mut keyring = utils::new_test_keyring(); let expected = "stuff".as_bytes(); - let mut key = keyring.add_key::<User, _, _>("wibble", expected).unwrap(); - let payload = key.read().unwrap(); - assert_eq!(payload, expected); - let new_expected = "lizard".as_bytes(); - key.update(new_expected).unwrap(); - let new_payload = key.read().unwrap(); - assert_eq!(new_payload, new_expected); - keyring.unlink_key(&key).unwrap(); + let key = keyring + .add_key::<User, _, _>("add_key_to_non_keyring", expected) + .unwrap(); + + let mut not_a_keyring = utils::key_as_keyring(&key); + let err = not_a_keyring + .add_key::<User, _, _>("add_key_to_non_keyring", expected) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOTDIR)); +} + +#[test] +fn add_keyring_to_non_keyring() { + let mut keyring = utils::new_test_keyring(); + let expected = "stuff".as_bytes(); + let key = keyring + .add_key::<User, _, _>("add_keyring_to_non_keyring", expected) + .unwrap(); + + let mut not_a_keyring = utils::key_as_keyring(&key); + let err = not_a_keyring + .add_keyring("add_keyring_to_non_keyring") + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOTDIR)); +} + +#[test] +fn add_key() { + let mut keyring = utils::new_test_keyring(); + + let payload = "payload".as_bytes(); + let key = keyring.add_key::<User, _, _>("add_key", payload).unwrap(); + assert_eq!(key.read().unwrap(), payload); +} + +#[test] +fn add_keyring() { + let mut keyring = utils::new_test_keyring(); + let new_keyring = keyring.add_keyring("add_keyring").unwrap(); + + let (keys, keyrings) = new_keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); +} + +#[test] +fn add_key_replace() { + let mut keyring = utils::new_test_keyring(); + + let description = "add_key_replace"; + + let payload = "payload".as_bytes(); + let key = keyring.add_key::<User, _, _>(description, payload).unwrap(); + assert_eq!(key.read().unwrap(), payload); + + let payload = "updated_payload".as_bytes(); + let key_updated = keyring.add_key::<User, _, _>(description, payload).unwrap(); + assert_eq!(key, key_updated); + assert_eq!(key.read().unwrap(), payload); + assert_eq!(key_updated.read().unwrap(), payload); +} + +#[test] +fn add_keyring_replace() { + let mut keyring = utils::new_test_keyring(); + + let description = "add_keyring_replace"; + let new_keyring = keyring.add_keyring(description).unwrap(); + + let (keys, keyrings) = new_keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); + + let updated_keyring = keyring.add_keyring(description).unwrap(); + assert_ne!(new_keyring, updated_keyring); + + let (keys, keyrings) = updated_keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); } diff --git a/src/tests/clear.rs b/src/tests/clear.rs new file mode 100644 index 0000000..c617f01 --- /dev/null +++ b/src/tests/clear.rs @@ -0,0 +1,181 @@ +// Copyright (c) 2019, Ben Boeckel +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of this project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::keytypes::User; +use crate::Keyring; + +use super::utils; + +#[test] +fn invalid_keyring() { + let mut keyring = utils::invalid_keyring(); + let err = keyring.clear().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn clear_non_keyring() { + let mut keyring = utils::new_test_keyring(); + let key = keyring + .add_key::<User, _, _>("clear_non_keyring", "payload".as_bytes()) + .unwrap(); + + // Try clearing a non-keyring. + let mut not_a_keyring = unsafe { Keyring::new(key.serial()) }; + let err = not_a_keyring.clear().unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOTDIR)); + + keyring.unlink_key(&key).unwrap(); +} + +#[test] +fn clear_deleted_keyring() { + let mut keyring = utils::new_test_keyring(); + let mut sub_keyring = keyring.add_keyring("clear_deleted_keyring").unwrap(); + + keyring.unlink_keyring(&sub_keyring).unwrap(); + + // Keys are deleted asynchronously; permissions are revoked until it is actually deleted. + loop { + let err = sub_keyring.clear().unwrap_err(); + if err == errno::Errno(libc::EACCES) { + continue; + } + assert_eq!(err, errno::Errno(libc::ENOKEY)); + break; + } +} + +#[test] +fn clear_empty_keyring() { + let mut keyring = utils::new_test_keyring(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(keys.len(), 0); + assert_eq!(keyrings.len(), 0); + + // Clear the keyring. + keyring.clear().unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(keys.len(), 0); + assert_eq!(keyrings.len(), 0); +} + +#[test] +fn clear_keyring_one_key() { + let mut keyring = utils::new_test_keyring(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(keys.len(), 0); + assert_eq!(keyrings.len(), 0); + + let key_desc = "clear_keyring:key"; + + // Create a key. + let payload = "payload".as_bytes(); + keyring.add_key::<User, _, _>(key_desc, payload).unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(keys.len(), 1); + assert_eq!(keyrings.len(), 0); + + assert_eq!(keys[0].description().unwrap().description, key_desc); + + // Clear the keyring. + keyring.clear().unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(keys.len(), 0); + assert_eq!(keyrings.len(), 0); +} + +#[test] +fn clear_keyring_many_keys() { + let mut keyring = utils::new_test_keyring(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(keys.len(), 0); + assert_eq!(keyrings.len(), 0); + + let count = 40; + let payload = "payload".as_bytes(); + let mut descs = Vec::with_capacity(count); + for i in 0..count { + let key_desc = format!("clear_keyring:key{:02}", i); + + // Create a key. + keyring + .add_key::<User, _, _>(key_desc.as_ref(), payload) + .unwrap(); + descs.push(key_desc); + } + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(keys.len(), count); + assert_eq!(keyrings.len(), 0); + + let mut actual_descs = keys + .iter() + .map(|key| key.description().unwrap().description) + .collect::<Vec<_>>(); + actual_descs.sort(); + assert_eq!(actual_descs, descs); + + // Clear the keyring. + keyring.clear().unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(keys.len(), 0); + assert_eq!(keyrings.len(), 0); +} + +#[test] +fn clear_keyring_keyring() { + let mut keyring = utils::new_test_keyring(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(keys.len(), 0); + assert_eq!(keyrings.len(), 0); + + let keyring_desc = "clear_keyring:keyring"; + + // Create a key. + keyring.add_keyring(keyring_desc).unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(keys.len(), 0); + assert_eq!(keyrings.len(), 1); + + assert_eq!(keyrings[0].description().unwrap().description, keyring_desc); + + // Clear the keyring. + keyring.clear().unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(keys.len(), 0); + assert_eq!(keyrings.len(), 0); +} diff --git a/src/tests/describe.rs b/src/tests/describe.rs new file mode 100644 index 0000000..ecff11c --- /dev/null +++ b/src/tests/describe.rs @@ -0,0 +1,127 @@ +// Copyright (c) 2019, Ben Boeckel +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of this project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::keytypes::{Keyring, User}; +use crate::{Key, KeyType, Permission}; + +use super::utils; +use super::utils::kernel::*; + +#[test] +fn invalid_key() { + let key = utils::invalid_key(); + let err = key.description().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_keyring() { + let keyring = utils::invalid_keyring(); + let err = keyring.description().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn non_existent_key() { + let mut keyring = utils::new_test_keyring(); + let key = keyring + .add_key::<User, _, _>("non_existent_key", "payload".as_bytes()) + .unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); +} + +#[test] +fn describe_keyring() { + let mut keyring = utils::new_test_keyring(); + let description = "describe_keyring"; + let keyring = keyring.add_keyring(description).unwrap(); + + let perms = Permission::POSSESSOR_ALL | Permission::USER_VIEW; + + let desc = keyring.description().unwrap(); + assert_eq!(desc.type_, Keyring::name()); + assert_eq!(desc.uid, *UID); + assert_eq!(desc.gid, *GID); + assert_eq!(desc.perms, perms); + assert_eq!(desc.description, description); + + keyring.invalidate().unwrap() +} + +#[test] +fn describe_key() { + let mut keyring = utils::new_test_keyring(); + let description = "describe_key"; + let key = keyring + .add_key::<User, _, _>(description, "payload".as_bytes()) + .unwrap(); + + let perms = Permission::POSSESSOR_ALL | Permission::USER_VIEW; + + let desc = key.description().unwrap(); + assert_eq!(desc.type_, User::name()); + assert_eq!(desc.uid, *UID); + assert_eq!(desc.gid, *GID); + assert_eq!(desc.perms, perms); + assert_eq!(desc.description, description); +} + +#[test] +fn describe_key_no_perm() { + let mut keyring = utils::new_test_keyring(); + let description = "describe_key_no_perm"; + let mut key = keyring + .add_key::<User, _, _>(description, "payload".as_bytes()) + .unwrap(); + + let old_perms = key.description().unwrap().perms; + let perms = { + let mut perms = old_perms; + let view_bits = Permission::POSSESSOR_VIEW | Permission::USER_VIEW; + perms.remove(view_bits); + perms + }; + key.set_permissions(perms).unwrap(); + + let err = key.description().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EACCES)); +} + +#[test] +fn describe_revoked_key() { + let mut keyring = utils::new_test_keyring(); + let key = keyring + .add_key::<User, _, _>("describe_revoked_key", "payload".as_bytes()) + .unwrap(); + + let key_mirror = unsafe { Key::new(key.serial()) }; + key.revoke().unwrap(); + + let err = key_mirror.description().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYREVOKED)); +} diff --git a/src/tests/instantiate.rs b/src/tests/instantiate.rs new file mode 100644 index 0000000..4c935c0 --- /dev/null +++ b/src/tests/instantiate.rs @@ -0,0 +1,212 @@ +// Copyright (c) 2019, Ben Boeckel +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of this project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::time::Duration; + +use crate::keytypes::User; +use crate::KeyManager; + +use super::utils; + +#[test] +fn instantiate_invalid_key() { + let key = utils::invalid_key(); + let manager = KeyManager::test_new(key); + + let payload = "payload".as_bytes(); + let err = manager.instantiate(None, payload).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EPERM)); +} + +#[test] +fn reject_invalid_key() { + let key = utils::invalid_key(); + let manager = KeyManager::test_new(key); + + let duration = Duration::from_secs(1); + let errno = errno::Errno(libc::EKEYREJECTED); + let err = manager.reject(None, duration, errno).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EPERM)); +} + +#[test] +fn negate_invalid_key() { + let key = utils::invalid_key(); + let manager = KeyManager::test_new(key); + + let duration = Duration::from_secs(1); + let err = manager.negate(None, duration).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EPERM)); +} + +#[test] +fn instantiate_into_not_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("instantiate_into_not_key", payload) + .unwrap(); + let mut not_a_keyring = utils::key_as_keyring(&key); + let manager = KeyManager::test_new(key); + + let payload = "payload".as_bytes(); + let err = manager + .instantiate(&mut not_a_keyring, payload) + .unwrap_err(); + // Should be ENOTDIR, but the kernel doesn't have an authorization key for us to use. + assert_eq!(err, errno::Errno(libc::EPERM)); +} + +#[test] +fn reject_into_not_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("reject_into_not_key", payload) + .unwrap(); + let mut not_a_keyring = utils::key_as_keyring(&key); + let manager = KeyManager::test_new(key); + + let duration = Duration::from_secs(1); + let errno = errno::Errno(libc::EKEYREJECTED); + let err = manager + .reject(&mut not_a_keyring, duration, errno) + .unwrap_err(); + // Should be ENOTDIR, but the kernel doesn't have an authorization key for us to use. + assert_eq!(err, errno::Errno(libc::EPERM)); +} + +#[test] +fn negate_into_not_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("negate_into_not_key", payload) + .unwrap(); + let mut not_a_keyring = utils::key_as_keyring(&key); + let manager = KeyManager::test_new(key); + + let duration = Duration::from_secs(1); + let err = manager.negate(&mut not_a_keyring, duration).unwrap_err(); + // Should be ENOTDIR, but the kernel doesn't have an authorization key for us to use. + assert_eq!(err, errno::Errno(libc::EPERM)); +} + +#[test] +fn instantiate_already_instantiated() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("instantiate_already_instantiated", payload) + .unwrap(); + let manager = KeyManager::test_new(key); + + let err = manager.instantiate(None, payload).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EPERM)); +} + +#[test] +fn reject_already_instantiated() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("reject_already_instantiated", payload) + .unwrap(); + let manager = KeyManager::test_new(key); + + let duration = Duration::from_secs(1); + let errno = errno::Errno(libc::EKEYREJECTED); + let err = manager.reject(None, duration, errno).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EPERM)); +} + +#[test] +fn negate_already_instantiated() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("negate_already_instantiated", payload) + .unwrap(); + let manager = KeyManager::test_new(key); + + let duration = Duration::from_secs(1); + let err = manager.negate(None, duration).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EPERM)); +} + +#[test] +fn instantiate_unlinked_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("instantiate_unlinked_key", payload) + .unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); + + let manager = KeyManager::test_new(key); + + let err = manager.instantiate(None, payload).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EPERM)); +} + +#[test] +fn reject_unlinked_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("reject_unlinked_key", payload) + .unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); + + let manager = KeyManager::test_new(key); + + let duration = Duration::from_secs(1); + let errno = errno::Errno(libc::EKEYREJECTED); + let err = manager.reject(None, duration, errno).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EPERM)); +} + +#[test] +fn negate_unlinked_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("negate_unlinked_key", payload) + .unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); + + let manager = KeyManager::test_new(key); + + let duration = Duration::from_secs(1); + let err = manager.negate(None, duration).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EPERM)); +} diff --git a/src/tests/invalidate.rs b/src/tests/invalidate.rs new file mode 100644 index 0000000..e8b351b --- /dev/null +++ b/src/tests/invalidate.rs @@ -0,0 +1,124 @@ +// Copyright (c) 2019, Ben Boeckel +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of this project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::keytypes::User; + +use super::utils; +use super::utils::kernel::*; + +#[test] +fn have_invalidate() { + let can_test = *HAVE_INVALIDATE; + if !can_test { + eprintln!( + "This kernel does not support key invalidation. Please ignore test failures in \ + this test failure." + ); + } +} + +#[test] +fn invalid_key() { + let key = utils::invalid_key(); + let err = key.invalidate().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_keyring() { + let keyring = utils::invalid_keyring(); + let err = keyring.invalidate().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn unlinked_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("unlinked_key", payload) + .unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); + + let err = key.invalidate().unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn invalidate_key() { + let mut keyring = utils::new_test_keyring(); + + { + let (keys, keyrings) = keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); + } + + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("invalidate_key", payload) + .unwrap(); + let key_observer = key.clone(); + + key.invalidate().unwrap(); + utils::wait_for_key_gc(&key_observer); + + { + let (keys, keyrings) = keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); + } +} + +#[test] +fn invalidate_keyring() { + let mut keyring = utils::new_test_keyring_manual(); + + { + let (keys, keyrings) = keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); + } + + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("invalidate_keyring", payload) + .unwrap(); + let keyring_observer = keyring.clone(); + + keyring.invalidate().unwrap(); + utils::wait_for_keyring_gc(&keyring_observer); + + let err = keyring_observer.read().unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); + + utils::wait_for_key_gc(&key); + + let err = key.description().unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} diff --git a/src/tests/link.rs b/src/tests/link.rs new file mode 100644 index 0000000..c8c6e07 --- /dev/null +++ b/src/tests/link.rs @@ -0,0 +1,299 @@ +// Copyright (c) 2019, Ben Boeckel +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of this project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::keytypes::User; +use crate::Permission; + +use super::utils; + +#[test] +fn invalid_target() { + let mut invalid_keyring = utils::invalid_keyring(); + let keyring = utils::new_test_keyring(); + + let err = invalid_keyring.link_keyring(&keyring).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_source() { + let invalid_keyring = utils::invalid_keyring(); + let mut keyring = utils::new_test_keyring(); + + let err = keyring.link_keyring(&invalid_keyring).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn link_to_non_keyring() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("link_to_non_keyring", payload) + .unwrap(); + let linked_key = keyring + .add_key::<User, _, _>("link_to_non_keyring_linked", payload) + .unwrap(); + let mut not_a_keyring = utils::key_as_keyring(&key); + + let err = not_a_keyring.link_key(&linked_key).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOTDIR)); +} + +#[test] +fn link_unlinked_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("link_unlinked_key", payload) + .unwrap(); + let mut target_keyring = keyring.add_keyring("link_unlinked_key_target").unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); + + let err = target_keyring.link_key(&key).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn link_into_unlinked_keyring() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("link_into_unlinked_keyring", payload) + .unwrap(); + let mut target_keyring = keyring + .add_keyring("link_into_unlinked_keyring_target") + .unwrap(); + + keyring.unlink_keyring(&target_keyring).unwrap(); + utils::wait_for_keyring_gc(&target_keyring); + + let err = target_keyring.link_key(&key).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn link_self() { + let mut keyring = utils::new_test_keyring(); + let keyring_observer = keyring.clone(); + + let err = keyring.link_keyring(&keyring_observer).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EDEADLK)); +} + +#[test] +fn link_self_via_child() { + let mut keyring = utils::new_test_keyring(); + let mut target_keyring = keyring.add_keyring("link_self_via_child").unwrap(); + + let err = target_keyring.link_keyring(&keyring).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EDEADLK)); +} + +#[test] +fn link_self_via_child_chains() { + let mut keyring = utils::new_test_keyring(); + let mut target_keyring = keyring.clone(); + let perms = Permission::POSSESSOR_ALL | Permission::USER_ALL; + target_keyring.set_permissions(perms).unwrap(); + + let maxdepth = 8; + for depth in 1..maxdepth { + let mut new_keyring = keyring + .add_keyring(format!("link_self_via_child_chains{}", depth)) + .unwrap(); + new_keyring.set_permissions(perms).unwrap(); + + target_keyring.link_keyring(&new_keyring).unwrap(); + target_keyring = new_keyring; + + let err = target_keyring.link_keyring(&keyring).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EDEADLK)); + } + + let mut new_keyring = keyring + .add_keyring(format!("link_self_via_child_chains{}", maxdepth)) + .unwrap(); + new_keyring.set_permissions(perms).unwrap(); + + target_keyring.link_keyring(&new_keyring).unwrap(); + keyring.unlink_keyring(&new_keyring).unwrap(); + target_keyring = new_keyring; + + let err = target_keyring.link_keyring(&keyring).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ELOOP)); +} + +#[test] +fn link_self_via_keyring_stacks() { + let mut keyring = utils::new_test_keyring(); + let keyring_a_root = keyring + .add_keyring("link_self_via_keyring_stacks_a") + .unwrap(); + let keyring_b_root = keyring + .add_keyring("link_self_via_keyring_stacks_b") + .unwrap(); + let mut keyring_a = keyring_a_root.clone(); + let mut keyring_b = keyring_b_root.clone(); + + let maxdepth = 4; + for depth in 1..maxdepth { + keyring_a = keyring_a + .add_keyring(format!("link_self_via_keyring_stacks_a{}", depth)) + .unwrap(); + keyring_b = keyring_b + .add_keyring(format!("link_self_via_keyring_stacks_b{}", depth)) + .unwrap(); + } + + keyring_b.link_keyring(&keyring_a_root).unwrap(); + + let err = keyring_a.link_keyring(&keyring_b_root).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EDEADLK)); + + keyring_b.unlink_keyring(&keyring_a_root).unwrap(); + + keyring_a.link_keyring(&keyring_b_root).unwrap(); + + let err = keyring_b.link_keyring(&keyring_a_root).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EDEADLK)); +} + +#[test] +fn link_self_via_keyring_deep_stacks() { + let mut keyring = utils::new_test_keyring(); + let keyring_a_root = keyring + .add_keyring("link_self_via_keyring_deep_stacks_a") + .unwrap(); + let keyring_b_root = keyring + .add_keyring("link_self_via_keyring_deep_stacks_b") + .unwrap(); + let mut keyring_a = keyring_a_root.clone(); + let mut keyring_b = keyring_b_root.clone(); + + let maxdepth = 5; + for depth in 1..maxdepth { + keyring_a = keyring_a + .add_keyring(format!("link_self_via_keyring_deep_stacks_a{}", depth)) + .unwrap(); + keyring_b = keyring_b + .add_keyring(format!("link_self_via_keyring_deep_stacks_b{}", depth)) + .unwrap(); + } + + keyring_b.link_keyring(&keyring_a_root).unwrap(); + + let err = keyring_a.link_keyring(&keyring_b_root).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ELOOP)); + + keyring_b.unlink_keyring(&keyring_a_root).unwrap(); + + keyring_a.link_keyring(&keyring_b_root).unwrap(); + + let err = keyring_b.link_keyring(&keyring_a_root).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ELOOP)); +} + +#[test] +fn multiply_link_key_into_keyring() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("multiply_link_key_into_keyring") + .unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert!(keys.is_empty()); + assert_eq!(keyrings.len(), 1); + assert_eq!(keyrings[0], new_keyring); + + let payload = "payload".as_bytes(); + let key = new_keyring + .add_key::<User, _, _>("multiply_link_key_into_keyring_key", payload) + .unwrap(); + + let (keys, keyrings) = new_keyring.read().unwrap(); + assert_eq!(keys.len(), 1); + assert_eq!(keys[0], key); + assert!(keyrings.is_empty()); + + keyring.link_key(&key).unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(keys.len(), 1); + assert_eq!(keys[0], key); + assert_eq!(keyrings.len(), 1); + assert_eq!(keyrings[0], new_keyring); + + // Linking the same key should not change the result. + keyring.link_key(&key).unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(keys.len(), 1); + assert_eq!(keys[0], key); + assert_eq!(keyrings.len(), 1); + assert_eq!(keyrings[0], new_keyring); +} + +#[test] +fn multiply_link_keyring_into_keyring() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("multiply_link_keyring_into_keyring") + .unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert!(keys.is_empty()); + assert_eq!(keyrings.len(), 1); + assert_eq!(keyrings[0], new_keyring); + + let inner_keyring = new_keyring + .add_keyring("multiply_link_keyring_into_keyring_keyring_inner") + .unwrap(); + + let (keys, keyrings) = new_keyring.read().unwrap(); + assert!(keys.is_empty()); + assert_eq!(keyrings.len(), 1); + assert_eq!(keyrings[0], inner_keyring); + + keyring.link_keyring(&inner_keyring).unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert!(keys.is_empty()); + assert_eq!(keyrings.len(), 2); + assert_eq!(keyrings[0], new_keyring); + assert_eq!(keyrings[1], inner_keyring); + + // Linking the same keyring should not change the result. + keyring.link_keyring(&inner_keyring).unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert!(keys.is_empty()); + assert_eq!(keyrings.len(), 2); + assert_eq!(keyrings[0], new_keyring); + assert_eq!(keyrings[1], inner_keyring); +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index ad7f5bb..3e93f17 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -26,6 +26,21 @@ //! The test structure here comes from the structure in libkeyutils. -mod utils; +pub(crate) mod utils; mod add; +mod clear; +mod describe; +mod instantiate; +mod invalidate; +mod link; +mod newring; +mod permitting; +mod reading; +mod revoke; +mod search; +// FIXME(#39): These tests fail when run in the same process. Something should be done about this. +// mod session; +mod timeout; +mod unlink; +mod update; diff --git a/src/tests/newring.rs b/src/tests/newring.rs new file mode 100644 index 0000000..6df9669 --- /dev/null +++ b/src/tests/newring.rs @@ -0,0 +1,132 @@ +// Copyright (c) 2019, Ben Boeckel +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of this project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::iter; + +use crate::keytypes::User; + +use super::utils; +use super::utils::kernel::*; + +#[test] +fn invalid_keyring() { + let mut keyring = utils::invalid_keyring(); + let err = keyring.add_keyring("invalid_keyring").unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn unlinked_keyring() { + let mut keyring = utils::new_test_keyring(); + let mut unlinked_keyring = keyring.add_keyring("unlinked_keyring_unlinked").unwrap(); + + keyring.unlink_keyring(&unlinked_keyring).unwrap(); + utils::wait_for_keyring_gc(&unlinked_keyring); + + let err = unlinked_keyring + .add_keyring("unlinked_keyring") + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn not_a_keyring() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("not_a_keyring_key", payload) + .unwrap(); + let mut not_a_keyring = utils::key_as_keyring(&key); + + let err = not_a_keyring.add_keyring("not_a_keyring").unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOTDIR)); +} + +#[test] +fn empty_keyring_description() { + let mut keyring = utils::new_test_keyring(); + let err = keyring.add_keyring("").unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn max_keyring_description() { + let mut keyring = utils::new_test_keyring(); + // Subtract one because the NUL is added in the kernel API. + let maxdesc: String = iter::repeat('a').take(*PAGE_SIZE - 1).collect(); + let res = keyring.add_keyring(maxdesc.as_ref()); + // If the user's quota is smaller than this, it's an error. + if KEY_INFO.maxbytes < *PAGE_SIZE { + assert_eq!(res.unwrap_err(), errno::Errno(libc::EDQUOT)); + } else { + let keyring = res.unwrap(); + assert_eq!(keyring.description().unwrap().description, maxdesc); + keyring.invalidate().unwrap(); + } +} + +#[test] +fn overlong_keyring_description() { + let mut keyring = utils::new_test_keyring(); + // On MIPS with < 3.19, there is a bug where this is allowed. 3.19 was released in Feb 2015, + // so this is being ignored here. + let maxdesc: String = iter::repeat('a').take(*PAGE_SIZE).collect(); + let err = keyring.add_keyring(maxdesc.as_ref()).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn new_keyring() { + let mut keyring = utils::new_test_keyring(); + let new_keyring = keyring.add_keyring("new_keyring").unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(keys.len(), 0); + assert_eq!(keyrings.len(), 1); + assert_eq!(keyrings[0], new_keyring); +} + +#[test] +fn duplicate_keyring_names() { + let mut keyring = utils::new_test_keyring(); + let new_keyring1 = keyring.add_keyring("duplicate_keyring_names").unwrap(); + let new_keyring2 = keyring.add_keyring("duplicate_keyring_names").unwrap(); + + // The keyring should have been displaced. + assert_ne!(new_keyring1, new_keyring2); + + // The original keyring should not be in the parent keyring. + let (keys, keyrings) = keyring.read().unwrap(); + assert!(keys.is_empty()); + assert_eq!(1, keyrings.len()); + assert_eq!(new_keyring2, keyrings[0]); + + utils::wait_for_keyring_gc(&new_keyring1); + + // It should be inaccessible. + let err = new_keyring1.description().unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} diff --git a/src/tests/permitting.rs b/src/tests/permitting.rs new file mode 100644 index 0000000..a081b9f --- /dev/null +++ b/src/tests/permitting.rs @@ -0,0 +1,270 @@ +// Copyright (c) 2019, Ben Boeckel +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of this project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::keytypes::User; +use crate::{KeyPermissions, Permission}; + +use super::utils; +use super::utils::kernel::*; + +#[test] +fn invalid_key_chown() { + let mut key = utils::invalid_key(); + let err = key.chown(*UID).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_key_chgrp() { + let mut key = utils::invalid_key(); + let err = key.chgrp(*GID).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_key_chmod() { + let mut key = utils::invalid_key(); + let err = key.set_permissions(Permission::POSSESSOR_VIEW).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_keyring_chown() { + let mut keyring = utils::invalid_key(); + let err = keyring.chown(*UID).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_keyring_chgrp() { + let mut keyring = utils::invalid_key(); + let err = keyring.chgrp(*GID).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_keyring_chmod() { + let mut keyring = utils::invalid_keyring(); + let err = keyring.set_permissions(Permission::empty()).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_key_permissions() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("invalid_key_permissions", payload) + .unwrap(); + + let err = key + .set_permissions_raw(KeyPermissions::max_value()) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_keyring_permissions() { + let mut keyring = utils::new_test_keyring(); + + let err = keyring + .set_permissions_raw(KeyPermissions::max_value()) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn unlinked_key_chown() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("unlinked_key_chown", payload) + .unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); + + let err = key.chown(*UID).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn unlinked_key_chgrp() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("unlinked_key_chgrp", payload) + .unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); + + let err = key.chgrp(*GID).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn unlinked_key_chmod() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("unlinked_key_chmod", payload) + .unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); + + let err = key.set_permissions(Permission::POSSESSOR_VIEW).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn chown_keyring() { + let mut keyring = utils::new_test_keyring(); + + if *UID == 0 { + match keyring.chown(1) { + // If that worked, make sure we can move it back. + Ok(_) => keyring.chown(0).unwrap(), + // Otherwise, we got the right error. + Err(err) => assert_eq!(err, errno::Errno(libc::EACCES)), + } + } else { + let err = keyring.chown(1).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EACCES)); + } +} + +#[test] +fn chown_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring.add_key::<User, _, _>("chown_key", payload).unwrap(); + + if *UID == 0 { + match key.chown(1) { + // If that worked, make sure we can move it back. + Ok(_) => key.chown(0).unwrap(), + // Otherwise, we got the right error. + Err(err) => assert_eq!(err, errno::Errno(libc::EACCES)), + } + let err = key.chown(1).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EACCES)); + } +} + +#[test] +fn set_each_permission_bit() { + let permission_bits = [ + Permission::OTHER_VIEW, + Permission::OTHER_READ, + Permission::OTHER_WRITE, + Permission::OTHER_SEARCH, + Permission::OTHER_LINK, + Permission::OTHER_SET_ATTRIBUTE, + Permission::GROUP_VIEW, + Permission::GROUP_READ, + Permission::GROUP_WRITE, + Permission::GROUP_SEARCH, + Permission::GROUP_LINK, + Permission::GROUP_SET_ATTRIBUTE, + Permission::USER_VIEW, + Permission::USER_READ, + Permission::USER_WRITE, + Permission::USER_SEARCH, + Permission::USER_LINK, + Permission::USER_SET_ATTRIBUTE, + Permission::POSSESSOR_VIEW, + Permission::POSSESSOR_READ, + Permission::POSSESSOR_WRITE, + Permission::POSSESSOR_SEARCH, + Permission::POSSESSOR_LINK, + Permission::POSSESSOR_SET_ATTRIBUTE, + ]; + let required_permissions = Permission::USER_SET_ATTRIBUTE | Permission::USER_VIEW; + + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("set_each_permission_bit", payload) + .unwrap(); + + for permission_bit in &permission_bits { + let perms = required_permissions | *permission_bit; + key.set_permissions(perms).unwrap(); + let description = key.description().unwrap(); + assert_eq!(perms, description.perms); + } +} + +#[test] +fn cannot_view_via_group() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("cannot_view_via_group", payload) + .unwrap(); + + let perms = Permission::GROUP_ALL | Permission::USER_SET_ATTRIBUTE; + key.set_permissions(perms).unwrap(); + + let err = key.read().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EACCES)); +} + +#[test] +fn cannot_view_via_other() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("cannot_view_via_other", payload) + .unwrap(); + + let perms = Permission::OTHER_ALL | Permission::USER_SET_ATTRIBUTE; + key.set_permissions(perms).unwrap(); + + let err = key.read().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EACCES)); +} + +#[test] +fn remove_setattr() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("remove_setattr", payload) + .unwrap(); + + let perms = Permission::all() + - (Permission::POSSESSOR_SET_ATTRIBUTE + | Permission::USER_SET_ATTRIBUTE + | Permission::GROUP_SET_ATTRIBUTE + | Permission::OTHER_SET_ATTRIBUTE); + key.set_permissions(perms).unwrap(); + + let err = key.set_permissions(Permission::all()).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EACCES)); +} diff --git a/src/tests/reading.rs b/src/tests/reading.rs new file mode 100644 index 0000000..cb308f6 --- /dev/null +++ b/src/tests/reading.rs @@ -0,0 +1,189 @@ +// Copyright (c) 2019, Ben Boeckel +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of this project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::keytypes::User; +use crate::Permission; + +use super::utils; + +#[test] +fn invalid_key() { + let key = utils::invalid_key(); + let err = key.read().unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn invalid_keyring() { + let keyring = utils::invalid_keyring(); + let err = keyring.read().unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn unlinked_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("unlinked_key", payload) + .unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); + + let err = key.read().unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn unlinked_keyring() { + let mut keyring = utils::new_test_keyring(); + let new_keyring = keyring.add_keyring("unlinked_keyring").unwrap(); + + keyring.unlink_keyring(&new_keyring).unwrap(); + utils::wait_for_keyring_gc(&new_keyring); + + let err = new_keyring.read().unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn read_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring.add_key::<User, _, _>("read_key", payload).unwrap(); + + let actual_payload = key.read().unwrap(); + assert_eq!(payload, actual_payload.as_slice()); +} + +#[test] +fn read_keyring() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("read_keyring", payload) + .unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert_eq!(1, keys.len()); + assert_eq!(key, keys[0]); + assert!(keyrings.is_empty()); +} + +#[test] +fn read_key_as_keyring() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("read_key_as_keyring", payload) + .unwrap(); + let not_a_keyring = utils::key_as_keyring(&key); + + let err = not_a_keyring.read().unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOTDIR)); +} + +#[test] +fn read_keyring_as_key() { + let keyring = utils::new_test_keyring(); + let not_a_key = utils::keyring_as_key(&keyring); + + let payload = not_a_key.read().unwrap(); + assert_eq!(b"", payload.as_slice()); +} + +#[test] +fn read_no_read_perm_with_search() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("read_no_read_perm_with_search", payload) + .unwrap(); + + // Remove the "read" permission from the key. + let no_read_search_perms = Permission::USER_ALL - Permission::USER_READ; + key.set_permissions(no_read_search_perms).unwrap(); + + // This should still work because we have "search" permission on its keyring. + let actual_payload = key.read().unwrap(); + assert_eq!(payload, actual_payload.as_slice()); +} + +#[test] +fn read_no_read_search_perm_with_search() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("read_no_read_search_perm_with_search", payload) + .unwrap(); + + // Remove the "read" and "search" permissions from the key. + let no_read_perms = Permission::USER_ALL - Permission::USER_READ - Permission::USER_SEARCH; + key.set_permissions(no_read_perms).unwrap(); + + let err = key.read().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EACCES)); +} + +#[test] +fn read_rely_on_possessor() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("read_rely_on_possessor", payload) + .unwrap(); + + // Remove the "read" and "search" permissions from the key. + let no_read_perms = Permission::POSSESSOR_ALL - Permission::POSSESSOR_READ; + key.set_permissions(no_read_perms).unwrap(); + + let actual_payload = key.read().unwrap(); + assert_eq!(payload, actual_payload.as_slice()); +} + +#[test] +fn reinstated_read_perm() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("reinstated_read_perm", payload) + .unwrap(); + + // Remove the "read" and "search" permissions from the key. + let no_read_perms = Permission::USER_ALL - Permission::USER_READ - Permission::USER_SEARCH; + key.set_permissions(no_read_perms).unwrap(); + + let err = key.read().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EACCES)); + + // Reinstate "read" permissions. + let no_read_perms = Permission::USER_ALL - Permission::USER_SEARCH; + key.set_permissions(no_read_perms).unwrap(); + + let actual_payload = key.read().unwrap(); + assert_eq!(payload, actual_payload.as_slice()); +} diff --git a/src/tests/revoke.rs b/src/tests/revoke.rs new file mode 100644 index 0000000..0005894 --- /dev/null +++ b/src/tests/revoke.rs @@ -0,0 +1,107 @@ +// Copyright (c) 2019, Ben Boeckel +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of this project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::time::Duration; + +use crate::keytypes::User; + +use super::utils; + +#[test] +fn invalid_key() { + let key = utils::invalid_key(); + let err = key.revoke().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_keyring() { + let keyring = utils::invalid_keyring(); + let err = keyring.revoke().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn unlinked_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("unlinked_key", payload) + .unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); + + let err = key.revoke().unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn revoked_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("revoked_key", payload) + .unwrap(); + let mut key_observer = key.clone(); + + key.revoke().unwrap(); + + let err = key_observer.description().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYREVOKED)); + + let err = key_observer.read().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYREVOKED)); + + let duration = Duration::from_secs(1); + let err = key_observer.set_timeout(duration).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYREVOKED)); + + let err = key_observer.invalidate().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYREVOKED)); +} + +#[test] +fn revoked_keyring() { + let mut keyring = utils::new_test_keyring(); + let new_keyring = keyring.add_keyring("revoked_keyring").unwrap(); + let mut keyring_observer = new_keyring.clone(); + + new_keyring.revoke().unwrap(); + + let err = keyring_observer.read().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYREVOKED)); + + let err = keyring_observer.description().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYREVOKED)); + + let duration = Duration::from_secs(1); + let err = keyring_observer.set_timeout(duration).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYREVOKED)); + + let err = keyring_observer.invalidate().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYREVOKED)); +} diff --git a/src/tests/search.rs b/src/tests/search.rs new file mode 100644 index 0000000..fa5c110 --- /dev/null +++ b/src/tests/search.rs @@ -0,0 +1,703 @@ +// Copyright (c) 2019, Ben Boeckel +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of this project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::iter; + +use crate::keytypes::User; +use crate::Permission; + +use super::utils; +use super::utils::kernel::*; +use super::utils::keys::*; + +#[test] +fn empty_key_type() { + let keyring = utils::new_test_keyring(); + + let err = keyring + .search_for_key::<EmptyKey, _, _>("empty_key_type", None) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn unsupported_key_type() { + let keyring = utils::new_test_keyring(); + + let err = keyring + .search_for_key::<UnsupportedKey, _, _>("unsupported_key_type", None) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn invalid_key_type() { + let keyring = utils::new_test_keyring(); + + let err = keyring + .search_for_key::<InvalidKey, _, _>("invalid_key_type", None) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::EPERM)); +} + +#[test] +fn max_key_type() { + let keyring = utils::new_test_keyring(); + + let err = keyring + .search_for_key::<MaxLenKey, _, _>("invalid_key_type", None) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn overlong_key_type() { + let keyring = utils::new_test_keyring(); + + let err = keyring + .search_for_key::<OverlongKey, _, _>("overlong_key_type", None) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn max_user_description() { + let keyring = utils::new_test_keyring(); + + // Subtract one because the NUL is added in the kernel API. + let maxdesc: String = iter::repeat('a').take(*PAGE_SIZE - 1).collect(); + let err = keyring + .search_for_key::<User, _, _>(maxdesc, None) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn overlong_user_description() { + let keyring = utils::new_test_keyring(); + + // On MIPS with < 3.19, there is a bug where this is allowed. 3.19 was released in Feb 2015, + // so this is being ignored here. + let maxdesc: String = iter::repeat('a').take(*PAGE_SIZE).collect(); + let err = keyring + .search_for_key::<User, _, _>(maxdesc, None) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_keyring() { + let keyring = utils::invalid_keyring(); + + let err = keyring + .search_for_key::<User, _, _>("invalid_keyring", None) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn search_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("search_key", payload) + .unwrap(); + let not_a_keyring = utils::key_as_keyring(&key); + + let err = not_a_keyring + .search_for_key::<User, _, _>("search_key", None) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOTDIR)); +} + +#[test] +fn search_key_no_result() { + let keyring = utils::new_test_keyring(); + + let err = keyring + .search_for_key::<User, _, _>("search_key_no_result", None) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn search_keyring_no_result() { + let keyring = utils::new_test_keyring(); + + let err = keyring + .search_for_keyring("search_keyring_no_result", None) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn search_key_mismatched_type() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring.add_keyring("search_key_mismatched_type").unwrap(); + let description = "search_key_mismatched_type_keyring"; + let _ = new_keyring.add_keyring(description).unwrap(); + + let err = keyring + .search_for_key::<User, _, _>(description, None) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn search_keyring_mismatched_type() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_keyring_mismatched_type") + .unwrap(); + let description = "search_keyring_mismatched_type_key"; + let payload = "payload".as_bytes(); + let _ = new_keyring + .add_key::<User, _, _>(description, payload) + .unwrap(); + + let err = keyring.search_for_keyring(description, None).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn search_and_find_key() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring.add_keyring("search_and_find_key").unwrap(); + let description = "search_and_find_key_key"; + let payload = "payload".as_bytes(); + let key = new_keyring + .add_key::<User, _, _>(description, payload) + .unwrap(); + + let found_key = keyring + .search_for_key::<User, _, _>(description, None) + .unwrap(); + assert_eq!(found_key, key); + + let actual_payload = key.read().unwrap(); + assert_eq!(payload, actual_payload.as_slice()); +} + +#[test] +fn search_and_find_keyring() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring.add_keyring("search_and_find_keyring").unwrap(); + let description = "search_and_find_keyring_keyring"; + let target_keyring = new_keyring.add_keyring(description).unwrap(); + + let found_keyring = keyring.search_for_keyring(description, None).unwrap(); + assert_eq!(found_keyring, target_keyring); +} + +#[test] +fn search_and_find_key_no_search_perm_interm() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_and_find_key_no_search_perm_interm") + .unwrap(); + let description = "search_and_find_key_no_search_perm_interm_key"; + let payload = "payload".as_bytes(); + let _ = new_keyring + .add_key::<User, _, _>(description, payload) + .unwrap(); + + let perms = { + let mut orig_perms = new_keyring.description().unwrap().perms; + orig_perms.remove(Permission::POSSESSOR_SEARCH); + orig_perms.remove(Permission::USER_SEARCH); + orig_perms.remove(Permission::GROUP_SEARCH); + orig_perms.remove(Permission::OTHER_SEARCH); + orig_perms + }; + new_keyring.set_permissions(perms).unwrap(); + + let err = keyring + .search_for_key::<User, _, _>(description, None) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn search_and_find_keyring_no_search_perm_interm() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_and_find_keyring_no_search_perm_interm") + .unwrap(); + let description = "search_and_find_keyring_no_search_perm_interm_keyring"; + let _ = new_keyring.add_keyring(description).unwrap(); + + let perms = { + let mut orig_perms = new_keyring.description().unwrap().perms; + orig_perms.remove(Permission::POSSESSOR_SEARCH); + orig_perms.remove(Permission::USER_SEARCH); + orig_perms.remove(Permission::GROUP_SEARCH); + orig_perms.remove(Permission::OTHER_SEARCH); + orig_perms + }; + new_keyring.set_permissions(perms).unwrap(); + + let err = keyring.search_for_keyring(description, None).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn search_and_find_key_no_search_perm_direct() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_and_find_key_no_search_perm_direct") + .unwrap(); + let description = "search_and_find_key_no_search_perm_direct_key"; + let payload = "payload".as_bytes(); + let mut key = new_keyring + .add_key::<User, _, _>(description, payload) + .unwrap(); + + let perms = { + let mut orig_perms = key.description().unwrap().perms; + orig_perms.remove(Permission::POSSESSOR_SEARCH); + orig_perms.remove(Permission::USER_SEARCH); + orig_perms.remove(Permission::GROUP_SEARCH); + orig_perms.remove(Permission::OTHER_SEARCH); + orig_perms + }; + key.set_permissions(perms).unwrap(); + + let err = keyring + .search_for_key::<User, _, _>(description, None) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::EACCES)); +} + +#[test] +fn search_and_find_keyring_no_search_perm_direct() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_and_find_keyring_no_search_perm_direct") + .unwrap(); + let description = "search_and_find_keyring_no_search_perm_direct_keyring"; + let mut target_keyring = new_keyring.add_keyring(description).unwrap(); + + let perms = { + let mut orig_perms = target_keyring.description().unwrap().perms; + orig_perms.remove(Permission::POSSESSOR_SEARCH); + orig_perms.remove(Permission::USER_SEARCH); + orig_perms.remove(Permission::GROUP_SEARCH); + orig_perms.remove(Permission::OTHER_SEARCH); + orig_perms + }; + target_keyring.set_permissions(perms).unwrap(); + + let err = keyring.search_for_keyring(description, None).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EACCES)); +} + +#[test] +fn search_and_find_key_link() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring.add_keyring("search_and_find_key_link").unwrap(); + let mut destination_keyring = keyring + .add_keyring("search_and_find_key_link_destination") + .unwrap(); + let description = "search_and_find_key_link_key"; + let payload = "payload".as_bytes(); + let key = new_keyring + .add_key::<User, _, _>(description, payload) + .unwrap(); + + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); + + let found_key = keyring + .search_for_key::<User, _, _>(description, &mut destination_keyring) + .unwrap(); + assert_eq!(found_key, key); + + let actual_payload = key.read().unwrap(); + assert_eq!(payload, actual_payload.as_slice()); + + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert_eq!(keys.len(), 1); + assert_eq!(keys[0], key); + assert!(keyrings.is_empty()); +} + +#[test] +fn search_and_find_keyring_link() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring.add_keyring("search_and_find_keyring_link").unwrap(); + let mut destination_keyring = keyring + .add_keyring("search_and_find_keyring_link_destination") + .unwrap(); + let description = "search_and_find_keyring_link_keyring"; + let target_keyring = new_keyring.add_keyring(description).unwrap(); + + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); + + let found_keyring = keyring + .search_for_keyring(description, &mut destination_keyring) + .unwrap(); + assert_eq!(found_keyring, target_keyring); + + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert!(keys.is_empty()); + assert_eq!(keyrings.len(), 1); + assert_eq!(keyrings[0], target_keyring); +} + +#[test] +fn search_and_find_key_link_replace() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_and_find_key_link_replace") + .unwrap(); + let mut destination_keyring = keyring + .add_keyring("search_and_find_key_link_replace_destination") + .unwrap(); + let description = "search_and_find_key_link_replace_key"; + let payload = "payload".as_bytes(); + let key = new_keyring + .add_key::<User, _, _>(description, payload) + .unwrap(); + let other_payload = "payload".as_bytes(); + let orig_key = destination_keyring + .add_key::<User, _, _>(description, other_payload) + .unwrap(); + + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert_eq!(keys.len(), 1); + assert_eq!(keys[0], orig_key); + assert!(keyrings.is_empty()); + + let found_key = keyring + .search_for_key::<User, _, _>(description, &mut destination_keyring) + .unwrap(); + assert_eq!(found_key, key); + + let actual_payload = key.read().unwrap(); + assert_eq!(payload, actual_payload.as_slice()); + + // The original key should have been replaced. + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert_eq!(keys.len(), 1); + assert_eq!(keys[0], key); + assert!(keyrings.is_empty()); +} + +#[test] +fn search_and_find_key_link_replace_keyring() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_and_find_key_link_replace_keyring") + .unwrap(); + let mut destination_keyring = keyring + .add_keyring("search_and_find_key_link_replace_keyring_destination") + .unwrap(); + let description = "search_and_find_key_link_replace_keyring_key"; + let payload = "payload".as_bytes(); + let key = new_keyring + .add_key::<User, _, _>(description, payload) + .unwrap(); + let orig_keyring = destination_keyring.add_keyring(description).unwrap(); + + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert!(keys.is_empty()); + assert_eq!(keyrings.len(), 1); + assert_eq!(keyrings[0], orig_keyring); + + let found_key = keyring + .search_for_key::<User, _, _>(description, &mut destination_keyring) + .unwrap(); + assert_eq!(found_key, key); + + let actual_payload = key.read().unwrap(); + assert_eq!(payload, actual_payload.as_slice()); + + // The original keyring should not have been replaced. + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert_eq!(keys.len(), 1); + assert_eq!(keys[0], key); + assert_eq!(keyrings.len(), 1); + assert_eq!(keyrings[0], orig_keyring); +} + +#[test] +fn search_and_find_keyring_link_replace() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_and_find_keyring_link_replace") + .unwrap(); + let mut destination_keyring = keyring + .add_keyring("search_and_find_keyring_link_replace_destination") + .unwrap(); + let description = "search_and_find_keyring_link_replace_keyring"; + let target_keyring = new_keyring.add_keyring(description).unwrap(); + let orig_keyring = destination_keyring.add_keyring(description).unwrap(); + + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert!(keys.is_empty()); + assert_eq!(keyrings.len(), 1); + assert_eq!(keyrings[0], orig_keyring); + + let found_keyring = keyring + .search_for_keyring(description, &mut destination_keyring) + .unwrap(); + assert_eq!(found_keyring, target_keyring); + + // The original keyring should have been replaced. + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert!(keys.is_empty()); + assert_eq!(keyrings.len(), 1); + assert_eq!(keyrings[0], target_keyring); +} + +#[test] +fn search_and_find_keyring_link_replace_key() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_and_find_keyring_link_replace_key") + .unwrap(); + let mut destination_keyring = keyring + .add_keyring("search_and_find_keyring_link_replace_key_destination") + .unwrap(); + let description = "search_and_find_keyring_link_replace_key_keyring"; + let target_keyring = new_keyring.add_keyring(description).unwrap(); + let payload = "payload".as_bytes(); + let orig_key = destination_keyring + .add_key::<User, _, _>(description, payload) + .unwrap(); + + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert_eq!(keys.len(), 1); + assert_eq!(keys[0], orig_key); + assert!(keyrings.is_empty()); + + let found_keyring = keyring + .search_for_keyring(description, &mut destination_keyring) + .unwrap(); + assert_eq!(found_keyring, target_keyring); + + // The original keyring should not have been replaced. + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert_eq!(keys.len(), 1); + assert_eq!(keys[0], orig_key); + assert_eq!(keyrings.len(), 1); + assert_eq!(keyrings[0], target_keyring); +} + +#[test] +fn search_and_find_key_no_link_perm_no_dest() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_and_find_key_no_link_perm_no_dest") + .unwrap(); + let description = "search_and_find_key_no_link_perm_no_dest_key"; + let payload = "payload".as_bytes(); + let mut key = new_keyring + .add_key::<User, _, _>(description, payload) + .unwrap(); + + let perms = { + let mut orig_perms = key.description().unwrap().perms; + orig_perms.remove(Permission::POSSESSOR_LINK); + orig_perms.remove(Permission::USER_LINK); + orig_perms.remove(Permission::GROUP_LINK); + orig_perms.remove(Permission::OTHER_LINK); + orig_perms + }; + key.set_permissions(perms).unwrap(); + + let found_key = keyring + .search_for_key::<User, _, _>(description, None) + .unwrap(); + assert_eq!(found_key, key); + + let actual_payload = key.read().unwrap(); + assert_eq!(payload, actual_payload.as_slice()); +} + +#[test] +fn search_and_find_keyring_no_link_perm_no_dest() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_and_find_keyring_no_link_perm_no_dest") + .unwrap(); + let description = "search_and_find_keyring_no_link_perm_no_dest_keyring"; + let mut target_keyring = new_keyring.add_keyring(description).unwrap(); + + let perms = { + let mut orig_perms = target_keyring.description().unwrap().perms; + orig_perms.remove(Permission::POSSESSOR_LINK); + orig_perms.remove(Permission::USER_LINK); + orig_perms.remove(Permission::GROUP_LINK); + orig_perms.remove(Permission::OTHER_LINK); + orig_perms + }; + target_keyring.set_permissions(perms).unwrap(); + + let found_keyring = keyring.search_for_keyring(description, None).unwrap(); + assert_eq!(found_keyring, target_keyring); +} + +#[test] +fn search_and_find_key_no_link_perm() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_and_find_key_no_link_perm") + .unwrap(); + let mut destination_keyring = keyring + .add_keyring("search_and_find_key_no_link_perm_destination") + .unwrap(); + let description = "search_and_find_key_no_link_perm_key"; + let payload = "payload".as_bytes(); + let mut key = new_keyring + .add_key::<User, _, _>(description, payload) + .unwrap(); + + let perms = { + let mut orig_perms = key.description().unwrap().perms; + orig_perms.remove(Permission::POSSESSOR_LINK); + orig_perms.remove(Permission::USER_LINK); + orig_perms.remove(Permission::GROUP_LINK); + orig_perms.remove(Permission::OTHER_LINK); + orig_perms + }; + key.set_permissions(perms).unwrap(); + + let err = keyring + .search_for_key::<User, _, _>(description, &mut destination_keyring) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::EACCES)); + + // Assert that it was not linked to the destination keyring. + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); +} + +#[test] +fn search_and_find_keyring_no_link_perm() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_and_find_keyring_no_link_perm") + .unwrap(); + let mut destination_keyring = keyring + .add_keyring("search_and_find_keyring_no_link_perm_destination") + .unwrap(); + let description = "search_and_find_keyring_no_link_perm_keyring"; + let mut target_keyring = new_keyring.add_keyring(description).unwrap(); + + let perms = { + let mut orig_perms = target_keyring.description().unwrap().perms; + orig_perms.remove(Permission::POSSESSOR_LINK); + orig_perms.remove(Permission::USER_LINK); + orig_perms.remove(Permission::GROUP_LINK); + orig_perms.remove(Permission::OTHER_LINK); + orig_perms + }; + target_keyring.set_permissions(perms).unwrap(); + + let err = keyring + .search_for_keyring(description, &mut destination_keyring) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::EACCES)); + + // Assert that it was not linked to the destination keyring. + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); +} + +#[test] +fn search_and_find_key_no_write_perm() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_and_find_key_no_write_perm") + .unwrap(); + let mut destination_keyring = keyring + .add_keyring("search_and_find_key_no_write_perm_destination") + .unwrap(); + let description = "search_and_find_key_no_write_perm_key"; + let payload = "payload".as_bytes(); + let _ = new_keyring + .add_key::<User, _, _>(description, payload) + .unwrap(); + + let perms = { + let mut orig_perms = destination_keyring.description().unwrap().perms; + orig_perms.remove(Permission::POSSESSOR_WRITE); + orig_perms.remove(Permission::USER_WRITE); + orig_perms.remove(Permission::GROUP_WRITE); + orig_perms.remove(Permission::OTHER_WRITE); + orig_perms + }; + destination_keyring.set_permissions(perms).unwrap(); + + let err = keyring + .search_for_key::<User, _, _>(description, &mut destination_keyring) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::EACCES)); + + // Assert that it was not linked to the destination keyring. + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); +} + +#[test] +fn search_and_find_keyring_no_write_perm() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring + .add_keyring("search_and_find_keyring_no_write_perm") + .unwrap(); + let mut destination_keyring = keyring + .add_keyring("search_and_find_keyring_no_write_perm_destination") + .unwrap(); + let description = "search_and_find_keyring_no_write_perm_keyring"; + let _ = new_keyring.add_keyring(description).unwrap(); + + let perms = { + let mut orig_perms = destination_keyring.description().unwrap().perms; + orig_perms.remove(Permission::POSSESSOR_WRITE); + orig_perms.remove(Permission::USER_WRITE); + orig_perms.remove(Permission::GROUP_WRITE); + orig_perms.remove(Permission::OTHER_WRITE); + orig_perms + }; + destination_keyring.set_permissions(perms).unwrap(); + + let err = keyring + .search_for_keyring(description, &mut destination_keyring) + .unwrap_err(); + assert_eq!(err, errno::Errno(libc::EACCES)); + + // Assert that it was not linked to the destination keyring. + let (keys, keyrings) = destination_keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); +} diff --git a/src/tests/session.rs b/src/tests/session.rs new file mode 100644 index 0000000..3831629 --- /dev/null +++ b/src/tests/session.rs @@ -0,0 +1,110 @@ +// Copyright (c) 2019, Ben Boeckel +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of this project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use serial_test_derive::serial; + +use crate::keytypes; +use crate::{KeyType, Keyring, Permission, SpecialKeyring}; + +use super::utils::kernel::*; + +#[test] +#[serial(join_session)] +fn join_anonymous_session() { + let session_before = Keyring::attach_or_create(SpecialKeyring::Session).unwrap(); + let keyring = Keyring::join_anonymous_session().unwrap(); + let session_after = Keyring::attach_or_create(SpecialKeyring::Session).unwrap(); + + assert_ne!(session_before, keyring); + assert_eq!(session_after, keyring); + + let desc = keyring.description().unwrap(); + assert_eq!(desc.type_, keytypes::Keyring::name()); + assert_eq!(desc.uid, *UID); + assert_eq!(desc.gid, *GID); + assert_eq!( + desc.perms, + Permission::POSSESSOR_ALL | Permission::USER_VIEW | Permission::USER_READ + ); + assert_eq!(desc.description, "_ses"); + + keyring.invalidate().unwrap() +} + +#[test] +#[serial(join_session)] +fn join_new_named_session() { + let session_before = Keyring::attach_or_create(SpecialKeyring::Session).unwrap(); + let name = "join_new_named_session"; + let keyring = Keyring::join_session(name).unwrap(); + let session_after = Keyring::attach_or_create(SpecialKeyring::Session).unwrap(); + + assert_ne!(session_before, keyring); + assert_eq!(session_after, keyring); + + let desc = keyring.description().unwrap(); + assert_eq!(desc.type_, keytypes::Keyring::name()); + assert_eq!(desc.uid, *UID); + assert_eq!(desc.gid, *GID); + assert_eq!( + desc.perms, + Permission::POSSESSOR_ALL + | Permission::USER_VIEW + | Permission::USER_READ + | Permission::USER_LINK + ); + assert_eq!(desc.description, name); + + keyring.invalidate().unwrap() +} + +#[test] +#[serial(join_session)] +fn join_existing_named_session() { + let name = "join_existing_named_session"; + + let session_before = Keyring::attach_or_create(SpecialKeyring::Session).unwrap(); + let keyring = Keyring::join_session(name).unwrap(); + let session_after = Keyring::attach_or_create(SpecialKeyring::Session).unwrap(); + + assert_ne!(session_before, keyring); + assert_eq!(session_after, keyring); + + let desc = keyring.description().unwrap(); + assert_eq!(desc.type_, keytypes::Keyring::name()); + assert_eq!(desc.uid, *UID); + assert_eq!(desc.gid, *GID); + assert_eq!( + desc.perms, + Permission::POSSESSOR_ALL + | Permission::USER_VIEW + | Permission::USER_READ + | Permission::USER_LINK + ); + assert_eq!(desc.description, name); + + keyring.invalidate().unwrap() +} diff --git a/src/tests/timeout.rs b/src/tests/timeout.rs new file mode 100644 index 0000000..003f834 --- /dev/null +++ b/src/tests/timeout.rs @@ -0,0 +1,146 @@ +// Copyright (c) 2019, Ben Boeckel +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of this project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::thread; +use std::time::Duration; + +use crate::keytypes::User; + +use super::utils; + +#[test] +fn invalid_key() { + let mut key = utils::invalid_key(); + let duration = Duration::from_secs(1); + let err = key.set_timeout(duration).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_keyring() { + let mut keyring = utils::invalid_keyring(); + let duration = Duration::from_secs(1); + let err = keyring.set_timeout(duration).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn unlinked_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("unlinked_key", payload) + .unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); + + let duration = Duration::from_secs(1); + let err = key.set_timeout(duration).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn big_timeout_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("unlinked_key", payload) + .unwrap(); + + let duration = Duration::from_secs(1024); + key.set_timeout(duration).unwrap(); + + let actual_payload = key.read().unwrap(); + assert_eq!(payload, actual_payload.as_slice()); +} + +#[test] +fn big_timeout_keyring() { + let mut keyring = utils::new_test_keyring(); + + let duration = Duration::from_secs(1024); + keyring.set_timeout(duration).unwrap(); + + let (keys, keyrings) = keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); +} + +#[test] +fn expired_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("expired_key", payload) + .unwrap(); + let key_observer1 = key.clone(); + let key_observer2 = key.clone(); + + let duration = Duration::from_secs(1); + key.set_timeout(duration).unwrap(); + + thread::sleep(duration); + thread::sleep(duration); + + let err = key.read().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYEXPIRED)); + + let err = key.set_timeout(duration).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYEXPIRED)); + + let err = key.invalidate().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYEXPIRED)); + + let err = key_observer1.revoke().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYEXPIRED)); + + keyring.unlink_key(&key_observer2).unwrap(); +} + +#[test] +fn expired_keyring() { + let mut keyring = utils::new_test_keyring_manual(); + let keyring_observer = keyring.clone(); + + let duration = Duration::from_secs(1); + keyring.set_timeout(duration).unwrap(); + + thread::sleep(duration); + thread::sleep(duration); + + let err = keyring.read().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYEXPIRED)); + + let err = keyring.set_timeout(duration).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYEXPIRED)); + + let err = keyring.invalidate().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYEXPIRED)); + + let err = keyring_observer.revoke().unwrap_err(); + assert_eq!(err, errno::Errno(libc::EKEYEXPIRED)); +} diff --git a/src/tests/unlink.rs b/src/tests/unlink.rs new file mode 100644 index 0000000..92fabe3 --- /dev/null +++ b/src/tests/unlink.rs @@ -0,0 +1,228 @@ +// Copyright (c) 2019, Ben Boeckel +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of this project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::keytypes::User; + +use super::utils; + +#[test] +fn invalid_target_key() { + let mut invalid_keyring = utils::invalid_keyring(); + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("invalid_target_key", payload) + .unwrap(); + + let err = invalid_keyring.unlink_key(&key).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_target_keyring() { + let mut invalid_keyring = utils::invalid_keyring(); + let keyring = utils::new_test_keyring(); + + let err = invalid_keyring.unlink_keyring(&keyring).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_source_key() { + let mut keyring = utils::new_test_keyring(); + let invalid_key = utils::invalid_key(); + + let err = keyring.unlink_key(&invalid_key).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn invalid_source_keyring() { + let mut keyring = utils::new_test_keyring(); + let invalid_keyring = utils::invalid_keyring(); + + let err = keyring.unlink_keyring(&invalid_keyring).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn unlink_key_from_non_keyring() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("unlink_key_from_non_keyring", payload) + .unwrap(); + let mut not_a_keyring = utils::key_as_keyring(&key); + + let err = not_a_keyring.unlink_key(&key).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOTDIR)); +} + +#[test] +fn unlink_keyring_from_non_keyring() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("unlink_keyring_from_non_keyring", payload) + .unwrap(); + let mut not_a_keyring = utils::key_as_keyring(&key); + + let err = not_a_keyring.unlink_keyring(&keyring).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOTDIR)); +} + +#[test] +fn unlink_key_as_keyring() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("unlink_keyring_from_non_keyring", payload) + .unwrap(); + let not_a_keyring = utils::key_as_keyring(&key); + + // This is OK because the kernel doesn't have the type knowledge that our API does. + keyring.unlink_keyring(¬_a_keyring).unwrap(); +} + +#[test] +fn unlink_keyring_as_key() { + let mut keyring = utils::new_test_keyring(); + let new_keyring = keyring.add_keyring("unlink_keyring_as_key").unwrap(); + let not_a_key = utils::keyring_as_key(&new_keyring); + + // This is OK because the kernel doesn't have the type knowledge that our API does. + keyring.unlink_key(¬_a_key).unwrap(); +} + +#[test] +fn unlink_unlinked_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("unlink_unlinked_key", payload) + .unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); + + let err = keyring.unlink_key(&key).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn unlink_unlinked_keyring() { + let mut keyring = utils::new_test_keyring(); + let new_keyring = keyring.add_keyring("unlink_unlinked_keyring").unwrap(); + + keyring.unlink_keyring(&new_keyring).unwrap(); + utils::wait_for_keyring_gc(&new_keyring); + + let err = keyring.unlink_keyring(&new_keyring).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn unlink_key_from_unlinked_keyring() { + let mut keyring = utils::new_test_keyring_manual(); + let mut keyring_observer = keyring.clone(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("unlink_key_from_unlinked_keyring", payload) + .unwrap(); + + keyring.invalidate().unwrap(); + utils::wait_for_keyring_gc(&keyring_observer); + + let err = keyring_observer.unlink_key(&key).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn unlink_keyring_from_unlinked_keyring() { + let mut keyring = utils::new_test_keyring_manual(); + let mut keyring_observer = keyring.clone(); + let new_keyring = keyring.add_keyring("unlink_from_unlinked_keyring").unwrap(); + + keyring.invalidate().unwrap(); + utils::wait_for_keyring_gc(&keyring_observer); + + let err = keyring_observer.unlink_keyring(&new_keyring).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn unlink_unassociated_key() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring.add_keyring("unlink_unassociated_key").unwrap(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("unlink_unassociated_key", payload) + .unwrap(); + + let err = new_keyring.unlink_key(&key).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOENT)); +} + +#[test] +fn unlink_unassociated_keyring() { + let mut keyring = utils::new_test_keyring(); + let mut new_keyring = keyring.add_keyring("unlink_unassociated_keyring").unwrap(); + let inner_keyring = keyring + .add_keyring("unlink_unassociated_keyring_keyring") + .unwrap(); + + let err = new_keyring.unlink_keyring(&inner_keyring).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOENT)); +} + +#[test] +fn unlink_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let key = keyring + .add_key::<User, _, _>("unlink_unlinked_key", payload) + .unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); + + let (keys, keyrings) = keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); +} + +#[test] +fn unlink_keyring() { + let mut keyring = utils::new_test_keyring(); + let new_keyring = keyring.add_keyring("unlink_keyring").unwrap(); + + keyring.unlink_keyring(&new_keyring).unwrap(); + utils::wait_for_keyring_gc(&new_keyring); + + let (keys, keyrings) = keyring.read().unwrap(); + assert!(keys.is_empty()); + assert!(keyrings.is_empty()); +} diff --git a/src/tests/update.rs b/src/tests/update.rs new file mode 100644 index 0000000..47b8ea5 --- /dev/null +++ b/src/tests/update.rs @@ -0,0 +1,80 @@ +// Copyright (c) 2019, Ben Boeckel +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of this project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::keytypes::User; + +use super::utils; + +#[test] +fn keyring() { + let keyring = utils::new_test_keyring(); + let mut key = utils::keyring_as_key(&keyring); + + let payload = "payload".as_bytes(); + let err = key.update(payload).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EOPNOTSUPP)); +} + +#[test] +fn invalid_key() { + let mut key = utils::invalid_key(); + + let payload = "payload".as_bytes(); + let err = key.update(payload).unwrap_err(); + assert_eq!(err, errno::Errno(libc::EINVAL)); +} + +#[test] +fn unlinked_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring + .add_key::<User, _, _>("unlinked_key", payload) + .unwrap(); + + keyring.unlink_key(&key).unwrap(); + utils::wait_for_key_gc(&key); + + let payload = "payload".as_bytes(); + let err = key.update(payload).unwrap_err(); + assert_eq!(err, errno::Errno(libc::ENOKEY)); +} + +#[test] +fn user_key() { + let mut keyring = utils::new_test_keyring(); + let payload = "payload".as_bytes(); + let mut key = keyring.add_key::<User, _, _>("user_key", payload).unwrap(); + + let actual_payload = key.read().unwrap(); + assert_eq!(payload, actual_payload.as_slice()); + + let payload = "updated_payload".as_bytes(); + key.update(payload).unwrap(); + + let actual_payload = key.read().unwrap(); + assert_eq!(payload, actual_payload.as_slice()); +} diff --git a/src/tests/utils/kernel.rs b/src/tests/utils/kernel.rs index c39a80f..2f6c196 100644 --- a/src/tests/utils/kernel.rs +++ b/src/tests/utils/kernel.rs @@ -25,16 +25,63 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::collections::HashMap; +use std::ffi::CStr; use std::fs; +use std::mem; use std::str::FromStr; +use lazy_static::lazy_static; use regex::{Captures, Regex}; +use semver::{Version, VersionReq}; lazy_static! { + pub static ref KERNEL_VERSION: String = kernel_version(); + pub static ref SEMVER_KERNEL_VERSION: &'static str = semver_kernel_version(); + pub static ref HAVE_INVALIDATE: bool = have_invalidate(); pub static ref PAGE_SIZE: usize = page_size(); + pub static ref UID: libc::uid_t = getuid(); + pub static ref GID: libc::gid_t = getgid(); pub static ref KEY_INFO: KeyQuota = key_user_info(); } +// The full version of the running kernel. +fn kernel_version() -> String { + let mut utsname = unsafe { mem::zeroed() }; + let ret = unsafe { libc::uname(&mut utsname) }; + if ret < 0 { + panic!("failed to query the kernel version: {}", errno::errno()); + } + let cstr = unsafe { CStr::from_ptr(utsname.release.as_ptr()) }; + cstr.to_str() + .expect("kernel version should be ASCII") + .into() +} + +// A semver-compatible string for the kernel version. +fn semver_kernel_version() -> &'static str { + match (*KERNEL_VERSION).find('-') { + Some(pos) => &(*KERNEL_VERSION)[..pos], + None => &*KERNEL_VERSION, + } +} + +// Whether the kernel supports the `invalidate` action on a key. +fn have_invalidate() -> bool { + match Version::parse(*SEMVER_KERNEL_VERSION) { + Ok(ver) => { + let minver = VersionReq::parse(">=3.5").unwrap(); + minver.matches(&ver) + }, + Err(err) => { + eprintln!( + "failed to parse kernel version `{}` ({}): assuming incompatibility", + *SEMVER_KERNEL_VERSION, err + ); + false + }, + } +} + fn page_size() -> usize { errno::set_errno(errno::Errno(0)); let ret = unsafe { libc::sysconf(libc::_SC_PAGESIZE) }; @@ -120,3 +167,11 @@ fn key_user_info() -> KeyQuota { .get(&uid) .expect("the current user has no keys?") } + +fn getuid() -> libc::uid_t { + unsafe { libc::getuid() } +} + +fn getgid() -> libc::gid_t { + unsafe { libc::getgid() } +} diff --git a/src/tests/utils/mod.rs b/src/tests/utils/mod.rs index 36a3282..4f669b3 100644 --- a/src/tests/utils/mod.rs +++ b/src/tests/utils/mod.rs @@ -24,5 +24,109 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::ops::{Deref, DerefMut}; +use std::sync::atomic; + +use crate::{Key, Keyring, KeyringSerial, SpecialKeyring}; + pub mod kernel; pub mod keys; + +#[derive(Debug)] +pub struct ScopedKeyring { + keyring: Keyring, +} + +impl Drop for ScopedKeyring { + fn drop(&mut self) { + self.keyring.clone().invalidate().unwrap(); + wait_for_keyring_gc(&self.keyring); + } +} + +impl Deref for ScopedKeyring { + type Target = Keyring; + + fn deref(&self) -> &Self::Target { + &self.keyring + } +} + +impl DerefMut for ScopedKeyring { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.keyring + } +} + +// For testing, each test gets a new keyring attached to the Thread keyring. This makes sure tests +// don't interfere with each other, and keys are not prematurely garbage collected. +pub fn new_test_keyring_manual() -> Keyring { + let mut thread_keyring = Keyring::attach_or_create(SpecialKeyring::Thread).unwrap(); + + static KEYRING_COUNT: atomic::AtomicUsize = atomic::AtomicUsize::new(0); + let num = KEYRING_COUNT.fetch_add(1, atomic::Ordering::SeqCst); + thread_keyring + .add_keyring(format!("test:rust-keyutils{}", num)) + .unwrap() +} + +// For testing, each test gets a new keyring attached to the Thread keyring. This makes sure tests +// don't interfere with each other, and keys are not prematurely garbage collected. +pub fn new_test_keyring() -> ScopedKeyring { + ScopedKeyring { + keyring: new_test_keyring_manual(), + } +} + +unsafe fn invalid_serial() -> KeyringSerial { + // Yes, we're explicitly breaking the NonZeroI32 rules here. However, it is not passing through + // any bits which care (e.g., `Option`), so this is purely to test that using an invalid + // keyring ID gives back `EINVAL` as expected. + KeyringSerial::new_unchecked(0) +} + +pub fn invalid_keyring() -> Keyring { + unsafe { Keyring::new(invalid_serial()) } +} + +pub fn invalid_key() -> Key { + unsafe { Key::new(invalid_serial()) } +} + +pub fn keyring_as_key(keyring: &Keyring) -> Key { + unsafe { Key::new(keyring.serial()) } +} + +pub fn key_as_keyring(key: &Key) -> Keyring { + unsafe { Keyring::new(key.serial()) } +} + +/// Keys are deleted asynchronously; describing the key succeeds until it has been garbage +/// collected. +pub fn wait_for_key_gc(key: &Key) { + loop { + match key.description() { + Ok(_) => (), + Err(errno::Errno(libc::ENOKEY)) => break, + e @ Err(_) => { + e.unwrap(); + unreachable!() + }, + } + } +} + +/// Keys are deleted asynchronously; describing the key succeeds until it has been garbage +/// collected. +pub fn wait_for_keyring_gc(keyring: &Keyring) { + loop { + match keyring.read() { + Ok(_) | Err(errno::Errno(libc::EACCES)) => (), + Err(errno::Errno(libc::ENOKEY)) => break, + e @ Err(_) => { + e.unwrap(); + unreachable!() + }, + } + } +}