1
0
Fork 0

Merge branch 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity

Pull integrity updates from Mimi Zohar:
 "Bug fixes, code clean up, and new features:

   - IMA policy rules can be defined in terms of LSM labels, making the
     IMA policy dependent on LSM policy label changes, in particular LSM
     label deletions. The new environment, in which IMA-appraisal is
     being used, frequently updates the LSM policy and permits LSM label
     deletions.

   - Prevent an mmap'ed shared file opened for write from also being
     mmap'ed execute. In the long term, making this and other similar
     changes at the VFS layer would be preferable.

   - The IMA per policy rule template format support is needed for a
     couple of new/proposed features (eg. kexec boot command line
     measurement, appended signatures, and VFS provided file hashes).

   - Other than the "boot-aggregate" record in the IMA measuremeent
     list, all other measurements are of file data. Measuring and
     storing the kexec boot command line in the IMA measurement list is
     the first buffer based measurement included in the measurement
     list"

* 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity:
  integrity: Introduce struct evm_xattr
  ima: Update MAX_TEMPLATE_NAME_LEN to fit largest reasonable definition
  KEXEC: Call ima_kexec_cmdline to measure the boot command line args
  IMA: Define a new template field buf
  IMA: Define a new hook to measure the kexec boot command line arguments
  IMA: support for per policy rule template formats
  integrity: Fix __integrity_init_keyring() section mismatch
  ima: Use designated initializers for struct ima_event_data
  ima: use the lsm policy update notifier
  LSM: switch to blocking policy update notifiers
  x86/ima: fix the Kconfig dependency for IMA_ARCH_POLICY
  ima: Make arch_policy_entry static
  ima: prevent a file already mmap'ed write to be mmap'ed execute
  x86/ima: check EFI SetupMode too
alistair/sunxi64-5.4-dsi
Linus Torvalds 2019-07-08 20:28:59 -07:00
commit 8b68150883
23 changed files with 413 additions and 98 deletions

View File

@ -24,11 +24,11 @@ Description:
[euid=] [fowner=] [fsname=]]
lsm: [[subj_user=] [subj_role=] [subj_type=]
[obj_user=] [obj_role=] [obj_type=]]
option: [[appraise_type=]] [permit_directio]
option: [[appraise_type=]] [template=] [permit_directio]
base: func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
[FIRMWARE_CHECK]
[KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
[KEXEC_CMDLINE]
mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
[[^]MAY_EXEC]
fsmagic:= hex value
@ -38,6 +38,8 @@ Description:
fowner:= decimal value
lsm: are LSM specific
option: appraise_type:= [imasig]
template:= name of a defined IMA template type
(eg, ima-ng). Only valid when action is "measure".
pcr:= decimal value
default policy:

View File

@ -69,15 +69,16 @@ descriptors by adding their identifier to the format string
algorithm (field format: [<hash algo>:]digest, where the digest
prefix is shown only if the hash algorithm is not SHA1 or MD5);
- 'n-ng': the name of the event, without size limitations;
- 'sig': the file signature.
- 'sig': the file signature;
- 'buf': the buffer data that was used to generate the hash without size limitations;
Below, there is the list of defined template descriptors:
- "ima": its format is ``d|n``;
- "ima-ng" (default): its format is ``d-ng|n-ng``;
- "ima-sig": its format is ``d-ng|n-ng|sig``.
- "ima-sig": its format is ``d-ng|n-ng|sig``;
- "ima-buf": its format is ``d-ng|n-ng|buf``;
Use

View File

@ -11,10 +11,11 @@ extern struct boot_params boot_params;
static enum efi_secureboot_mode get_sb_mode(void)
{
efi_char16_t efi_SecureBoot_name[] = L"SecureBoot";
efi_char16_t efi_SetupMode_name[] = L"SecureBoot";
efi_guid_t efi_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
efi_status_t status;
unsigned long size;
u8 secboot;
u8 secboot, setupmode;
size = sizeof(secboot);
@ -36,7 +37,14 @@ static enum efi_secureboot_mode get_sb_mode(void)
return efi_secureboot_mode_unknown;
}
if (secboot == 0) {
size = sizeof(setupmode);
status = efi.get_variable(efi_SetupMode_name, &efi_variable_guid,
NULL, &size, &setupmode);
if (status != EFI_SUCCESS) /* ignore unknown SetupMode */
setupmode = 0;
if (secboot == 0 || setupmode == 1) {
pr_info("ima: secureboot mode disabled\n");
return efi_secureboot_mode_disabled;
}

View File

@ -2520,7 +2520,7 @@ static int __init ib_core_init(void)
goto err_mad;
}
ret = register_lsm_notifier(&ibdev_lsm_nb);
ret = register_blocking_lsm_notifier(&ibdev_lsm_nb);
if (ret) {
pr_warn("Couldn't register LSM notifier. ret %d\n", ret);
goto err_sa;
@ -2539,7 +2539,7 @@ static int __init ib_core_init(void)
return 0;
err_compat:
unregister_lsm_notifier(&ibdev_lsm_nb);
unregister_blocking_lsm_notifier(&ibdev_lsm_nb);
err_sa:
ib_sa_cleanup();
err_mad:
@ -2565,7 +2565,7 @@ static void __exit ib_core_cleanup(void)
nldev_exit();
rdma_nl_unregister(RDMA_NL_LS);
unregister_pernet_device(&rdma_dev_net_ops);
unregister_lsm_notifier(&ibdev_lsm_nb);
unregister_blocking_lsm_notifier(&ibdev_lsm_nb);
ib_sa_cleanup();
ib_mad_cleanup();
addr_cleanup();

View File

@ -23,6 +23,7 @@ extern int ima_read_file(struct file *file, enum kernel_read_file_id id);
extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
enum kernel_read_file_id id);
extern void ima_post_path_mknod(struct dentry *dentry);
extern void ima_kexec_cmdline(const void *buf, int size);
#ifdef CONFIG_IMA_KEXEC
extern void ima_add_kexec_buffer(struct kimage *image);
@ -89,6 +90,7 @@ static inline void ima_post_path_mknod(struct dentry *dentry)
return;
}
static inline void ima_kexec_cmdline(const void *buf, int size) {}
#endif /* CONFIG_IMA */
#ifndef CONFIG_IMA_KEXEC

View File

@ -189,9 +189,9 @@ static inline const char *kernel_load_data_id_str(enum kernel_load_data_id id)
#ifdef CONFIG_SECURITY
int call_lsm_notifier(enum lsm_event event, void *data);
int register_lsm_notifier(struct notifier_block *nb);
int unregister_lsm_notifier(struct notifier_block *nb);
int call_blocking_lsm_notifier(enum lsm_event event, void *data);
int register_blocking_lsm_notifier(struct notifier_block *nb);
int unregister_blocking_lsm_notifier(struct notifier_block *nb);
/* prototypes */
extern int security_init(void);
@ -394,17 +394,17 @@ int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
#else /* CONFIG_SECURITY */
static inline int call_lsm_notifier(enum lsm_event event, void *data)
static inline int call_blocking_lsm_notifier(enum lsm_event event, void *data)
{
return 0;
}
static inline int register_lsm_notifier(struct notifier_block *nb)
static inline int register_blocking_lsm_notifier(struct notifier_block *nb)
{
return 0;
}
static inline int unregister_lsm_notifier(struct notifier_block *nb)
static inline int unregister_blocking_lsm_notifier(struct notifier_block *nb)
{
return 0;
}

View File

@ -196,9 +196,6 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
return ret;
image->kernel_buf_len = size;
/* IMA needs to pass the measurement list to the next kernel. */
ima_add_kexec_buffer(image);
/* Call arch image probe handlers */
ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
image->kernel_buf_len);
@ -239,8 +236,14 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
ret = -EINVAL;
goto out;
}
ima_kexec_cmdline(image->cmdline_buf,
image->cmdline_buf_len - 1);
}
/* IMA needs to pass the measurement list to the next kernel. */
ima_add_kexec_buffer(image);
/* Call arch image load handlers */
ldata = arch_kexec_kernel_image_load(image);

View File

@ -70,8 +70,9 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
return -EOPNOTSUPP;
}
static int __integrity_init_keyring(const unsigned int id, struct key_acl *acl,
struct key_restriction *restriction)
static int __init __integrity_init_keyring(const unsigned int id,
struct key_acl *acl,
struct key_restriction *restriction)
{
const struct cred *cred = current_cred();
int err = 0;

View File

@ -166,7 +166,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
/* check value type */
switch (xattr_data->type) {
case EVM_XATTR_HMAC:
if (xattr_len != sizeof(struct evm_ima_xattr_data)) {
if (xattr_len != sizeof(struct evm_xattr)) {
evm_status = INTEGRITY_FAIL;
goto out;
}
@ -176,7 +176,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
xattr_value_len, &digest);
if (rc)
break;
rc = crypto_memneq(xattr_data->digest, digest.digest,
rc = crypto_memneq(xattr_data->data, digest.digest,
SHA1_DIGEST_SIZE);
if (rc)
rc = -EINVAL;
@ -520,7 +520,7 @@ int evm_inode_init_security(struct inode *inode,
const struct xattr *lsm_xattr,
struct xattr *evm_xattr)
{
struct evm_ima_xattr_data *xattr_data;
struct evm_xattr *xattr_data;
int rc;
if (!evm_key_loaded() || !evm_protected_xattr(lsm_xattr->name))
@ -530,7 +530,7 @@ int evm_inode_init_security(struct inode *inode,
if (!xattr_data)
return -ENOMEM;
xattr_data->type = EVM_XATTR_HMAC;
xattr_data->data.type = EVM_XATTR_HMAC;
rc = evm_init_hmac(inode, lsm_xattr, xattr_data->digest);
if (rc < 0)
goto out;

View File

@ -160,7 +160,8 @@ config IMA_APPRAISE
config IMA_ARCH_POLICY
bool "Enable loading an IMA architecture specific policy"
depends on KEXEC_VERIFY_SIG || IMA_APPRAISE && INTEGRITY_ASYMMETRIC_KEYS
depends on (KEXEC_VERIFY_SIG && IMA) || IMA_APPRAISE \
&& INTEGRITY_ASYMMETRIC_KEYS
default n
help
This option enables loading an IMA architecture specific policy

View File

@ -61,6 +61,8 @@ struct ima_event_data {
struct evm_ima_xattr_data *xattr_value;
int xattr_len;
const char *violation;
const void *buf;
int buf_len;
};
/* IMA template field data definition */
@ -142,7 +144,11 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
int ima_init_crypto(void);
void ima_putc(struct seq_file *m, void *data, int datalen);
void ima_print_digest(struct seq_file *m, u8 *digest, u32 size);
int template_desc_init_fields(const char *template_fmt,
const struct ima_template_field ***fields,
int *num_fields);
struct ima_template_desc *ima_template_desc_current(void);
struct ima_template_desc *lookup_template_desc(const char *name);
int ima_restore_measurement_entry(struct ima_template_entry *entry);
int ima_restore_measurement_list(loff_t bufsize, void *buf);
int ima_measurements_show(struct seq_file *m, void *v);
@ -150,6 +156,8 @@ unsigned long ima_get_binary_runtime_size(void);
int ima_init_template(void);
void ima_init_template_list(void);
int __init ima_init_digests(void);
int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
void *lsm_data);
/*
* used to protect h_table and sha_table
@ -180,6 +188,7 @@ static inline unsigned long ima_hash_key(u8 *digest)
hook(KEXEC_KERNEL_CHECK) \
hook(KEXEC_INITRAMFS_CHECK) \
hook(POLICY_CHECK) \
hook(KEXEC_CMDLINE) \
hook(MAX_CHECK)
#define __ima_hook_enumify(ENUM) ENUM,
@ -189,7 +198,8 @@ enum ima_hooks {
/* LIM API function definitions */
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
int mask, enum ima_hooks func, int *pcr);
int mask, enum ima_hooks func, int *pcr,
struct ima_template_desc **template_desc);
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
int ima_collect_measurement(struct integrity_iint_cache *iint,
struct file *file, void *buf, loff_t size,
@ -197,11 +207,13 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, int pcr);
int xattr_len, int pcr,
struct ima_template_desc *template_desc);
void ima_audit_measurement(struct integrity_iint_cache *iint,
const unsigned char *filename);
int ima_alloc_init_template(struct ima_event_data *event_data,
struct ima_template_entry **entry);
struct ima_template_entry **entry,
struct ima_template_desc *template_desc);
int ima_store_template(struct ima_template_entry *entry, int violation,
struct inode *inode,
const unsigned char *filename, int pcr);
@ -210,7 +222,8 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
/* IMA policy related functions */
int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
enum ima_hooks func, int mask, int flags, int *pcr);
enum ima_hooks func, int mask, int flags, int *pcr,
struct ima_template_desc **template_desc);
void ima_init_policy(void);
void ima_update_policy(void);
void ima_update_policy_flag(void);

View File

@ -34,11 +34,17 @@ void ima_free_template_entry(struct ima_template_entry *entry)
* ima_alloc_init_template - create and initialize a new template entry
*/
int ima_alloc_init_template(struct ima_event_data *event_data,
struct ima_template_entry **entry)
struct ima_template_entry **entry,
struct ima_template_desc *desc)
{
struct ima_template_desc *template_desc = ima_template_desc_current();
struct ima_template_desc *template_desc;
int i, result = 0;
if (desc)
template_desc = desc;
else
template_desc = ima_template_desc_current();
*entry = kzalloc(sizeof(**entry) + template_desc->num_fields *
sizeof(struct ima_field_data), GFP_NOFS);
if (!*entry)
@ -129,15 +135,17 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
{
struct ima_template_entry *entry;
struct inode *inode = file_inode(file);
struct ima_event_data event_data = {iint, file, filename, NULL, 0,
cause};
struct ima_event_data event_data = { .iint = iint,
.file = file,
.filename = filename,
.violation = cause };
int violation = 1;
int result;
/* can overflow, only indicator */
atomic_long_inc(&ima_htable.violations);
result = ima_alloc_init_template(&event_data, &entry);
result = ima_alloc_init_template(&event_data, &entry, NULL);
if (result < 0) {
result = -ENOMEM;
goto err_out;
@ -160,11 +168,13 @@ err_out:
* MAY_APPEND)
* @func: caller identifier
* @pcr: pointer filled in if matched measure policy sets pcr=
* @template_desc: pointer filled in if matched measure policy sets template=
*
* The policy is defined in terms of keypairs:
* subj=, obj=, type=, func=, mask=, fsmagic=
* subj,obj, and type: are LSM specific.
* func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK
* | KEXEC_CMDLINE
* mask: contains the permission mask
* fsmagic: hex value
*
@ -172,13 +182,15 @@ err_out:
*
*/
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
int mask, enum ima_hooks func, int *pcr)
int mask, enum ima_hooks func, int *pcr,
struct ima_template_desc **template_desc)
{
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
flags &= ima_policy_flag;
return ima_match_policy(inode, cred, secid, func, mask, flags, pcr);
return ima_match_policy(inode, cred, secid, func, mask, flags, pcr,
template_desc);
}
/*
@ -273,21 +285,25 @@ out:
void ima_store_measurement(struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, int pcr)
int xattr_len, int pcr,
struct ima_template_desc *template_desc)
{
static const char op[] = "add_template_measure";
static const char audit_cause[] = "ENOMEM";
int result = -ENOMEM;
struct inode *inode = file_inode(file);
struct ima_template_entry *entry;
struct ima_event_data event_data = {iint, file, filename, xattr_value,
xattr_len, NULL};
struct ima_event_data event_data = { .iint = iint,
.file = file,
.filename = filename,
.xattr_value = xattr_value,
.xattr_len = xattr_len };
int violation = 0;
if (iint->measured_pcrs & (0x1 << pcr))
return;
result = ima_alloc_init_template(&event_data, &entry);
result = ima_alloc_init_template(&event_data, &entry, template_desc);
if (result < 0) {
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
op, audit_cause, result, 0);

View File

@ -54,7 +54,7 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
security_task_getsecid(current, &secid);
return ima_match_policy(inode, current_cred(), secid, func, mask,
IMA_APPRAISE | IMA_HASH, NULL);
IMA_APPRAISE | IMA_HASH, NULL, NULL);
}
static int ima_fix_xattr(struct dentry *dentry,
@ -165,7 +165,8 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
return sig->hash_algo;
break;
case IMA_XATTR_DIGEST_NG:
ret = xattr_value->digest[0];
/* first byte contains algorithm id */
ret = xattr_value->data[0];
if (ret < HASH_ALGO__LAST)
return ret;
break;
@ -173,7 +174,7 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
/* this is for backward compatibility */
if (xattr_len == 21) {
unsigned int zero = 0;
if (!memcmp(&xattr_value->digest[16], &zero, 4))
if (!memcmp(&xattr_value->data[16], &zero, 4))
return HASH_ALGO_MD5;
else
return HASH_ALGO_SHA1;
@ -272,7 +273,7 @@ int ima_appraise_measurement(enum ima_hooks func,
/* xattr length may be longer. md5 hash in previous
version occupied 20 bytes in xattr, instead of 16
*/
rc = memcmp(&xattr_value->digest[hash_start],
rc = memcmp(&xattr_value->data[hash_start],
iint->ima_hash->digest,
iint->ima_hash->length);
else

View File

@ -45,8 +45,8 @@ static int __init ima_add_boot_aggregate(void)
const char *audit_cause = "ENOMEM";
struct ima_template_entry *entry;
struct integrity_iint_cache tmp_iint, *iint = &tmp_iint;
struct ima_event_data event_data = {iint, NULL, boot_aggregate_name,
NULL, 0, NULL};
struct ima_event_data event_data = { .iint = iint,
.filename = boot_aggregate_name };
int result = -ENOMEM;
int violation = 0;
struct {
@ -68,7 +68,7 @@ static int __init ima_add_boot_aggregate(void)
}
}
result = ima_alloc_init_template(&event_data, &entry);
result = ima_alloc_init_template(&event_data, &entry, NULL);
if (result < 0) {
audit_cause = "alloc_entry";
goto err_out;

View File

@ -39,6 +39,10 @@ int ima_appraise;
int ima_hash_algo = HASH_ALGO_SHA1;
static int hash_setup_done;
static struct notifier_block ima_lsm_policy_notifier = {
.notifier_call = ima_lsm_policy_change,
};
static int __init hash_setup(char *str)
{
struct ima_template_desc *template_desc = ima_template_desc_current();
@ -68,6 +72,27 @@ out:
}
__setup("ima_hash=", hash_setup);
/* Prevent mmap'ing a file execute that is already mmap'ed write */
static int mmap_violation_check(enum ima_hooks func, struct file *file,
char **pathbuf, const char **pathname,
char *filename)
{
struct inode *inode;
int rc = 0;
if ((func == MMAP_CHECK) && mapping_writably_mapped(file->f_mapping)) {
rc = -ETXTBSY;
inode = file_inode(file);
if (!*pathbuf) /* ima_rdwr_violation possibly pre-fetched */
*pathname = ima_d_path(&file->f_path, pathbuf,
filename);
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, *pathname,
"mmap_file", "mmapped_writers", rc, 0);
}
return rc;
}
/*
* ima_rdwr_violation_check
*
@ -170,7 +195,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
{
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint = NULL;
struct ima_template_desc *template_desc;
struct ima_template_desc *template_desc = NULL;
char *pathbuf = NULL;
char filename[NAME_MAX];
const char *pathname = NULL;
@ -188,7 +213,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
* bitmask based on the appraise/audit/measurement policy.
* Included is the appraise submask.
*/
action = ima_get_action(inode, cred, secid, mask, func, &pcr);
action = ima_get_action(inode, cred, secid, mask, func, &pcr,
&template_desc);
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
(ima_policy_flag & IMA_MEASURE));
if (!action && !violation_check)
@ -266,12 +292,15 @@ static int process_measurement(struct file *file, const struct cred *cred,
/* Nothing to do, just return existing appraised status */
if (!action) {
if (must_appraise)
rc = ima_get_cache_status(iint, func);
if (must_appraise) {
rc = mmap_violation_check(func, file, &pathbuf,
&pathname, filename);
if (!rc)
rc = ima_get_cache_status(iint, func);
}
goto out_locked;
}
template_desc = ima_template_desc_current();
if ((action & IMA_APPRAISE_SUBMASK) ||
strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0)
/* read 'security.ima' */
@ -288,12 +317,16 @@ static int process_measurement(struct file *file, const struct cred *cred,
if (action & IMA_MEASURE)
ima_store_measurement(iint, file, pathname,
xattr_value, xattr_len, pcr);
xattr_value, xattr_len, pcr,
template_desc);
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
inode_lock(inode);
rc = ima_appraise_measurement(func, iint, file, pathname,
xattr_value, xattr_len);
inode_unlock(inode);
if (!rc)
rc = mmap_violation_check(func, file, &pathbuf,
&pathname, filename);
}
if (action & IMA_AUDIT)
ima_audit_measurement(iint, pathname);
@ -572,6 +605,80 @@ int ima_load_data(enum kernel_load_data_id id)
return 0;
}
/*
* process_buffer_measurement - Measure the buffer to ima log.
* @buf: pointer to the buffer that needs to be added to the log.
* @size: size of buffer(in bytes).
* @eventname: event name to be used for the buffer entry.
* @cred: a pointer to a credentials structure for user validation.
* @secid: the secid of the task to be validated.
*
* Based on policy, the buffer is measured into the ima log.
*/
static void process_buffer_measurement(const void *buf, int size,
const char *eventname,
const struct cred *cred, u32 secid)
{
int ret = 0;
struct ima_template_entry *entry = NULL;
struct integrity_iint_cache iint = {};
struct ima_event_data event_data = {.iint = &iint,
.filename = eventname,
.buf = buf,
.buf_len = size};
struct ima_template_desc *template_desc = NULL;
struct {
struct ima_digest_data hdr;
char digest[IMA_MAX_DIGEST_SIZE];
} hash = {};
int violation = 0;
int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
int action = 0;
action = ima_get_action(NULL, cred, secid, 0, KEXEC_CMDLINE, &pcr,
&template_desc);
if (!(action & IMA_MEASURE))
return;
iint.ima_hash = &hash.hdr;
iint.ima_hash->algo = ima_hash_algo;
iint.ima_hash->length = hash_digest_size[ima_hash_algo];
ret = ima_calc_buffer_hash(buf, size, iint.ima_hash);
if (ret < 0)
goto out;
ret = ima_alloc_init_template(&event_data, &entry, template_desc);
if (ret < 0)
goto out;
ret = ima_store_template(entry, violation, NULL, buf, pcr);
if (ret < 0)
ima_free_template_entry(entry);
out:
return;
}
/**
* ima_kexec_cmdline - measure kexec cmdline boot args
* @buf: pointer to buffer
* @size: size of buffer
*
* Buffers can only be measured, not appraised.
*/
void ima_kexec_cmdline(const void *buf, int size)
{
u32 secid;
if (buf && size != 0) {
security_task_getsecid(current, &secid);
process_buffer_measurement(buf, size, "kexec-cmdline",
current_cred(), secid);
}
}
static int __init init_ima(void)
{
int error;
@ -589,6 +696,10 @@ static int __init init_ima(void)
error = ima_init();
}
error = register_blocking_lsm_notifier(&ima_lsm_policy_notifier);
if (error)
pr_warn("Couldn't register LSM notifier, error %d\n", error);
if (!error)
ima_update_policy_flag();

View File

@ -76,6 +76,7 @@ struct ima_rule_entry {
int type; /* audit type */
} lsm[MAX_LSM_RULES];
char *fsname;
struct ima_template_desc *template;
};
/*
@ -195,7 +196,7 @@ static struct ima_rule_entry secure_boot_rules[] __ro_after_init = {
};
/* An array of architecture specific rules */
struct ima_rule_entry *arch_policy_entry __ro_after_init;
static struct ima_rule_entry *arch_policy_entry __ro_after_init;
static LIST_HEAD(ima_default_rules);
static LIST_HEAD(ima_policy_rules);
@ -245,31 +246,113 @@ static int __init default_appraise_policy_setup(char *str)
}
__setup("ima_appraise_tcb", default_appraise_policy_setup);
static void ima_lsm_free_rule(struct ima_rule_entry *entry)
{
int i;
for (i = 0; i < MAX_LSM_RULES; i++) {
kfree(entry->lsm[i].rule);
kfree(entry->lsm[i].args_p);
}
kfree(entry);
}
static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)
{
struct ima_rule_entry *nentry;
int i, result;
nentry = kmalloc(sizeof(*nentry), GFP_KERNEL);
if (!nentry)
return NULL;
/*
* Immutable elements are copied over as pointers and data; only
* lsm rules can change
*/
memcpy(nentry, entry, sizeof(*nentry));
memset(nentry->lsm, 0, FIELD_SIZEOF(struct ima_rule_entry, lsm));
for (i = 0; i < MAX_LSM_RULES; i++) {
if (!entry->lsm[i].rule)
continue;
nentry->lsm[i].type = entry->lsm[i].type;
nentry->lsm[i].args_p = kstrdup(entry->lsm[i].args_p,
GFP_KERNEL);
if (!nentry->lsm[i].args_p)
goto out_err;
result = security_filter_rule_init(nentry->lsm[i].type,
Audit_equal,
nentry->lsm[i].args_p,
&nentry->lsm[i].rule);
if (result == -EINVAL)
pr_warn("ima: rule for LSM \'%d\' is undefined\n",
entry->lsm[i].type);
}
return nentry;
out_err:
ima_lsm_free_rule(nentry);
return NULL;
}
static int ima_lsm_update_rule(struct ima_rule_entry *entry)
{
struct ima_rule_entry *nentry;
nentry = ima_lsm_copy_rule(entry);
if (!nentry)
return -ENOMEM;
list_replace_rcu(&entry->list, &nentry->list);
synchronize_rcu();
ima_lsm_free_rule(entry);
return 0;
}
/*
* The LSM policy can be reloaded, leaving the IMA LSM based rules referring
* to the old, stale LSM policy. Update the IMA LSM based rules to reflect
* the reloaded LSM policy. We assume the rules still exist; and BUG_ON() if
* they don't.
* the reloaded LSM policy.
*/
static void ima_lsm_update_rules(void)
{
struct ima_rule_entry *entry;
int result;
int i;
struct ima_rule_entry *entry, *e;
int i, result, needs_update;
list_for_each_entry(entry, &ima_policy_rules, list) {
list_for_each_entry_safe(entry, e, &ima_policy_rules, list) {
needs_update = 0;
for (i = 0; i < MAX_LSM_RULES; i++) {
if (!entry->lsm[i].rule)
continue;
result = security_filter_rule_init(entry->lsm[i].type,
Audit_equal,
entry->lsm[i].args_p,
&entry->lsm[i].rule);
BUG_ON(!entry->lsm[i].rule);
if (entry->lsm[i].rule) {
needs_update = 1;
break;
}
}
if (!needs_update)
continue;
result = ima_lsm_update_rule(entry);
if (result) {
pr_err("ima: lsm rule update error %d\n",
result);
return;
}
}
}
int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
void *lsm_data)
{
if (event != LSM_POLICY_CHANGE)
return NOTIFY_DONE;
ima_lsm_update_rules();
return NOTIFY_OK;
}
/**
* ima_match_rules - determine whether an inode matches the measure rule.
* @rule: a pointer to a rule
@ -287,6 +370,11 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
{
int i;
if (func == KEXEC_CMDLINE) {
if ((rule->flags & IMA_FUNC) && (rule->func == func))
return true;
return false;
}
if ((rule->flags & IMA_FUNC) &&
(rule->func != func && func != POST_SETATTR))
return false;
@ -323,11 +411,10 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
for (i = 0; i < MAX_LSM_RULES; i++) {
int rc = 0;
u32 osid;
int retried = 0;
if (!rule->lsm[i].rule)
continue;
retry:
switch (i) {
case LSM_OBJ_USER:
case LSM_OBJ_ROLE:
@ -348,11 +435,6 @@ retry:
default:
break;
}
if ((rc < 0) && (!retried)) {
retried = 1;
ima_lsm_update_rules();
goto retry;
}
if (!rc)
return false;
}
@ -393,6 +475,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
* @func: IMA hook identifier
* @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
* @pcr: set the pcr to extend
* @template_desc: the template that should be used for this rule
*
* Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
* conditions.
@ -402,7 +485,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
* than writes so ima_match_policy() is classical RCU candidate.
*/
int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
enum ima_hooks func, int mask, int flags, int *pcr)
enum ima_hooks func, int mask, int flags, int *pcr,
struct ima_template_desc **template_desc)
{
struct ima_rule_entry *entry;
int action = 0, actmask = flags | (flags << 1);
@ -434,6 +518,11 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
if ((pcr) && (entry->flags & IMA_PCR))
*pcr = entry->pcr;
if (template_desc && entry->template)
*template_desc = entry->template;
else if (template_desc)
*template_desc = ima_template_desc_current();
if (!actmask)
break;
}
@ -672,7 +761,7 @@ enum {
Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt,
Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt,
Opt_appraise_type, Opt_permit_directio,
Opt_pcr, Opt_err
Opt_pcr, Opt_template, Opt_err
};
static const match_table_t policy_tokens = {
@ -706,6 +795,7 @@ static const match_table_t policy_tokens = {
{Opt_appraise_type, "appraise_type=%s"},
{Opt_permit_directio, "permit_directio"},
{Opt_pcr, "pcr=%s"},
{Opt_template, "template=%s"},
{Opt_err, NULL}
};
@ -759,6 +849,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
char *from;
char *p;
bool uid_token;
struct ima_template_desc *template_desc;
int result = 0;
ab = integrity_audit_log_start(audit_context(), GFP_KERNEL,
@ -866,6 +957,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->func = KEXEC_INITRAMFS_CHECK;
else if (strcmp(args[0].from, "POLICY_CHECK") == 0)
entry->func = POLICY_CHECK;
else if (strcmp(args[0].from, "KEXEC_CMDLINE") == 0)
entry->func = KEXEC_CMDLINE;
else
result = -EINVAL;
if (!result)
@ -1054,6 +1147,28 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
else
entry->flags |= IMA_PCR;
break;
case Opt_template:
ima_log_string(ab, "template", args[0].from);
if (entry->action != MEASURE) {
result = -EINVAL;
break;
}
template_desc = lookup_template_desc(args[0].from);
if (!template_desc || entry->template) {
result = -EINVAL;
break;
}
/*
* template_desc_init_fields() does nothing if
* the template is already initialised, so
* it's safe to do this unconditionally
*/
template_desc_init_fields(template_desc->fmt,
&(template_desc->fields),
&(template_desc->num_fields));
entry->template = template_desc;
break;
case Opt_err:
ima_log_string(ab, "UNKNOWN", p);
@ -1330,6 +1445,8 @@ int ima_policy_show(struct seq_file *m, void *v)
}
}
}
if (entry->template)
seq_printf(m, "template=%s ", entry->template->name);
if (entry->flags & IMA_DIGSIG_REQUIRED)
seq_puts(m, "appraise_type=imasig ");
if (entry->flags & IMA_PERMIT_DIRECTIO)

View File

@ -22,6 +22,7 @@ static struct ima_template_desc builtin_templates[] = {
{.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT},
{.name = "ima-ng", .fmt = "d-ng|n-ng"},
{.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
{.name = "ima-buf", .fmt = "d-ng|n-ng|buf"},
{.name = "", .fmt = ""}, /* placeholder for a custom format */
};
@ -39,14 +40,18 @@ static const struct ima_template_field supported_fields[] = {
.field_show = ima_show_template_string},
{.field_id = "sig", .field_init = ima_eventsig_init,
.field_show = ima_show_template_sig},
{.field_id = "buf", .field_init = ima_eventbuf_init,
.field_show = ima_show_template_buf},
};
#define MAX_TEMPLATE_NAME_LEN 15
/*
* Used when restoring measurements carried over from a kexec. 'd' and 'n' don't
* need to be accounted for since they shouldn't be defined in the same template
* description as 'd-ng' and 'n-ng' respectively.
*/
#define MAX_TEMPLATE_NAME_LEN sizeof("d-ng|n-ng|sig|buf")
static struct ima_template_desc *ima_template;
static struct ima_template_desc *lookup_template_desc(const char *name);
static int template_desc_init_fields(const char *template_fmt,
const struct ima_template_field ***fields,
int *num_fields);
static int __init ima_template_setup(char *str)
{
@ -104,7 +109,7 @@ static int __init ima_template_fmt_setup(char *str)
}
__setup("ima_template_fmt=", ima_template_fmt_setup);
static struct ima_template_desc *lookup_template_desc(const char *name)
struct ima_template_desc *lookup_template_desc(const char *name)
{
struct ima_template_desc *template_desc;
int found = 0;
@ -149,9 +154,9 @@ static int template_fmt_size(const char *template_fmt)
return j + 1;
}
static int template_desc_init_fields(const char *template_fmt,
const struct ima_template_field ***fields,
int *num_fields)
int template_desc_init_fields(const char *template_fmt,
const struct ima_template_field ***fields,
int *num_fields)
{
const char *template_fmt_ptr;
const struct ima_template_field *found_fields[IMA_TEMPLATE_NUM_FIELDS_MAX];

View File

@ -158,6 +158,12 @@ void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data);
}
void ima_show_template_buf(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data)
{
ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data);
}
/**
* ima_parse_buf() - Parses lengths and data from an input buffer
* @bufstartp: Buffer start address.
@ -385,3 +391,18 @@ int ima_eventsig_init(struct ima_event_data *event_data,
return ima_write_template_field_data(xattr_value, event_data->xattr_len,
DATA_FMT_HEX, field_data);
}
/*
* ima_eventbuf_init - include the buffer(kexec-cmldine) as part of the
* template data.
*/
int ima_eventbuf_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
if ((!event_data->buf) || (event_data->buf_len == 0))
return 0;
return ima_write_template_field_data(event_data->buf,
event_data->buf_len, DATA_FMT_HEX,
field_data);
}

View File

@ -25,6 +25,8 @@ void ima_show_template_string(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
void ima_show_template_buf(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
int maxfields, struct ima_field_data *fields, int *curfields,
unsigned long *len_mask, int enforce_mask, char *bufname);
@ -38,4 +40,6 @@ int ima_eventname_ng_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventbuf_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
#endif /* __LINUX_IMA_TEMPLATE_LIB_H */

View File

@ -76,6 +76,12 @@ enum evm_ima_xattr_type {
struct evm_ima_xattr_data {
u8 type;
u8 data[];
} __packed;
/* Only used in the EVM HMAC code. */
struct evm_xattr {
struct evm_ima_xattr_data data;
u8 digest[SHA1_DIGEST_SIZE];
} __packed;

View File

@ -35,7 +35,7 @@
#define LSM_COUNT (__end_lsm_info - __start_lsm_info)
struct security_hook_heads security_hook_heads __lsm_ro_after_init;
static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
static struct kmem_cache *lsm_file_cache;
static struct kmem_cache *lsm_inode_cache;
@ -426,23 +426,26 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
panic("%s - Cannot get early memory.\n", __func__);
}
int call_lsm_notifier(enum lsm_event event, void *data)
int call_blocking_lsm_notifier(enum lsm_event event, void *data)
{
return atomic_notifier_call_chain(&lsm_notifier_chain, event, data);
return blocking_notifier_call_chain(&blocking_lsm_notifier_chain,
event, data);
}
EXPORT_SYMBOL(call_lsm_notifier);
EXPORT_SYMBOL(call_blocking_lsm_notifier);
int register_lsm_notifier(struct notifier_block *nb)
int register_blocking_lsm_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_register(&lsm_notifier_chain, nb);
return blocking_notifier_chain_register(&blocking_lsm_notifier_chain,
nb);
}
EXPORT_SYMBOL(register_lsm_notifier);
EXPORT_SYMBOL(register_blocking_lsm_notifier);
int unregister_lsm_notifier(struct notifier_block *nb)
int unregister_blocking_lsm_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_unregister(&lsm_notifier_chain, nb);
return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain,
nb);
}
EXPORT_SYMBOL(unregister_lsm_notifier);
EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
/**
* lsm_cred_alloc - allocate a composite cred blob

View File

@ -194,7 +194,7 @@ static int selinux_lsm_notifier_avc_callback(u32 event)
{
if (event == AVC_CALLBACK_RESET) {
sel_ib_pkey_flush();
call_lsm_notifier(LSM_POLICY_CHANGE, NULL);
call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL);
}
return 0;

View File

@ -178,7 +178,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
selnl_notify_setenforce(new_value);
selinux_status_update_setenforce(state, new_value);
if (!new_value)
call_lsm_notifier(LSM_POLICY_CHANGE, NULL);
call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL);
}
length = count;
out: