linux_keyutils/
key.rs

1use crate::ffi::{self, KeyCtlOperation, KeySerialId};
2use crate::utils::Vec;
3use crate::{KeyError, KeyPermissions, Metadata};
4use core::fmt;
5
6/// A key corresponding to a specific real ID.
7///
8/// Generally you will either create or obtain a Key via the [KeyRing](crate::KeyRing)
9/// interface. Since keys must be linked with a keyring to be valid.
10///
11/// For example:
12///
13/// ```
14/// use linux_keyutils::{Key, KeyRing, KeyRingIdentifier, KeyError};
15/// use zeroize::Zeroize;
16///
17/// // Name of my program's key
18/// const KEYNAME: &'static str = "my-process-key";
19///
20/// // Locate the key in the process keyring and update the secret
21/// fn update_secret<T: AsRef<[u8]> + Zeroize>(data: &T) -> Result<(), KeyError> {
22///     // Get the current process keyring
23///     let ring = KeyRing::from_special_id(KeyRingIdentifier::Process, false)?;
24///
25///     // Locate the key we previously created
26///     let key = ring.search(KEYNAME)?;
27///
28///     // Change the data it contains
29///     key.update(data)?;
30///     Ok(())
31/// }
32/// ```
33#[derive(Debug, Copy, Clone, PartialEq, Eq)]
34pub struct Key(KeySerialId);
35
36impl fmt::Display for Key {
37    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38        let info = self.metadata().map_err(|_| fmt::Error)?;
39        write!(f, "Key({:?})", info)
40    }
41}
42
43impl Key {
44    /// Initialize a new [Key] object from the provided ID
45    pub fn from_id(id: KeySerialId) -> Self {
46        Self(id)
47    }
48
49    /// Obtain a copy of the ID of this key
50    pub fn get_id(&self) -> KeySerialId {
51        self.0
52    }
53
54    /// Obtain information describing the attributes of this key.
55    ///
56    /// The key must grant the caller view permission.
57    pub fn metadata(&self) -> Result<Metadata, KeyError> {
58        Metadata::from_id(self.0)
59    }
60
61    /// Read the payload data of a key into a provided mutable slice.
62    ///
63    /// The returned usize is the number of bytes read into the slice.
64    ///
65    /// The key must either grant the caller read permission, or grant
66    /// the caller search permission when searched for from the process
67    /// keyrings (i.e., the key is possessed).
68    pub fn read<T: AsMut<[u8]>>(&self, buffer: &mut T) -> Result<usize, KeyError> {
69        // TODO: alternate key types? Currenlty we don't support KeyType::BigKey
70        let len = ffi::keyctl!(
71            KeyCtlOperation::Read,
72            self.0.as_raw_id() as libc::c_ulong,
73            buffer.as_mut().as_mut_ptr() as _,
74            buffer.as_mut().len() as _
75        )? as usize;
76        Ok(len)
77    }
78
79    /// Read the payload data of a key, returning a newly allocated vector.
80    ///
81    /// The key must either grant the caller read permission, or grant
82    /// the caller search permission when searched for from the process
83    /// keyrings (i.e., the key is possessed).
84    pub fn read_to_vec(&self) -> Result<Vec<u8>, KeyError> {
85        // Ensure we have enough room to write up to the maximum for a UserKey
86        let mut buffer = Vec::with_capacity(65536);
87
88        // Obtain the key
89        let len = ffi::keyctl!(
90            KeyCtlOperation::Read,
91            self.0.as_raw_id() as libc::c_ulong,
92            buffer.as_mut_ptr() as _,
93            buffer.capacity() as _
94        )? as usize;
95
96        // Update length
97        unsafe {
98            buffer.set_len(len);
99        }
100        Ok(buffer)
101    }
102
103    /// Update a key's data payload.
104    ///
105    /// The caller must have write permission on the key specified and the key
106    /// type must support updating.
107    ///
108    /// A negatively instantiated key (see the description of [Key::reject])
109    /// can be positively instantiated with this operation.
110    pub fn update<T: AsRef<[u8]>>(&self, update: &T) -> Result<(), KeyError> {
111        _ = ffi::keyctl!(
112            KeyCtlOperation::Update,
113            self.0.as_raw_id() as libc::c_ulong,
114            update.as_ref().as_ptr() as _,
115            update.as_ref().len() as _
116        )?;
117        Ok(())
118    }
119
120    /// Change the permissions of the key with the ID provided
121    ///
122    /// If the caller doesn't have the CAP_SYS_ADMIN capability, it can change
123    /// permissions only only for the keys it owns. (More precisely: the caller's
124    /// filesystem UID must match the UID of the key.)
125    pub fn set_perms(&self, perm: KeyPermissions) -> Result<(), KeyError> {
126        _ = ffi::keyctl!(
127            KeyCtlOperation::SetPerm,
128            self.0.as_raw_id() as libc::c_ulong,
129            perm.bits() as _
130        )?;
131        Ok(())
132    }
133
134    /// Change the ownership (user and group ID) of a key.
135    ///
136    /// For the UID to be changed, or for the GID to be changed to a group
137    /// the caller is not a member of, the caller must have the CAP_SYS_ADMIN
138    /// capability (see capabilities(7)).
139    ///
140    /// If the UID is to be changed, the new user must have sufficient quota
141    /// to accept the key. The quota deduction will be removed from the old
142    /// user to the new user should the UID be changed.
143    pub fn chown(&self, uid: Option<u32>, gid: Option<u32>) -> Result<(), KeyError> {
144        let uid_opt = uid.unwrap_or(u32::MAX);
145        let gid_opt = gid.unwrap_or(u32::MAX);
146        _ = ffi::keyctl!(
147            KeyCtlOperation::Chown,
148            self.0.as_raw_id() as libc::c_ulong,
149            uid_opt as _,
150            gid_opt as _
151        )?;
152        Ok(())
153    }
154
155    /// Set a timeout on a key.
156    ///
157    /// Specifying the timeout value as 0 clears any existing timeout on the key.
158    ///
159    /// The `/proc/keys` file displays the remaining time until each key will expire.
160    /// (This is the only method of discovering the timeout on a key.)
161    ///
162    /// The caller must either have the setattr permission on the key or hold an
163    /// instantiation authorization token for the key.
164    ///
165    /// The key and any links to the key will be automatically garbage collected
166    /// after the  timeout  expires. Subsequent attempts to access the key will
167    /// then fail with the error EKEYEXPIRED.
168    ///
169    /// This operation cannot be used to set timeouts on revoked, expired, or
170    /// negatively instantiated keys.
171    pub fn set_timeout(&self, seconds: usize) -> Result<(), KeyError> {
172        _ = ffi::keyctl!(
173            KeyCtlOperation::SetTimeout,
174            self.0.as_raw_id() as libc::c_ulong,
175            seconds as _
176        )?;
177        Ok(())
178    }
179
180    /// Revoke this key. Similar to [Key::reject] just without the timeout.
181    ///
182    /// The key is scheduled for garbage collection; it will no longer be findable,
183    /// and will be unavailable for further operations. Further attempts to use the
184    /// key will fail with the error `EKEYREVOKED`.
185    ///
186    /// The caller must have write or setattr permission on the key.
187    pub fn revoke(&self) -> Result<(), KeyError> {
188        _ = ffi::keyctl!(KeyCtlOperation::Revoke, self.0.as_raw_id() as libc::c_ulong)?;
189        Ok(())
190    }
191
192    /// Mark a key as negatively instantiated and set an expiration timer on the key.
193    ///
194    /// This will prevent others from retrieving the key in further searches. And they
195    /// will receive a `EKEYREJECTED` error when performing the search.
196    ///
197    /// Similar to [Key::revoke] but with a timeout.
198    pub fn reject(&self, seconds: usize) -> Result<(), KeyError> {
199        _ = ffi::keyctl!(
200            KeyCtlOperation::Reject,
201            self.0.as_raw_id() as libc::c_ulong,
202            seconds as _,
203            libc::EKEYREJECTED as _
204        )?;
205        Ok(())
206    }
207
208    /// Mark a key as invalid.
209    ///
210    /// To invalidate a key, the caller must have search permission on the
211    /// key.
212    ///
213    /// This operation marks the key as invalid and schedules immediate
214    /// garbage collection. The garbage collector removes the invali‐
215    /// dated key from all keyrings and deletes the key when  its  refer‐
216    /// ence count reaches zero. After this operation, the key will be
217    /// ignored by all searches, even if it is not yet deleted.
218    ///
219    /// Keys that are marked invalid become invisible to normal key oper‐
220    /// ations  immediately,  though they are still visible in `/proc/keys`
221    /// (marked with an 'i' flag) until they are actually removed.
222    pub fn invalidate(&self) -> Result<(), KeyError> {
223        ffi::keyctl!(
224            KeyCtlOperation::Invalidate,
225            self.0.as_raw_id() as libc::c_ulong
226        )?;
227        Ok(())
228    }
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234    use crate::{KeyRing, KeyRingIdentifier, KeyType, Permission};
235    use zeroize::Zeroizing;
236
237    #[test]
238    fn test_from_raw_id() {
239        let raw: i32 = 0x12345;
240        let _key = Key::from_id(raw.into());
241    }
242
243    #[test]
244    fn test_metadata() {
245        let secret = "Test Data";
246
247        // Obtain the default User keyring
248        let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap();
249
250        // Create the key
251        let key = ring.add_key("my-info-key", secret).unwrap();
252
253        // Obtain and verify the info
254        let info = key.metadata().unwrap();
255        assert_eq!(info.get_type(), KeyType::User);
256        assert_eq!(info.get_uid(), unsafe { libc::geteuid() });
257        assert_eq!(info.get_gid(), unsafe { libc::getegid() });
258        assert_eq!(info.get_perms().bits(), 0x3F010000);
259        assert_eq!(info.get_description(), "my-info-key");
260
261        // Cleanup
262        key.invalidate().unwrap()
263    }
264
265    #[test]
266    fn test_read_into_vec() {
267        let secret = "Test Data";
268
269        // Obtain the default User keyring
270        let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap();
271
272        // Create the key
273        let key = ring.add_key("vec-read-key", secret).unwrap();
274
275        // Verify the payload
276        let payload = key.read_to_vec().unwrap();
277        assert_eq!(secret.as_bytes(), &payload);
278        key.invalidate().unwrap();
279    }
280
281    #[test]
282    fn test_user_keyring_add_key() {
283        let secret = "Test Data";
284
285        // Obtain the default User keyring
286        let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap();
287
288        // Create the key
289        let key = ring.add_key("my-super-secret-test-key", secret).unwrap();
290
291        // A buffer that is ensured to be zeroed when
292        // out of scope
293        let mut buf = Zeroizing::new([0u8; 4096]);
294
295        // Allow P/U/G full permissions
296        let mut perms = KeyPermissions::new();
297        perms.set_posessor_perms(Permission::ALL);
298        perms.set_user_perms(Permission::ALL);
299        perms.set_group_perms(Permission::ALL);
300
301        // Set the permissions
302        key.set_perms(perms).unwrap();
303
304        // Read the secret and verify it matches
305        let len = key.read(&mut buf).unwrap();
306        assert_eq!(secret.as_bytes(), &buf[..len]);
307
308        // Update it
309        key.update(&"wow".as_bytes()).unwrap();
310
311        // Verify it matches the new content
312        let len = key.read(&mut buf).unwrap();
313        assert_eq!("wow".as_bytes(), &buf[..len]);
314        key.invalidate().unwrap()
315    }
316}