Skip to content

Port libkeyutils tests #34

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 47 commits into from
Jan 10, 2020
Merged
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
87d6cd2
tests: factor out creating an invalid keyring
mathstuf Jul 7, 2019
e55d2a0
tests: use test names as key descriptions
mathstuf Jul 7, 2019
58b940a
tests/add: add a test for treating a key as a keyring
mathstuf Jul 7, 2019
9891f15
tests/add: whitespace nit
mathstuf Jul 7, 2019
6a7de94
tests: move test keyring creation to tests/utils
mathstuf Jul 8, 2019
25820b0
tests: remove unnecessary cloning
mathstuf Jul 8, 2019
4156e62
tests: clean up test keyrings
mathstuf Jul 8, 2019
7d1c913
tests/add: migrate old test_add_key test
mathstuf Jul 8, 2019
19cb0f5
tests/clear: add tests for clearing keyrings
mathstuf Jul 7, 2019
ee811f0
tests/utils/kernel: add statics for the uid and gid
mathstuf Jul 8, 2019
a2a04fb
tests/utils: add a function to create an invalid key
mathstuf Jul 8, 2019
e33d00a
tests/describe: port description tests from libkeyutils
mathstuf Jul 8, 2019
c12e3c1
tests/utils/kernel: explicitly use the lazy_static macro
mathstuf Jul 24, 2019
b32d2e4
tests/session: add session-based tests
mathstuf Jul 24, 2019
d0b3225
tests/utils: refactor out waiting for key garbage collection
mathstuf Jul 26, 2019
bac112c
utils/kernel: add functions for extracting the kernel version
mathstuf Jul 26, 2019
396a24c
tests/invalidate: test key and keyring invalidation
mathstuf Jul 26, 2019
7112055
tests/update: port update tests from libkeyutils
mathstuf Jul 26, 2019
bd7b8af
tests/revoke: add key and keyring revokation tests
mathstuf Jul 26, 2019
192315c
tests/timeout: test setting timeouts on keys and keyrings
mathstuf Jul 29, 2019
d7c251a
tests/newring: add keyring creation tests
mathstuf Jul 29, 2019
9f59d83
tests/reading: add tests for reading keys and keyrings
mathstuf Jul 30, 2019
7588661
tests/add: remove unnecessary key unlinking
mathstuf Aug 2, 2019
5d697d4
tests/add: simplify the payload values a bit
mathstuf Aug 2, 2019
911f06f
tests/add: use a utility function to transform a keyring
mathstuf Aug 2, 2019
375d8c8
KeyManager: fix API to match kernel semantics
mathstuf Aug 2, 2019
30f0e53
tests/instantiate: add tests for key instantiation
mathstuf Aug 2, 2019
ba3637d
Keyring::read: guard against a key-as-keyring panic
mathstuf Aug 2, 2019
b283904
tests/reading: test reading keys and keyrings as each other
mathstuf Aug 2, 2019
317a16c
constants: fix a typo in the Permission docstring
mathstuf Aug 13, 2019
2e0c1b9
encrypted: add support for the `enc32` format
mathstuf Aug 13, 2019
8e054fc
tests/link: add tests for link behavior
mathstuf Aug 14, 2019
afa55af
tests/unlink: add tests for unlinking keyrings and keys
mathstuf Aug 14, 2019
fdbfb5a
Keyring: fix API for searching
mathstuf Aug 14, 2019
2c2c748
tests/search: test searching for keys and keyrings
mathstuf Aug 14, 2019
05b8ddb
tests/permitting: add tests for permission modification
mathstuf Aug 14, 2019
87003fa
api: fix request methods
mathstuf Aug 14, 2019
8a6eeb6
tests/add: expand on tests for adding keyrings
mathstuf Sep 30, 2019
bd5dcd7
api: fix the `request` API
mathstuf Sep 30, 2019
1a70ad2
tests: make a ScopedKeyring for handling cleanup automatically
mathstuf Oct 3, 2019
21f2f02
ci: run tests with a single thread
mathstuf Jan 10, 2020
7004edc
tests: disable session tests (for now)
mathstuf Jan 10, 2020
f39e205
ci: update to tarpaulin 0.10.0
mathstuf Jan 10, 2020
2f6daae
clippy: add safety sections to unsafe functions
mathstuf Jan 10, 2020
dbac0f0
ci: require 1.35.0 for Option::copied
mathstuf Jan 10, 2020
da585d1
tests/permitting: allow root not having chown permission too
mathstuf Jan 10, 2020
7728eda
ci: download a precompiled cargo-tarpaulin binary
mathstuf Jan 10, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .ci/tarpaulin.sh
Original file line number Diff line number Diff line change
@@ -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"
12 changes: 6 additions & 6 deletions .cirrus.yml
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
8 changes: 4 additions & 4 deletions keyutils-raw/src/functions.rs
Original file line number Diff line number Diff line change
@@ -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)
707 changes: 147 additions & 560 deletions src/api.rs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/constants.rs
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions src/keytypes/encrypted.rs
Original file line number Diff line number Diff line change
@@ -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",
}
}
}
4 changes: 0 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -30,10 +30,6 @@
#![warn(missing_docs)]

#[cfg(test)]
#[macro_use]
extern crate lazy_static;

mod api;
mod constants;
mod keytype;
116 changes: 92 additions & 24 deletions src/tests/add.rs
Original file line number Diff line number Diff line change
@@ -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());
}
181 changes: 181 additions & 0 deletions src/tests/clear.rs
Original file line number Diff line number Diff line change
@@ -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);
}
127 changes: 127 additions & 0 deletions src/tests/describe.rs
Original file line number Diff line number Diff line change
@@ -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));
}
212 changes: 212 additions & 0 deletions src/tests/instantiate.rs
Original file line number Diff line number Diff line change
@@ -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));
}
124 changes: 124 additions & 0 deletions src/tests/invalidate.rs
Original file line number Diff line number Diff line change
@@ -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));
}
299 changes: 299 additions & 0 deletions src/tests/link.rs
Original file line number Diff line number Diff line change
@@ -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);
}
17 changes: 16 additions & 1 deletion src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
132 changes: 132 additions & 0 deletions src/tests/newring.rs
Original file line number Diff line number Diff line change
@@ -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));
}
270 changes: 270 additions & 0 deletions src/tests/permitting.rs
Original file line number Diff line number Diff line change
@@ -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));
}
189 changes: 189 additions & 0 deletions src/tests/reading.rs
Original file line number Diff line number Diff line change
@@ -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());
}
107 changes: 107 additions & 0 deletions src/tests/revoke.rs
Original file line number Diff line number Diff line change
@@ -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));
}
703 changes: 703 additions & 0 deletions src/tests/search.rs

Large diffs are not rendered by default.

110 changes: 110 additions & 0 deletions src/tests/session.rs
Original file line number Diff line number Diff line change
@@ -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()
}
146 changes: 146 additions & 0 deletions src/tests/timeout.rs
Original file line number Diff line number Diff line change
@@ -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));
}
228 changes: 228 additions & 0 deletions src/tests/unlink.rs
Original file line number Diff line number Diff line change
@@ -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(&not_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(&not_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());
}
80 changes: 80 additions & 0 deletions src/tests/update.rs
Original file line number Diff line number Diff line change
@@ -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());
}
55 changes: 55 additions & 0 deletions src/tests/utils/kernel.rs
Original file line number Diff line number Diff line change
@@ -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() }
}
104 changes: 104 additions & 0 deletions src/tests/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -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!()
},
}
}
}