Skip to content

Commit d3dd521

Browse files
authored
Finish implementing KeyRing interface (#2)
* Implement `KeyRing::link` and `KeyRing::unlink` * Implement `KeyRing::clear` * Add `Key::set_timeout` method * Add `Key::reject` and `Key::revoke` Co-authored-by: landhb <[email protected]>
1 parent 6507f2d commit d3dd521

File tree

8 files changed

+116
-79
lines changed

8 files changed

+116
-79
lines changed

.github/workflows/checks.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
override: true
3838
components: clippy
3939
- name: Run Clippy
40-
run: cargo clippy --verbose
40+
run: cargo clippy --verbose -- --deny "warnings"
4141
- name: Run RustFmt
4242
run: cargo fmt -- --check
4343

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ To use `linux-keyutils`, first add this to your `Cargo.toml`:
1212
linux_keyutils = "0.1"
1313
```
1414

15-
For more please view the full [documentation](https://docs.rs/linux-keyutils).
15+
For more information please view the full [documentation](https://docs.rs/linux-keyutils).
1616

1717
## Features
1818

src/errors.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use core::fmt::Result;
66
#[cfg(feature = "std")]
77
use std::error::Error;
88

9+
/// Error type for this library, optionally implements `std::error::Error`.
910
#[allow(dead_code)]
1011
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1112
pub enum KeyError {

src/ffi/types.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,14 @@
33
use crate::KeyError;
44
use core::ffi::CStr;
55

6-
/// Serial Number for a Key
7-
///
8-
/// Returned by the kernel.
6+
/// Primary kernel identifier for a key or keyring.
97
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
108
pub struct KeySerialId(pub i32);
119

12-
/// The key type is a string that specifies the key's type. Internally, the kernel
13-
/// defines a number of key types that are available in the core key management code.
14-
/// The types defined for user-space use and can be specified as the type argument to
15-
/// add_key() are defined in this enum.
10+
/// Pre-defined key types the kernel understands. See `man 7 keyrings`.
1611
pub enum KeyType {
1712
/// Keyrings are special key types that may contain links to sequences of other
18-
/// keys of any type. If `add_key` is used to create a keyring, then payload
19-
/// should be NULL and plen should be zero.
13+
/// keys of any type.
2014
KeyRing,
2115
/// This is a general purpose key type whose payload may be read and updated by
2216
/// user-space applications. The key is kept entirely within kernel memory.
@@ -32,6 +26,7 @@ pub enum KeyType {
3226
BigKey,
3327
}
3428

29+
/// Special identifiers for default keyrings. See `man 7 keyrings`.
3530
#[allow(dead_code)]
3631
pub enum KeyRingIdentifier {
3732
/// Key ID for thread-specific keyring
@@ -172,8 +167,6 @@ impl TryFrom<u64> for KeySerialId {
172167
type Error = KeyError;
173168

174169
fn try_from(n: u64) -> Result<Self, Self::Error> {
175-
Ok(Self {
176-
0: n.try_into().or(Err(KeyError::InvalidIdentifier))?,
177-
})
170+
Ok(Self(n.try_into().or(Err(KeyError::InvalidIdentifier))?))
178171
}
179172
}

src/key.rs

Lines changed: 68 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ use crate::{KeyError, KeyPermissions};
33
use alloc::string::String;
44
use core::fmt;
55

6-
/// Rust Interface for KeyCtl operations using the kernel
7-
/// provided keyrings. Each method is implemented to leverage
8-
/// Rust strict typing.
6+
/// A key corresponding to a specific real ID.
97
#[derive(Debug, Copy, Clone)]
108
pub struct Key(KeySerialId);
119

@@ -17,7 +15,7 @@ impl fmt::Display for Key {
1715
}
1816

1917
impl Key {
20-
/// Initialize a new `KeyCtl` object from the provided ID
18+
/// Initialize a new [Key] object from the provided ID
2119
pub fn from_id(id: KeySerialId) -> Self {
2220
Self(id)
2321
}
@@ -31,8 +29,8 @@ impl Key {
3129
///
3230
/// The key must grant the caller view permission.
3331
///
34-
/// The returned string is null-terminated and contains the following
35-
/// information about the key:
32+
/// The returned string contains the following information about
33+
/// the key:
3634
///
3735
/// `type;uid;gid;perm;description`
3836
///
@@ -58,14 +56,15 @@ impl Key {
5856
/// The key must either grant the caller read permission, or grant
5957
/// the caller search permission when searched for from the process
6058
/// keyrings (i.e., the key is possessed).
61-
///
62-
/// The returned data will be processed for presentation according to
63-
/// the key type. For example, a keyring will return an array of
64-
/// key_serial_t entries representing the IDs of all the keys that
65-
/// are linked to it. The user key type will return its data as is.
66-
/// If a key type does not implement this function, the operation
67-
/// fails with the error EOPNOTSUPP.
6859
pub fn read<T: AsMut<[u8]>>(&self, buffer: &mut T) -> Result<usize, KeyError> {
60+
// TODO: alternate key types? Currenlty we only support KeyType::User
61+
//
62+
// The returned data will be processed for presentation according to
63+
// the key type. For example, a keyring will return an array of
64+
// key_serial_t entries representing the IDs of all the keys that
65+
// are linked to it. The user key type will return its data as is.
66+
// If a key type does not implement this function, the operation
67+
// fails with the error EOPNOTSUPP.
6968
let len = ffi::keyctl!(
7069
KeyCtlOperation::Read,
7170
self.0.as_raw_id() as libc::c_ulong,
@@ -80,7 +79,7 @@ impl Key {
8079
/// The caller must have write permission on the key specified and the key
8180
/// type must support updating.
8281
///
83-
/// A negatively instantiated key (see the description of `KeyCtl::reject`)
82+
/// A negatively instantiated key (see the description of [Key::reject])
8483
/// can be positively instantiated with this operation.
8584
pub fn update<T: AsRef<[u8]>>(&self, update: &T) -> Result<(), KeyError> {
8685
_ = ffi::keyctl!(
@@ -127,23 +126,72 @@ impl Key {
127126
Ok(())
128127
}
129128

130-
pub fn set_timeout() {
131-
todo!()
129+
/// Set a timeout on a key.
130+
///
131+
/// Specifying the timeout value as 0 clears any existing timeout on the key.
132+
///
133+
/// The `/proc/keys` file displays the remaining time until each key will expire.
134+
/// (This is the only method of discovering the timeout on a key.)
135+
///
136+
/// The caller must either have the setattr permission on the key or hold an
137+
/// instantiation authorization token for the key.
138+
///
139+
/// The key and any links to the key will be automatically garbage collected
140+
/// after the timeout expires. Subsequent attempts to access the key will
141+
/// then fail with the error EKEYEXPIRED.
142+
///
143+
/// This operation cannot be used to set timeouts on revoked, expired, or
144+
/// negatively instantiated keys.
145+
pub fn set_timeout(&self, seconds: usize) -> Result<(), KeyError> {
146+
_ = ffi::keyctl!(
147+
KeyCtlOperation::SetTimeout,
148+
self.0.as_raw_id() as libc::c_ulong,
149+
seconds as _
150+
)?;
151+
Ok(())
152+
}
153+
154+
/// Revoke this key. Similar to [Key::reject] just without the timeout.
155+
///
156+
/// The key is scheduled for garbage collection; it will no longer be findable,
157+
/// and will be unavailable for further operations. Further attempts to use the
158+
/// key will fail with the error EKEYREVOKED.
159+
///
160+
/// The caller must have write or setattr permission on the key.
161+
pub fn revoke(&self) -> Result<(), KeyError> {
162+
_ = ffi::keyctl!(KeyCtlOperation::Revoke, self.0.as_raw_id() as libc::c_ulong)?;
163+
Ok(())
164+
}
165+
166+
/// Mark a key as negatively instantiated and set an expiration timer on the key.
167+
///
168+
/// This will prevent others from retrieving the key in further searches. And they
169+
/// will receive a `EKEYREJECTED` error when performing the search.
170+
///
171+
/// Similar to [Key::revoke] but with a timeout.
172+
pub fn reject(&self, seconds: usize) -> Result<(), KeyError> {
173+
_ = ffi::keyctl!(
174+
KeyCtlOperation::Reject,
175+
self.0.as_raw_id() as libc::c_ulong,
176+
seconds as _,
177+
libc::EKEYREJECTED as _
178+
)?;
179+
Ok(())
132180
}
133181

134182
/// Mark a key as invalid.
135183
///
136184
/// To invalidate a key, the caller must have search permission on the
137185
/// key.
138186
///
139-
/// This operation marks the key as invalid and schedules immediate
140-
/// garbage collection. The garbage collector removes the invali‐
187+
/// This operation marks the key as invalid and schedules immediate
188+
/// garbage collection. The garbage collector removes the invali‐
141189
/// dated key from all keyrings and deletes the key when its refer‐
142-
/// ence count reaches zero. After this operation, the key will be
190+
/// ence count reaches zero. After this operation, the key will be
143191
/// ignored by all searches, even if it is not yet deleted.
144192
///
145193
/// Keys that are marked invalid become invisible to normal key oper‐
146-
/// ations immediately, though they are still visible in /proc/keys
194+
/// ations immediately, though they are still visible in `/proc/keys`
147195
/// (marked with an 'i' flag) until they are actually removed.
148196
pub fn invalidate(&self) -> Result<(), KeyError> {
149197
ffi::keyctl!(
@@ -195,25 +243,4 @@ mod tests {
195243
assert_eq!("wow".as_bytes(), &buf[..len]);
196244
key.invalidate().unwrap()
197245
}
198-
/*
199-
#[test]
200-
fn test_user_keyring_chmod() {
201-
let secret = "Test Data";
202-
let id = ffi::add_key(
203-
KeyType::User,
204-
KeyringIdentifier::User,
205-
"my-super-secret-test-key2",
206-
secret.as_bytes(),
207-
)
208-
.unwrap();
209-
let mut buf = [0u8; 4096];
210-
211-
let euid = unsafe { libc::geteuid() };
212-
213-
let keyctl = KeyCtl::from_id(id);
214-
keyctl.chown(Some(euid), Some(0)).unwrap();
215-
let len = keyctl.read(&mut buf).unwrap();
216-
assert_eq!(secret.as_bytes(), &buf[..len]);
217-
keyctl.invalidate().unwrap()
218-
}*/
219246
}

src/keyring.rs

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,27 @@ use alloc::ffi::CString;
44
use core::convert::TryInto;
55
use core::ffi::CStr;
66

7-
/// Rust Interface for KeyRing operations using the kernel
8-
/// provided keyrings. Used to locate, create, search, add,
9-
/// and remove keys to & from keyrings.
7+
/// Interface to perform keyring operations. Used to locate, create,
8+
/// search, add, and link/unlink keys to & from keyrings.
109
#[derive(Debug, Copy, Clone)]
1110
pub struct KeyRing {
1211
id: KeySerialId,
1312
}
1413

1514
impl KeyRing {
16-
/// Obtain a KeyRing directly from its ID
17-
pub const fn from_id(id: KeySerialId) -> Self {
18-
Self { id }
15+
/// Create a new keyring with the given description
16+
pub fn create<D: AsRef<str> + ?Sized>(_description: &D) -> Result<Self, KeyError> {
17+
todo!()
1918
}
2019

2120
/// Obtain a KeyRing from its special identifier.
2221
///
23-
/// If the create argument is true, then this method will
24-
/// attempt to create the keyring. Otherwise it will only
25-
/// succeed if the keyring already exists and is valid.
22+
/// If the create argument is true, then this method will attempt
23+
/// to create the keyring. Otherwise it will only succeed if the
24+
/// keyring already exists and is valid.
2625
///
27-
/// Internally this uses KEYCTL_GET_KEYRING_ID to resolve
28-
/// a keyrings real ID from the special identifier.
26+
/// Internally this uses KEYCTL_GET_KEYRING_ID to resolve a keyrings
27+
/// real ID from the special identifier.
2928
pub fn from_special_id(id: KeyRingIdentifier, create: bool) -> Result<Self, KeyError> {
3029
let id: KeySerialId = ffi::keyctl!(
3130
KeyCtlOperation::GetKeyRingId,
@@ -59,12 +58,10 @@ impl KeyRing {
5958
Ok(Key::from_id(id))
6059
}
6160

62-
/// Search for a key in a keyring tree, returning its ID and optionally linking
63-
/// it to a specified keyring.
61+
/// Search for a key in the keyring tree, starting with this keyring as the head,
62+
/// returning its ID.
6463
///
65-
/// The tree to be searched is specified by passing the ID of the head keyring
66-
/// in arg2 (cast to key_serial_t). The search is performed breadth-first and
67-
/// recursively.
64+
/// The search is performed breadth-first and recursively.
6865
///
6966
/// The source keyring must grant search permission to the caller. When
7067
/// performing the recursive search, only keyrings that grant the caller search
@@ -92,7 +89,7 @@ impl KeyRing {
9289
Ok(Key::from_id(id))
9390
}
9491

95-
/// Create a link from a keyring to a key.
92+
/// Create a link from this keyring to a key.
9693
///
9794
/// If a key with the same type and description is already linked in the keyring,
9895
/// then that key is displaced from the keyring.
@@ -106,22 +103,37 @@ impl KeyRing {
106103
///
107104
/// The caller must have link permission on the key being added and write
108105
/// permission on the keyring.
109-
pub fn link_key() {}
106+
pub fn link_key(&self, key: Key) -> Result<(), KeyError> {
107+
_ = ffi::keyctl!(
108+
KeyCtlOperation::Link,
109+
key.get_id().as_raw_id() as _,
110+
self.id.as_raw_id() as libc::c_ulong
111+
)?;
112+
Ok(())
113+
}
110114

111-
/// Unlink a key from a keyring.
115+
/// Unlink a key from this keyring.
112116
///
113117
/// If the key is not currently linked into the keyring, an error results. If the
114118
/// last link to a key is removed, then that key will be scheduled for destruction.
115119
///
116120
/// The caller must have write permission on the keyring from which the key is being
117121
/// removed.
118-
pub fn unlink_key() {}
122+
pub fn unlink_key(&self, key: Key) -> Result<(), KeyError> {
123+
_ = ffi::keyctl!(
124+
KeyCtlOperation::Unlink,
125+
key.get_id().as_raw_id() as _,
126+
self.id.as_raw_id() as libc::c_ulong
127+
)?;
128+
Ok(())
129+
}
119130

120131
/// Clear the contents of (i.e., unlink all keys from) this keyring.
121132
///
122133
/// The caller must have write permission on the keyring.
123-
pub fn clear(&self) {
124-
todo!()
134+
pub fn clear(&self) -> Result<(), KeyError> {
135+
_ = ffi::keyctl!(KeyCtlOperation::Clear, self.id.as_raw_id() as libc::c_ulong)?;
136+
Ok(())
125137
}
126138
}
127139

src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
//! // Perform manipulations on the key such as setting permissions
2828
//! key.set_perm(perms)?;
2929
//!
30+
//! // Or setting a timeout for how long the key should exist
31+
//! key.set_timeout(300)?;
32+
//!
3033
//! // Or invalidating (removing) the key
3134
//! key.invalidate()?;
3235
//! Ok(())
@@ -41,8 +44,8 @@
4144
//!
4245
//! fn get_key(description: &str) -> Result<Key, KeyError> {
4346
//! // Obtain the default session keyring for the current process
44-
//! // See [KeyRingIdentifier] and `man 2 keyctl` for more information on default
45-
//! // keyrings for processes.
47+
//! // See `KeyRingIdentifier` and `man 7 keyrings` for more information on default
48+
//! // keyrings for processes and users.
4649
//! let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false)?;
4750
//!
4851
//! // Lookup an existing key

src/permissions.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub struct KeyPermissions(u32);
3030
pub struct KeyPermissionsBuilder(KeyPermissions);
3131

3232
bitflags! {
33+
/// Pre-defined bit-flags to construct permissions easily.
3334
pub struct Permission: u8 {
3435
/// Allows viewing a key's attributes
3536
const VIEW = 0x1;

0 commit comments

Comments
 (0)