mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-16 09:13:55 +08:00
integrity-v5.15
-----BEGIN PGP SIGNATURE----- iIoEABYIADIWIQQdXVVFGN5XqKr1Hj7LwZzRsCrn5QUCYS4c6hQcem9oYXJAbGlu dXguaWJtLmNvbQAKCRDLwZzRsCrn5b4OAP9l7cnpkOzVUtjoNIIYdIiKTDp+Kb8v 3o08lxtyzALfKgEAlrizzLfphqLa2yCdxbyaTjkx19J7tav27xVti8uVGgs= =hIxY -----END PGP SIGNATURE----- Merge tag 'integrity-v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity Pull integrity subsystem updates from Mimi Zohar: - Limit the allowed hash algorithms when writing security.ima xattrs or verifying them, based on the IMA policy and the configured hash algorithms. - Return the calculated "critical data" measurement hash and size to avoid code duplication. (Preparatory change for a proposed LSM.) - and a single patch to address a compiler warning. * tag 'integrity-v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity: IMA: reject unknown hash algorithms in ima_get_hash_algo IMA: prevent SETXATTR_CHECK policy rules with unavailable algorithms IMA: introduce a new policy option func=SETXATTR_CHECK IMA: add a policy option to restrict xattr hash algorithms on appraisal IMA: add support to restrict the hash algorithms used for file appraisal IMA: block writes of the security.ima xattr with unsupported algorithms IMA: remove the dependency on CRYPTO_MD5 ima: Add digest and digest_len params to the functions to measure a buffer ima: Return int in the functions to measure a buffer ima: Introduce ima_get_current_hash_algo() IMA: remove -Wmissing-prototypes warning
This commit is contained in:
commit
aef4892a63
@ -27,12 +27,13 @@ Description:
|
|||||||
lsm: [[subj_user=] [subj_role=] [subj_type=]
|
lsm: [[subj_user=] [subj_role=] [subj_type=]
|
||||||
[obj_user=] [obj_role=] [obj_type=]]
|
[obj_user=] [obj_role=] [obj_type=]]
|
||||||
option: [[appraise_type=]] [template=] [permit_directio]
|
option: [[appraise_type=]] [template=] [permit_directio]
|
||||||
[appraise_flag=] [keyrings=]
|
[appraise_flag=] [appraise_algos=] [keyrings=]
|
||||||
base:
|
base:
|
||||||
func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
|
func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
|
||||||
[FIRMWARE_CHECK]
|
[FIRMWARE_CHECK]
|
||||||
[KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
|
[KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
|
||||||
[KEXEC_CMDLINE] [KEY_CHECK] [CRITICAL_DATA]
|
[KEXEC_CMDLINE] [KEY_CHECK] [CRITICAL_DATA]
|
||||||
|
[SETXATTR_CHECK]
|
||||||
mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
|
mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
|
||||||
[[^]MAY_EXEC]
|
[[^]MAY_EXEC]
|
||||||
fsmagic:= hex value
|
fsmagic:= hex value
|
||||||
@ -55,6 +56,10 @@ Description:
|
|||||||
label:= [selinux]|[kernel_info]|[data_label]
|
label:= [selinux]|[kernel_info]|[data_label]
|
||||||
data_label:= a unique string used for grouping and limiting critical data.
|
data_label:= a unique string used for grouping and limiting critical data.
|
||||||
For example, "selinux" to measure critical data for SELinux.
|
For example, "selinux" to measure critical data for SELinux.
|
||||||
|
appraise_algos:= comma-separated list of hash algorithms
|
||||||
|
For example, "sha256,sha512" to only accept to appraise
|
||||||
|
files where the security.ima xattr was hashed with one
|
||||||
|
of these two algorithms.
|
||||||
|
|
||||||
default policy:
|
default policy:
|
||||||
# PROC_SUPER_MAGIC
|
# PROC_SUPER_MAGIC
|
||||||
@ -134,3 +139,9 @@ Description:
|
|||||||
keys added to .builtin_trusted_keys or .ima keyring:
|
keys added to .builtin_trusted_keys or .ima keyring:
|
||||||
|
|
||||||
measure func=KEY_CHECK keyrings=.builtin_trusted_keys|.ima
|
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
|
||||||
|
@ -136,7 +136,8 @@ static void dm_ima_measure_data(const char *event_name, const void *buf, size_t
|
|||||||
if (noio)
|
if (noio)
|
||||||
noio_flag = memalloc_noio_save();
|
noio_flag = memalloc_noio_save();
|
||||||
|
|
||||||
ima_measure_critical_data(DM_NAME, event_name, buf, buf_len, false);
|
ima_measure_critical_data(DM_NAME, event_name, buf, buf_len,
|
||||||
|
false, NULL, 0);
|
||||||
|
|
||||||
if (noio)
|
if (noio)
|
||||||
memalloc_noio_restore(noio_flag);
|
memalloc_noio_restore(noio_flag);
|
||||||
|
@ -11,9 +11,11 @@
|
|||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/security.h>
|
#include <linux/security.h>
|
||||||
#include <linux/kexec.h>
|
#include <linux/kexec.h>
|
||||||
|
#include <crypto/hash_info.h>
|
||||||
struct linux_binprm;
|
struct linux_binprm;
|
||||||
|
|
||||||
#ifdef CONFIG_IMA
|
#ifdef CONFIG_IMA
|
||||||
|
extern enum hash_algo ima_get_current_hash_algo(void);
|
||||||
extern int ima_bprm_check(struct linux_binprm *bprm);
|
extern int ima_bprm_check(struct linux_binprm *bprm);
|
||||||
extern int ima_file_check(struct file *file, int mask);
|
extern int ima_file_check(struct file *file, int mask);
|
||||||
extern void ima_post_create_tmpfile(struct user_namespace *mnt_userns,
|
extern void ima_post_create_tmpfile(struct user_namespace *mnt_userns,
|
||||||
@ -33,10 +35,10 @@ extern void ima_post_path_mknod(struct user_namespace *mnt_userns,
|
|||||||
extern int ima_file_hash(struct file *file, char *buf, size_t buf_size);
|
extern int ima_file_hash(struct file *file, char *buf, size_t buf_size);
|
||||||
extern int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size);
|
extern int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size);
|
||||||
extern void ima_kexec_cmdline(int kernel_fd, const void *buf, int size);
|
extern void ima_kexec_cmdline(int kernel_fd, const void *buf, int size);
|
||||||
extern void ima_measure_critical_data(const char *event_label,
|
extern int ima_measure_critical_data(const char *event_label,
|
||||||
const char *event_name,
|
const char *event_name,
|
||||||
const void *buf, size_t buf_len,
|
const void *buf, size_t buf_len,
|
||||||
bool hash);
|
bool hash, u8 *digest, size_t digest_len);
|
||||||
|
|
||||||
#ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
|
#ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
|
||||||
extern void ima_appraise_parse_cmdline(void);
|
extern void ima_appraise_parse_cmdline(void);
|
||||||
@ -64,6 +66,11 @@ static inline const char * const *arch_get_ima_policy(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
static inline enum hash_algo ima_get_current_hash_algo(void)
|
||||||
|
{
|
||||||
|
return HASH_ALGO__LAST;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int ima_bprm_check(struct linux_binprm *bprm)
|
static inline int ima_bprm_check(struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
@ -137,10 +144,14 @@ static inline int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size
|
|||||||
|
|
||||||
static inline void ima_kexec_cmdline(int kernel_fd, const void *buf, int size) {}
|
static inline void ima_kexec_cmdline(int kernel_fd, const void *buf, int size) {}
|
||||||
|
|
||||||
static inline void ima_measure_critical_data(const char *event_label,
|
static inline int ima_measure_critical_data(const char *event_label,
|
||||||
const char *event_name,
|
const char *event_name,
|
||||||
const void *buf, size_t buf_len,
|
const void *buf, size_t buf_len,
|
||||||
bool hash) {}
|
bool hash, u8 *digest,
|
||||||
|
size_t digest_len)
|
||||||
|
{
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_IMA */
|
#endif /* CONFIG_IMA */
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ config IMA
|
|||||||
select SECURITYFS
|
select SECURITYFS
|
||||||
select CRYPTO
|
select CRYPTO
|
||||||
select CRYPTO_HMAC
|
select CRYPTO_HMAC
|
||||||
select CRYPTO_MD5
|
|
||||||
select CRYPTO_SHA1
|
select CRYPTO_SHA1
|
||||||
select CRYPTO_HASH_INFO
|
select CRYPTO_HASH_INFO
|
||||||
select TCG_TPM if HAS_IOMEM && !UML
|
select TCG_TPM if HAS_IOMEM && !UML
|
||||||
|
@ -46,8 +46,11 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 };
|
|||||||
/* current content of the policy */
|
/* current content of the policy */
|
||||||
extern int ima_policy_flag;
|
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 */
|
/* set during initialization */
|
||||||
extern int ima_hash_algo;
|
extern int ima_hash_algo __ro_after_init;
|
||||||
extern int ima_sha1_idx __ro_after_init;
|
extern int ima_sha1_idx __ro_after_init;
|
||||||
extern int ima_hash_algo_idx __ro_after_init;
|
extern int ima_hash_algo_idx __ro_after_init;
|
||||||
extern int ima_extra_slots __ro_after_init;
|
extern int ima_extra_slots __ro_after_init;
|
||||||
@ -198,6 +201,7 @@ static inline unsigned int ima_hash_key(u8 *digest)
|
|||||||
hook(KEXEC_CMDLINE, kexec_cmdline) \
|
hook(KEXEC_CMDLINE, kexec_cmdline) \
|
||||||
hook(KEY_CHECK, key) \
|
hook(KEY_CHECK, key) \
|
||||||
hook(CRITICAL_DATA, critical_data) \
|
hook(CRITICAL_DATA, critical_data) \
|
||||||
|
hook(SETXATTR_CHECK, setxattr_check) \
|
||||||
hook(MAX_CHECK, none)
|
hook(MAX_CHECK, none)
|
||||||
|
|
||||||
#define __ima_hook_enumify(ENUM, str) ENUM,
|
#define __ima_hook_enumify(ENUM, str) ENUM,
|
||||||
@ -254,7 +258,7 @@ int ima_get_action(struct user_namespace *mnt_userns, struct inode *inode,
|
|||||||
const struct cred *cred, u32 secid, int mask,
|
const struct cred *cred, u32 secid, int mask,
|
||||||
enum ima_hooks func, int *pcr,
|
enum ima_hooks func, int *pcr,
|
||||||
struct ima_template_desc **template_desc,
|
struct ima_template_desc **template_desc,
|
||||||
const char *func_data);
|
const char *func_data, unsigned int *allowed_algos);
|
||||||
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
|
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
|
||||||
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||||
struct file *file, void *buf, loff_t size,
|
struct file *file, void *buf, loff_t size,
|
||||||
@ -264,11 +268,11 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
|
|||||||
struct evm_ima_xattr_data *xattr_value,
|
struct evm_ima_xattr_data *xattr_value,
|
||||||
int xattr_len, const struct modsig *modsig, int pcr,
|
int xattr_len, const struct modsig *modsig, int pcr,
|
||||||
struct ima_template_desc *template_desc);
|
struct ima_template_desc *template_desc);
|
||||||
void process_buffer_measurement(struct user_namespace *mnt_userns,
|
int process_buffer_measurement(struct user_namespace *mnt_userns,
|
||||||
struct inode *inode, const void *buf, int size,
|
struct inode *inode, const void *buf, int size,
|
||||||
const char *eventname, enum ima_hooks func,
|
const char *eventname, enum ima_hooks func,
|
||||||
int pcr, const char *func_data,
|
int pcr, const char *func_data,
|
||||||
bool buf_hash);
|
bool buf_hash, u8 *digest, size_t digest_len);
|
||||||
void ima_audit_measurement(struct integrity_iint_cache *iint,
|
void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||||
const unsigned char *filename);
|
const unsigned char *filename);
|
||||||
int ima_alloc_init_template(struct ima_event_data *event_data,
|
int ima_alloc_init_template(struct ima_event_data *event_data,
|
||||||
@ -285,10 +289,10 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
|
|||||||
const struct cred *cred, u32 secid, enum ima_hooks func,
|
const struct cred *cred, u32 secid, enum ima_hooks func,
|
||||||
int mask, int flags, int *pcr,
|
int mask, int flags, int *pcr,
|
||||||
struct ima_template_desc **template_desc,
|
struct ima_template_desc **template_desc,
|
||||||
const char *func_data);
|
const char *func_data, unsigned int *allowed_algos);
|
||||||
void ima_init_policy(void);
|
void ima_init_policy(void);
|
||||||
void ima_update_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 *);
|
ssize_t ima_parse_add_rule(char *);
|
||||||
void ima_delete_rules(void);
|
void ima_delete_rules(void);
|
||||||
int ima_check_policy(void);
|
int ima_check_policy(void);
|
||||||
@ -319,7 +323,7 @@ int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode,
|
|||||||
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
|
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
|
||||||
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
|
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
|
||||||
enum ima_hooks func);
|
enum ima_hooks func);
|
||||||
enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
|
enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
|
||||||
int xattr_len);
|
int xattr_len);
|
||||||
int ima_read_xattr(struct dentry *dentry,
|
int ima_read_xattr(struct dentry *dentry,
|
||||||
struct evm_ima_xattr_data **xattr_value);
|
struct evm_ima_xattr_data **xattr_value);
|
||||||
|
@ -172,6 +172,7 @@ err_out:
|
|||||||
* @pcr: pointer filled in if matched measure policy sets pcr=
|
* @pcr: pointer filled in if matched measure policy sets pcr=
|
||||||
* @template_desc: pointer filled in if matched measure policy sets template=
|
* @template_desc: pointer filled in if matched measure policy sets template=
|
||||||
* @func_data: func specific data, may be NULL
|
* @func_data: func specific data, may be NULL
|
||||||
|
* @allowed_algos: allowlist of hash algorithms for the IMA xattr
|
||||||
*
|
*
|
||||||
* The policy is defined in terms of keypairs:
|
* The policy is defined in terms of keypairs:
|
||||||
* subj=, obj=, type=, func=, mask=, fsmagic=
|
* subj=, obj=, type=, func=, mask=, fsmagic=
|
||||||
@ -188,14 +189,15 @@ int ima_get_action(struct user_namespace *mnt_userns, struct inode *inode,
|
|||||||
const struct cred *cred, u32 secid, int mask,
|
const struct cred *cred, u32 secid, int mask,
|
||||||
enum ima_hooks func, int *pcr,
|
enum ima_hooks func, int *pcr,
|
||||||
struct ima_template_desc **template_desc,
|
struct ima_template_desc **template_desc,
|
||||||
const char *func_data)
|
const char *func_data, unsigned int *allowed_algos)
|
||||||
{
|
{
|
||||||
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
|
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
|
||||||
|
|
||||||
flags &= ima_policy_flag;
|
flags &= ima_policy_flag;
|
||||||
|
|
||||||
return ima_match_policy(mnt_userns, inode, cred, secid, func, mask,
|
return ima_match_policy(mnt_userns, inode, cred, secid, func, mask,
|
||||||
flags, pcr, template_desc, func_data);
|
flags, pcr, template_desc, func_data,
|
||||||
|
allowed_algos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -77,8 +77,9 @@ int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
security_task_getsecid_subj(current, &secid);
|
security_task_getsecid_subj(current, &secid);
|
||||||
return ima_match_policy(mnt_userns, inode, current_cred(), secid, func,
|
return ima_match_policy(mnt_userns, inode, current_cred(), secid,
|
||||||
mask, IMA_APPRAISE | IMA_HASH, NULL, NULL, NULL);
|
func, mask, IMA_APPRAISE | IMA_HASH, NULL,
|
||||||
|
NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ima_fix_xattr(struct dentry *dentry,
|
static int ima_fix_xattr(struct dentry *dentry,
|
||||||
@ -171,7 +172,7 @@ static void ima_cache_flags(struct integrity_iint_cache *iint,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
|
enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
|
||||||
int xattr_len)
|
int xattr_len)
|
||||||
{
|
{
|
||||||
struct signature_v2_hdr *sig;
|
struct signature_v2_hdr *sig;
|
||||||
@ -184,7 +185,8 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
|
|||||||
switch (xattr_value->type) {
|
switch (xattr_value->type) {
|
||||||
case EVM_IMA_XATTR_DIGSIG:
|
case EVM_IMA_XATTR_DIGSIG:
|
||||||
sig = (typeof(sig))xattr_value;
|
sig = (typeof(sig))xattr_value;
|
||||||
if (sig->version != 2 || xattr_len <= sizeof(*sig))
|
if (sig->version != 2 || xattr_len <= sizeof(*sig)
|
||||||
|
|| sig->hash_algo >= HASH_ALGO__LAST)
|
||||||
return ima_hash_algo;
|
return ima_hash_algo;
|
||||||
return sig->hash_algo;
|
return sig->hash_algo;
|
||||||
break;
|
break;
|
||||||
@ -357,7 +359,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
|
|||||||
if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
|
if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
|
||||||
process_buffer_measurement(&init_user_ns, NULL, digest, digestsize,
|
process_buffer_measurement(&init_user_ns, NULL, digest, digestsize,
|
||||||
"blacklisted-hash", NONE,
|
"blacklisted-hash", NONE,
|
||||||
pcr, NULL, false);
|
pcr, NULL, false, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
@ -575,6 +577,66 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig)
|
|||||||
clear_bit(IMA_DIGSIG, &iint->atomic_flags);
|
clear_bit(IMA_DIGSIG, &iint->atomic_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* validate_hash_algo() - Block setxattr with unsupported hash algorithms
|
||||||
|
* @dentry: object of the setxattr()
|
||||||
|
* @xattr_value: userland supplied xattr value
|
||||||
|
* @xattr_value_len: length of xattr_value
|
||||||
|
*
|
||||||
|
* The xattr value is mapped to its hash algorithm, and this algorithm
|
||||||
|
* must be built in the kernel for the setxattr to be allowed.
|
||||||
|
*
|
||||||
|
* Emit an audit message when the algorithm is invalid.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, else an error.
|
||||||
|
*/
|
||||||
|
static int validate_hash_algo(struct dentry *dentry,
|
||||||
|
const struct evm_ima_xattr_data *xattr_value,
|
||||||
|
size_t xattr_value_len)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
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)
|
||||||
|
return -EACCES;
|
||||||
|
|
||||||
|
path = dentry_path(dentry, pathbuf, PATH_MAX);
|
||||||
|
|
||||||
|
integrity_audit_msg(AUDIT_INTEGRITY_DATA, d_inode(dentry), path,
|
||||||
|
"set_data", errmsg, -EACCES, 0);
|
||||||
|
|
||||||
|
kfree(pathbuf);
|
||||||
|
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
|
int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
|
||||||
const void *xattr_value, size_t xattr_value_len)
|
const void *xattr_value, size_t xattr_value_len)
|
||||||
{
|
{
|
||||||
@ -592,9 +654,11 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
|
|||||||
digsig = (xvalue->type == EVM_XATTR_PORTABLE_DIGSIG);
|
digsig = (xvalue->type == EVM_XATTR_PORTABLE_DIGSIG);
|
||||||
}
|
}
|
||||||
if (result == 1 || evm_revalidate_status(xattr_name)) {
|
if (result == 1 || evm_revalidate_status(xattr_name)) {
|
||||||
|
result = validate_hash_algo(dentry, xvalue, xattr_value_len);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
|
||||||
ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
|
ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
|
||||||
if (result == 1)
|
|
||||||
result = 0;
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -62,5 +62,5 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
|
|||||||
*/
|
*/
|
||||||
process_buffer_measurement(&init_user_ns, NULL, payload, payload_len,
|
process_buffer_measurement(&init_user_ns, NULL, payload, payload_len,
|
||||||
keyring->description, KEY_CHECK, 0,
|
keyring->description, KEY_CHECK, 0,
|
||||||
keyring->description, false);
|
keyring->description, false, NULL, 0);
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,8 @@ int __init ima_init(void)
|
|||||||
ima_init_key_queue();
|
ima_init_key_queue();
|
||||||
|
|
||||||
ima_measure_critical_data("kernel_info", "kernel_version",
|
ima_measure_critical_data("kernel_info", "kernel_version",
|
||||||
UTS_RELEASE, strlen(UTS_RELEASE), false);
|
UTS_RELEASE, strlen(UTS_RELEASE), false,
|
||||||
|
NULL, 0);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ int ima_appraise = IMA_APPRAISE_ENFORCE;
|
|||||||
int ima_appraise;
|
int ima_appraise;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int ima_hash_algo = HASH_ALGO_SHA1;
|
int __ro_after_init ima_hash_algo = HASH_ALGO_SHA1;
|
||||||
static int hash_setup_done;
|
static int hash_setup_done;
|
||||||
|
|
||||||
static struct notifier_block ima_lsm_policy_notifier = {
|
static struct notifier_block ima_lsm_policy_notifier = {
|
||||||
@ -76,6 +76,11 @@ out:
|
|||||||
}
|
}
|
||||||
__setup("ima_hash=", hash_setup);
|
__setup("ima_hash=", hash_setup);
|
||||||
|
|
||||||
|
enum hash_algo ima_get_current_hash_algo(void)
|
||||||
|
{
|
||||||
|
return ima_hash_algo;
|
||||||
|
}
|
||||||
|
|
||||||
/* Prevent mmap'ing a file execute that is already mmap'ed write */
|
/* Prevent mmap'ing a file execute that is already mmap'ed write */
|
||||||
static int mmap_violation_check(enum ima_hooks func, struct file *file,
|
static int mmap_violation_check(enum ima_hooks func, struct file *file,
|
||||||
char **pathbuf, const char **pathname,
|
char **pathbuf, const char **pathname,
|
||||||
@ -210,6 +215,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
|
|||||||
int xattr_len = 0;
|
int xattr_len = 0;
|
||||||
bool violation_check;
|
bool violation_check;
|
||||||
enum hash_algo hash_algo;
|
enum hash_algo hash_algo;
|
||||||
|
unsigned int allowed_algos = 0;
|
||||||
|
|
||||||
if (!ima_policy_flag || !S_ISREG(inode->i_mode))
|
if (!ima_policy_flag || !S_ISREG(inode->i_mode))
|
||||||
return 0;
|
return 0;
|
||||||
@ -219,7 +225,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
|
|||||||
* Included is the appraise submask.
|
* Included is the appraise submask.
|
||||||
*/
|
*/
|
||||||
action = ima_get_action(file_mnt_user_ns(file), inode, cred, secid,
|
action = ima_get_action(file_mnt_user_ns(file), inode, cred, secid,
|
||||||
mask, func, &pcr, &template_desc, NULL);
|
mask, func, &pcr, &template_desc, NULL,
|
||||||
|
&allowed_algos);
|
||||||
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
|
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
|
||||||
(ima_policy_flag & IMA_MEASURE));
|
(ima_policy_flag & IMA_MEASURE));
|
||||||
if (!action && !violation_check)
|
if (!action && !violation_check)
|
||||||
@ -356,6 +363,16 @@ static int process_measurement(struct file *file, const struct cred *cred,
|
|||||||
|
|
||||||
if ((file->f_flags & O_DIRECT) && (iint->flags & IMA_PERMIT_DIRECTIO))
|
if ((file->f_flags & O_DIRECT) && (iint->flags & IMA_PERMIT_DIRECTIO))
|
||||||
rc = 0;
|
rc = 0;
|
||||||
|
|
||||||
|
/* Ensure the digest was generated using an allowed algorithm */
|
||||||
|
if (rc == 0 && must_appraise && allowed_algos != 0 &&
|
||||||
|
(allowed_algos & (1U << hash_algo)) == 0) {
|
||||||
|
rc = -EACCES;
|
||||||
|
|
||||||
|
integrity_audit_msg(AUDIT_INTEGRITY_DATA, file_inode(file),
|
||||||
|
pathname, "collect_data",
|
||||||
|
"denied-hash-algorithm", rc, 0);
|
||||||
|
}
|
||||||
out_locked:
|
out_locked:
|
||||||
if ((mask & MAY_WRITE) && test_bit(IMA_DIGSIG, &iint->atomic_flags) &&
|
if ((mask & MAY_WRITE) && test_bit(IMA_DIGSIG, &iint->atomic_flags) &&
|
||||||
!(iint->flags & IMA_NEW_FILE))
|
!(iint->flags & IMA_NEW_FILE))
|
||||||
@ -433,7 +450,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
|
|||||||
inode = file_inode(vma->vm_file);
|
inode = file_inode(vma->vm_file);
|
||||||
action = ima_get_action(file_mnt_user_ns(vma->vm_file), inode,
|
action = ima_get_action(file_mnt_user_ns(vma->vm_file), inode,
|
||||||
current_cred(), secid, MAY_EXEC, MMAP_CHECK,
|
current_cred(), secid, MAY_EXEC, MMAP_CHECK,
|
||||||
&pcr, &template, NULL);
|
&pcr, &template, NULL, NULL);
|
||||||
|
|
||||||
/* Is the mmap'ed file in policy? */
|
/* Is the mmap'ed file in policy? */
|
||||||
if (!(action & (IMA_MEASURE | IMA_APPRAISE_SUBMASK)))
|
if (!(action & (IMA_MEASURE | IMA_APPRAISE_SUBMASK)))
|
||||||
@ -822,7 +839,7 @@ int ima_post_load_data(char *buf, loff_t size,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* process_buffer_measurement - Measure the buffer or the buffer data hash
|
* process_buffer_measurement - Measure the buffer or the buffer data hash
|
||||||
* @mnt_userns: user namespace of the mount the inode was found from
|
* @mnt_userns: user namespace of the mount the inode was found from
|
||||||
* @inode: inode associated with the object being measured (NULL for KEY_CHECK)
|
* @inode: inode associated with the object being measured (NULL for KEY_CHECK)
|
||||||
@ -833,14 +850,20 @@ int ima_post_load_data(char *buf, loff_t size,
|
|||||||
* @pcr: pcr to extend the measurement
|
* @pcr: pcr to extend the measurement
|
||||||
* @func_data: func specific data, may be NULL
|
* @func_data: func specific data, may be NULL
|
||||||
* @buf_hash: measure buffer data hash
|
* @buf_hash: measure buffer data hash
|
||||||
|
* @digest: buffer digest will be written to
|
||||||
|
* @digest_len: buffer length
|
||||||
*
|
*
|
||||||
* Based on policy, either the buffer data or buffer data hash is measured
|
* Based on policy, either the buffer data or buffer data hash is measured
|
||||||
|
*
|
||||||
|
* Return: 0 if the buffer has been successfully measured, 1 if the digest
|
||||||
|
* has been written to the passed location but not added to a measurement entry,
|
||||||
|
* a negative value otherwise.
|
||||||
*/
|
*/
|
||||||
void process_buffer_measurement(struct user_namespace *mnt_userns,
|
int process_buffer_measurement(struct user_namespace *mnt_userns,
|
||||||
struct inode *inode, const void *buf, int size,
|
struct inode *inode, const void *buf, int size,
|
||||||
const char *eventname, enum ima_hooks func,
|
const char *eventname, enum ima_hooks func,
|
||||||
int pcr, const char *func_data,
|
int pcr, const char *func_data,
|
||||||
bool buf_hash)
|
bool buf_hash, u8 *digest, size_t digest_len)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
const char *audit_cause = "ENOMEM";
|
const char *audit_cause = "ENOMEM";
|
||||||
@ -861,8 +884,11 @@ void process_buffer_measurement(struct user_namespace *mnt_userns,
|
|||||||
int action = 0;
|
int action = 0;
|
||||||
u32 secid;
|
u32 secid;
|
||||||
|
|
||||||
if (!ima_policy_flag)
|
if (digest && digest_len < digest_hash_len)
|
||||||
return;
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!ima_policy_flag && !digest)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
template = ima_template_desc_buf();
|
template = ima_template_desc_buf();
|
||||||
if (!template) {
|
if (!template) {
|
||||||
@ -882,9 +908,9 @@ void process_buffer_measurement(struct user_namespace *mnt_userns,
|
|||||||
security_task_getsecid_subj(current, &secid);
|
security_task_getsecid_subj(current, &secid);
|
||||||
action = ima_get_action(mnt_userns, inode, current_cred(),
|
action = ima_get_action(mnt_userns, inode, current_cred(),
|
||||||
secid, 0, func, &pcr, &template,
|
secid, 0, func, &pcr, &template,
|
||||||
func_data);
|
func_data, NULL);
|
||||||
if (!(action & IMA_MEASURE))
|
if (!(action & IMA_MEASURE) && !digest)
|
||||||
return;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pcr)
|
if (!pcr)
|
||||||
@ -914,6 +940,12 @@ void process_buffer_measurement(struct user_namespace *mnt_userns,
|
|||||||
event_data.buf_len = digest_hash_len;
|
event_data.buf_len = digest_hash_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (digest)
|
||||||
|
memcpy(digest, iint.ima_hash->digest, digest_hash_len);
|
||||||
|
|
||||||
|
if (!ima_policy_flag || (func && !(action & IMA_MEASURE)))
|
||||||
|
return 1;
|
||||||
|
|
||||||
ret = ima_alloc_init_template(&event_data, &entry, template);
|
ret = ima_alloc_init_template(&event_data, &entry, template);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
audit_cause = "alloc_entry";
|
audit_cause = "alloc_entry";
|
||||||
@ -932,7 +964,7 @@ out:
|
|||||||
func_measure_str(func),
|
func_measure_str(func),
|
||||||
audit_cause, ret, 0, ret);
|
audit_cause, ret, 0, ret);
|
||||||
|
|
||||||
return;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -956,7 +988,7 @@ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
|
|||||||
|
|
||||||
process_buffer_measurement(file_mnt_user_ns(f.file), file_inode(f.file),
|
process_buffer_measurement(file_mnt_user_ns(f.file), file_inode(f.file),
|
||||||
buf, size, "kexec-cmdline", KEXEC_CMDLINE, 0,
|
buf, size, "kexec-cmdline", KEXEC_CMDLINE, 0,
|
||||||
NULL, false);
|
NULL, false, NULL, 0);
|
||||||
fdput(f);
|
fdput(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -967,23 +999,30 @@ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
|
|||||||
* @buf: pointer to buffer data
|
* @buf: pointer to buffer data
|
||||||
* @buf_len: length of buffer data (in bytes)
|
* @buf_len: length of buffer data (in bytes)
|
||||||
* @hash: measure buffer data hash
|
* @hash: measure buffer data hash
|
||||||
|
* @digest: buffer digest will be written to
|
||||||
|
* @digest_len: buffer length
|
||||||
*
|
*
|
||||||
* Measure data critical to the integrity of the kernel into the IMA log
|
* Measure data critical to the integrity of the kernel into the IMA log
|
||||||
* and extend the pcr. Examples of critical data could be various data
|
* and extend the pcr. Examples of critical data could be various data
|
||||||
* structures, policies, and states stored in kernel memory that can
|
* structures, policies, and states stored in kernel memory that can
|
||||||
* impact the integrity of the system.
|
* impact the integrity of the system.
|
||||||
|
*
|
||||||
|
* Return: 0 if the buffer has been successfully measured, 1 if the digest
|
||||||
|
* has been written to the passed location but not added to a measurement entry,
|
||||||
|
* a negative value otherwise.
|
||||||
*/
|
*/
|
||||||
void ima_measure_critical_data(const char *event_label,
|
int ima_measure_critical_data(const char *event_label,
|
||||||
const char *event_name,
|
const char *event_name,
|
||||||
const void *buf, size_t buf_len,
|
const void *buf, size_t buf_len,
|
||||||
bool hash)
|
bool hash, u8 *digest, size_t digest_len)
|
||||||
{
|
{
|
||||||
if (!event_name || !event_label || !buf || !buf_len)
|
if (!event_name || !event_label || !buf || !buf_len)
|
||||||
return;
|
return -ENOPARAM;
|
||||||
|
|
||||||
process_buffer_measurement(&init_user_ns, NULL, buf, buf_len, event_name,
|
return process_buffer_measurement(&init_user_ns, NULL, buf, buf_len,
|
||||||
CRITICAL_DATA, 0, event_label,
|
event_name, CRITICAL_DATA, 0,
|
||||||
hash);
|
event_label, hash, digest,
|
||||||
|
digest_len);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ima_measure_critical_data);
|
EXPORT_SYMBOL_GPL(ima_measure_critical_data);
|
||||||
|
|
||||||
@ -1013,7 +1052,7 @@ static int __init init_ima(void)
|
|||||||
pr_warn("Couldn't register LSM notifier, error %d\n", error);
|
pr_warn("Couldn't register LSM notifier, error %d\n", error);
|
||||||
|
|
||||||
if (!error)
|
if (!error)
|
||||||
ima_update_policy_flag();
|
ima_update_policy_flags();
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ struct key *ima_blacklist_keyring;
|
|||||||
/*
|
/*
|
||||||
* Allocate the IMA blacklist keyring
|
* Allocate the IMA blacklist keyring
|
||||||
*/
|
*/
|
||||||
__init int ima_mok_init(void)
|
static __init int ima_mok_init(void)
|
||||||
{
|
{
|
||||||
struct key_restriction *restriction;
|
struct key_restriction *restriction;
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#define IMA_FSNAME 0x0200
|
#define IMA_FSNAME 0x0200
|
||||||
#define IMA_KEYRINGS 0x0400
|
#define IMA_KEYRINGS 0x0400
|
||||||
#define IMA_LABEL 0x0800
|
#define IMA_LABEL 0x0800
|
||||||
|
#define IMA_VALIDATE_ALGOS 0x1000
|
||||||
|
|
||||||
#define UNKNOWN 0
|
#define UNKNOWN 0
|
||||||
#define MEASURE 0x0001 /* same as IMA_MEASURE */
|
#define MEASURE 0x0001 /* same as IMA_MEASURE */
|
||||||
@ -52,6 +53,8 @@ int ima_policy_flag;
|
|||||||
static int temp_ima_appraise;
|
static int temp_ima_appraise;
|
||||||
static int build_ima_appraise __ro_after_init;
|
static int build_ima_appraise __ro_after_init;
|
||||||
|
|
||||||
|
atomic_t ima_setxattr_allowed_hash_algorithms;
|
||||||
|
|
||||||
#define MAX_LSM_RULES 6
|
#define MAX_LSM_RULES 6
|
||||||
enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
|
enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
|
||||||
LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
|
LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
|
||||||
@ -79,6 +82,7 @@ struct ima_rule_entry {
|
|||||||
bool (*uid_op)(kuid_t, kuid_t); /* Handlers for operators */
|
bool (*uid_op)(kuid_t, kuid_t); /* Handlers for operators */
|
||||||
bool (*fowner_op)(kuid_t, kuid_t); /* uid_eq(), uid_gt(), uid_lt() */
|
bool (*fowner_op)(kuid_t, kuid_t); /* uid_eq(), uid_gt(), uid_lt() */
|
||||||
int pcr;
|
int pcr;
|
||||||
|
unsigned int allowed_algos; /* bitfield of allowed hash algorithms */
|
||||||
struct {
|
struct {
|
||||||
void *rule; /* LSM file metadata specific */
|
void *rule; /* LSM file metadata specific */
|
||||||
char *args_p; /* audit value */
|
char *args_p; /* audit value */
|
||||||
@ -90,6 +94,14 @@ struct ima_rule_entry {
|
|||||||
struct ima_template_desc *template;
|
struct ima_template_desc *template;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sanity check in case the kernels gains more hash algorithms that can
|
||||||
|
* fit in an unsigned int
|
||||||
|
*/
|
||||||
|
static_assert(
|
||||||
|
8 * sizeof(unsigned int) >= HASH_ALGO__LAST,
|
||||||
|
"The bitfield allowed_algos in ima_rule_entry is too small to contain all the supported hash algorithms, consider using a bigger type");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Without LSM specific knowledge, the default policy can only be
|
* Without LSM specific knowledge, the default policy can only be
|
||||||
* written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner
|
* written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner
|
||||||
@ -646,6 +658,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
|
|||||||
* @pcr: set the pcr to extend
|
* @pcr: set the pcr to extend
|
||||||
* @template_desc: the template that should be used for this rule
|
* @template_desc: the template that should be used for this rule
|
||||||
* @func_data: func specific data, may be NULL
|
* @func_data: func specific data, may be NULL
|
||||||
|
* @allowed_algos: allowlist of hash algorithms for the IMA xattr
|
||||||
*
|
*
|
||||||
* Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
|
* Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
|
||||||
* conditions.
|
* conditions.
|
||||||
@ -658,7 +671,7 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
|
|||||||
const struct cred *cred, u32 secid, enum ima_hooks func,
|
const struct cred *cred, u32 secid, enum ima_hooks func,
|
||||||
int mask, int flags, int *pcr,
|
int mask, int flags, int *pcr,
|
||||||
struct ima_template_desc **template_desc,
|
struct ima_template_desc **template_desc,
|
||||||
const char *func_data)
|
const char *func_data, unsigned int *allowed_algos)
|
||||||
{
|
{
|
||||||
struct ima_rule_entry *entry;
|
struct ima_rule_entry *entry;
|
||||||
int action = 0, actmask = flags | (flags << 1);
|
int action = 0, actmask = flags | (flags << 1);
|
||||||
@ -684,8 +697,11 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
|
|||||||
action &= ~IMA_HASH;
|
action &= ~IMA_HASH;
|
||||||
if (ima_fail_unverifiable_sigs)
|
if (ima_fail_unverifiable_sigs)
|
||||||
action |= IMA_FAIL_UNVERIFIABLE_SIGS;
|
action |= IMA_FAIL_UNVERIFIABLE_SIGS;
|
||||||
}
|
|
||||||
|
|
||||||
|
if (allowed_algos &&
|
||||||
|
entry->flags & IMA_VALIDATE_ALGOS)
|
||||||
|
*allowed_algos = entry->allowed_algos;
|
||||||
|
}
|
||||||
|
|
||||||
if (entry->action & IMA_DO_MASK)
|
if (entry->action & IMA_DO_MASK)
|
||||||
actmask &= ~(entry->action | entry->action << 1);
|
actmask &= ~(entry->action | entry->action << 1);
|
||||||
@ -706,24 +722,57 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
|
|||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Initialize the ima_policy_flag variable based on the currently
|
* ima_update_policy_flags() - Update global IMA variables
|
||||||
* loaded policy. Based on this flag, the decision to short circuit
|
*
|
||||||
* out of a function or not call the function in the first place
|
* Update ima_policy_flag and ima_setxattr_allowed_hash_algorithms
|
||||||
* can be made earlier.
|
* 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;
|
struct ima_rule_entry *entry;
|
||||||
|
int new_policy_flag = 0;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
list_for_each_entry(entry, ima_rules, list) {
|
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)
|
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);
|
ima_appraise |= (build_ima_appraise | temp_ima_appraise);
|
||||||
if (!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)
|
static int ima_appraise_flag(enum ima_hooks func)
|
||||||
@ -889,7 +938,9 @@ void __init ima_init_policy(void)
|
|||||||
ARRAY_SIZE(critical_data_rules),
|
ARRAY_SIZE(critical_data_rules),
|
||||||
IMA_DEFAULT_POLICY);
|
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. */
|
/* Make sure we have a valid policy, at least containing some rules. */
|
||||||
@ -929,7 +980,7 @@ void ima_update_policy(void)
|
|||||||
*/
|
*/
|
||||||
kfree(arch_policy_entry);
|
kfree(arch_policy_entry);
|
||||||
}
|
}
|
||||||
ima_update_policy_flag();
|
ima_update_policy_flags();
|
||||||
|
|
||||||
/* Custom IMA policy has been loaded */
|
/* Custom IMA policy has been loaded */
|
||||||
ima_process_queued_keys();
|
ima_process_queued_keys();
|
||||||
@ -946,7 +997,7 @@ enum {
|
|||||||
Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq,
|
Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq,
|
||||||
Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt,
|
Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt,
|
||||||
Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt,
|
Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt,
|
||||||
Opt_appraise_type, Opt_appraise_flag,
|
Opt_appraise_type, Opt_appraise_flag, Opt_appraise_algos,
|
||||||
Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings,
|
Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings,
|
||||||
Opt_label, Opt_err
|
Opt_label, Opt_err
|
||||||
};
|
};
|
||||||
@ -981,6 +1032,7 @@ static const match_table_t policy_tokens = {
|
|||||||
{Opt_fowner_lt, "fowner<%s"},
|
{Opt_fowner_lt, "fowner<%s"},
|
||||||
{Opt_appraise_type, "appraise_type=%s"},
|
{Opt_appraise_type, "appraise_type=%s"},
|
||||||
{Opt_appraise_flag, "appraise_flag=%s"},
|
{Opt_appraise_flag, "appraise_flag=%s"},
|
||||||
|
{Opt_appraise_algos, "appraise_algos=%s"},
|
||||||
{Opt_permit_directio, "permit_directio"},
|
{Opt_permit_directio, "permit_directio"},
|
||||||
{Opt_pcr, "pcr=%s"},
|
{Opt_pcr, "pcr=%s"},
|
||||||
{Opt_template, "template=%s"},
|
{Opt_template, "template=%s"},
|
||||||
@ -1081,7 +1133,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (entry->action != APPRAISE &&
|
if (entry->action != APPRAISE &&
|
||||||
entry->flags & (IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED | IMA_CHECK_BLACKLIST))
|
entry->flags & (IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED |
|
||||||
|
IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1111,7 +1164,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
|
|||||||
IMA_UID | IMA_FOWNER | IMA_FSUUID |
|
IMA_UID | IMA_FOWNER | IMA_FSUUID |
|
||||||
IMA_INMASK | IMA_EUID | IMA_PCR |
|
IMA_INMASK | IMA_EUID | IMA_PCR |
|
||||||
IMA_FSNAME | IMA_DIGSIG_REQUIRED |
|
IMA_FSNAME | IMA_DIGSIG_REQUIRED |
|
||||||
IMA_PERMIT_DIRECTIO))
|
IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -1123,7 +1176,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
|
|||||||
IMA_INMASK | IMA_EUID | IMA_PCR |
|
IMA_INMASK | IMA_EUID | IMA_PCR |
|
||||||
IMA_FSNAME | IMA_DIGSIG_REQUIRED |
|
IMA_FSNAME | IMA_DIGSIG_REQUIRED |
|
||||||
IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED |
|
IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED |
|
||||||
IMA_CHECK_BLACKLIST))
|
IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -1160,6 +1213,23 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
|
|||||||
if (ima_rule_contains_lsm_cond(entry))
|
if (ima_rule_contains_lsm_cond(entry))
|
||||||
return false;
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
@ -1173,6 +1243,34 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int ima_parse_appraise_algos(char *arg)
|
||||||
|
{
|
||||||
|
unsigned int res = 0;
|
||||||
|
int idx;
|
||||||
|
char *token;
|
||||||
|
|
||||||
|
while ((token = strsep(&arg, ",")) != NULL) {
|
||||||
|
idx = match_string(hash_algo_name, HASH_ALGO__LAST, token);
|
||||||
|
|
||||||
|
if (idx < 0) {
|
||||||
|
pr_err("unknown hash algorithm \"%s\"",
|
||||||
|
token);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!crypto_has_alg(hash_algo_name[idx], 0, 0)) {
|
||||||
|
pr_err("unavailable hash algorithm \"%s\", check your kernel configuration",
|
||||||
|
token);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add the hash algorithm to the 'allowed' bitfield */
|
||||||
|
res |= (1U << idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||||
{
|
{
|
||||||
struct audit_buffer *ab;
|
struct audit_buffer *ab;
|
||||||
@ -1294,6 +1392,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
|||||||
entry->func = KEY_CHECK;
|
entry->func = KEY_CHECK;
|
||||||
else if (strcmp(args[0].from, "CRITICAL_DATA") == 0)
|
else if (strcmp(args[0].from, "CRITICAL_DATA") == 0)
|
||||||
entry->func = CRITICAL_DATA;
|
entry->func = CRITICAL_DATA;
|
||||||
|
else if (strcmp(args[0].from, "SETXATTR_CHECK") == 0)
|
||||||
|
entry->func = SETXATTR_CHECK;
|
||||||
else
|
else
|
||||||
result = -EINVAL;
|
result = -EINVAL;
|
||||||
if (!result)
|
if (!result)
|
||||||
@ -1508,6 +1608,25 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
|||||||
else
|
else
|
||||||
result = -EINVAL;
|
result = -EINVAL;
|
||||||
break;
|
break;
|
||||||
|
case Opt_appraise_algos:
|
||||||
|
ima_log_string(ab, "appraise_algos", args[0].from);
|
||||||
|
|
||||||
|
if (entry->allowed_algos) {
|
||||||
|
result = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->allowed_algos =
|
||||||
|
ima_parse_appraise_algos(args[0].from);
|
||||||
|
/* invalid or empty list of algorithms */
|
||||||
|
if (!entry->allowed_algos) {
|
||||||
|
result = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->flags |= IMA_VALIDATE_ALGOS;
|
||||||
|
|
||||||
|
break;
|
||||||
case Opt_permit_directio:
|
case Opt_permit_directio:
|
||||||
entry->flags |= IMA_PERMIT_DIRECTIO;
|
entry->flags |= IMA_PERMIT_DIRECTIO;
|
||||||
break;
|
break;
|
||||||
@ -1700,6 +1819,23 @@ static void ima_show_rule_opt_list(struct seq_file *m,
|
|||||||
seq_printf(m, "%s%s", i ? "|" : "", opt_list->items[i]);
|
seq_printf(m, "%s%s", i ? "|" : "", opt_list->items[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ima_policy_show_appraise_algos(struct seq_file *m,
|
||||||
|
unsigned int allowed_hashes)
|
||||||
|
{
|
||||||
|
int idx, list_size = 0;
|
||||||
|
|
||||||
|
for (idx = 0; idx < HASH_ALGO__LAST; idx++) {
|
||||||
|
if (!(allowed_hashes & (1U << idx)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* only add commas if the list contains multiple entries */
|
||||||
|
if (list_size++)
|
||||||
|
seq_puts(m, ",");
|
||||||
|
|
||||||
|
seq_puts(m, hash_algo_name[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int ima_policy_show(struct seq_file *m, void *v)
|
int ima_policy_show(struct seq_file *m, void *v)
|
||||||
{
|
{
|
||||||
struct ima_rule_entry *entry = v;
|
struct ima_rule_entry *entry = v;
|
||||||
@ -1811,6 +1947,12 @@ int ima_policy_show(struct seq_file *m, void *v)
|
|||||||
seq_puts(m, " ");
|
seq_puts(m, " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entry->flags & IMA_VALIDATE_ALGOS) {
|
||||||
|
seq_puts(m, "appraise_algos=");
|
||||||
|
ima_policy_show_appraise_algos(m, entry->allowed_algos);
|
||||||
|
seq_puts(m, " ");
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < MAX_LSM_RULES; i++) {
|
for (i = 0; i < MAX_LSM_RULES; i++) {
|
||||||
if (entry->lsm[i].rule) {
|
if (entry->lsm[i].rule) {
|
||||||
switch (i) {
|
switch (i) {
|
||||||
|
@ -165,7 +165,7 @@ void ima_process_queued_keys(void)
|
|||||||
entry->keyring_name,
|
entry->keyring_name,
|
||||||
KEY_CHECK, 0,
|
KEY_CHECK, 0,
|
||||||
entry->keyring_name,
|
entry->keyring_name,
|
||||||
false);
|
false, NULL, 0);
|
||||||
list_del(&entry->list);
|
list_del(&entry->list);
|
||||||
ima_free_key_entry(entry);
|
ima_free_key_entry(entry);
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,8 @@ void selinux_ima_measure_state_locked(struct selinux_state *state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ima_measure_critical_data("selinux", "selinux-state",
|
ima_measure_critical_data("selinux", "selinux-state",
|
||||||
state_str, strlen(state_str), false);
|
state_str, strlen(state_str), false,
|
||||||
|
NULL, 0);
|
||||||
|
|
||||||
kfree(state_str);
|
kfree(state_str);
|
||||||
|
|
||||||
@ -103,7 +104,8 @@ void selinux_ima_measure_state_locked(struct selinux_state *state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ima_measure_critical_data("selinux", "selinux-policy-hash",
|
ima_measure_critical_data("selinux", "selinux-policy-hash",
|
||||||
policy, policy_len, true);
|
policy, policy_len, true,
|
||||||
|
NULL, 0);
|
||||||
|
|
||||||
vfree(policy);
|
vfree(policy);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user