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