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}