Skip to main content

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 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    /// Assume the authority for the calling thread to instantiate a key.
231    ///
232    /// Authority over a key can be assumed only if the calling thread has present
233    /// in its keyrings the authorization key that is associated with the specified key.
234    ///
235    /// In other words, the KEYCTL_ASSUME_AUTHORITY operation is available only from
236    /// a request-key(8)-style program.
237    ///
238    /// The caller must have search permission on the authorization key.
239    pub fn assume_authority(&self) -> Result<(), KeyError> {
240        ffi::keyctl!(
241            KeyCtlOperation::AssumeAuthority,
242            self.0.as_raw_id() as libc::c_ulong
243        )?;
244        Ok(())
245    }
246
247    /// Instantiate a partially constructed key.
248    ///
249    /// To instantiate a key, the caller must have the appropriate
250    /// authorization key. This is automatically granted when the caller
251    /// is invoked by /sbin/request-key.
252    pub fn instantiate<T: AsRef<[u8]> + ?Sized>(
253        &self,
254        payload: &T,
255        id: KeySerialId,
256    ) -> Result<(), KeyError> {
257        // When instanting keyrings the payload will be NULL
258        let buffer = payload.as_ref();
259        let (payload, plen) = match buffer.len() {
260            0 => (core::ptr::null(), 0),
261            _ => (buffer.as_ptr(), buffer.len()),
262        };
263        _ = ffi::keyctl!(
264            KeyCtlOperation::Instantiate,
265            self.0.as_raw_id() as libc::c_ulong,
266            payload as _,
267            plen as _,
268            id.as_raw_id() as libc::c_ulong
269        )?;
270        Ok(())
271    }
272}
273
274#[cfg(test)]
275mod tests {
276    use super::*;
277    use crate::{KeyRing, KeyRingIdentifier, KeyType, Permission};
278    use zeroize::Zeroizing;
279
280    #[test]
281    fn test_from_raw_id() {
282        let raw: i32 = 0x12345;
283        let _key = Key::from_id(raw.into());
284    }
285
286    #[test]
287    fn test_metadata() {
288        let secret = "Test Data";
289
290        // Obtain the default User keyring
291        let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap();
292
293        // Create the key
294        let key = ring.add_key("my-info-key", secret).unwrap();
295
296        // Obtain and verify the info
297        let info = key.metadata().unwrap();
298        assert_eq!(info.get_type(), KeyType::User);
299        assert_eq!(info.get_uid(), unsafe { libc::geteuid() });
300        assert_eq!(info.get_gid(), unsafe { libc::getegid() });
301        assert_eq!(info.get_perms().bits(), 0x3F010000);
302        assert_eq!(info.get_description(), "my-info-key");
303
304        // Cleanup
305        key.invalidate().unwrap()
306    }
307
308    #[test]
309    fn test_read_into_vec() {
310        let secret = "Test Data";
311
312        // Obtain the default User keyring
313        let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap();
314
315        // Create the key
316        let key = ring.add_key("vec-read-key", secret).unwrap();
317
318        // Verify the payload
319        let payload = key.read_to_vec().unwrap();
320        assert_eq!(secret.as_bytes(), &payload);
321        key.invalidate().unwrap();
322    }
323
324    #[test]
325    fn test_user_keyring_add_key() {
326        let secret = "Test Data";
327
328        // Obtain the default User keyring
329        let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap();
330
331        // Create the key
332        let key = ring.add_key("my-super-secret-test-key", secret).unwrap();
333
334        // A buffer that is ensured to be zeroed when
335        // out of scope
336        let mut buf = Zeroizing::new([0u8; 4096]);
337
338        // Allow P/U/G full permissions
339        let mut perms = KeyPermissions::new();
340        perms.set_posessor_perms(Permission::ALL);
341        perms.set_user_perms(Permission::ALL);
342        perms.set_group_perms(Permission::ALL);
343
344        // Set the permissions
345        key.set_perms(perms).unwrap();
346
347        // Read the secret and verify it matches
348        let len = key.read(&mut buf).unwrap();
349        assert_eq!(secret.as_bytes(), &buf[..len]);
350
351        // Update it
352        key.update(&"wow".as_bytes()).unwrap();
353
354        // Verify it matches the new content
355        let len = key.read(&mut buf).unwrap();
356        assert_eq!("wow".as_bytes(), &buf[..len]);
357        key.invalidate().unwrap()
358    }
359}