162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Key permission checking 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/export.h> 962306a36Sopenharmony_ci#include <linux/security.h> 1062306a36Sopenharmony_ci#include "internal.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/** 1362306a36Sopenharmony_ci * key_task_permission - Check a key can be used 1462306a36Sopenharmony_ci * @key_ref: The key to check. 1562306a36Sopenharmony_ci * @cred: The credentials to use. 1662306a36Sopenharmony_ci * @need_perm: The permission required. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * Check to see whether permission is granted to use a key in the desired way, 1962306a36Sopenharmony_ci * but permit the security modules to override. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * The caller must hold either a ref on cred or must hold the RCU readlock. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Returns 0 if successful, -EACCES if access is denied based on the 2462306a36Sopenharmony_ci * permissions bits or the LSM check. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ciint key_task_permission(const key_ref_t key_ref, const struct cred *cred, 2762306a36Sopenharmony_ci enum key_need_perm need_perm) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct key *key; 3062306a36Sopenharmony_ci key_perm_t kperm, mask; 3162306a36Sopenharmony_ci int ret; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci switch (need_perm) { 3462306a36Sopenharmony_ci default: 3562306a36Sopenharmony_ci WARN_ON(1); 3662306a36Sopenharmony_ci return -EACCES; 3762306a36Sopenharmony_ci case KEY_NEED_UNLINK: 3862306a36Sopenharmony_ci case KEY_SYSADMIN_OVERRIDE: 3962306a36Sopenharmony_ci case KEY_AUTHTOKEN_OVERRIDE: 4062306a36Sopenharmony_ci case KEY_DEFER_PERM_CHECK: 4162306a36Sopenharmony_ci goto lsm; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci case KEY_NEED_VIEW: mask = KEY_OTH_VIEW; break; 4462306a36Sopenharmony_ci case KEY_NEED_READ: mask = KEY_OTH_READ; break; 4562306a36Sopenharmony_ci case KEY_NEED_WRITE: mask = KEY_OTH_WRITE; break; 4662306a36Sopenharmony_ci case KEY_NEED_SEARCH: mask = KEY_OTH_SEARCH; break; 4762306a36Sopenharmony_ci case KEY_NEED_LINK: mask = KEY_OTH_LINK; break; 4862306a36Sopenharmony_ci case KEY_NEED_SETATTR: mask = KEY_OTH_SETATTR; break; 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci key = key_ref_to_ptr(key_ref); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* use the second 8-bits of permissions for keys the caller owns */ 5462306a36Sopenharmony_ci if (uid_eq(key->uid, cred->fsuid)) { 5562306a36Sopenharmony_ci kperm = key->perm >> 16; 5662306a36Sopenharmony_ci goto use_these_perms; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* use the third 8-bits of permissions for keys the caller has a group 6062306a36Sopenharmony_ci * membership in common with */ 6162306a36Sopenharmony_ci if (gid_valid(key->gid) && key->perm & KEY_GRP_ALL) { 6262306a36Sopenharmony_ci if (gid_eq(key->gid, cred->fsgid)) { 6362306a36Sopenharmony_ci kperm = key->perm >> 8; 6462306a36Sopenharmony_ci goto use_these_perms; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci ret = groups_search(cred->group_info, key->gid); 6862306a36Sopenharmony_ci if (ret) { 6962306a36Sopenharmony_ci kperm = key->perm >> 8; 7062306a36Sopenharmony_ci goto use_these_perms; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* otherwise use the least-significant 8-bits */ 7562306a36Sopenharmony_ci kperm = key->perm; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciuse_these_perms: 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* use the top 8-bits of permissions for keys the caller possesses 8062306a36Sopenharmony_ci * - possessor permissions are additive with other permissions 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci if (is_key_possessed(key_ref)) 8362306a36Sopenharmony_ci kperm |= key->perm >> 24; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if ((kperm & mask) != mask) 8662306a36Sopenharmony_ci return -EACCES; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* let LSM be the final arbiter */ 8962306a36Sopenharmony_cilsm: 9062306a36Sopenharmony_ci return security_key_permission(key_ref, cred, need_perm); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ciEXPORT_SYMBOL(key_task_permission); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/** 9562306a36Sopenharmony_ci * key_validate - Validate a key. 9662306a36Sopenharmony_ci * @key: The key to be validated. 9762306a36Sopenharmony_ci * 9862306a36Sopenharmony_ci * Check that a key is valid, returning 0 if the key is okay, -ENOKEY if the 9962306a36Sopenharmony_ci * key is invalidated, -EKEYREVOKED if the key's type has been removed or if 10062306a36Sopenharmony_ci * the key has been revoked or -EKEYEXPIRED if the key has expired. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ciint key_validate(const struct key *key) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci unsigned long flags = READ_ONCE(key->flags); 10562306a36Sopenharmony_ci time64_t expiry = READ_ONCE(key->expiry); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (flags & (1 << KEY_FLAG_INVALIDATED)) 10862306a36Sopenharmony_ci return -ENOKEY; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* check it's still accessible */ 11162306a36Sopenharmony_ci if (flags & ((1 << KEY_FLAG_REVOKED) | 11262306a36Sopenharmony_ci (1 << KEY_FLAG_DEAD))) 11362306a36Sopenharmony_ci return -EKEYREVOKED; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* check it hasn't expired */ 11662306a36Sopenharmony_ci if (expiry) { 11762306a36Sopenharmony_ci if (ktime_get_real_seconds() >= expiry) 11862306a36Sopenharmony_ci return -EKEYEXPIRED; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ciEXPORT_SYMBOL(key_validate); 124