ima: Add appraisal support to IMA namespace
Enable each IMA namespace to appraise independently according to
their own appraisal policy. Keys used to check the signature are
stored on a per-namespace IMA keyring. Appraisal will only check
against keys on the namespace's keyring, but not recursively down
to the native. Native is responsible for controlling what and when
keys can be loaded on a namespace's IMA keyring.
Signed-off-by: Yuqiong Sun <suny@us.ibm.com>
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 06554c4..fa24edc 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -48,30 +48,31 @@ static bool init_keyring __initdata;
#define restrict_link_to_ima restrict_link_by_builtin_trusted
#endif
-int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
- const char *digest, int digestlen)
+int integrity_digsig_verify(struct ima_namespace *ns, const char *sig,
+ int siglen, const char *digest, int digestlen)
{
- if (id >= INTEGRITY_KEYRING_MAX || siglen < 2)
- return -EINVAL;
+ struct key *ima_keyring;
- if (!keyring[id]) {
- keyring[id] =
- request_key(&key_type_keyring, keyring_name[id], NULL);
- if (IS_ERR(keyring[id])) {
- int err = PTR_ERR(keyring[id]);
- pr_err("no %s keyring: %d\n", keyring_name[id], err);
- keyring[id] = NULL;
+ if (!ns->keyring[0]) {
+ ima_keyring =
+ request_key(&key_type_keyring, ns->ima_keyring, NULL);
+ if (IS_ERR(ima_keyring)) {
+ int err = PTR_ERR(ima_keyring);
+
+ pr_err("no %s keyring: %d\n", ns->ima_keyring, err);
+ ima_keyring = NULL;
return err;
}
+ ns->keyring[0] = ima_keyring;
}
switch (sig[1]) {
case 1:
/* v1 API expect signature without xattr type */
- return digsig_verify(keyring[id], sig + 1, siglen - 1,
+ return digsig_verify(ns->keyring[0], sig + 1, siglen - 1,
digest, digestlen);
case 2:
- return asymmetric_verify(keyring[id], sig, siglen,
+ return asymmetric_verify(ns->keyring[0], sig, siglen,
digest, digestlen);
}
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 21132f9..20d4c88 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -74,10 +74,6 @@ static void iint_free(struct integrity_iint_cache *iint)
iint->ima_hash = NULL;
iint->version = 0;
iint->flags = 0UL;
- iint->ima_file_status = INTEGRITY_UNKNOWN;
- iint->ima_mmap_status = INTEGRITY_UNKNOWN;
- iint->ima_bprm_status = INTEGRITY_UNKNOWN;
- iint->ima_read_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
kmem_cache_free(iint_cache, iint);
}
@@ -158,10 +154,6 @@ static void init_once(void *foo)
memset(iint, 0, sizeof(*iint));
iint->version = 0;
iint->flags = 0UL;
- iint->ima_file_status = INTEGRITY_UNKNOWN;
- iint->ima_mmap_status = INTEGRITY_UNKNOWN;
- iint->ima_bprm_status = INTEGRITY_UNKNOWN;
- iint->ima_read_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
}
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index afd2743..4fd2d4e 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -62,6 +62,10 @@ struct ns_status {
unsigned long flags;
unsigned long measured_pcrs;
struct ima_namespace *ns;
+ enum integrity_status ima_file_status:4;
+ enum integrity_status ima_mmap_status:4;
+ enum integrity_status ima_bprm_status:4;
+ enum integrity_status ima_read_status:4;
};
/* IMA event related data */
@@ -245,11 +249,13 @@ int ima_appraise_measurement(enum ima_hooks func,
struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len, int opened);
+ int xattr_len, int opened,
+ struct ima_namespace *ns,
+ struct ns_status *ns_status);
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func,
struct ima_namespace *ns);
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 ns_status *status,
enum ima_hooks func);
enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
int xattr_len);
@@ -262,7 +268,9 @@ static inline int ima_appraise_measurement(enum ima_hooks func,
struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len, int opened)
+ int xattr_len, int opened,
+ struct ima_namespace *ns,
+ struct ns_status *ns_status)
{
return INTEGRITY_UNKNOWN;
}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 1c28236..a04e4ee 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -81,62 +81,62 @@ static int ima_fix_xattr(struct dentry *dentry,
}
/* Return specific func appraised cached result */
-enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
+enum integrity_status ima_get_cache_status(struct ns_status *status,
enum ima_hooks func)
{
switch (func) {
case MMAP_CHECK:
- return iint->ima_mmap_status;
+ return status->ima_mmap_status;
case BPRM_CHECK:
- return iint->ima_bprm_status;
+ return status->ima_bprm_status;
case FILE_CHECK:
case POST_SETATTR:
- return iint->ima_file_status;
+ return status->ima_file_status;
case MODULE_CHECK ... MAX_CHECK - 1:
default:
- return iint->ima_read_status;
+ return status->ima_read_status;
}
}
-static void ima_set_cache_status(struct integrity_iint_cache *iint,
- enum ima_hooks func,
- enum integrity_status status)
+static void ima_set_cache_status(enum ima_hooks func,
+ enum integrity_status status,
+ struct ns_status *ns_status)
{
switch (func) {
case MMAP_CHECK:
- iint->ima_mmap_status = status;
+ ns_status->ima_mmap_status = status;
break;
case BPRM_CHECK:
- iint->ima_bprm_status = status;
+ ns_status->ima_bprm_status = status;
break;
case FILE_CHECK:
case POST_SETATTR:
- iint->ima_file_status = status;
+ ns_status->ima_file_status = status;
break;
case MODULE_CHECK ... MAX_CHECK - 1:
default:
- iint->ima_read_status = status;
+ ns_status->ima_read_status = status;
break;
}
}
static void ima_cache_flags(struct integrity_iint_cache *iint,
- enum ima_hooks func)
+ enum ima_hooks func, struct ns_status *status)
{
switch (func) {
case MMAP_CHECK:
- iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED);
+ status->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED);
break;
case BPRM_CHECK:
- iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED);
+ status->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED);
break;
case FILE_CHECK:
case POST_SETATTR:
- iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED);
+ status->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED);
break;
case MODULE_CHECK ... MAX_CHECK - 1:
default:
- iint->flags |= (IMA_READ_APPRAISED | IMA_APPRAISED);
+ status->flags |= (IMA_READ_APPRAISED | IMA_APPRAISED);
break;
}
}
@@ -204,7 +204,9 @@ int ima_appraise_measurement(enum ima_hooks func,
struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len, int opened)
+ int xattr_len, int opened,
+ struct ima_namespace *ns,
+ struct ns_status *ns_status)
{
static const char op[] = "appraise_data";
char *cause = "unknown";
@@ -224,9 +226,9 @@ int ima_appraise_measurement(enum ima_hooks func,
"IMA-signature-required" : "missing-hash";
status = INTEGRITY_NOLABEL;
if (opened & FILE_CREATED)
- iint->flags |= IMA_NEW_FILE;
- if ((iint->flags & IMA_NEW_FILE) &&
- !(iint->flags & IMA_DIGSIG_REQUIRED))
+ ns_status->flags |= IMA_NEW_FILE;
+ if ((ns_status->flags & IMA_NEW_FILE) &&
+ !(ns_status->flags & IMA_DIGSIG_REQUIRED))
status = INTEGRITY_PASS;
goto out;
}
@@ -246,7 +248,7 @@ int ima_appraise_measurement(enum ima_hooks func,
hash_start = 1;
/* fall through */
case IMA_XATTR_DIGEST:
- if (iint->flags & IMA_DIGSIG_REQUIRED) {
+ if (ns_status->flags & IMA_DIGSIG_REQUIRED) {
cause = "IMA-signature-required";
status = INTEGRITY_FAIL;
break;
@@ -269,10 +271,9 @@ int ima_appraise_measurement(enum ima_hooks func,
status = INTEGRITY_PASS;
break;
case EVM_IMA_XATTR_DIGSIG:
- iint->flags |= IMA_DIGSIG;
- rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
- (const char *)xattr_value, rc,
- iint->ima_hash->digest,
+ ns_status->flags |= IMA_DIGSIG;
+ rc = integrity_digsig_verify(ns, (const char *)xattr_value,
+ rc, iint->ima_hash->digest,
iint->ima_hash->length);
if (rc == -EOPNOTSUPP) {
status = INTEGRITY_UNKNOWN;
@@ -305,9 +306,9 @@ int ima_appraise_measurement(enum ima_hooks func,
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
op, cause, rc, 0);
} else {
- ima_cache_flags(iint, func);
+ ima_cache_flags(iint, func, ns_status);
}
- ima_set_cache_status(iint, func, status);
+ ima_set_cache_status(func, status, ns_status);
return status;
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 23eadfc..c44e3c2 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -176,7 +176,7 @@ void ima_file_free(struct file *file)
else
ns = get_current_ns();
- if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
+ if (!ns || !ns->ima_policy_flag || !S_ISREG(inode->i_mode))
return;
iint = integrity_iint_find(inode);
@@ -201,6 +201,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
int xattr_len = 0;
bool violation_check;
enum hash_algo hash_algo;
+ bool already_appraised = false;
struct ns_status *status = NULL;
struct ima_namespace *ns = get_current_ns();
@@ -227,6 +228,10 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
continue;
must_appraise = action & IMA_APPRAISE;
+ if (!already_appraised)
+ must_appraise = action & IMA_APPRAISE;
+ else
+ must_appraise = 0;
/* Is the appraise rule hook specific? */
if (action & IMA_FILE_APPRAISE)
@@ -285,7 +290,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
/* Nothing to do, just return existing appraised status */
if (!action) {
if (must_appraise)
- rc = ima_get_cache_status(iint, func);
+ rc = ima_get_cache_status(status, func);
if ((mask & MAY_WRITE) && (status->flags & IMA_DIGSIG))
rc = -EACCES;
inode_unlock(inode);
@@ -329,10 +334,13 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
ima_store_measurement(iint, file, pathname,
xattr_value, xattr_len,
pcr, ns, status);
- if (action & IMA_APPRAISE_SUBMASK)
+ if ((action & IMA_APPRAISE_SUBMASK) && !already_appraised) {
rc = ima_appraise_measurement(func, iint, file,
pathname, xattr_value,
- xattr_len, opened);
+ xattr_len, opened,
+ ns, status);
+ already_appraised = true;
+ }
if (action & IMA_AUDIT)
ima_audit_measurement(iint, pathname, status);
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index b465cb8..693346e 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -105,10 +105,6 @@ struct integrity_iint_cache {
u64 version; /* track inode changes */
unsigned long flags;
struct list_head ns_list;
- enum integrity_status ima_file_status:4;
- enum integrity_status ima_mmap_status:4;
- enum integrity_status ima_bprm_status:4;
- enum integrity_status ima_read_status:4;
enum integrity_status evm_status:4;
struct ima_digest_data *ima_hash;
};
@@ -132,14 +128,14 @@ int __init integrity_read_file(const char *path, char **data);
#ifdef CONFIG_INTEGRITY_SIGNATURE
-int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
- const char *digest, int digestlen);
+int integrity_digsig_verify(struct ima_namespace *ns, const char *sig,
+ int siglen, const char *digest, int digestlen);
int __init integrity_init_keyring(const unsigned int id);
int __init integrity_load_x509(const unsigned int id, const char *path);
#else
-static inline int integrity_digsig_verify(const unsigned int id,
+static inline int integrity_digsig_verify(struct ima_namespace *ns,
const char *sig, int siglen,
const char *digest, int digestlen)
{