提交 a44c2ae6 编写于 作者: R Roberto Sassu 提交者: Zheng Zengkai

ima: Add support for appraisal with digest lists

hulk inclusion
category: feature
feature: IMA Digest Lists extension
bugzilla: 46797

-------------------------------------------------

IMA-Appraise grants access to files with a valid signature or with actual
file digest equal to the digest included in security.ima.

This patch adds support for appraisal based on digest lists. Instead of
using the reference value from security.ima, this patch checks if the
calculated file digest is included in the uploaded digest lists.

This functionality must be explicitly enabled by providing one of the
following values for the ima_appraise_digest_list= kernel option:

- digest: this mode enables appraisal verification with digest lists until
  EVM is initialized; after that, EVM verification must be successful even
  if the file digest is found in a digest list;

- digest-nometadata: this mode enables appraisal verification with digest
  lists even after EVM has been initialized; files without security.evm are
  allowed if the digest of the content is found in the digest list, and
  security.evm is created with current values of xattrs (trust at first
  use); all files created in this way will have the new security.ima type
  EVM_IMA_XATTR_DIGEST_LIST; they can be accessed later only if this mode
  has been selected.
Signed-off-by: NRoberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: NTianxing Zhang <zhangtianxing3@huawei.com>
Reviewed-by: NJason Yan <yanaijie@huawei.com>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 31604143
...@@ -1737,6 +1737,15 @@ ...@@ -1737,6 +1737,15 @@
"enforce-evm" | "log-evm" } "enforce-evm" | "log-evm" }
default: "enforce" default: "enforce"
ima_appraise_digest_list= [IMA]
Format: { "digest" | "digest-nometadata" }
digest: enables appraisal of files with digest lists
until EVM is initialized.
digest-nometadata: enables appraisal of files with
digest lists even after EVM is initialized.
ima_appraise_tcb [IMA] Deprecated. Use ima_policy= instead. ima_appraise_tcb [IMA] Deprecated. Use ima_policy= instead.
The builtin appraise policy appraises all files The builtin appraise policy appraises all files
owned by uid=0. owned by uid=0.
......
...@@ -313,7 +313,8 @@ int ima_appraise_measurement(enum ima_hooks func, ...@@ -313,7 +313,8 @@ int ima_appraise_measurement(enum ima_hooks func,
struct integrity_iint_cache *iint, struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename, struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value, struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig); int xattr_len, const struct modsig *modsig,
struct ima_digest *found_digest);
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func); int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
...@@ -331,7 +332,8 @@ static inline int ima_appraise_measurement(enum ima_hooks func, ...@@ -331,7 +332,8 @@ static inline int ima_appraise_measurement(enum ima_hooks func,
const unsigned char *filename, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value, struct evm_ima_xattr_data *xattr_value,
int xattr_len, int xattr_len,
const struct modsig *modsig) const struct modsig *modsig,
struct ima_digest *found_digest)
{ {
return INTEGRITY_UNKNOWN; return INTEGRITY_UNKNOWN;
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <keys/system_keyring.h> #include <keys/system_keyring.h>
#include "ima.h" #include "ima.h"
#include "ima_digest_list.h"
static bool ima_appraise_req_evm __ro_after_init; static bool ima_appraise_req_evm __ro_after_init;
static int __init default_appraise_setup(char *str) static int __init default_appraise_setup(char *str)
...@@ -52,6 +53,22 @@ static int __init default_appraise_setup(char *str) ...@@ -52,6 +53,22 @@ static int __init default_appraise_setup(char *str)
__setup("ima_appraise=", default_appraise_setup); __setup("ima_appraise=", default_appraise_setup);
static bool ima_appraise_no_metadata __ro_after_init;
#ifdef CONFIG_IMA_DIGEST_LIST
static int __init appraise_digest_list_setup(char *str)
{
if (!strncmp(str, "digest", 6)) {
ima_digest_list_actions |= IMA_APPRAISE;
if (!strcmp(str + 6, "-nometadata"))
ima_appraise_no_metadata = true;
}
return 1;
}
__setup("ima_appraise_digest_list=", appraise_digest_list_setup);
#endif
/* /*
* is_ima_appraise_enabled - return appraise status * is_ima_appraise_enabled - return appraise status
* *
...@@ -91,6 +108,9 @@ static int ima_fix_xattr(struct dentry *dentry, ...@@ -91,6 +108,9 @@ static int ima_fix_xattr(struct dentry *dentry,
} else { } else {
offset = 0; offset = 0;
iint->ima_hash->xattr.ng.type = IMA_XATTR_DIGEST_NG; iint->ima_hash->xattr.ng.type = IMA_XATTR_DIGEST_NG;
if (test_bit(IMA_DIGEST_LIST, &iint->atomic_flags))
iint->ima_hash->xattr.ng.type =
EVM_IMA_XATTR_DIGEST_LIST;
iint->ima_hash->xattr.ng.algo = algo; iint->ima_hash->xattr.ng.algo = algo;
} }
rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
...@@ -178,17 +198,32 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, ...@@ -178,17 +198,32 @@ static void ima_cache_flags(struct integrity_iint_cache *iint,
*/ */
static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint, static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
struct evm_ima_xattr_data *xattr_value, int xattr_len, struct evm_ima_xattr_data *xattr_value, int xattr_len,
enum integrity_status *status, const char **cause) enum integrity_status *status, const char **cause,
struct ima_digest *found_digest)
{ {
int rc = -EINVAL, hash_start = 0; int rc = -EINVAL, hash_start = 0;
if (found_digest && *status != INTEGRITY_PASS &&
*status != INTEGRITY_PASS_IMMUTABLE)
set_bit(IMA_DIGEST_LIST, &iint->atomic_flags);
switch (xattr_value->type) { switch (xattr_value->type) {
case EVM_IMA_XATTR_DIGEST_LIST:
set_bit(IMA_DIGEST_LIST, &iint->atomic_flags);
if (!ima_appraise_no_metadata) {
*cause = "IMA-xattr-untrusted";
*status = INTEGRITY_FAIL;
break;
}
fallthrough;
case IMA_XATTR_DIGEST_NG: case IMA_XATTR_DIGEST_NG:
/* first byte contains algorithm id */ /* first byte contains algorithm id */
hash_start = 1; hash_start = 1;
fallthrough; fallthrough;
case IMA_XATTR_DIGEST: case IMA_XATTR_DIGEST:
if (*status != INTEGRITY_PASS_IMMUTABLE) { if (*status != INTEGRITY_PASS_IMMUTABLE &&
(!found_digest || !ima_digest_is_immutable(found_digest))) {
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;
...@@ -321,14 +356,16 @@ int ima_appraise_measurement(enum ima_hooks func, ...@@ -321,14 +356,16 @@ int ima_appraise_measurement(enum ima_hooks func,
struct integrity_iint_cache *iint, struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename, struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value, struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig) int xattr_len, const struct modsig *modsig,
struct ima_digest *found_digest)
{ {
static const char op[] = "appraise_data"; static const char op[] = "appraise_data";
const char *cause = "unknown"; const char *cause = "unknown";
struct dentry *dentry = file_dentry(file); struct dentry *dentry = file_dentry(file);
struct inode *inode = d_backing_inode(dentry); struct inode *inode = d_backing_inode(dentry);
enum integrity_status status = INTEGRITY_UNKNOWN; enum integrity_status status = INTEGRITY_UNKNOWN;
int rc = xattr_len; int rc = xattr_len, rc_evm;
char _buf[sizeof(struct evm_ima_xattr_data) + 1 + SHA512_DIGEST_SIZE];
bool try_modsig = iint->flags & IMA_MODSIG_ALLOWED && modsig; bool try_modsig = iint->flags & IMA_MODSIG_ALLOWED && modsig;
/* If not appraising a modsig, we need an xattr. */ /* If not appraising a modsig, we need an xattr. */
...@@ -339,6 +376,25 @@ int ima_appraise_measurement(enum ima_hooks func, ...@@ -339,6 +376,25 @@ int ima_appraise_measurement(enum ima_hooks func,
xattr_len == sizeof(struct signature_v2_hdr)) xattr_len == sizeof(struct signature_v2_hdr))
rc = -ENODATA; rc = -ENODATA;
if (rc == -ENODATA && found_digest &&
!(file->f_mode & FMODE_CREATED)) {
struct evm_ima_xattr_data *xattr_data = NULL;
rc_evm = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM,
(char **)&xattr_data, 0, GFP_NOFS);
if (rc_evm > 0) {
kfree(xattr_data);
} else {
xattr_value = (struct evm_ima_xattr_data *)_buf;
xattr_value->type = IMA_XATTR_DIGEST_NG;
xattr_value->data[0] = found_digest->algo;
memcpy(&xattr_value->data[1], found_digest->digest,
hash_digest_size[found_digest->algo]);
xattr_len = hash_digest_size[found_digest->algo] + 2;
rc = xattr_len;
}
}
/* If reading the xattr failed and there's no modsig, error out. */ /* If reading the xattr failed and there's no modsig, error out. */
if (rc <= 0 && !try_modsig) { if (rc <= 0 && !try_modsig) {
if (rc && rc != -ENODATA) if (rc && rc != -ENODATA)
...@@ -363,7 +419,7 @@ int ima_appraise_measurement(enum ima_hooks func, ...@@ -363,7 +419,7 @@ int ima_appraise_measurement(enum ima_hooks func,
break; break;
case INTEGRITY_UNKNOWN: case INTEGRITY_UNKNOWN:
if (ima_appraise_req_evm && if (ima_appraise_req_evm &&
xattr_value->type != EVM_IMA_XATTR_DIGSIG) xattr_value->type != EVM_IMA_XATTR_DIGSIG && !found_digest)
goto out; goto out;
break; break;
case INTEGRITY_NOXATTRS: /* No EVM protected xattrs. */ case INTEGRITY_NOXATTRS: /* No EVM protected xattrs. */
...@@ -372,6 +428,23 @@ int ima_appraise_measurement(enum ima_hooks func, ...@@ -372,6 +428,23 @@ int ima_appraise_measurement(enum ima_hooks func,
break; break;
fallthrough; fallthrough;
case INTEGRITY_NOLABEL: /* No security.evm xattr. */ case INTEGRITY_NOLABEL: /* No security.evm xattr. */
/*
* If the digest-nometadata mode is selected, allow access
* without metadata check. EVM will eventually create an HMAC
* based on current xattr values.
*/
if (ima_appraise_no_metadata && found_digest)
break;
/* Allow access to digest lists without metadata, only if they
* are signed or found in a digest list (immutable)
*/
if (func == DIGEST_LIST_CHECK) {
if (xattr_value->type == EVM_IMA_XATTR_DIGSIG)
break;
if (found_digest &&
ima_digest_is_immutable(found_digest))
break;
}
cause = "missing-HMAC"; cause = "missing-HMAC";
goto out; goto out;
case INTEGRITY_FAIL_IMMUTABLE: case INTEGRITY_FAIL_IMMUTABLE:
...@@ -386,7 +459,7 @@ int ima_appraise_measurement(enum ima_hooks func, ...@@ -386,7 +459,7 @@ int ima_appraise_measurement(enum ima_hooks func,
if (xattr_value) if (xattr_value)
rc = xattr_verify(func, iint, xattr_value, xattr_len, &status, rc = xattr_verify(func, iint, xattr_value, xattr_len, &status,
&cause); &cause, found_digest);
/* /*
* If we have a modsig and either no imasig or the imasig's key isn't * If we have a modsig and either no imasig or the imasig's key isn't
......
...@@ -413,8 +413,10 @@ static int process_measurement(struct file *file, const struct cred *cred, ...@@ -413,8 +413,10 @@ static int process_measurement(struct file *file, const struct cred *cred,
if (rc != -EPERM) { if (rc != -EPERM) {
inode_lock(inode); inode_lock(inode);
rc = ima_appraise_measurement(func, iint, file, rc = ima_appraise_measurement(func, iint, file,
pathname, xattr_value, pathname, xattr_value,
xattr_len, modsig); xattr_len, modsig,
ima_digest_allow(found_digest,
IMA_APPRAISE));
inode_unlock(inode); inode_unlock(inode);
} }
if (!rc) if (!rc)
......
...@@ -71,6 +71,7 @@ ...@@ -71,6 +71,7 @@
#define IMA_CHANGE_ATTR 2 #define IMA_CHANGE_ATTR 2
#define IMA_DIGSIG 3 #define IMA_DIGSIG 3
#define IMA_MUST_MEASURE 4 #define IMA_MUST_MEASURE 4
#define IMA_DIGEST_LIST 5
enum evm_ima_xattr_type { enum evm_ima_xattr_type {
IMA_XATTR_DIGEST = 0x01, IMA_XATTR_DIGEST = 0x01,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册