keyring_search/
keyutils.rs1use std::collections::HashMap;
2
3use super::error::Error as ErrorCode;
4use super::search::{CredentialSearch, CredentialSearchApi, CredentialSearchResult};
5use linux_keyutils::{KeyRing, KeyRingIdentifier, KeyType, Permission};
6
7pub struct KeyutilsCredentialSearch {}
8
9pub fn default_credential_search() -> Box<CredentialSearch> {
14 Box::new(KeyutilsCredentialSearch {})
15}
16
17impl CredentialSearchApi for KeyutilsCredentialSearch {
18 fn by(&self, _by: &str, query: &str) -> CredentialSearchResult {
23 search_by_keyring("session", query)
24 }
25}
26pub fn search_by_keyring(by: &str, query: &str) -> CredentialSearchResult {
32 let by = match by {
33 "thread" => KeyRingIdentifier::Thread,
34 "process" => KeyRingIdentifier::Process,
35 "session" => KeyRingIdentifier::Session,
36 "user" => KeyRingIdentifier::User,
37 "user session" => KeyRingIdentifier::UserSession,
38 "group" => KeyRingIdentifier::Group,
39 _ => return Err(ErrorCode::SearchError("must match keyutils keyring identifiers: thread, process, session, user, user session, group".to_string())),
40 };
41
42 let ring = match KeyRing::from_special_id(by, false) {
43 Ok(ring) => ring,
44 Err(err) => return Err(ErrorCode::SearchError(err.to_string())),
45 };
46
47 let result = match ring.search(query) {
48 Ok(result) => result,
49 Err(err) => match err {
50 linux_keyutils::KeyError::KeyDoesNotExist => return Err(ErrorCode::NoResults),
51 _ => return Err(ErrorCode::SearchError(err.to_string())),
52 },
53 };
54
55 let result_data = match result.metadata() {
56 Ok(data) => data,
57 Err(err) => return Err(ErrorCode::SearchError(err.to_string())),
58 };
59
60 let key_type = get_key_type(result_data.get_type());
61
62 let permission_bits = result_data.get_perms().bits().to_be_bytes();
63
64 let permission_string = get_permission_chars(permission_bits[0]);
65
66 let mut outer_map: HashMap<String, HashMap<String, String>> = HashMap::new();
67 let mut inner_map: HashMap<String, String> = HashMap::new();
68
69 inner_map.insert("perm".to_string(), permission_string);
70 inner_map.insert("gid".to_string(), result_data.get_gid().to_string());
71 inner_map.insert("uid".to_string(), result_data.get_uid().to_string());
72 inner_map.insert("ktype".to_string(), key_type);
73 inner_map.insert(
74 "description".to_string(),
75 result_data.get_description().to_string(),
76 );
77
78 outer_map.insert(result.get_id().0.to_string(), inner_map);
79
80 Ok(outer_map)
81}
82fn get_key_type(key_type: KeyType) -> String {
83 match key_type {
84 KeyType::KeyRing => "KeyRing".to_string(),
85 KeyType::BigKey => "BigKey".to_string(),
86 KeyType::Logon => "Logon".to_string(),
87 KeyType::User => "User".to_string(),
88 }
89}
90fn get_permission_chars(permission_data: u8) -> String {
92 let perm_types = [
93 Permission::VIEW.bits(),
94 Permission::READ.bits(),
95 Permission::WRITE.bits(),
96 Permission::SEARCH.bits(),
97 Permission::LINK.bits(),
98 Permission::SETATTR.bits(),
99 Permission::ALL.bits(),
100 ];
101
102 let perm_chars = ['v', 'r', 'w', 's', 'l', 'a', '-'];
103
104 let mut perm_string = String::new();
105 perm_string.push('-');
106
107 for i in (0..perm_types.len()).rev() {
108 if permission_data & perm_types[i] != 0 {
109 perm_string.push(perm_chars[i]);
110 } else {
111 perm_string.push('-');
112 }
113 }
114
115 perm_string
116}
117
118#[cfg(test)]
119mod tests {
120 use super::{get_key_type, get_permission_chars, KeyRing, KeyRingIdentifier};
121 use crate::{tests::generate_random_string, Error, Limit, List, Search};
122 use keyring::{credential::CredentialApi, keyutils::KeyutilsCredential};
123 use std::collections::HashSet;
124
125 #[test]
126 fn test_search() {
127 let name = generate_random_string();
128 let entry = keyring::keyutils::KeyutilsCredential::new_with_target(None, &name, &name)
129 .expect("Failed to create searchable entry");
130 let password = "search test password";
131 entry
132 .set_password(password)
133 .expect("Failed to set password");
134
135 let actual: &KeyutilsCredential =
136 &entry.get_credential().expect("Not a keyutils credential 1");
137
138 let keyring = KeyRing::from_special_id(KeyRingIdentifier::Session, false)
139 .expect("No session keyring");
140 let credential = keyring
141 .search(&actual.description)
142 .expect("Failed to downcast to linux-keyutils type");
143 let metadata = credential
144 .metadata()
145 .expect("Failed to get credential metadata");
146
147 let mut expected = format!("{}\n", credential.get_id().0,);
148 expected.push_str(format!("gid: {}\n", metadata.get_gid()).as_str());
149 expected.push_str(format!("uid: {}\n", metadata.get_uid()).as_str());
150 expected.push_str(format!("description: {}\n", actual.description).as_str());
151 expected.push_str(
152 format!(
153 "perm: {}\n",
154 get_permission_chars(metadata.get_perms().bits().to_be_bytes()[0])
155 )
156 .as_str(),
157 );
158 expected.push_str(format!("ktype: {}\n", get_key_type(metadata.get_type())).as_str());
159
160 let query = format!("keyring-rs:{}@{}", name, name);
161 let result = Search {
162 inner: Box::new(super::KeyutilsCredentialSearch {}),
163 }
164 .by_user(&query);
165 let list = List::list_credentials(&result, Limit::All);
166
167 let expected_set: HashSet<&str> = expected.lines().collect();
168 let result_set: HashSet<&str> = list.lines().collect();
169 assert_eq!(expected_set, result_set, "Search results do not match");
170 entry
171 .delete_password()
172 .expect("Couldn't delete test-search-by-user");
173 }
174
175 #[test]
176 fn test_no_results() {
177 let name = generate_random_string();
178 let search = Search::new()
179 .expect("Error creating new search")
180 .by_user(&name);
181
182 assert!(matches!(search.unwrap_err(), Error::NoResults));
183 }
184}