Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 1 | // Copyright 2020, The Android Open Source Project |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | //! This crate provides access control primitives for Keystore 2.0. |
| 16 | //! It provides high level functions for checking permissions in the keystore2 and keystore2_key |
| 17 | //! SELinux classes based on the keystore2_selinux backend. |
| 18 | //! It also provides KeystorePerm and KeyPerm as convenience wrappers for the SELinux permission |
| 19 | //! defined by keystore2 and keystore2_key respectively. |
| 20 | |
Janis Danisevskis | a2f4850 | 2021-10-18 16:07:09 -0700 | [diff] [blame] | 21 | use crate::error::Error as KsError; |
Rajesh Nyamagoud | caee93e | 2022-05-26 00:20:38 +0000 | [diff] [blame] | 22 | use crate::error::ResponseCode; |
Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 23 | use crate::ks_err; |
Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 24 | use android_system_keystore2::aidl::android::system::keystore2::{ |
| 25 | Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission, |
| 26 | }; |
Janis Danisevskis | a2f4850 | 2021-10-18 16:07:09 -0700 | [diff] [blame] | 27 | use anyhow::Context as AnyhowContext; |
| 28 | use keystore2_selinux as selinux; |
| 29 | use lazy_static::lazy_static; |
Janis Danisevskis | 56af031 | 2021-10-18 16:11:41 -0700 | [diff] [blame] | 30 | use selinux::{implement_class, Backend, ClassPermission}; |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 31 | use std::cmp::PartialEq; |
| 32 | use std::convert::From; |
Janis Danisevskis | 935e6c6 | 2020-08-18 12:52:27 -0700 | [diff] [blame] | 33 | use std::ffi::CStr; |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 34 | |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 35 | // Replace getcon with a mock in the test situation |
| 36 | #[cfg(not(test))] |
| 37 | use selinux::getcon; |
| 38 | #[cfg(test)] |
| 39 | use tests::test_getcon as getcon; |
| 40 | |
David Drysdale | 2566fb3 | 2024-07-09 14:46:37 +0100 | [diff] [blame] | 41 | #[cfg(test)] |
| 42 | mod tests; |
| 43 | |
Janis Danisevskis | 4ad056f | 2020-08-05 19:46:46 +0000 | [diff] [blame] | 44 | lazy_static! { |
| 45 | // Panicking here is allowed because keystore cannot function without this backend |
| 46 | // and it would happen early and indicate a gross misconfiguration of the device. |
| 47 | static ref KEYSTORE2_KEY_LABEL_BACKEND: selinux::KeystoreKeyBackend = |
| 48 | selinux::KeystoreKeyBackend::new().unwrap(); |
| 49 | } |
| 50 | |
| 51 | fn lookup_keystore2_key_context(namespace: i64) -> anyhow::Result<selinux::Context> { |
| 52 | KEYSTORE2_KEY_LABEL_BACKEND.lookup(&namespace.to_string()) |
| 53 | } |
| 54 | |
Janis Danisevskis | 39d57e7 | 2021-10-19 16:56:20 -0700 | [diff] [blame] | 55 | implement_class!( |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 56 | /// KeyPerm provides a convenient abstraction from the SELinux class `keystore2_key`. |
| 57 | /// At the same time it maps `KeyPermissions` from the Keystore 2.0 AIDL Grant interface to |
Janis Danisevskis | 39d57e7 | 2021-10-19 16:56:20 -0700 | [diff] [blame] | 58 | /// the SELinux permissions. |
| 59 | #[repr(i32)] |
| 60 | #[selinux(class_name = keystore2_key)] |
Chris Wailes | 263de9f | 2022-08-11 15:00:51 -0700 | [diff] [blame] | 61 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
Janis Danisevskis | 39d57e7 | 2021-10-19 16:56:20 -0700 | [diff] [blame] | 62 | pub enum KeyPerm { |
| 63 | /// Checked when convert_storage_key_to_ephemeral is called. |
| 64 | #[selinux(name = convert_storage_key_to_ephemeral)] |
| 65 | ConvertStorageKeyToEphemeral = KeyPermission::CONVERT_STORAGE_KEY_TO_EPHEMERAL.0, |
| 66 | /// Checked when the caller tries do delete a key. |
| 67 | #[selinux(name = delete)] |
| 68 | Delete = KeyPermission::DELETE.0, |
| 69 | /// Checked when the caller tries to use a unique id. |
| 70 | #[selinux(name = gen_unique_id)] |
| 71 | GenUniqueId = KeyPermission::GEN_UNIQUE_ID.0, |
| 72 | /// Checked when the caller tries to load a key. |
| 73 | #[selinux(name = get_info)] |
| 74 | GetInfo = KeyPermission::GET_INFO.0, |
| 75 | /// Checked when the caller attempts to grant a key to another uid. |
| 76 | /// Also used for gating key migration attempts. |
| 77 | #[selinux(name = grant)] |
| 78 | Grant = KeyPermission::GRANT.0, |
| 79 | /// Checked when the caller attempts to use Domain::BLOB. |
| 80 | #[selinux(name = manage_blob)] |
| 81 | ManageBlob = KeyPermission::MANAGE_BLOB.0, |
| 82 | /// Checked when the caller tries to create a key which implies rebinding |
| 83 | /// an alias to the new key. |
| 84 | #[selinux(name = rebind)] |
| 85 | Rebind = KeyPermission::REBIND.0, |
| 86 | /// Checked when the caller attempts to create a forced operation. |
| 87 | #[selinux(name = req_forced_op)] |
| 88 | ReqForcedOp = KeyPermission::REQ_FORCED_OP.0, |
| 89 | /// Checked when the caller attempts to update public key artifacts. |
| 90 | #[selinux(name = update)] |
| 91 | Update = KeyPermission::UPDATE.0, |
| 92 | /// Checked when the caller attempts to use a private or public key. |
| 93 | #[selinux(name = use)] |
| 94 | Use = KeyPermission::USE.0, |
Eran Messeri | 653932e | 2022-06-14 17:04:10 +0100 | [diff] [blame] | 95 | /// Does nothing, and is not checked. For use of device identifiers, |
| 96 | /// the caller must hold the READ_PRIVILEGED_PHONE_STATE Android |
| 97 | /// permission. |
Janis Danisevskis | 39d57e7 | 2021-10-19 16:56:20 -0700 | [diff] [blame] | 98 | #[selinux(name = use_dev_id)] |
| 99 | UseDevId = KeyPermission::USE_DEV_ID.0, |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 100 | } |
| 101 | ); |
| 102 | |
Janis Danisevskis | 56af031 | 2021-10-18 16:11:41 -0700 | [diff] [blame] | 103 | implement_class!( |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 104 | /// KeystorePerm provides a convenient abstraction from the SELinux class `keystore2`. |
| 105 | /// Using the implement_permission macro we get the same features as `KeyPerm`. |
Janis Danisevskis | 56af031 | 2021-10-18 16:11:41 -0700 | [diff] [blame] | 106 | #[selinux(class_name = keystore2)] |
Chris Wailes | 263de9f | 2022-08-11 15:00:51 -0700 | [diff] [blame] | 107 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
Janis Danisevskis | 751d2c8 | 2021-10-18 15:37:09 -0700 | [diff] [blame] | 108 | pub enum KeystorePerm { |
Janis Danisevskis | 1b3a6e2 | 2020-08-07 12:39:56 -0700 | [diff] [blame] | 109 | /// Checked when a new auth token is installed. |
Janis Danisevskis | 751d2c8 | 2021-10-18 15:37:09 -0700 | [diff] [blame] | 110 | #[selinux(name = add_auth)] |
| 111 | AddAuth, |
Janis Danisevskis | 1b3a6e2 | 2020-08-07 12:39:56 -0700 | [diff] [blame] | 112 | /// Checked when an app is uninstalled or wiped. |
Janis Danisevskis | 751d2c8 | 2021-10-18 15:37:09 -0700 | [diff] [blame] | 113 | #[selinux(name = clear_ns)] |
| 114 | ClearNs, |
Janis Danisevskis | ee10b5f | 2020-09-22 16:42:35 -0700 | [diff] [blame] | 115 | /// Checked when Keystore 2.0 is asked to list a namespace that the caller |
| 116 | /// does not have the get_info permission for. |
Janis Danisevskis | 751d2c8 | 2021-10-18 15:37:09 -0700 | [diff] [blame] | 117 | #[selinux(name = list)] |
| 118 | List, |
Janis Danisevskis | 1b3a6e2 | 2020-08-07 12:39:56 -0700 | [diff] [blame] | 119 | /// Checked when Keystore 2.0 gets locked. |
Janis Danisevskis | 751d2c8 | 2021-10-18 15:37:09 -0700 | [diff] [blame] | 120 | #[selinux(name = lock)] |
| 121 | Lock, |
Janis Danisevskis | 1b3a6e2 | 2020-08-07 12:39:56 -0700 | [diff] [blame] | 122 | /// Checked when Keystore 2.0 shall be reset. |
Janis Danisevskis | 751d2c8 | 2021-10-18 15:37:09 -0700 | [diff] [blame] | 123 | #[selinux(name = reset)] |
| 124 | Reset, |
Janis Danisevskis | 1b3a6e2 | 2020-08-07 12:39:56 -0700 | [diff] [blame] | 125 | /// Checked when Keystore 2.0 shall be unlocked. |
Janis Danisevskis | 751d2c8 | 2021-10-18 15:37:09 -0700 | [diff] [blame] | 126 | #[selinux(name = unlock)] |
| 127 | Unlock, |
Hasini Gunasinghe | 803c2d4 | 2021-01-27 00:48:40 +0000 | [diff] [blame] | 128 | /// Checked when user is added or removed. |
Janis Danisevskis | 751d2c8 | 2021-10-18 15:37:09 -0700 | [diff] [blame] | 129 | #[selinux(name = change_user)] |
| 130 | ChangeUser, |
Hasini Gunasinghe | 803c2d4 | 2021-01-27 00:48:40 +0000 | [diff] [blame] | 131 | /// Checked when password of the user is changed. |
Janis Danisevskis | 751d2c8 | 2021-10-18 15:37:09 -0700 | [diff] [blame] | 132 | #[selinux(name = change_password)] |
| 133 | ChangePassword, |
Hasini Gunasinghe | 803c2d4 | 2021-01-27 00:48:40 +0000 | [diff] [blame] | 134 | /// Checked when a UID is cleared. |
Janis Danisevskis | 751d2c8 | 2021-10-18 15:37:09 -0700 | [diff] [blame] | 135 | #[selinux(name = clear_uid)] |
| 136 | ClearUID, |
Hasini Gunasinghe | 5fc9525 | 2020-12-04 00:35:08 +0000 | [diff] [blame] | 137 | /// Checked when Credstore calls IKeystoreAuthorization to obtain auth tokens. |
Janis Danisevskis | 751d2c8 | 2021-10-18 15:37:09 -0700 | [diff] [blame] | 138 | #[selinux(name = get_auth_token)] |
| 139 | GetAuthToken, |
Satya Tangirala | 5b9e5b1 | 2021-03-09 12:54:21 -0800 | [diff] [blame] | 140 | /// Checked when earlyBootEnded() is called. |
Janis Danisevskis | 751d2c8 | 2021-10-18 15:37:09 -0700 | [diff] [blame] | 141 | #[selinux(name = early_boot_ended)] |
| 142 | EarlyBootEnded, |
Eric Biggers | b5613da | 2024-03-13 19:31:42 +0000 | [diff] [blame] | 143 | /// Checked when IKeystoreMetrics::pullMetrics is called. |
Janis Danisevskis | 751d2c8 | 2021-10-18 15:37:09 -0700 | [diff] [blame] | 144 | #[selinux(name = pull_metrics)] |
| 145 | PullMetrics, |
Paul Crowley | 46c703e | 2021-08-06 15:13:53 -0700 | [diff] [blame] | 146 | /// Checked when IKeystoreMaintenance::deleteAllKeys is called. |
Janis Danisevskis | 751d2c8 | 2021-10-18 15:37:09 -0700 | [diff] [blame] | 147 | #[selinux(name = delete_all_keys)] |
| 148 | DeleteAllKeys, |
Seth Moore | 7ee79f9 | 2021-12-07 11:42:49 -0800 | [diff] [blame] | 149 | /// Checked on calls to IRemotelyProvisionedKeyPool::getAttestationKey |
| 150 | #[selinux(name = get_attestation_key)] |
| 151 | GetAttestationKey, |
James Willcox | d215da8 | 2023-10-03 21:31:31 +0000 | [diff] [blame] | 152 | /// Checked on IKeystoreAuthorization::getLastAuthTime() is called. |
| 153 | #[selinux(name = get_last_auth_time)] |
| 154 | GetLastAuthTime, |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 155 | } |
| 156 | ); |
| 157 | |
| 158 | /// Represents a set of `KeyPerm` permissions. |
| 159 | /// `IntoIterator` is implemented for this struct allowing the iteration through all the |
| 160 | /// permissions in the set. |
| 161 | /// It also implements a function `includes(self, other)` that checks if the permissions |
| 162 | /// in `other` are included in `self`. |
| 163 | /// |
| 164 | /// KeyPermSet can be created with the macro `key_perm_set![]`. |
| 165 | /// |
| 166 | /// ## Example |
| 167 | /// ``` |
Janis Danisevskis | 39d57e7 | 2021-10-19 16:56:20 -0700 | [diff] [blame] | 168 | /// let perms1 = key_perm_set![KeyPerm::Use, KeyPerm::ManageBlob, KeyPerm::Grant]; |
| 169 | /// let perms2 = key_perm_set![KeyPerm::Use, KeyPerm::ManageBlob]; |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 170 | /// |
| 171 | /// assert!(perms1.includes(perms2)) |
| 172 | /// assert!(!perms2.includes(perms1)) |
| 173 | /// |
| 174 | /// let i = perms1.into_iter(); |
| 175 | /// // iteration in ascending order of the permission's numeric representation. |
Janis Danisevskis | 39d57e7 | 2021-10-19 16:56:20 -0700 | [diff] [blame] | 176 | /// assert_eq(Some(KeyPerm::ManageBlob), i.next()); |
| 177 | /// assert_eq(Some(KeyPerm::Grant), i.next()); |
| 178 | /// assert_eq(Some(KeyPerm::Use), i.next()); |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 179 | /// assert_eq(None, i.next()); |
| 180 | /// ``` |
Janis Danisevskis | 1b3a6e2 | 2020-08-07 12:39:56 -0700 | [diff] [blame] | 181 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] |
| 182 | pub struct KeyPermSet(pub i32); |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 183 | |
| 184 | mod perm { |
| 185 | use super::*; |
| 186 | |
| 187 | pub struct IntoIter { |
| 188 | vec: KeyPermSet, |
| 189 | pos: u8, |
| 190 | } |
| 191 | |
| 192 | impl IntoIter { |
| 193 | pub fn new(v: KeyPermSet) -> Self { |
| 194 | Self { vec: v, pos: 0 } |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | impl std::iter::Iterator for IntoIter { |
| 199 | type Item = KeyPerm; |
| 200 | |
| 201 | fn next(&mut self) -> Option<Self::Item> { |
| 202 | loop { |
| 203 | if self.pos == 32 { |
| 204 | return None; |
| 205 | } |
| 206 | let p = self.vec.0 & (1 << self.pos); |
| 207 | self.pos += 1; |
| 208 | if p != 0 { |
Janis Danisevskis | 39d57e7 | 2021-10-19 16:56:20 -0700 | [diff] [blame] | 209 | return Some(KeyPerm::from(p)); |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 210 | } |
| 211 | } |
| 212 | } |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | impl From<KeyPerm> for KeyPermSet { |
| 217 | fn from(p: KeyPerm) -> Self { |
Janis Danisevskis | 39d57e7 | 2021-10-19 16:56:20 -0700 | [diff] [blame] | 218 | Self(p as i32) |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 219 | } |
| 220 | } |
| 221 | |
Janis Danisevskis | 1b3a6e2 | 2020-08-07 12:39:56 -0700 | [diff] [blame] | 222 | /// allow conversion from the AIDL wire type i32 to a permission set. |
| 223 | impl From<i32> for KeyPermSet { |
| 224 | fn from(p: i32) -> Self { |
| 225 | Self(p) |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | impl From<KeyPermSet> for i32 { |
| 230 | fn from(p: KeyPermSet) -> i32 { |
| 231 | p.0 |
| 232 | } |
| 233 | } |
| 234 | |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 235 | impl KeyPermSet { |
| 236 | /// Returns true iff this permission set has all of the permissions that are in `other`. |
Janis Danisevskis | 1b3a6e2 | 2020-08-07 12:39:56 -0700 | [diff] [blame] | 237 | pub fn includes<T: Into<KeyPermSet>>(&self, other: T) -> bool { |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 238 | let o: KeyPermSet = other.into(); |
| 239 | (self.0 & o.0) == o.0 |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | /// This macro can be used to create a `KeyPermSet` from a list of `KeyPerm` values. |
| 244 | /// |
| 245 | /// ## Example |
| 246 | /// ``` |
| 247 | /// let v = key_perm_set![Perm::delete(), Perm::manage_blob()]; |
| 248 | /// ``` |
| 249 | #[macro_export] |
| 250 | macro_rules! key_perm_set { |
| 251 | () => { KeyPermSet(0) }; |
| 252 | ($head:expr $(, $tail:expr)* $(,)?) => { |
Janis Danisevskis | 39d57e7 | 2021-10-19 16:56:20 -0700 | [diff] [blame] | 253 | KeyPermSet($head as i32 $(| $tail as i32)*) |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 254 | }; |
| 255 | } |
| 256 | |
| 257 | impl IntoIterator for KeyPermSet { |
| 258 | type Item = KeyPerm; |
| 259 | type IntoIter = perm::IntoIter; |
| 260 | |
| 261 | fn into_iter(self) -> Self::IntoIter { |
| 262 | Self::IntoIter::new(self) |
| 263 | } |
| 264 | } |
| 265 | |
Janis Danisevskis | 56af031 | 2021-10-18 16:11:41 -0700 | [diff] [blame] | 266 | /// Uses `selinux::check_permission` to check if the given caller context `caller_cxt` may access |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 267 | /// the given permision `perm` of the `keystore2` security class. |
Janis Danisevskis | 935e6c6 | 2020-08-18 12:52:27 -0700 | [diff] [blame] | 268 | pub fn check_keystore_permission(caller_ctx: &CStr, perm: KeystorePerm) -> anyhow::Result<()> { |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 269 | let target_context = getcon().context("check_keystore_permission: getcon failed.")?; |
Janis Danisevskis | 56af031 | 2021-10-18 16:11:41 -0700 | [diff] [blame] | 270 | selinux::check_permission(caller_ctx, &target_context, perm) |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 271 | } |
| 272 | |
Janis Danisevskis | 56af031 | 2021-10-18 16:11:41 -0700 | [diff] [blame] | 273 | /// Uses `selinux::check_permission` to check if the given caller context `caller_cxt` has |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 274 | /// all the permissions indicated in `access_vec` for the target domain indicated by the key |
| 275 | /// descriptor `key` in the security class `keystore2_key`. |
| 276 | /// |
| 277 | /// Also checks if the caller has the grant permission for the given target domain. |
| 278 | /// |
| 279 | /// Attempts to grant the grant permission are always denied. |
| 280 | /// |
| 281 | /// The only viable target domains are |
Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 282 | /// * `Domain::APP` in which case u:r:keystore:s0 is used as target context and |
| 283 | /// * `Domain::SELINUX` in which case the `key.nspace` parameter is looked up in |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 284 | /// SELinux keystore key backend, and the result is used |
| 285 | /// as target context. |
| 286 | pub fn check_grant_permission( |
Janis Danisevskis | 935e6c6 | 2020-08-18 12:52:27 -0700 | [diff] [blame] | 287 | caller_ctx: &CStr, |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 288 | access_vec: KeyPermSet, |
Janis Danisevskis | 1b3a6e2 | 2020-08-07 12:39:56 -0700 | [diff] [blame] | 289 | key: &KeyDescriptor, |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 290 | ) -> anyhow::Result<()> { |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 291 | let target_context = match key.domain { |
Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 292 | Domain::APP => getcon().context("check_grant_permission: getcon failed.")?, |
| 293 | Domain::SELINUX => lookup_keystore2_key_context(key.nspace) |
| 294 | .context("check_grant_permission: Domain::SELINUX: Failed to lookup namespace.")?, |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 295 | _ => return Err(KsError::sys()).context(format!("Cannot grant {:?}.", key.domain)), |
| 296 | }; |
| 297 | |
Janis Danisevskis | 39d57e7 | 2021-10-19 16:56:20 -0700 | [diff] [blame] | 298 | selinux::check_permission(caller_ctx, &target_context, KeyPerm::Grant) |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 299 | .context("Grant permission is required when granting.")?; |
| 300 | |
Janis Danisevskis | 39d57e7 | 2021-10-19 16:56:20 -0700 | [diff] [blame] | 301 | if access_vec.includes(KeyPerm::Grant) { |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 302 | return Err(selinux::Error::perm()).context("Grant permission cannot be granted."); |
| 303 | } |
| 304 | |
| 305 | for p in access_vec.into_iter() { |
Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 306 | selinux::check_permission(caller_ctx, &target_context, p).context(ks_err!( |
| 307 | "check_permission failed. \ |
Janis Danisevskis | 56af031 | 2021-10-18 16:11:41 -0700 | [diff] [blame] | 308 | The caller may have tried to grant a permission that they don't possess. {:?}", |
| 309 | p |
| 310 | ))? |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 311 | } |
| 312 | Ok(()) |
| 313 | } |
| 314 | |
Janis Danisevskis | 56af031 | 2021-10-18 16:11:41 -0700 | [diff] [blame] | 315 | /// Uses `selinux::check_permission` to check if the given caller context `caller_cxt` |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 316 | /// has the permissions indicated by `perm` for the target domain indicated by the key |
| 317 | /// descriptor `key` in the security class `keystore2_key`. |
| 318 | /// |
| 319 | /// The behavior differs slightly depending on the selected target domain: |
Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 320 | /// * `Domain::APP` u:r:keystore:s0 is used as target context. |
| 321 | /// * `Domain::SELINUX` `key.nspace` parameter is looked up in the SELinux keystore key |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 322 | /// backend, and the result is used as target context. |
Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 323 | /// * `Domain::BLOB` Same as SELinux but the "manage_blob" permission is always checked additionally |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 324 | /// to the one supplied in `perm`. |
Janis Danisevskis | 56af031 | 2021-10-18 16:11:41 -0700 | [diff] [blame] | 325 | /// * `Domain::GRANT` Does not use selinux::check_permission. Instead the `access_vector` |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 326 | /// parameter is queried for permission, which must be supplied in this case. |
| 327 | /// |
| 328 | /// ## Return values. |
| 329 | /// * Ok(()) If the requested permissions were granted. |
| 330 | /// * Err(selinux::Error::perm()) If the requested permissions were denied. |
Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 331 | /// * Err(KsError::sys()) This error is produced if `Domain::GRANT` is selected but no `access_vec` |
| 332 | /// was supplied. It is also produced if `Domain::KEY_ID` was selected, and |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 333 | /// on various unexpected backend failures. |
| 334 | pub fn check_key_permission( |
Janis Danisevskis | 4576002 | 2021-01-19 16:34:10 -0800 | [diff] [blame] | 335 | caller_uid: u32, |
Janis Danisevskis | 935e6c6 | 2020-08-18 12:52:27 -0700 | [diff] [blame] | 336 | caller_ctx: &CStr, |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 337 | perm: KeyPerm, |
Janis Danisevskis | 1b3a6e2 | 2020-08-07 12:39:56 -0700 | [diff] [blame] | 338 | key: &KeyDescriptor, |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 339 | access_vector: &Option<KeyPermSet>, |
| 340 | ) -> anyhow::Result<()> { |
Janis Danisevskis | 4576002 | 2021-01-19 16:34:10 -0800 | [diff] [blame] | 341 | // If an access vector was supplied, the key is either accessed by GRANT or by KEY_ID. |
| 342 | // In the former case, key.domain was set to GRANT and we check the failure cases |
| 343 | // further below. If the access is requested by KEY_ID, key.domain would have been |
| 344 | // resolved to APP or SELINUX depending on where the key actually resides. |
| 345 | // Either way we can return here immediately if the access vector covers the requested |
| 346 | // permission. If it does not, we can still check if the caller has access by means of |
| 347 | // ownership. |
| 348 | if let Some(access_vector) = access_vector { |
| 349 | if access_vector.includes(perm) { |
| 350 | return Ok(()); |
| 351 | } |
| 352 | } |
| 353 | |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 354 | let target_context = match key.domain { |
| 355 | // apps get the default keystore context |
Janis Danisevskis | 4576002 | 2021-01-19 16:34:10 -0800 | [diff] [blame] | 356 | Domain::APP => { |
| 357 | if caller_uid as i64 != key.nspace { |
| 358 | return Err(selinux::Error::perm()) |
| 359 | .context("Trying to access key without ownership."); |
| 360 | } |
Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 361 | getcon().context(ks_err!("getcon failed."))? |
Janis Danisevskis | 4576002 | 2021-01-19 16:34:10 -0800 | [diff] [blame] | 362 | } |
Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 363 | Domain::SELINUX => lookup_keystore2_key_context(key.nspace) |
Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 364 | .context(ks_err!("Domain::SELINUX: Failed to lookup namespace."))?, |
Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 365 | Domain::GRANT => { |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 366 | match access_vector { |
Janis Danisevskis | 4576002 | 2021-01-19 16:34:10 -0800 | [diff] [blame] | 367 | Some(_) => { |
| 368 | return Err(selinux::Error::perm()) |
Janis Danisevskis | 56af031 | 2021-10-18 16:11:41 -0700 | [diff] [blame] | 369 | .context(format!("\"{}\" not granted", perm.name())); |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 370 | } |
| 371 | None => { |
| 372 | // If DOMAIN_GRANT was selected an access vector must be supplied. |
Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 373 | return Err(KsError::sys()).context(ks_err!( |
Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 374 | "Cannot check permission for Domain::GRANT without access vector.", |
Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 375 | )); |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 376 | } |
| 377 | } |
| 378 | } |
Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 379 | Domain::KEY_ID => { |
| 380 | // We should never be called with `Domain::KEY_ID. The database |
| 381 | // lookup should have converted this into one of `Domain::APP` |
| 382 | // or `Domain::SELINUX`. |
Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 383 | return Err(KsError::sys()) |
| 384 | .context(ks_err!("Cannot check permission for Domain::KEY_ID.",)); |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 385 | } |
Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 386 | Domain::BLOB => { |
| 387 | let tctx = lookup_keystore2_key_context(key.nspace) |
Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 388 | .context(ks_err!("Domain::BLOB: Failed to lookup namespace."))?; |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 389 | // If DOMAIN_KEY_BLOB was specified, we check for the "manage_blob" |
| 390 | // permission in addition to the requested permission. |
Janis Danisevskis | 39d57e7 | 2021-10-19 16:56:20 -0700 | [diff] [blame] | 391 | selinux::check_permission(caller_ctx, &tctx, KeyPerm::ManageBlob)?; |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 392 | |
| 393 | tctx |
| 394 | } |
Janis Danisevskis | 1b3a6e2 | 2020-08-07 12:39:56 -0700 | [diff] [blame] | 395 | _ => { |
Rajesh Nyamagoud | caee93e | 2022-05-26 00:20:38 +0000 | [diff] [blame] | 396 | return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT)) |
Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 397 | .context(format!("Unknown domain value: \"{:?}\".", key.domain)) |
Janis Danisevskis | 1b3a6e2 | 2020-08-07 12:39:56 -0700 | [diff] [blame] | 398 | } |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 399 | }; |
| 400 | |
Janis Danisevskis | 56af031 | 2021-10-18 16:11:41 -0700 | [diff] [blame] | 401 | selinux::check_permission(caller_ctx, &target_context, perm) |
Janis Danisevskis | 78bd48c | 2020-07-21 12:27:13 -0700 | [diff] [blame] | 402 | } |