IMA: introduce a new policy option func=SETXATTR_CHECK

While users can restrict the accepted hash algorithms for the
security.ima xattr file signature when appraising said file, users
cannot restrict the algorithms that can be set on that attribute:
any algorithm built in the kernel is accepted on a write.

Define a new value for the ima policy option 'func' that restricts
globally the hash algorithms accepted when writing the security.ima
xattr.

When a policy contains a rule of the form
	appraise func=SETXATTR_CHECK appraise_algos=sha256,sha384,sha512
only values corresponding to one of these three digest algorithms
will be accepted for writing the security.ima xattr.  Attempting to
write the attribute using another algorithm (or "free-form" data)
will be denied with an audit log message.  In the absence of such a
policy rule, the default is still to only accept hash algorithms
built in the kernel (with all the limitations that entails).

Signed-off-by: THOBY Simon <Simon.THOBY@viveris.fr>
Reviewed-by: Lakshmi Ramasubramanian <nramas@linux.microsoft.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
This commit is contained in:
THOBY Simon 2021-08-16 08:11:01 +00:00 committed by Mimi Zohar
parent 583a80ae86
commit 4f2946aa0c
5 changed files with 104 additions and 18 deletions

View File

@ -30,9 +30,10 @@ Description:
[appraise_flag=] [appraise_algos=] [keyrings=]
base:
func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
[FIRMWARE_CHECK]
[FIRMWARE_CHECK]
[KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
[KEXEC_CMDLINE] [KEY_CHECK] [CRITICAL_DATA]
[SETXATTR_CHECK]
mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
[[^]MAY_EXEC]
fsmagic:= hex value
@ -138,3 +139,9 @@ Description:
keys added to .builtin_trusted_keys or .ima keyring:
measure func=KEY_CHECK keyrings=.builtin_trusted_keys|.ima
Example of the special SETXATTR_CHECK appraise rule, that
restricts the hash algorithms allowed when writing to the
security.ima xattr of a file:
appraise func=SETXATTR_CHECK appraise_algos=sha256,sha384,sha512

View File

@ -46,6 +46,9 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 };
/* current content of the policy */
extern int ima_policy_flag;
/* bitset of digests algorithms allowed in the setxattr hook */
extern atomic_t ima_setxattr_allowed_hash_algorithms;
/* set during initialization */
extern int ima_hash_algo __ro_after_init;
extern int ima_sha1_idx __ro_after_init;
@ -198,6 +201,7 @@ static inline unsigned int ima_hash_key(u8 *digest)
hook(KEXEC_CMDLINE, kexec_cmdline) \
hook(KEY_CHECK, key) \
hook(CRITICAL_DATA, critical_data) \
hook(SETXATTR_CHECK, setxattr_check) \
hook(MAX_CHECK, none)
#define __ima_hook_enumify(ENUM, str) ENUM,
@ -288,7 +292,7 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
const char *func_data, unsigned int *allowed_algos);
void ima_init_policy(void);
void ima_update_policy(void);
void ima_update_policy_flag(void);
void ima_update_policy_flags(void);
ssize_t ima_parse_add_rule(char *);
void ima_delete_rules(void);
int ima_check_policy(void);

View File

@ -595,12 +595,32 @@ static int validate_hash_algo(struct dentry *dentry,
{
char *path = NULL, *pathbuf = NULL;
enum hash_algo xattr_hash_algo;
const char *errmsg = "unavailable-hash-algorithm";
unsigned int allowed_hashes;
xattr_hash_algo = ima_get_hash_algo(xattr_value, xattr_value_len);
if (likely(xattr_hash_algo == ima_hash_algo ||
crypto_has_alg(hash_algo_name[xattr_hash_algo], 0, 0)))
return 0;
allowed_hashes = atomic_read(&ima_setxattr_allowed_hash_algorithms);
if (allowed_hashes) {
/* success if the algorithm is allowed in the ima policy */
if (allowed_hashes & (1U << xattr_hash_algo))
return 0;
/*
* We use a different audit message when the hash algorithm
* is denied by a policy rule, instead of not being built
* in the kernel image
*/
errmsg = "denied-hash-algorithm";
} else {
if (likely(xattr_hash_algo == ima_hash_algo))
return 0;
/* allow any xattr using an algorithm built in the kernel */
if (crypto_has_alg(hash_algo_name[xattr_hash_algo], 0, 0))
return 0;
}
pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
if (!pathbuf)
@ -609,8 +629,7 @@ static int validate_hash_algo(struct dentry *dentry,
path = dentry_path(dentry, pathbuf, PATH_MAX);
integrity_audit_msg(AUDIT_INTEGRITY_DATA, d_inode(dentry), path,
"set_data", "unavailable-hash-algorithm",
-EACCES, 0);
"set_data", errmsg, -EACCES, 0);
kfree(pathbuf);

View File

@ -1051,7 +1051,7 @@ static int __init init_ima(void)
pr_warn("Couldn't register LSM notifier, error %d\n", error);
if (!error)
ima_update_policy_flag();
ima_update_policy_flags();
return error;
}

View File

@ -53,6 +53,8 @@ int ima_policy_flag;
static int temp_ima_appraise;
static int build_ima_appraise __ro_after_init;
atomic_t ima_setxattr_allowed_hash_algorithms;
#define MAX_LSM_RULES 6
enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
@ -720,24 +722,57 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
return action;
}
/*
* Initialize the ima_policy_flag variable based on the currently
* loaded policy. Based on this flag, the decision to short circuit
* out of a function or not call the function in the first place
* can be made earlier.
/**
* ima_update_policy_flags() - Update global IMA variables
*
* Update ima_policy_flag and ima_setxattr_allowed_hash_algorithms
* based on the currently loaded policy.
*
* With ima_policy_flag, the decision to short circuit out of a function
* or not call the function in the first place can be made earlier.
*
* With ima_setxattr_allowed_hash_algorithms, the policy can restrict the
* set of hash algorithms accepted when updating the security.ima xattr of
* a file.
*
* Context: called after a policy update and at system initialization.
*/
void ima_update_policy_flag(void)
void ima_update_policy_flags(void)
{
struct ima_rule_entry *entry;
int new_policy_flag = 0;
rcu_read_lock();
list_for_each_entry(entry, ima_rules, list) {
/*
* SETXATTR_CHECK rules do not implement a full policy check
* because rule checking would probably have an important
* performance impact on setxattr(). As a consequence, only one
* SETXATTR_CHECK can be active at a given time.
* Because we want to preserve that property, we set out to use
* atomic_cmpxchg. Either:
* - the atomic was non-zero: a setxattr hash policy is
* already enforced, we do nothing
* - the atomic was zero: no setxattr policy was set, enable
* the setxattr hash policy
*/
if (entry->func == SETXATTR_CHECK) {
atomic_cmpxchg(&ima_setxattr_allowed_hash_algorithms,
0, entry->allowed_algos);
/* SETXATTR_CHECK doesn't impact ima_policy_flag */
continue;
}
if (entry->action & IMA_DO_MASK)
ima_policy_flag |= entry->action;
new_policy_flag |= entry->action;
}
rcu_read_unlock();
ima_appraise |= (build_ima_appraise | temp_ima_appraise);
if (!ima_appraise)
ima_policy_flag &= ~IMA_APPRAISE;
new_policy_flag &= ~IMA_APPRAISE;
ima_policy_flag = new_policy_flag;
}
static int ima_appraise_flag(enum ima_hooks func)
@ -903,7 +938,9 @@ void __init ima_init_policy(void)
ARRAY_SIZE(critical_data_rules),
IMA_DEFAULT_POLICY);
ima_update_policy_flag();
atomic_set(&ima_setxattr_allowed_hash_algorithms, 0);
ima_update_policy_flags();
}
/* Make sure we have a valid policy, at least containing some rules. */
@ -943,7 +980,7 @@ void ima_update_policy(void)
*/
kfree(arch_policy_entry);
}
ima_update_policy_flag();
ima_update_policy_flags();
/* Custom IMA policy has been loaded */
ima_process_queued_keys();
@ -1176,6 +1213,23 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (ima_rule_contains_lsm_cond(entry))
return false;
break;
case SETXATTR_CHECK:
/* any action other than APPRAISE is unsupported */
if (entry->action != APPRAISE)
return false;
/* SETXATTR_CHECK requires an appraise_algos parameter */
if (!(entry->flags & IMA_VALIDATE_ALGOS))
return false;
/*
* full policies are not supported, they would have too
* much of a performance impact
*/
if (entry->flags & ~(IMA_FUNC | IMA_VALIDATE_ALGOS))
return false;
break;
default:
return false;
@ -1332,6 +1386,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->func = KEY_CHECK;
else if (strcmp(args[0].from, "CRITICAL_DATA") == 0)
entry->func = CRITICAL_DATA;
else if (strcmp(args[0].from, "SETXATTR_CHECK") == 0)
entry->func = SETXATTR_CHECK;
else
result = -EINVAL;
if (!result)