linux_keyutils/keyring.rs
1use crate::ffi::{self, KeyCtlOperation};
2use crate::utils::{CStr, CString, Vec};
3use crate::{Key, KeyError, KeyRingIdentifier, KeySerialId, KeyType, LinkNode, Links, Metadata};
4use core::convert::TryInto;
5
6/// Interface to perform keyring operations. Used to locate, create,
7/// search, add, and link/unlink keys to & from keyrings.
8#[derive(Debug, Copy, Clone, PartialEq, Eq)]
9pub struct KeyRing {
10 id: KeySerialId,
11}
12
13impl KeyRing {
14 /// Initialize a new [Key] object from the provided ID
15 pub(crate) fn from_id(id: KeySerialId) -> Self {
16 Self { id }
17 }
18
19 /// Obtain a KeyRing from its special identifier.
20 ///
21 /// If the create argument is true, then this method will attempt
22 /// to create the keyring. Otherwise it will only succeed if the
23 /// keyring already exists and is valid.
24 ///
25 /// Internally this uses KEYCTL_GET_KEYRING_ID to resolve a keyrings
26 /// real ID from the special identifier.
27 pub fn from_special_id(id: KeyRingIdentifier, create: bool) -> Result<Self, KeyError> {
28 let id: KeySerialId = ffi::keyctl!(
29 KeyCtlOperation::GetKeyRingId,
30 id as libc::c_ulong,
31 u32::from(create).into()
32 )?
33 .try_into()
34 .or(Err(KeyError::InvalidIdentifier))?;
35 Ok(Self { id })
36 }
37
38 /// Get the persistent keyring (persistent-keyring(7)) of the current user
39 /// and link it to a specified keyring.
40 ///
41 /// If the call is successful, a link to the persistent keyring is added to the
42 /// keyring specified in the `link_with` argument.
43 ///
44 /// The caller must have write permission on the keyring.
45 ///
46 /// The persistent keyring will be created by the kernel if it does not yet exist.
47 ///
48 /// Each time the [KeyRing::get_persistent] operation is performed, the persistent
49 /// keyring will have its expiration timeout reset to the value in:
50 ///
51 /// `/proc/sys/kernel/keys/persistent_keyring_expiry`
52 ///
53 /// Should the timeout be reached, the persistent keyring will be removed and
54 /// everything it pins can then be garbage collected.
55 ///
56 /// Persistent keyrings were added to Linux in kernel version 3.13.
57 pub fn get_persistent(link_with: KeyRingIdentifier) -> Result<Self, KeyError> {
58 let id: KeySerialId = ffi::keyctl!(
59 KeyCtlOperation::GetPersistent,
60 u32::MAX as _,
61 link_with as libc::c_ulong
62 )?
63 .try_into()
64 .or(Err(KeyError::InvalidIdentifier))?;
65 Ok(Self { id })
66 }
67
68 /// Obtain information describing the attributes of this keyring.
69 ///
70 /// The keyring must grant the caller view permission.
71 pub fn metadata(&self) -> Result<Metadata, KeyError> {
72 Metadata::from_id(self.id)
73 }
74
75 /// Creates or updates a key of the given type and description, instantiates
76 /// it with the payload of length plen, attaches it to the User keyring.
77 ///
78 /// If the destination keyring already contains a key that matches
79 /// the specified type and description, then, if the key type supports
80 /// it, that key will be updated rather than a new key being created;
81 /// if not, a new key (with a different ID) will be created and it will
82 /// displace the link to the extant key from the keyring.
83 pub fn add_key<D: AsRef<str> + ?Sized, S: AsRef<[u8]> + ?Sized>(
84 &self,
85 description: &D,
86 secret: &S,
87 ) -> Result<Key, KeyError> {
88 let id = ffi::add_key(
89 KeyType::User,
90 self.id.as_raw_id() as libc::c_ulong,
91 description.as_ref(),
92 Some(secret.as_ref()),
93 )?;
94 Ok(Key::from_id(id))
95 }
96
97 /// Search for a key in the keyring tree, starting with this keyring as the head,
98 /// returning its ID.
99 ///
100 /// The search is performed breadth-first and recursively.
101 ///
102 /// The source keyring must grant search permission to the caller. When
103 /// performing the recursive search, only keyrings that grant the caller search
104 /// permission will be searched. Only keys with for which the caller has
105 /// search permission can be found.
106 ///
107 /// If the key is found, its ID is returned as the function result.
108 pub fn search<D: AsRef<str> + ?Sized>(&self, description: &D) -> Result<Key, KeyError> {
109 // The provided description must be properly null terminated for the kernel
110 let description =
111 CString::new(description.as_ref()).or(Err(KeyError::InvalidDescription))?;
112
113 // Perform the raw syscall and validate that the result is a valid ID
114 let id: KeySerialId = ffi::keyctl!(
115 KeyCtlOperation::Search,
116 self.id.as_raw_id() as libc::c_ulong,
117 Into::<&'static CStr>::into(KeyType::User).as_ptr() as _,
118 description.as_ptr() as _,
119 0
120 )?
121 .try_into()
122 .or(Err(KeyError::InvalidIdentifier))?;
123
124 // Construct a key object from the ID
125 Ok(Key::from_id(id))
126 }
127
128 /// Obtain a list of the keys/keyrings linked to this keyring.
129 ///
130 /// This method allocates, but you can provide a maximum number of entries
131 /// to read. Each returned entry is 4 bytes.
132 ///
133 /// The keyring must either grant the caller read permission, or grant
134 /// the caller search permission.
135 pub fn get_links(&self, max: usize) -> Result<Links, KeyError> {
136 // Allocate the requested capacity
137 let mut buffer = Vec::<KeySerialId>::with_capacity(max);
138
139 // Perform the read
140 let len = ffi::keyctl!(
141 KeyCtlOperation::Read,
142 self.id.as_raw_id() as libc::c_ulong,
143 buffer.as_mut_ptr() as _,
144 buffer.capacity() as _
145 )? as usize;
146
147 // Set the size of the results
148 unsafe {
149 buffer.set_len(len / core::mem::size_of::<KeySerialId>());
150 }
151
152 // Remap the results to complete keys
153 Ok(buffer
154 .iter()
155 .filter_map(|&id| LinkNode::from_id(id).ok())
156 .collect())
157 }
158
159 /// Create a link from this keyring to a key.
160 ///
161 /// If a key with the same type and description is already linked in the keyring,
162 /// then that key is displaced from the keyring.
163 ///
164 /// Before creating the link, the kernel checks the nesting of the keyrings
165 /// and returns appropriate errors if the link would produce a cycle or if the
166 /// nesting of keyrings would be too deep (The limit on the nesting of keyrings is
167 /// determined by the kernel constant KEYRING_SEARCH_MAX_DEPTH, defined with the
168 /// value 6, and is necessary to prevent overflows on the kernel stack when
169 /// recursively searching keyrings).
170 ///
171 /// The caller must have link permission on the key being added and write
172 /// permission on the keyring.
173 pub fn link_key(&self, key: Key) -> Result<(), KeyError> {
174 _ = ffi::keyctl!(
175 KeyCtlOperation::Link,
176 key.get_id().as_raw_id() as _,
177 self.id.as_raw_id() as libc::c_ulong
178 )?;
179 Ok(())
180 }
181
182 /// Unlink a key from this keyring.
183 ///
184 /// If the key is not currently linked into the keyring, an error results. If the
185 /// last link to a key is removed, then that key will be scheduled for destruction.
186 ///
187 /// The caller must have write permission on the keyring from which the key is being
188 /// removed.
189 pub fn unlink_key(&self, key: Key) -> Result<(), KeyError> {
190 _ = ffi::keyctl!(
191 KeyCtlOperation::Unlink,
192 key.get_id().as_raw_id() as _,
193 self.id.as_raw_id() as libc::c_ulong
194 )?;
195 Ok(())
196 }
197
198 /// Clear the contents of (i.e., unlink all keys from) this keyring.
199 ///
200 /// The caller must have write permission on the keyring.
201 pub fn clear(&self) -> Result<(), KeyError> {
202 _ = ffi::keyctl!(KeyCtlOperation::Clear, self.id.as_raw_id() as libc::c_ulong)?;
203 Ok(())
204 }
205}
206
207#[cfg(test)]
208mod test {
209 use super::*;
210 use crate::{KeyPermissionsBuilder, Permission};
211
212 #[test]
213 fn test_from_special_id() {
214 // Test that a keyring that normally doesn't exist by default is
215 // created when called.
216 let ring = KeyRing::from_special_id(KeyRingIdentifier::Thread, true).unwrap();
217 assert!(ring.id.as_raw_id() > 0);
218
219 // Test that a keyring that should already exist is returned
220 let ring = KeyRing::from_special_id(KeyRingIdentifier::User, false).unwrap();
221 assert!(ring.id.as_raw_id() > 0);
222 }
223
224 #[test]
225 fn test_get_persistent() {
226 // Test that a keyring that should already exist is returned
227 let user_ring = KeyRing::from_special_id(KeyRingIdentifier::User, false).unwrap();
228 assert!(user_ring.id.as_raw_id() > 0);
229
230 let user_perm_ring = KeyRing::get_persistent(KeyRingIdentifier::User).unwrap();
231 assert_ne!(user_ring, user_perm_ring);
232 }
233
234 #[test]
235 fn test_metadata() {
236 // Test that a keyring that normally doesn't exist by default is
237 // created when called.
238 let ring = KeyRing::from_special_id(KeyRingIdentifier::Thread, true).unwrap();
239 assert!(ring.id.as_raw_id() > 0);
240
241 // Obtain and verify the info
242 let info = ring.metadata().unwrap();
243 assert_eq!(info.get_type(), KeyType::KeyRing);
244 assert_eq!(info.get_uid(), unsafe { libc::geteuid() });
245 assert_eq!(info.get_gid(), unsafe { libc::getegid() });
246 assert_eq!(info.get_description(), "_tid");
247 }
248
249 #[test]
250 fn test_search_existing_key() {
251 // Test that a keyring that normally doesn't exist by default is
252 // created when called.
253 let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap();
254 let key = ring.add_key("test_search", b"data").unwrap();
255
256 // Ensure we have search permission on the key
257 let perms = KeyPermissionsBuilder::builder()
258 .posessor(Permission::ALL)
259 .user(Permission::ALL)
260 .build();
261
262 // Enforce perms
263 key.set_perms(perms).unwrap();
264
265 // Search should succeed
266 let result = ring.search("test_search").unwrap();
267
268 // Assert that the ID is the same
269 assert_eq!(key.get_id(), result.get_id());
270
271 // Invalidate the key
272 key.invalidate().unwrap();
273 }
274
275 #[test]
276 fn test_search_non_existing_key() {
277 // Test that a keyring that normally doesn't exist by default is
278 // created when called.
279 let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap();
280
281 // Search should succeed
282 let result = ring.search("test_search_no_exist");
283
284 // Assert that the ID is the same
285 assert!(result.is_err());
286 assert_eq!(result.unwrap_err(), KeyError::KeyDoesNotExist);
287 }
288
289 #[test]
290 fn test_get_linked_items() {
291 // Test that a keyring that should already exist is returned
292 let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap();
293 assert!(ring.id.as_raw_id() > 0);
294
295 // Add the key
296 let key = ring.add_key("test_read_key", b"test").unwrap();
297
298 // Obtain a list of the linked keys
299 let items = ring.get_links(200).unwrap();
300
301 // Assert that the key is in the ring
302 assert!(items.len() > 0);
303 assert!(items.contains(&key));
304
305 // Use the alternate reference to the key
306 let key_ref = items.get(&key).unwrap().as_key().unwrap();
307
308 // Invalidate the key
309 key_ref.invalidate().unwrap();
310
311 // Assert that the key is no longer on the ring
312 let items = ring.get_links(200).unwrap();
313 assert!(!items.contains(&key));
314 }
315}