Commit a60c538e authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'integrity-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity

Pull integrity subsystem updates from Mimi Zohar:
 "The large majority of the changes are EVM portable & immutable
  signature related: removing a dependency on loading an HMAC key,
  safely allowing file metadata included in the EVM portable & immutable
  signatures to be modified, allowing EVM signatures to fulfill IMA file
  signature policy requirements, including the EVM file metadata
  signature in lieu of an IMA file data signature in the measurement
  list, and adding dynamic debugging of EVM file metadata.

  In addition, in order to detect critical data or file change
  reversions, duplicate measurement records are permitted in the IMA
  measurement list.

  The remaining patches address compiler, sparse, and doc warnings"

* tag 'integrity-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity: (31 commits)
  evm: Check xattr size discrepancy between kernel and user
  evm: output EVM digest calculation info
  IMA: support for duplicate measurement records
  ima: Fix warning: no previous prototype for function 'ima_add_kexec_buffer'
  ima: differentiate between EVM failures in the audit log
  ima: Fix fall-through warning for Clang
  ima: Pass NULL instead of 0 to ima_get_action() in ima_file_mprotect()
  ima: Include header defining ima_post_key_create_or_update()
  ima/evm: Fix type mismatch
  ima: Set correct casting types
  doc: Fix warning in Documentation/security/IMA-templates.rst
  evm: Don't return an error in evm_write_xattrs() if audit is not enabled
  ima: Define new template evm-sig
  ima: Define new template fields xattrnames, xattrlengths and xattrvalues
  evm: Verify portable signatures against all protected xattrs
  ima: Define new template field imode
  ima: Define new template fields iuid and igid
  ima: Add ima_show_template_uint() template library function
  ima: Don't remove security.ima if file must not be appraised
  ima: Introduce template field evmsig and write to field sig as fallback
  ...
parents 9cd19f02 907a399d
...@@ -24,7 +24,7 @@ Description: ...@@ -24,7 +24,7 @@ Description:
1 Enable digital signature validation 1 Enable digital signature validation
2 Permit modification of EVM-protected metadata at 2 Permit modification of EVM-protected metadata at
runtime. Not supported if HMAC validation and runtime. Not supported if HMAC validation and
creation is enabled. creation is enabled (deprecated).
31 Disable further runtime modification of EVM policy 31 Disable further runtime modification of EVM policy
=== ================================================== === ==================================================
...@@ -47,10 +47,38 @@ Description: ...@@ -47,10 +47,38 @@ Description:
will enable digital signature validation, permit will enable digital signature validation, permit
modification of EVM-protected metadata and modification of EVM-protected metadata and
disable all further modification of policy disable all further modification of policy. This option is now
deprecated in favor of::
Note that once a key has been loaded, it will no longer be echo 0x80000002 ><securityfs>/evm
possible to enable metadata modification.
as the outstanding issues that prevent the usage of EVM portable
signatures have been solved.
Echoing a value is additive, the new value is added to the
existing initialization flags.
For example, after::
echo 2 ><securityfs>/evm
another echo can be performed::
echo 1 ><securityfs>/evm
and the resulting value will be 3.
Note that once an HMAC key has been loaded, it will no longer
be possible to enable metadata modification. Signaling that an
HMAC key has been loaded will clear the corresponding flag.
For example, if the current value is 6 (2 and 4 set)::
echo 1 ><securityfs>/evm
will set the new value to 3 (4 cleared).
Loading an HMAC key is the only way to disable metadata
modification.
Until key loading has been signaled EVM can not create Until key loading has been signaled EVM can not create
or validate the 'security.evm' xattr, but returns or validate the 'security.evm' xattr, but returns
......
...@@ -70,9 +70,18 @@ descriptors by adding their identifier to the format string ...@@ -70,9 +70,18 @@ descriptors by adding their identifier to the format string
prefix is shown only if the hash algorithm is not SHA1 or MD5); prefix is shown only if the hash algorithm is not SHA1 or MD5);
- 'd-modsig': the digest of the event without the appended modsig; - 'd-modsig': the digest of the event without the appended modsig;
- 'n-ng': the name of the event, without size limitations; - 'n-ng': the name of the event, without size limitations;
- 'sig': the file signature; - 'sig': the file signature, or the EVM portable signature if the file
signature is not found;
- 'modsig' the appended file signature; - 'modsig' the appended file signature;
- 'buf': the buffer data that was used to generate the hash without size limitations; - 'buf': the buffer data that was used to generate the hash without size limitations;
- 'evmsig': the EVM portable signature;
- 'iuid': the inode UID;
- 'igid': the inode GID;
- 'imode': the inode mode;
- 'xattrnames': a list of xattr names (separated by ``|``), only if the xattr is
present;
- 'xattrlengths': a list of xattr lengths (u32), only if the xattr is present;
- 'xattrvalues': a list of xattr values;
Below, there is the list of defined template descriptors: Below, there is the list of defined template descriptors:
...@@ -82,6 +91,7 @@ Below, there is the list of defined template descriptors: ...@@ -82,6 +91,7 @@ Below, there is the list of defined template descriptors:
- "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``; - "ima-buf": its format is ``d-ng|n-ng|buf``;
- "ima-modsig": its format is ``d-ng|n-ng|sig|d-modsig|modsig``; - "ima-modsig": its format is ``d-ng|n-ng|sig|d-modsig|modsig``;
- "evm-sig": its format is ``d-ng|n-ng|evmsig|xattrnames|xattrlengths|xattrvalues|iuid|igid|imode``;
Use Use
......
...@@ -23,18 +23,25 @@ extern enum integrity_status evm_verifyxattr(struct dentry *dentry, ...@@ -23,18 +23,25 @@ extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
struct integrity_iint_cache *iint); struct integrity_iint_cache *iint);
extern int evm_inode_setattr(struct dentry *dentry, struct iattr *attr); extern int evm_inode_setattr(struct dentry *dentry, struct iattr *attr);
extern void evm_inode_post_setattr(struct dentry *dentry, int ia_valid); extern void evm_inode_post_setattr(struct dentry *dentry, int ia_valid);
extern int evm_inode_setxattr(struct dentry *dentry, const char *name, extern int evm_inode_setxattr(struct user_namespace *mnt_userns,
struct dentry *dentry, const char *name,
const void *value, size_t size); const void *value, size_t size);
extern void evm_inode_post_setxattr(struct dentry *dentry, extern void evm_inode_post_setxattr(struct dentry *dentry,
const char *xattr_name, const char *xattr_name,
const void *xattr_value, const void *xattr_value,
size_t xattr_value_len); size_t xattr_value_len);
extern int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name); extern int evm_inode_removexattr(struct user_namespace *mnt_userns,
struct dentry *dentry, const char *xattr_name);
extern void evm_inode_post_removexattr(struct dentry *dentry, extern void evm_inode_post_removexattr(struct dentry *dentry,
const char *xattr_name); const char *xattr_name);
extern int evm_inode_init_security(struct inode *inode, extern int evm_inode_init_security(struct inode *inode,
const struct xattr *xattr_array, const struct xattr *xattr_array,
struct xattr *evm); struct xattr *evm);
extern bool evm_revalidate_status(const char *xattr_name);
extern int evm_protected_xattr_if_enabled(const char *req_xattr_name);
extern int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
int buffer_size, char type,
bool canonical_fmt);
#ifdef CONFIG_FS_POSIX_ACL #ifdef CONFIG_FS_POSIX_ACL
extern int posix_xattr_acl(const char *xattrname); extern int posix_xattr_acl(const char *xattrname);
#else #else
...@@ -71,7 +78,8 @@ static inline void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) ...@@ -71,7 +78,8 @@ static inline void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
return; return;
} }
static inline int evm_inode_setxattr(struct dentry *dentry, const char *name, static inline int evm_inode_setxattr(struct user_namespace *mnt_userns,
struct dentry *dentry, const char *name,
const void *value, size_t size) const void *value, size_t size)
{ {
return 0; return 0;
...@@ -85,7 +93,8 @@ static inline void evm_inode_post_setxattr(struct dentry *dentry, ...@@ -85,7 +93,8 @@ static inline void evm_inode_post_setxattr(struct dentry *dentry,
return; return;
} }
static inline int evm_inode_removexattr(struct dentry *dentry, static inline int evm_inode_removexattr(struct user_namespace *mnt_userns,
struct dentry *dentry,
const char *xattr_name) const char *xattr_name)
{ {
return 0; return 0;
...@@ -104,5 +113,22 @@ static inline int evm_inode_init_security(struct inode *inode, ...@@ -104,5 +113,22 @@ static inline int evm_inode_init_security(struct inode *inode,
return 0; return 0;
} }
static inline bool evm_revalidate_status(const char *xattr_name)
{
return false;
}
static inline int evm_protected_xattr_if_enabled(const char *req_xattr_name)
{
return false;
}
static inline int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
int buffer_size, char type,
bool canonical_fmt)
{
return -EOPNOTSUPP;
}
#endif /* CONFIG_EVM */ #endif /* CONFIG_EVM */
#endif /* LINUX_EVM_H */ #endif /* LINUX_EVM_H */
...@@ -13,6 +13,7 @@ enum integrity_status { ...@@ -13,6 +13,7 @@ enum integrity_status {
INTEGRITY_PASS = 0, INTEGRITY_PASS = 0,
INTEGRITY_PASS_IMMUTABLE, INTEGRITY_PASS_IMMUTABLE,
INTEGRITY_FAIL, INTEGRITY_FAIL,
INTEGRITY_FAIL_IMMUTABLE,
INTEGRITY_NOLABEL, INTEGRITY_NOLABEL,
INTEGRITY_NOXATTRS, INTEGRITY_NOXATTRS,
INTEGRITY_UNKNOWN, INTEGRITY_UNKNOWN,
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
struct xattr_list { struct xattr_list {
struct list_head list; struct list_head list;
char *name; char *name;
bool enabled;
}; };
extern int evm_initialized; extern int evm_initialized;
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
* Using root's kernel master key (kmk), calculate the HMAC * Using root's kernel master key (kmk), calculate the HMAC
*/ */
#define pr_fmt(fmt) "EVM: "fmt
#include <linux/export.h> #include <linux/export.h>
#include <linux/crypto.h> #include <linux/crypto.h>
#include <linux/xattr.h> #include <linux/xattr.h>
...@@ -175,6 +177,30 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, ...@@ -175,6 +177,30 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
type != EVM_XATTR_PORTABLE_DIGSIG) type != EVM_XATTR_PORTABLE_DIGSIG)
crypto_shash_update(desc, (u8 *)&inode->i_sb->s_uuid, UUID_SIZE); crypto_shash_update(desc, (u8 *)&inode->i_sb->s_uuid, UUID_SIZE);
crypto_shash_final(desc, digest); crypto_shash_final(desc, digest);
pr_debug("hmac_misc: (%zu) [%*phN]\n", sizeof(struct h_misc),
(int)sizeof(struct h_misc), &hmac_misc);
}
/*
* Dump large security xattr values as a continuous ascii hexademical string.
* (pr_debug is limited to 64 bytes.)
*/
static void dump_security_xattr(const char *prefix, const void *src,
size_t count)
{
#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
char *asciihex, *p;
p = asciihex = kmalloc(count * 2 + 1, GFP_KERNEL);
if (!asciihex)
return;
p = bin2hex(p, src, count);
*p = 0;
pr_debug("%s: (%zu) %.*s\n", prefix, count, (int)count * 2, asciihex);
kfree(asciihex);
#endif
} }
/* /*
...@@ -196,7 +222,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, ...@@ -196,7 +222,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
size_t xattr_size = 0; size_t xattr_size = 0;
char *xattr_value = NULL; char *xattr_value = NULL;
int error; int error;
int size; int size, user_space_size;
bool ima_present = false; bool ima_present = false;
if (!(inode->i_opflags & IOP_XATTR) || if (!(inode->i_opflags & IOP_XATTR) ||
...@@ -216,6 +242,13 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, ...@@ -216,6 +242,13 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
if (strcmp(xattr->name, XATTR_NAME_IMA) == 0) if (strcmp(xattr->name, XATTR_NAME_IMA) == 0)
is_ima = true; is_ima = true;
/*
* Skip non-enabled xattrs for locally calculated
* signatures/HMACs.
*/
if (type != EVM_XATTR_PORTABLE_DIGSIG && !xattr->enabled)
continue;
if ((req_xattr_name && req_xattr_value) if ((req_xattr_name && req_xattr_value)
&& !strcmp(xattr->name, req_xattr_name)) { && !strcmp(xattr->name, req_xattr_name)) {
error = 0; error = 0;
...@@ -223,6 +256,16 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, ...@@ -223,6 +256,16 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
req_xattr_value_len); req_xattr_value_len);
if (is_ima) if (is_ima)
ima_present = true; ima_present = true;
if (req_xattr_value_len < 64)
pr_debug("%s: (%zu) [%*phN]\n", req_xattr_name,
req_xattr_value_len,
(int)req_xattr_value_len,
req_xattr_value);
else
dump_security_xattr(req_xattr_name,
req_xattr_value,
req_xattr_value_len);
continue; continue;
} }
size = vfs_getxattr_alloc(&init_user_ns, dentry, xattr->name, size = vfs_getxattr_alloc(&init_user_ns, dentry, xattr->name,
...@@ -234,11 +277,24 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, ...@@ -234,11 +277,24 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
if (size < 0) if (size < 0)
continue; continue;
user_space_size = vfs_getxattr(&init_user_ns, dentry,
xattr->name, NULL, 0);
if (user_space_size != size)
pr_debug("file %s: xattr %s size mismatch (kernel: %d, user: %d)\n",
dentry->d_name.name, xattr->name, size,
user_space_size);
error = 0; error = 0;
xattr_size = size; xattr_size = size;
crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size); crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
if (is_ima) if (is_ima)
ima_present = true; ima_present = true;
if (xattr_size < 64)
pr_debug("%s: (%zu) [%*phN]", xattr->name, xattr_size,
(int)xattr_size, xattr_value);
else
dump_security_xattr(xattr->name, xattr_value,
xattr_size);
} }
hmac_add_misc(desc, inode, type, data->digest); hmac_add_misc(desc, inode, type, data->digest);
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
* evm_inode_removexattr, and evm_verifyxattr * evm_inode_removexattr, and evm_verifyxattr
*/ */
#define pr_fmt(fmt) "EVM: "fmt
#include <linux/init.h> #include <linux/init.h>
#include <linux/crypto.h> #include <linux/crypto.h>
#include <linux/audit.h> #include <linux/audit.h>
...@@ -18,6 +20,7 @@ ...@@ -18,6 +20,7 @@
#include <linux/integrity.h> #include <linux/integrity.h>
#include <linux/evm.h> #include <linux/evm.h>
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/posix_acl_xattr.h>
#include <crypto/hash.h> #include <crypto/hash.h>
#include <crypto/hash_info.h> #include <crypto/hash_info.h>
...@@ -27,29 +30,50 @@ ...@@ -27,29 +30,50 @@
int evm_initialized; int evm_initialized;
static const char * const integrity_status_msg[] = { static const char * const integrity_status_msg[] = {
"pass", "pass_immutable", "fail", "no_label", "no_xattrs", "unknown" "pass", "pass_immutable", "fail", "fail_immutable", "no_label",
"no_xattrs", "unknown"
}; };
int evm_hmac_attrs; int evm_hmac_attrs;
static struct xattr_list evm_config_default_xattrnames[] = { static struct xattr_list evm_config_default_xattrnames[] = {
{.name = XATTR_NAME_SELINUX,
#ifdef CONFIG_SECURITY_SELINUX #ifdef CONFIG_SECURITY_SELINUX
{.name = XATTR_NAME_SELINUX}, .enabled = true
#endif #endif
},
{.name = XATTR_NAME_SMACK,
#ifdef CONFIG_SECURITY_SMACK #ifdef CONFIG_SECURITY_SMACK
{.name = XATTR_NAME_SMACK}, .enabled = true
#endif
},
{.name = XATTR_NAME_SMACKEXEC,
#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS #ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS
{.name = XATTR_NAME_SMACKEXEC}, .enabled = true
{.name = XATTR_NAME_SMACKTRANSMUTE},
{.name = XATTR_NAME_SMACKMMAP},
#endif #endif
},
{.name = XATTR_NAME_SMACKTRANSMUTE,
#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS
.enabled = true
#endif
},
{.name = XATTR_NAME_SMACKMMAP,
#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS
.enabled = true
#endif #endif
},
{.name = XATTR_NAME_APPARMOR,
#ifdef CONFIG_SECURITY_APPARMOR #ifdef CONFIG_SECURITY_APPARMOR
{.name = XATTR_NAME_APPARMOR}, .enabled = true
#endif #endif
},
{.name = XATTR_NAME_IMA,
#ifdef CONFIG_IMA_APPRAISE #ifdef CONFIG_IMA_APPRAISE
{.name = XATTR_NAME_IMA}, .enabled = true
#endif #endif
{.name = XATTR_NAME_CAPS}, },
{.name = XATTR_NAME_CAPS,
.enabled = true
},
}; };
LIST_HEAD(evm_config_xattrnames); LIST_HEAD(evm_config_xattrnames);
...@@ -74,7 +98,9 @@ static void __init evm_init_config(void) ...@@ -74,7 +98,9 @@ static void __init evm_init_config(void)
pr_info("Initialising EVM extended attributes:\n"); pr_info("Initialising EVM extended attributes:\n");
for (i = 0; i < xattrs; i++) { for (i = 0; i < xattrs; i++) {
pr_info("%s\n", evm_config_default_xattrnames[i].name); pr_info("%s%s\n", evm_config_default_xattrnames[i].name,
!evm_config_default_xattrnames[i].enabled ?
" (disabled)" : "");
list_add_tail(&evm_config_default_xattrnames[i].list, list_add_tail(&evm_config_default_xattrnames[i].list,
&evm_config_xattrnames); &evm_config_xattrnames);
} }
...@@ -90,6 +116,24 @@ static bool evm_key_loaded(void) ...@@ -90,6 +116,24 @@ static bool evm_key_loaded(void)
return (bool)(evm_initialized & EVM_KEY_MASK); return (bool)(evm_initialized & EVM_KEY_MASK);
} }
/*
* This function determines whether or not it is safe to ignore verification
* errors, based on the ability of EVM to calculate HMACs. If the HMAC key
* is not loaded, and it cannot be loaded in the future due to the
* EVM_SETUP_COMPLETE initialization flag, allowing an operation despite the
* attrs/xattrs being found invalid will not make them valid.
*/
static bool evm_hmac_disabled(void)
{
if (evm_initialized & EVM_INIT_HMAC)
return false;
if (!(evm_initialized & EVM_SETUP_COMPLETE))
return false;
return true;
}
static int evm_find_protected_xattrs(struct dentry *dentry) static int evm_find_protected_xattrs(struct dentry *dentry)
{ {
struct inode *inode = d_backing_inode(dentry); struct inode *inode = d_backing_inode(dentry);
...@@ -137,7 +181,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, ...@@ -137,7 +181,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
enum integrity_status evm_status = INTEGRITY_PASS; enum integrity_status evm_status = INTEGRITY_PASS;
struct evm_digest digest; struct evm_digest digest;
struct inode *inode; struct inode *inode;
int rc, xattr_len; int rc, xattr_len, evm_immutable = 0;
if (iint && (iint->evm_status == INTEGRITY_PASS || if (iint && (iint->evm_status == INTEGRITY_PASS ||
iint->evm_status == INTEGRITY_PASS_IMMUTABLE)) iint->evm_status == INTEGRITY_PASS_IMMUTABLE))
...@@ -182,8 +226,10 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, ...@@ -182,8 +226,10 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
if (rc) if (rc)
rc = -EINVAL; rc = -EINVAL;
break; break;
case EVM_IMA_XATTR_DIGSIG:
case EVM_XATTR_PORTABLE_DIGSIG: case EVM_XATTR_PORTABLE_DIGSIG:
evm_immutable = 1;
fallthrough;
case EVM_IMA_XATTR_DIGSIG:
/* accept xattr with non-empty signature field */ /* accept xattr with non-empty signature field */
if (xattr_len <= sizeof(struct signature_v2_hdr)) { if (xattr_len <= sizeof(struct signature_v2_hdr)) {
evm_status = INTEGRITY_FAIL; evm_status = INTEGRITY_FAIL;
...@@ -220,9 +266,16 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, ...@@ -220,9 +266,16 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
break; break;
} }
if (rc) if (rc) {
evm_status = (rc == -ENODATA) ? if (rc == -ENODATA)
INTEGRITY_NOXATTRS : INTEGRITY_FAIL; evm_status = INTEGRITY_NOXATTRS;
else if (evm_immutable)
evm_status = INTEGRITY_FAIL_IMMUTABLE;
else
evm_status = INTEGRITY_FAIL;
}
pr_debug("digest: (%d) [%*phN]\n", digest.hdr.length, digest.hdr.length,
digest.digest);
out: out:
if (iint) if (iint)
iint->evm_status = evm_status; iint->evm_status = evm_status;
...@@ -230,7 +283,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, ...@@ -230,7 +283,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
return evm_status; return evm_status;
} }
static int evm_protected_xattr(const char *req_xattr_name) static int evm_protected_xattr_common(const char *req_xattr_name,
bool all_xattrs)
{ {
int namelen; int namelen;
int found = 0; int found = 0;
...@@ -238,6 +292,9 @@ static int evm_protected_xattr(const char *req_xattr_name) ...@@ -238,6 +292,9 @@ static int evm_protected_xattr(const char *req_xattr_name)
namelen = strlen(req_xattr_name); namelen = strlen(req_xattr_name);
list_for_each_entry_lockless(xattr, &evm_config_xattrnames, list) { list_for_each_entry_lockless(xattr, &evm_config_xattrnames, list) {
if (!all_xattrs && !xattr->enabled)
continue;
if ((strlen(xattr->name) == namelen) if ((strlen(xattr->name) == namelen)
&& (strncmp(req_xattr_name, xattr->name, namelen) == 0)) { && (strncmp(req_xattr_name, xattr->name, namelen) == 0)) {
found = 1; found = 1;
...@@ -254,6 +311,85 @@ static int evm_protected_xattr(const char *req_xattr_name) ...@@ -254,6 +311,85 @@ static int evm_protected_xattr(const char *req_xattr_name)
return found; return found;
} }
static int evm_protected_xattr(const char *req_xattr_name)
{
return evm_protected_xattr_common(req_xattr_name, false);
}
int evm_protected_xattr_if_enabled(const char *req_xattr_name)
{
return evm_protected_xattr_common(req_xattr_name, true);
}
/**
* evm_read_protected_xattrs - read EVM protected xattr names, lengths, values
* @dentry: dentry of the read xattrs
* @inode: inode of the read xattrs
* @buffer: buffer xattr names, lengths or values are copied to
* @buffer_size: size of buffer
* @type: n: names, l: lengths, v: values
* @canonical_fmt: data format (true: little endian, false: native format)
*
* Read protected xattr names (separated by |), lengths (u32) or values for a
* given dentry and return the total size of copied data. If buffer is NULL,
* just return the total size.
*
* Returns the total size on success, a negative value on error.
*/
int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
int buffer_size, char type, bool canonical_fmt)
{
struct xattr_list *xattr;
int rc, size, total_size = 0;
list_for_each_entry_lockless(xattr, &evm_config_xattrnames, list) {
rc = __vfs_getxattr(dentry, d_backing_inode(dentry),
xattr->name, NULL, 0);
if (rc < 0 && rc == -ENODATA)
continue;
else if (rc < 0)
return rc;
switch (type) {
case 'n':
size = strlen(xattr->name) + 1;
if (buffer) {
if (total_size)
*(buffer + total_size - 1) = '|';
memcpy(buffer + total_size, xattr->name, size);
}
break;
case 'l':
size = sizeof(u32);
if (buffer) {
if (canonical_fmt)
rc = (__force int)cpu_to_le32(rc);
*(u32 *)(buffer + total_size) = rc;
}
break;
case 'v':
size = rc;
if (buffer) {
rc = __vfs_getxattr(dentry,
d_backing_inode(dentry), xattr->name,
buffer + total_size,
buffer_size - total_size);
if (rc < 0)
return rc;
}
break;
default:
return -EINVAL;
}
total_size += size;
}
return total_size;
}
/** /**
* evm_verifyxattr - verify the integrity of the requested xattr * evm_verifyxattr - verify the integrity of the requested xattr
* @dentry: object of the verify xattr * @dentry: object of the verify xattr
...@@ -304,6 +440,92 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry) ...@@ -304,6 +440,92 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
return evm_verify_hmac(dentry, NULL, NULL, 0, NULL); return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
} }
/*
* evm_xattr_acl_change - check if passed ACL changes the inode mode
* @mnt_userns: user namespace of the idmapped mount
* @dentry: pointer to the affected dentry
* @xattr_name: requested xattr
* @xattr_value: requested xattr value
* @xattr_value_len: requested xattr value length
*
* Check if passed ACL changes the inode mode, which is protected by EVM.
*
* Returns 1 if passed ACL causes inode mode change, 0 otherwise.
*/
static int evm_xattr_acl_change(struct user_namespace *mnt_userns,
struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
{
#ifdef CONFIG_FS_POSIX_ACL
umode_t mode;
struct posix_acl *acl = NULL, *acl_res;
struct inode *inode = d_backing_inode(dentry);
int rc;
/*
* user_ns is not relevant here, ACL_USER/ACL_GROUP don't have impact
* on the inode mode (see posix_acl_equiv_mode()).
*/
acl = posix_acl_from_xattr(&init_user_ns, xattr_value, xattr_value_len);
if (IS_ERR_OR_NULL(acl))
return 1;
acl_res = acl;
/*
* Passing mnt_userns is necessary to correctly determine the GID in
* an idmapped mount, as the GID is used to clear the setgid bit in
* the inode mode.
*/
rc = posix_acl_update_mode(mnt_userns, inode, &mode, &acl_res);
posix_acl_release(acl);
if (rc)
return 1;
if (inode->i_mode != mode)
return 1;
#endif
return 0;
}
/*
* evm_xattr_change - check if passed xattr value differs from current value
* @mnt_userns: user namespace of the idmapped mount
* @dentry: pointer to the affected dentry
* @xattr_name: requested xattr
* @xattr_value: requested xattr value
* @xattr_value_len: requested xattr value length
*
* Check if passed xattr value differs from current value.
*
* Returns 1 if passed xattr value differs from current value, 0 otherwise.
*/
static int evm_xattr_change(struct user_namespace *mnt_userns,
struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
{
char *xattr_data = NULL;
int rc = 0;
if (posix_xattr_acl(xattr_name))
return evm_xattr_acl_change(mnt_userns, dentry, xattr_name,
xattr_value, xattr_value_len);
rc = vfs_getxattr_alloc(&init_user_ns, dentry, xattr_name, &xattr_data,
0, GFP_NOFS);
if (rc < 0)
return 1;
if (rc == xattr_value_len)
rc = !!memcmp(xattr_value, xattr_data, rc);
else
rc = 1;
kfree(xattr_data);
return rc;
}
/* /*
* evm_protect_xattr - protect the EVM extended attribute * evm_protect_xattr - protect the EVM extended attribute
* *
...@@ -316,7 +538,8 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry) ...@@ -316,7 +538,8 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
* For posix xattr acls only, permit security.evm, even if it currently * For posix xattr acls only, permit security.evm, even if it currently
* doesn't exist, to be updated unless the EVM signature is immutable. * doesn't exist, to be updated unless the EVM signature is immutable.
*/ */
static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, static int evm_protect_xattr(struct user_namespace *mnt_userns,
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)
{ {
enum integrity_status evm_status; enum integrity_status evm_status;
...@@ -338,6 +561,10 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, ...@@ -338,6 +561,10 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
if (evm_status == INTEGRITY_NOXATTRS) { if (evm_status == INTEGRITY_NOXATTRS) {
struct integrity_iint_cache *iint; struct integrity_iint_cache *iint;
/* Exception if the HMAC is not going to be calculated. */
if (evm_hmac_disabled())
return 0;
iint = integrity_iint_find(d_backing_inode(dentry)); iint = integrity_iint_find(d_backing_inode(dentry));
if (iint && (iint->flags & IMA_NEW_FILE)) if (iint && (iint->flags & IMA_NEW_FILE))
return 0; return 0;
...@@ -354,7 +581,25 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, ...@@ -354,7 +581,25 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
-EPERM, 0); -EPERM, 0);
} }
out: out:
if (evm_status != INTEGRITY_PASS) /* Exception if the HMAC is not going to be calculated. */
if (evm_hmac_disabled() && (evm_status == INTEGRITY_NOLABEL ||
evm_status == INTEGRITY_UNKNOWN))
return 0;
/*
* Writing other xattrs is safe for portable signatures, as portable
* signatures are immutable and can never be updated.
*/
if (evm_status == INTEGRITY_FAIL_IMMUTABLE)
return 0;
if (evm_status == INTEGRITY_PASS_IMMUTABLE &&
!evm_xattr_change(mnt_userns, dentry, xattr_name, xattr_value,
xattr_value_len))
return 0;
if (evm_status != INTEGRITY_PASS &&
evm_status != INTEGRITY_PASS_IMMUTABLE)
integrity_audit_msg(AUDIT_INTEGRITY_METADATA, d_backing_inode(dentry), integrity_audit_msg(AUDIT_INTEGRITY_METADATA, d_backing_inode(dentry),
dentry->d_name.name, "appraise_metadata", dentry->d_name.name, "appraise_metadata",
integrity_status_msg[evm_status], integrity_status_msg[evm_status],
...@@ -364,6 +609,7 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, ...@@ -364,6 +609,7 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
/** /**
* evm_inode_setxattr - protect the EVM extended attribute * evm_inode_setxattr - protect the EVM extended attribute
* @mnt_userns: user namespace of the idmapped mount
* @dentry: pointer to the affected dentry * @dentry: pointer to the affected dentry
* @xattr_name: pointer to the affected extended attribute name * @xattr_name: pointer to the affected extended attribute name
* @xattr_value: pointer to the new extended attribute value * @xattr_value: pointer to the new extended attribute value
...@@ -375,8 +621,9 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, ...@@ -375,8 +621,9 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
* userspace from writing HMAC value. Writing 'security.evm' requires * userspace from writing HMAC value. Writing 'security.evm' requires
* requires CAP_SYS_ADMIN privileges. * requires CAP_SYS_ADMIN privileges.
*/ */
int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name, int evm_inode_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
const void *xattr_value, size_t xattr_value_len) const char *xattr_name, const void *xattr_value,
size_t xattr_value_len)
{ {
const struct evm_ima_xattr_data *xattr_data = xattr_value; const struct evm_ima_xattr_data *xattr_data = xattr_value;
...@@ -393,19 +640,21 @@ int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name, ...@@ -393,19 +640,21 @@ int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG) xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG)
return -EPERM; return -EPERM;
} }
return evm_protect_xattr(dentry, xattr_name, xattr_value, return evm_protect_xattr(mnt_userns, dentry, xattr_name, xattr_value,
xattr_value_len); xattr_value_len);
} }
/** /**
* evm_inode_removexattr - protect the EVM extended attribute * evm_inode_removexattr - protect the EVM extended attribute
* @mnt_userns: user namespace of the idmapped mount
* @dentry: pointer to the affected dentry * @dentry: pointer to the affected dentry
* @xattr_name: pointer to the affected extended attribute name * @xattr_name: pointer to the affected extended attribute name
* *
* Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that * Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that
* the current value is valid. * the current value is valid.
*/ */
int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name) int evm_inode_removexattr(struct user_namespace *mnt_userns,
struct dentry *dentry, const char *xattr_name)
{ {
/* Policy permits modification of the protected xattrs even though /* Policy permits modification of the protected xattrs even though
* there's no HMAC key loaded * there's no HMAC key loaded
...@@ -413,7 +662,7 @@ int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name) ...@@ -413,7 +662,7 @@ int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name)
if (evm_initialized & EVM_ALLOW_METADATA_WRITES) if (evm_initialized & EVM_ALLOW_METADATA_WRITES)
return 0; return 0;
return evm_protect_xattr(dentry, xattr_name, NULL, 0); return evm_protect_xattr(mnt_userns, dentry, xattr_name, NULL, 0);
} }
static void evm_reset_status(struct inode *inode) static void evm_reset_status(struct inode *inode)
...@@ -425,6 +674,31 @@ static void evm_reset_status(struct inode *inode) ...@@ -425,6 +674,31 @@ static void evm_reset_status(struct inode *inode)
iint->evm_status = INTEGRITY_UNKNOWN; iint->evm_status = INTEGRITY_UNKNOWN;
} }
/**
* evm_revalidate_status - report whether EVM status re-validation is necessary
* @xattr_name: pointer to the affected extended attribute name
*
* Report whether callers of evm_verifyxattr() should re-validate the
* EVM status.
*
* Return true if re-validation is necessary, false otherwise.
*/
bool evm_revalidate_status(const char *xattr_name)
{
if (!evm_key_loaded())
return false;
/* evm_inode_post_setattr() passes NULL */
if (!xattr_name)
return true;
if (!evm_protected_xattr(xattr_name) && !posix_xattr_acl(xattr_name) &&
strcmp(xattr_name, XATTR_NAME_EVM))
return false;
return true;
}
/** /**
* evm_inode_post_setxattr - update 'security.evm' to reflect the changes * evm_inode_post_setxattr - update 'security.evm' to reflect the changes
* @dentry: pointer to the affected dentry * @dentry: pointer to the affected dentry
...@@ -441,12 +715,17 @@ static void evm_reset_status(struct inode *inode) ...@@ -441,12 +715,17 @@ static void evm_reset_status(struct inode *inode)
void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, void evm_inode_post_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)
{ {
if (!evm_key_loaded() || (!evm_protected_xattr(xattr_name) if (!evm_revalidate_status(xattr_name))
&& !posix_xattr_acl(xattr_name)))
return; return;
evm_reset_status(dentry->d_inode); evm_reset_status(dentry->d_inode);
if (!strcmp(xattr_name, XATTR_NAME_EVM))
return;
if (!(evm_initialized & EVM_INIT_HMAC))
return;
evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len);
} }
...@@ -462,14 +741,33 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, ...@@ -462,14 +741,33 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
*/ */
void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
{ {
if (!evm_key_loaded() || !evm_protected_xattr(xattr_name)) if (!evm_revalidate_status(xattr_name))
return; return;
evm_reset_status(dentry->d_inode); evm_reset_status(dentry->d_inode);
if (!strcmp(xattr_name, XATTR_NAME_EVM))
return;
if (!(evm_initialized & EVM_INIT_HMAC))
return;
evm_update_evmxattr(dentry, xattr_name, NULL, 0); evm_update_evmxattr(dentry, xattr_name, NULL, 0);
} }
static int evm_attr_change(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = d_backing_inode(dentry);
unsigned int ia_valid = attr->ia_valid;
if ((!(ia_valid & ATTR_UID) || uid_eq(attr->ia_uid, inode->i_uid)) &&
(!(ia_valid & ATTR_GID) || gid_eq(attr->ia_gid, inode->i_gid)) &&
(!(ia_valid & ATTR_MODE) || attr->ia_mode == inode->i_mode))
return 0;
return 1;
}
/** /**
* evm_inode_setattr - prevent updating an invalid EVM extended attribute * evm_inode_setattr - prevent updating an invalid EVM extended attribute
* @dentry: pointer to the affected dentry * @dentry: pointer to the affected dentry
...@@ -491,9 +789,21 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -491,9 +789,21 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))) if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)))
return 0; return 0;
evm_status = evm_verify_current_integrity(dentry); evm_status = evm_verify_current_integrity(dentry);
/*
* Writing attrs is safe for portable signatures, as portable signatures
* are immutable and can never be updated.
*/
if ((evm_status == INTEGRITY_PASS) || if ((evm_status == INTEGRITY_PASS) ||
(evm_status == INTEGRITY_NOXATTRS)) (evm_status == INTEGRITY_NOXATTRS) ||
(evm_status == INTEGRITY_FAIL_IMMUTABLE) ||
(evm_hmac_disabled() && (evm_status == INTEGRITY_NOLABEL ||
evm_status == INTEGRITY_UNKNOWN)))
return 0;
if (evm_status == INTEGRITY_PASS_IMMUTABLE &&
!evm_attr_change(dentry, attr))
return 0; return 0;
integrity_audit_msg(AUDIT_INTEGRITY_METADATA, d_backing_inode(dentry), integrity_audit_msg(AUDIT_INTEGRITY_METADATA, d_backing_inode(dentry),
dentry->d_name.name, "appraise_metadata", dentry->d_name.name, "appraise_metadata",
integrity_status_msg[evm_status], -EPERM, 0); integrity_status_msg[evm_status], -EPERM, 0);
...@@ -513,7 +823,12 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -513,7 +823,12 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
*/ */
void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
{ {
if (!evm_key_loaded()) if (!evm_revalidate_status(NULL))
return;
evm_reset_status(dentry->d_inode);
if (!(evm_initialized & EVM_INIT_HMAC))
return; return;
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
...@@ -521,7 +836,7 @@ void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) ...@@ -521,7 +836,7 @@ void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
} }
/* /*
* evm_inode_init_security - initializes security.evm * evm_inode_init_security - initializes security.evm HMAC value
*/ */
int evm_inode_init_security(struct inode *inode, int evm_inode_init_security(struct inode *inode,
const struct xattr *lsm_xattr, const struct xattr *lsm_xattr,
...@@ -530,7 +845,8 @@ int evm_inode_init_security(struct inode *inode, ...@@ -530,7 +845,8 @@ int evm_inode_init_security(struct inode *inode,
struct evm_xattr *xattr_data; struct evm_xattr *xattr_data;
int rc; int rc;
if (!evm_key_loaded() || !evm_protected_xattr(lsm_xattr->name)) if (!(evm_initialized & EVM_INIT_HMAC) ||
!evm_protected_xattr(lsm_xattr->name))
return 0; return 0;
xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS); xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS);
......
...@@ -66,12 +66,13 @@ static ssize_t evm_read_key(struct file *filp, char __user *buf, ...@@ -66,12 +66,13 @@ static ssize_t evm_read_key(struct file *filp, char __user *buf,
static ssize_t evm_write_key(struct file *file, const char __user *buf, static ssize_t evm_write_key(struct file *file, const char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
int i, ret; unsigned int i;
int ret;
if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP_COMPLETE)) if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP_COMPLETE))
return -EPERM; return -EPERM;
ret = kstrtoint_from_user(buf, count, 0, &i); ret = kstrtouint_from_user(buf, count, 0, &i);
if (ret) if (ret)
return ret; return ret;
...@@ -80,12 +81,12 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf, ...@@ -80,12 +81,12 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf,
if (!i || (i & ~EVM_INIT_MASK) != 0) if (!i || (i & ~EVM_INIT_MASK) != 0)
return -EINVAL; return -EINVAL;
/* Don't allow a request to freshly enable metadata writes if /*
* keys are loaded. * Don't allow a request to enable metadata writes if
* an HMAC key is loaded.
*/ */
if ((i & EVM_ALLOW_METADATA_WRITES) && if ((i & EVM_ALLOW_METADATA_WRITES) &&
((evm_initialized & EVM_KEY_MASK) != 0) && (evm_initialized & EVM_INIT_HMAC) != 0)
!(evm_initialized & EVM_ALLOW_METADATA_WRITES))
return -EPERM; return -EPERM;
if (i & EVM_INIT_HMAC) { if (i & EVM_INIT_HMAC) {
...@@ -138,8 +139,12 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf, ...@@ -138,8 +139,12 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf,
if (rc) if (rc)
return -ERESTARTSYS; return -ERESTARTSYS;
list_for_each_entry(xattr, &evm_config_xattrnames, list) list_for_each_entry(xattr, &evm_config_xattrnames, list) {
if (!xattr->enabled)
continue;
size += strlen(xattr->name) + 1; size += strlen(xattr->name) + 1;
}
temp = kmalloc(size + 1, GFP_KERNEL); temp = kmalloc(size + 1, GFP_KERNEL);
if (!temp) { if (!temp) {
...@@ -148,6 +153,9 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf, ...@@ -148,6 +153,9 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf,
} }
list_for_each_entry(xattr, &evm_config_xattrnames, list) { list_for_each_entry(xattr, &evm_config_xattrnames, list) {
if (!xattr->enabled)
continue;
sprintf(temp + offset, "%s\n", xattr->name); sprintf(temp + offset, "%s\n", xattr->name);
offset += strlen(xattr->name) + 1; offset += strlen(xattr->name) + 1;
} }
...@@ -189,7 +197,7 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf, ...@@ -189,7 +197,7 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
ab = audit_log_start(audit_context(), GFP_KERNEL, ab = audit_log_start(audit_context(), GFP_KERNEL,
AUDIT_INTEGRITY_EVM_XATTR); AUDIT_INTEGRITY_EVM_XATTR);
if (!ab) if (!ab && IS_ENABLED(CONFIG_AUDIT))
return -ENOMEM; return -ENOMEM;
xattr = kmalloc(sizeof(struct xattr_list), GFP_KERNEL); xattr = kmalloc(sizeof(struct xattr_list), GFP_KERNEL);
...@@ -198,6 +206,7 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf, ...@@ -198,6 +206,7 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
goto out; goto out;
} }
xattr->enabled = true;
xattr->name = memdup_user_nul(buf, count); xattr->name = memdup_user_nul(buf, count);
if (IS_ERR(xattr->name)) { if (IS_ERR(xattr->name)) {
err = PTR_ERR(xattr->name); err = PTR_ERR(xattr->name);
...@@ -244,6 +253,10 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf, ...@@ -244,6 +253,10 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
list_for_each_entry(tmp, &evm_config_xattrnames, list) { list_for_each_entry(tmp, &evm_config_xattrnames, list) {
if (strcmp(xattr->name, tmp->name) == 0) { if (strcmp(xattr->name, tmp->name) == 0) {
err = -EEXIST; err = -EEXIST;
if (!tmp->enabled) {
tmp->enabled = true;
err = count;
}
mutex_unlock(&xattr_list_mutex); mutex_unlock(&xattr_list_mutex);
goto out; goto out;
} }
...@@ -255,7 +268,7 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf, ...@@ -255,7 +268,7 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
audit_log_end(ab); audit_log_end(ab);
return count; return count;
out: out:
audit_log_format(ab, " res=%d", err); audit_log_format(ab, " res=%d", (err < 0) ? err : 0);
audit_log_end(ab); audit_log_end(ab);
if (xattr) { if (xattr) {
kfree(xattr->name); kfree(xattr->name);
......
...@@ -208,6 +208,8 @@ int integrity_kernel_read(struct file *file, loff_t offset, ...@@ -208,6 +208,8 @@ int integrity_kernel_read(struct file *file, loff_t offset,
void __init integrity_load_keys(void) void __init integrity_load_keys(void)
{ {
ima_load_x509(); ima_load_x509();
if (!IS_ENABLED(CONFIG_IMA_LOAD_X509))
evm_load_x509(); evm_load_x509();
} }
......
...@@ -334,3 +334,10 @@ config IMA_SECURE_AND_OR_TRUSTED_BOOT ...@@ -334,3 +334,10 @@ config IMA_SECURE_AND_OR_TRUSTED_BOOT
help help
This option is selected by architectures to enable secure and/or This option is selected by architectures to enable secure and/or
trusted boot based on IMA runtime policies. trusted boot based on IMA runtime policies.
config IMA_DISABLE_HTABLE
bool "Disable htable to allow measurement of duplicate records"
depends on IMA
default n
help
This option disables htable to allow measurement of duplicate records.
...@@ -242,12 +242,16 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint, ...@@ -242,12 +242,16 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
hash_start = 1; hash_start = 1;
fallthrough; fallthrough;
case IMA_XATTR_DIGEST: case IMA_XATTR_DIGEST:
if (*status != INTEGRITY_PASS_IMMUTABLE) {
if (iint->flags & IMA_DIGSIG_REQUIRED) { if (iint->flags & IMA_DIGSIG_REQUIRED) {
*cause = "IMA-signature-required"; *cause = "IMA-signature-required";
*status = INTEGRITY_FAIL; *status = INTEGRITY_FAIL;
break; break;
} }
clear_bit(IMA_DIGSIG, &iint->atomic_flags); clear_bit(IMA_DIGSIG, &iint->atomic_flags);
} else {
set_bit(IMA_DIGSIG, &iint->atomic_flags);
}
if (xattr_len - sizeof(xattr_value->type) - hash_start >= if (xattr_len - sizeof(xattr_value->type) - hash_start >=
iint->ima_hash->length) iint->ima_hash->length)
/* /*
...@@ -416,6 +420,10 @@ int ima_appraise_measurement(enum ima_hooks func, ...@@ -416,6 +420,10 @@ int ima_appraise_measurement(enum ima_hooks func,
case INTEGRITY_NOLABEL: /* No security.evm xattr. */ case INTEGRITY_NOLABEL: /* No security.evm xattr. */
cause = "missing-HMAC"; cause = "missing-HMAC";
goto out; goto out;
case INTEGRITY_FAIL_IMMUTABLE:
set_bit(IMA_DIGSIG, &iint->atomic_flags);
cause = "invalid-fail-immutable";
goto out;
case INTEGRITY_FAIL: /* Invalid HMAC/signature. */ case INTEGRITY_FAIL: /* Invalid HMAC/signature. */
cause = "invalid-HMAC"; cause = "invalid-HMAC";
goto out; goto out;
...@@ -459,9 +467,12 @@ int ima_appraise_measurement(enum ima_hooks func, ...@@ -459,9 +467,12 @@ int ima_appraise_measurement(enum ima_hooks func,
status = INTEGRITY_PASS; status = INTEGRITY_PASS;
} }
/* Permit new files with file signatures, but without data. */ /*
* Permit new files with file/EVM portable signatures, but
* without data.
*/
if (inode->i_size == 0 && iint->flags & IMA_NEW_FILE && if (inode->i_size == 0 && iint->flags & IMA_NEW_FILE &&
xattr_value && xattr_value->type == EVM_IMA_XATTR_DIGSIG) { test_bit(IMA_DIGSIG, &iint->atomic_flags)) {
status = INTEGRITY_PASS; status = INTEGRITY_PASS;
} }
...@@ -522,8 +533,6 @@ void ima_inode_post_setattr(struct user_namespace *mnt_userns, ...@@ -522,8 +533,6 @@ void ima_inode_post_setattr(struct user_namespace *mnt_userns,
return; return;
action = ima_must_appraise(mnt_userns, inode, MAY_ACCESS, POST_SETATTR); action = ima_must_appraise(mnt_userns, inode, MAY_ACCESS, POST_SETATTR);
if (!action)
__vfs_removexattr(&init_user_ns, dentry, XATTR_NAME_IMA);
iint = integrity_iint_find(inode); iint = integrity_iint_find(inode);
if (iint) { if (iint) {
set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags); set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
...@@ -570,6 +579,7 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, ...@@ -570,6 +579,7 @@ 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)
{ {
const struct evm_ima_xattr_data *xvalue = xattr_value; const struct evm_ima_xattr_data *xvalue = xattr_value;
int digsig = 0;
int result; int result;
result = ima_protect_xattr(dentry, xattr_name, xattr_value, result = ima_protect_xattr(dentry, xattr_name, xattr_value,
...@@ -577,8 +587,13 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, ...@@ -577,8 +587,13 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
if (result == 1) { if (result == 1) {
if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST)) if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST))
return -EINVAL; return -EINVAL;
ima_reset_appraise_flags(d_backing_inode(dentry), digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG);
xvalue->type == EVM_IMA_XATTR_DIGSIG); } else if (!strcmp(xattr_name, XATTR_NAME_EVM) && xattr_value_len > 0) {
digsig = (xvalue->type == EVM_XATTR_PORTABLE_DIGSIG);
}
if (result == 1 || evm_revalidate_status(xattr_name)) {
ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
if (result == 1)
result = 0; result = 0;
} }
return result; return result;
...@@ -589,8 +604,9 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name) ...@@ -589,8 +604,9 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
int result; int result;
result = ima_protect_xattr(dentry, xattr_name, NULL, 0); result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
if (result == 1) { if (result == 1 || evm_revalidate_status(xattr_name)) {
ima_reset_appraise_flags(d_backing_inode(dentry), 0); ima_reset_appraise_flags(d_backing_inode(dentry), 0);
if (result == 1)
result = 0; result = 0;
} }
return result; return result;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <keys/asymmetric-type.h> #include <keys/asymmetric-type.h>
#include <linux/user_namespace.h> #include <linux/user_namespace.h>
#include <linux/ima.h>
#include "ima.h" #include "ima.h"
/** /**
......
...@@ -598,8 +598,8 @@ static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data, ...@@ -598,8 +598,8 @@ static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data,
u8 buffer[IMA_EVENT_NAME_LEN_MAX + 1] = { 0 }; u8 buffer[IMA_EVENT_NAME_LEN_MAX + 1] = { 0 };
u8 *data_to_hash = field_data[i].data; u8 *data_to_hash = field_data[i].data;
u32 datalen = field_data[i].len; u32 datalen = field_data[i].len;
u32 datalen_to_hash = u32 datalen_to_hash = !ima_canonical_fmt ?
!ima_canonical_fmt ? datalen : cpu_to_le32(datalen); datalen : (__force u32)cpu_to_le32(datalen);
if (strcmp(td->name, IMA_TEMPLATE_IMA_NAME) != 0) { if (strcmp(td->name, IMA_TEMPLATE_IMA_NAME) != 0) {
rc = crypto_shash_update(shash, rc = crypto_shash_update(shash,
......
...@@ -147,7 +147,7 @@ int ima_measurements_show(struct seq_file *m, void *v) ...@@ -147,7 +147,7 @@ int ima_measurements_show(struct seq_file *m, void *v)
* PCR used defaults to the same (config option) in * PCR used defaults to the same (config option) in
* little-endian format, unless set in policy * little-endian format, unless set in policy
*/ */
pcr = !ima_canonical_fmt ? e->pcr : cpu_to_le32(e->pcr); pcr = !ima_canonical_fmt ? e->pcr : (__force u32)cpu_to_le32(e->pcr);
ima_putc(m, &pcr, sizeof(e->pcr)); ima_putc(m, &pcr, sizeof(e->pcr));
/* 2nd: template digest */ /* 2nd: template digest */
...@@ -155,7 +155,7 @@ int ima_measurements_show(struct seq_file *m, void *v) ...@@ -155,7 +155,7 @@ int ima_measurements_show(struct seq_file *m, void *v)
/* 3rd: template name size */ /* 3rd: template name size */
namelen = !ima_canonical_fmt ? strlen(template_name) : namelen = !ima_canonical_fmt ? strlen(template_name) :
cpu_to_le32(strlen(template_name)); (__force u32)cpu_to_le32(strlen(template_name));
ima_putc(m, &namelen, sizeof(namelen)); ima_putc(m, &namelen, sizeof(namelen));
/* 4th: template name */ /* 4th: template name */
...@@ -167,7 +167,7 @@ int ima_measurements_show(struct seq_file *m, void *v) ...@@ -167,7 +167,7 @@ int ima_measurements_show(struct seq_file *m, void *v)
if (!is_ima_template) { if (!is_ima_template) {
template_data_len = !ima_canonical_fmt ? e->template_data_len : template_data_len = !ima_canonical_fmt ? e->template_data_len :
cpu_to_le32(e->template_data_len); (__force u32)cpu_to_le32(e->template_data_len);
ima_putc(m, &template_data_len, sizeof(e->template_data_len)); ima_putc(m, &template_data_len, sizeof(e->template_data_len));
} }
......
...@@ -108,6 +108,10 @@ void __init ima_load_x509(void) ...@@ -108,6 +108,10 @@ void __init ima_load_x509(void)
ima_policy_flag &= ~unset_flags; ima_policy_flag &= ~unset_flags;
integrity_load_x509(INTEGRITY_KEYRING_IMA, CONFIG_IMA_X509_PATH); integrity_load_x509(INTEGRITY_KEYRING_IMA, CONFIG_IMA_X509_PATH);
/* load also EVM key to avoid appraisal */
evm_load_x509();
ima_policy_flag |= unset_flags; ima_policy_flag |= unset_flags;
} }
#endif #endif
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/kexec.h> #include <linux/kexec.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/ima.h>
#include "ima.h" #include "ima.h"
#ifdef CONFIG_IMA_KEXEC #ifdef CONFIG_IMA_KEXEC
......
...@@ -433,7 +433,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot) ...@@ -433,7 +433,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, 0); &pcr, &template, 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)))
......
...@@ -168,7 +168,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, ...@@ -168,7 +168,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
int result = 0, tpmresult = 0; int result = 0, tpmresult = 0;
mutex_lock(&ima_extend_list_mutex); mutex_lock(&ima_extend_list_mutex);
if (!violation) { if (!violation && !IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) {
if (ima_lookup_digest_entry(digest, entry->pcr)) { if (ima_lookup_digest_entry(digest, entry->pcr)) {
audit_cause = "hash_exists"; audit_cause = "hash_exists";
result = -EEXIST; result = -EEXIST;
...@@ -176,7 +176,8 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, ...@@ -176,7 +176,8 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
} }
} }
result = ima_add_digest_entry(entry, 1); result = ima_add_digest_entry(entry,
!IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE));
if (result < 0) { if (result < 0) {
audit_cause = "ENOMEM"; audit_cause = "ENOMEM";
audit_info = 0; audit_info = 0;
......
...@@ -22,6 +22,8 @@ static struct ima_template_desc builtin_templates[] = { ...@@ -22,6 +22,8 @@ static struct ima_template_desc builtin_templates[] = {
{.name = "ima-sig", .fmt = "d-ng|n-ng|sig"}, {.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
{.name = "ima-buf", .fmt = "d-ng|n-ng|buf"}, {.name = "ima-buf", .fmt = "d-ng|n-ng|buf"},
{.name = "ima-modsig", .fmt = "d-ng|n-ng|sig|d-modsig|modsig"}, {.name = "ima-modsig", .fmt = "d-ng|n-ng|sig|d-modsig|modsig"},
{.name = "evm-sig",
.fmt = "d-ng|n-ng|evmsig|xattrnames|xattrlengths|xattrvalues|iuid|igid|imode"},
{.name = "", .fmt = ""}, /* placeholder for a custom format */ {.name = "", .fmt = ""}, /* placeholder for a custom format */
}; };
...@@ -45,6 +47,23 @@ static const struct ima_template_field supported_fields[] = { ...@@ -45,6 +47,23 @@ static const struct ima_template_field supported_fields[] = {
.field_show = ima_show_template_digest_ng}, .field_show = ima_show_template_digest_ng},
{.field_id = "modsig", .field_init = ima_eventmodsig_init, {.field_id = "modsig", .field_init = ima_eventmodsig_init,
.field_show = ima_show_template_sig}, .field_show = ima_show_template_sig},
{.field_id = "evmsig", .field_init = ima_eventevmsig_init,
.field_show = ima_show_template_sig},
{.field_id = "iuid", .field_init = ima_eventinodeuid_init,
.field_show = ima_show_template_uint},
{.field_id = "igid", .field_init = ima_eventinodegid_init,
.field_show = ima_show_template_uint},
{.field_id = "imode", .field_init = ima_eventinodemode_init,
.field_show = ima_show_template_uint},
{.field_id = "xattrnames",
.field_init = ima_eventinodexattrnames_init,
.field_show = ima_show_template_string},
{.field_id = "xattrlengths",
.field_init = ima_eventinodexattrlengths_init,
.field_show = ima_show_template_sig},
{.field_id = "xattrvalues",
.field_init = ima_eventinodexattrvalues_init,
.field_show = ima_show_template_sig},
}; };
/* /*
...@@ -52,7 +71,8 @@ static const struct ima_template_field supported_fields[] = { ...@@ -52,7 +71,8 @@ static const struct ima_template_field supported_fields[] = {
* need to be accounted for since they shouldn't be defined in the same template * need to be accounted for since they shouldn't be defined in the same template
* description as 'd-ng' and 'n-ng' respectively. * description as 'd-ng' and 'n-ng' respectively.
*/ */
#define MAX_TEMPLATE_NAME_LEN sizeof("d-ng|n-ng|sig|buf|d-modisg|modsig") #define MAX_TEMPLATE_NAME_LEN \
sizeof("d-ng|n-ng|evmsig|xattrnames|xattrlengths|xattrvalues|iuid|igid|imode")
static struct ima_template_desc *ima_template; static struct ima_template_desc *ima_template;
static struct ima_template_desc *ima_buf_template; static struct ima_template_desc *ima_buf_template;
...@@ -403,9 +423,9 @@ int ima_restore_measurement_list(loff_t size, void *buf) ...@@ -403,9 +423,9 @@ int ima_restore_measurement_list(loff_t size, void *buf)
return 0; return 0;
if (ima_canonical_fmt) { if (ima_canonical_fmt) {
khdr->version = le16_to_cpu(khdr->version); khdr->version = le16_to_cpu((__force __le16)khdr->version);
khdr->count = le64_to_cpu(khdr->count); khdr->count = le64_to_cpu((__force __le64)khdr->count);
khdr->buffer_size = le64_to_cpu(khdr->buffer_size); khdr->buffer_size = le64_to_cpu((__force __le64)khdr->buffer_size);
} }
if (khdr->version != 1) { if (khdr->version != 1) {
...@@ -495,7 +515,7 @@ int ima_restore_measurement_list(loff_t size, void *buf) ...@@ -495,7 +515,7 @@ int ima_restore_measurement_list(loff_t size, void *buf)
} }
entry->pcr = !ima_canonical_fmt ? *(u32 *)(hdr[HDR_PCR].data) : entry->pcr = !ima_canonical_fmt ? *(u32 *)(hdr[HDR_PCR].data) :
le32_to_cpu(*(u32 *)(hdr[HDR_PCR].data)); le32_to_cpu(*(__le32 *)(hdr[HDR_PCR].data));
ret = ima_restore_measurement_entry(entry); ret = ima_restore_measurement_entry(entry);
if (ret < 0) if (ret < 0)
break; break;
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
*/ */
#include "ima_template_lib.h" #include "ima_template_lib.h"
#include <linux/xattr.h>
#include <linux/evm.h>
static bool ima_template_hash_algo_allowed(u8 algo) static bool ima_template_hash_algo_allowed(u8 algo)
{ {
...@@ -23,7 +25,8 @@ enum data_formats { ...@@ -23,7 +25,8 @@ enum data_formats {
DATA_FMT_DIGEST = 0, DATA_FMT_DIGEST = 0,
DATA_FMT_DIGEST_WITH_ALGO, DATA_FMT_DIGEST_WITH_ALGO,
DATA_FMT_STRING, DATA_FMT_STRING,
DATA_FMT_HEX DATA_FMT_HEX,
DATA_FMT_UINT
}; };
static int ima_write_template_field_data(const void *data, const u32 datalen, static int ima_write_template_field_data(const void *data, const u32 datalen,
...@@ -87,6 +90,36 @@ static void ima_show_template_data_ascii(struct seq_file *m, ...@@ -87,6 +90,36 @@ static void ima_show_template_data_ascii(struct seq_file *m,
case DATA_FMT_STRING: case DATA_FMT_STRING:
seq_printf(m, "%s", buf_ptr); seq_printf(m, "%s", buf_ptr);
break; break;
case DATA_FMT_UINT:
switch (field_data->len) {
case sizeof(u8):
seq_printf(m, "%u", *(u8 *)buf_ptr);
break;
case sizeof(u16):
if (ima_canonical_fmt)
seq_printf(m, "%u",
le16_to_cpu(*(__le16 *)buf_ptr));
else
seq_printf(m, "%u", *(u16 *)buf_ptr);
break;
case sizeof(u32):
if (ima_canonical_fmt)
seq_printf(m, "%u",
le32_to_cpu(*(__le32 *)buf_ptr));
else
seq_printf(m, "%u", *(u32 *)buf_ptr);
break;
case sizeof(u64):
if (ima_canonical_fmt)
seq_printf(m, "%llu",
le64_to_cpu(*(__le64 *)buf_ptr));
else
seq_printf(m, "%llu", *(u64 *)buf_ptr);
break;
default:
break;
}
break;
default: default:
break; break;
} }
...@@ -101,7 +134,8 @@ static void ima_show_template_data_binary(struct seq_file *m, ...@@ -101,7 +134,8 @@ static void ima_show_template_data_binary(struct seq_file *m,
strlen(field_data->data) : field_data->len; strlen(field_data->data) : field_data->len;
if (show != IMA_SHOW_BINARY_NO_FIELD_LEN) { if (show != IMA_SHOW_BINARY_NO_FIELD_LEN) {
u32 field_len = !ima_canonical_fmt ? len : cpu_to_le32(len); u32 field_len = !ima_canonical_fmt ?
len : (__force u32)cpu_to_le32(len);
ima_putc(m, &field_len, sizeof(field_len)); ima_putc(m, &field_len, sizeof(field_len));
} }
...@@ -162,6 +196,12 @@ void ima_show_template_buf(struct seq_file *m, enum ima_show_type show, ...@@ -162,6 +196,12 @@ void ima_show_template_buf(struct seq_file *m, enum ima_show_type show,
ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data); ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data);
} }
void ima_show_template_uint(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data)
{
ima_show_template_field_data(m, show, DATA_FMT_UINT, field_data);
}
/** /**
* ima_parse_buf() - Parses lengths and data from an input buffer * ima_parse_buf() - Parses lengths and data from an input buffer
* @bufstartp: Buffer start address. * @bufstartp: Buffer start address.
...@@ -188,9 +228,10 @@ int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp, ...@@ -188,9 +228,10 @@ int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
if (bufp > (bufendp - sizeof(u32))) if (bufp > (bufendp - sizeof(u32)))
break; break;
fields[i].len = *(u32 *)bufp;
if (ima_canonical_fmt) if (ima_canonical_fmt)
fields[i].len = le32_to_cpu(fields[i].len); fields[i].len = le32_to_cpu(*(__le32 *)bufp);
else
fields[i].len = *(u32 *)bufp;
bufp += sizeof(u32); bufp += sizeof(u32);
} }
...@@ -438,7 +479,7 @@ int ima_eventsig_init(struct ima_event_data *event_data, ...@@ -438,7 +479,7 @@ int ima_eventsig_init(struct ima_event_data *event_data,
struct evm_ima_xattr_data *xattr_value = event_data->xattr_value; struct evm_ima_xattr_data *xattr_value = event_data->xattr_value;
if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG)) if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG))
return 0; return ima_eventevmsig_init(event_data, field_data);
return ima_write_template_field_data(xattr_value, event_data->xattr_len, return ima_write_template_field_data(xattr_value, event_data->xattr_len,
DATA_FMT_HEX, field_data); DATA_FMT_HEX, field_data);
...@@ -484,3 +525,163 @@ int ima_eventmodsig_init(struct ima_event_data *event_data, ...@@ -484,3 +525,163 @@ int ima_eventmodsig_init(struct ima_event_data *event_data,
return ima_write_template_field_data(data, data_len, DATA_FMT_HEX, return ima_write_template_field_data(data, data_len, DATA_FMT_HEX,
field_data); field_data);
} }
/*
* ima_eventevmsig_init - include the EVM portable signature as part of the
* template data
*/
int ima_eventevmsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
struct evm_ima_xattr_data *xattr_data = NULL;
int rc = 0;
if (!event_data->file)
return 0;
rc = vfs_getxattr_alloc(&init_user_ns, file_dentry(event_data->file),
XATTR_NAME_EVM, (char **)&xattr_data, 0,
GFP_NOFS);
if (rc <= 0)
return 0;
if (xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG) {
kfree(xattr_data);
return 0;
}
rc = ima_write_template_field_data((char *)xattr_data, rc, DATA_FMT_HEX,
field_data);
kfree(xattr_data);
return rc;
}
static int ima_eventinodedac_init_common(struct ima_event_data *event_data,
struct ima_field_data *field_data,
bool get_uid)
{
unsigned int id;
if (!event_data->file)
return 0;
if (get_uid)
id = i_uid_read(file_inode(event_data->file));
else
id = i_gid_read(file_inode(event_data->file));
if (ima_canonical_fmt) {
if (sizeof(id) == sizeof(u16))
id = (__force u16)cpu_to_le16(id);
else
id = (__force u32)cpu_to_le32(id);
}
return ima_write_template_field_data((void *)&id, sizeof(id),
DATA_FMT_UINT, field_data);
}
/*
* ima_eventinodeuid_init - include the inode UID as part of the template
* data
*/
int ima_eventinodeuid_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
return ima_eventinodedac_init_common(event_data, field_data, true);
}
/*
* ima_eventinodegid_init - include the inode GID as part of the template
* data
*/
int ima_eventinodegid_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
return ima_eventinodedac_init_common(event_data, field_data, false);
}
/*
* ima_eventinodemode_init - include the inode mode as part of the template
* data
*/
int ima_eventinodemode_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
struct inode *inode;
u16 mode;
if (!event_data->file)
return 0;
inode = file_inode(event_data->file);
mode = inode->i_mode;
if (ima_canonical_fmt)
mode = (__force u16)cpu_to_le16(mode);
return ima_write_template_field_data((char *)&mode, sizeof(mode),
DATA_FMT_UINT, field_data);
}
static int ima_eventinodexattrs_init_common(struct ima_event_data *event_data,
struct ima_field_data *field_data,
char type)
{
u8 *buffer = NULL;
int rc;
if (!event_data->file)
return 0;
rc = evm_read_protected_xattrs(file_dentry(event_data->file), NULL, 0,
type, ima_canonical_fmt);
if (rc < 0)
return 0;
buffer = kmalloc(rc, GFP_KERNEL);
if (!buffer)
return 0;
rc = evm_read_protected_xattrs(file_dentry(event_data->file), buffer,
rc, type, ima_canonical_fmt);
if (rc < 0) {
rc = 0;
goto out;
}
rc = ima_write_template_field_data((char *)buffer, rc, DATA_FMT_HEX,
field_data);
out:
kfree(buffer);
return rc;
}
/*
* ima_eventinodexattrnames_init - include a list of xattr names as part of the
* template data
*/
int ima_eventinodexattrnames_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
return ima_eventinodexattrs_init_common(event_data, field_data, 'n');
}
/*
* ima_eventinodexattrlengths_init - include a list of xattr lengths as part of
* the template data
*/
int ima_eventinodexattrlengths_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
return ima_eventinodexattrs_init_common(event_data, field_data, 'l');
}
/*
* ima_eventinodexattrvalues_init - include a list of xattr values as part of
* the template data
*/
int ima_eventinodexattrvalues_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
return ima_eventinodexattrs_init_common(event_data, field_data, 'v');
}
...@@ -27,6 +27,8 @@ void ima_show_template_sig(struct seq_file *m, enum ima_show_type show, ...@@ -27,6 +27,8 @@ void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data); struct ima_field_data *field_data);
void ima_show_template_buf(struct seq_file *m, enum ima_show_type show, void ima_show_template_buf(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data); struct ima_field_data *field_data);
void ima_show_template_uint(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 ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
int maxfields, struct ima_field_data *fields, int *curfields, int maxfields, struct ima_field_data *fields, int *curfields,
unsigned long *len_mask, int enforce_mask, char *bufname); unsigned long *len_mask, int enforce_mask, char *bufname);
...@@ -46,4 +48,18 @@ int ima_eventbuf_init(struct ima_event_data *event_data, ...@@ -46,4 +48,18 @@ int ima_eventbuf_init(struct ima_event_data *event_data,
struct ima_field_data *field_data); struct ima_field_data *field_data);
int ima_eventmodsig_init(struct ima_event_data *event_data, int ima_eventmodsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data); struct ima_field_data *field_data);
int ima_eventevmsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventinodeuid_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventinodegid_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventinodemode_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventinodexattrnames_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventinodexattrlengths_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventinodexattrvalues_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
#endif /* __LINUX_IMA_TEMPLATE_LIB_H */ #endif /* __LINUX_IMA_TEMPLATE_LIB_H */
...@@ -1354,7 +1354,7 @@ int security_inode_setxattr(struct user_namespace *mnt_userns, ...@@ -1354,7 +1354,7 @@ int security_inode_setxattr(struct user_namespace *mnt_userns,
ret = ima_inode_setxattr(dentry, name, value, size); ret = ima_inode_setxattr(dentry, name, value, size);
if (ret) if (ret)
return ret; return ret;
return evm_inode_setxattr(dentry, name, value, size); return evm_inode_setxattr(mnt_userns, dentry, name, value, size);
} }
void security_inode_post_setxattr(struct dentry *dentry, const char *name, void security_inode_post_setxattr(struct dentry *dentry, const char *name,
...@@ -1399,7 +1399,7 @@ int security_inode_removexattr(struct user_namespace *mnt_userns, ...@@ -1399,7 +1399,7 @@ int security_inode_removexattr(struct user_namespace *mnt_userns,
ret = ima_inode_removexattr(dentry, name); ret = ima_inode_removexattr(dentry, name);
if (ret) if (ret)
return ret; return ret;
return evm_inode_removexattr(dentry, name); return evm_inode_removexattr(mnt_userns, dentry, name);
} }
int security_inode_need_killpriv(struct dentry *dentry) int security_inode_need_killpriv(struct dentry *dentry)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment