ima: Define ima-modsig template

Define new "d-modsig" template field which holds the digest that is
expected to match the one contained in the modsig, and also new "modsig"
template field which holds the appended file signature.

Add a new "ima-modsig" defined template descriptor with the new fields as
well as the ones from the "ima-sig" descriptor.

Change ima_store_measurement() to accept a struct modsig * argument so that
it can be passed along to the templates via struct ima_event_data.

Suggested-by: Mimi Zohar <zohar@linux.ibm.com>
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.ibm.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
This commit is contained in:
Thiago Jung Bauermann 2019-06-27 23:19:32 -03:00 committed by Mimi Zohar
parent 15588227e0
commit 3878d505aa
9 changed files with 159 additions and 6 deletions

View File

@ -68,8 +68,10 @@ descriptors by adding their identifier to the format string
- 'd-ng': the digest of the event, calculated with an arbitrary hash - 'd-ng': the digest of the event, calculated with an arbitrary hash
algorithm (field format: [<hash algo>:]digest, where the digest algorithm (field format: [<hash algo>:]digest, where the digest
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;
- '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;
- '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;
@ -79,6 +81,7 @@ Below, there is the list of defined template descriptors:
- "ima-ng" (default): its format is ``d-ng|n-ng``; - "ima-ng" (default): its format is ``d-ng|n-ng``;
- "ima-sig": its format is ``d-ng|n-ng|sig``; - "ima-sig": its format is ``d-ng|n-ng|sig``;
- "ima-buf": its format is ``d-ng|n-ng|buf``; - "ima-buf": its format is ``d-ng|n-ng|buf``;
- "ima-modsig": its format is ``d-ng|n-ng|sig|d-modsig|modsig``;
Use Use

View File

@ -60,6 +60,7 @@ struct ima_event_data {
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 char *violation; const char *violation;
const void *buf; const void *buf;
int buf_len; int buf_len;
@ -211,7 +212,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
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 pcr, int xattr_len, const struct modsig *modsig, int pcr,
struct ima_template_desc *template_desc); struct ima_template_desc *template_desc);
void ima_audit_measurement(struct integrity_iint_cache *iint, void ima_audit_measurement(struct integrity_iint_cache *iint,
const unsigned char *filename); const unsigned char *filename);
@ -312,6 +313,10 @@ bool ima_hook_supports_modsig(enum ima_hooks func);
int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len, int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
struct modsig **modsig); struct modsig **modsig);
void ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size); void ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size);
int ima_get_modsig_digest(const struct modsig *modsig, enum hash_algo *algo,
const u8 **digest, u32 *digest_size);
int ima_get_raw_modsig(const struct modsig *modsig, const void **data,
u32 *data_len);
void ima_free_modsig(struct modsig *modsig); void ima_free_modsig(struct modsig *modsig);
#else #else
static inline bool ima_hook_supports_modsig(enum ima_hooks func) static inline bool ima_hook_supports_modsig(enum ima_hooks func)
@ -330,6 +335,19 @@ static inline void ima_collect_modsig(struct modsig *modsig, const void *buf,
{ {
} }
static inline int ima_get_modsig_digest(const struct modsig *modsig,
enum hash_algo *algo, const u8 **digest,
u32 *digest_size)
{
return -EOPNOTSUPP;
}
static inline int ima_get_raw_modsig(const struct modsig *modsig,
const void **data, u32 *data_len)
{
return -EOPNOTSUPP;
}
static inline void ima_free_modsig(struct modsig *modsig) static inline void ima_free_modsig(struct modsig *modsig)
{ {
} }

View File

@ -288,7 +288,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
void ima_store_measurement(struct integrity_iint_cache *iint, void ima_store_measurement(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, int pcr, int xattr_len, const struct modsig *modsig, int pcr,
struct ima_template_desc *template_desc) struct ima_template_desc *template_desc)
{ {
static const char op[] = "add_template_measure"; static const char op[] = "add_template_measure";
@ -300,7 +300,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
.file = file, .file = file,
.filename = filename, .filename = filename,
.xattr_value = xattr_value, .xattr_value = xattr_value,
.xattr_len = xattr_len }; .xattr_len = xattr_len,
.modsig = modsig };
int violation = 0; int violation = 0;
if (iint->measured_pcrs & (0x1 << pcr)) if (iint->measured_pcrs & (0x1 << pcr))

View File

@ -323,7 +323,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
if (action & IMA_MEASURE) if (action & IMA_MEASURE)
ima_store_measurement(iint, file, pathname, ima_store_measurement(iint, file, pathname,
xattr_value, xattr_len, pcr, xattr_value, xattr_len, modsig, pcr,
template_desc); template_desc);
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) { if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
inode_lock(inode); inode_lock(inode);

View File

@ -138,6 +138,25 @@ int ima_modsig_verify(struct key *keyring, const struct modsig *modsig)
VERIFYING_MODULE_SIGNATURE, NULL, NULL); VERIFYING_MODULE_SIGNATURE, NULL, NULL);
} }
int ima_get_modsig_digest(const struct modsig *modsig, enum hash_algo *algo,
const u8 **digest, u32 *digest_size)
{
*algo = modsig->hash_algo;
*digest = modsig->digest;
*digest_size = modsig->digest_size;
return 0;
}
int ima_get_raw_modsig(const struct modsig *modsig, const void **data,
u32 *data_len)
{
*data = &modsig->raw_pkcs7;
*data_len = modsig->raw_pkcs7_len;
return 0;
}
void ima_free_modsig(struct modsig *modsig) void ima_free_modsig(struct modsig *modsig)
{ {
if (!modsig) if (!modsig)

View File

@ -6,6 +6,9 @@
* ima_policy.c * ima_policy.c
* - initialize default measure policy rules * - initialize default measure policy rules
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h> #include <linux/init.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/fs.h> #include <linux/fs.h>
@ -845,6 +848,38 @@ static void ima_log_string(struct audit_buffer *ab, char *key, char *value)
ima_log_string_op(ab, key, value, NULL); ima_log_string_op(ab, key, value, NULL);
} }
/*
* Validating the appended signature included in the measurement list requires
* the file hash calculated without the appended signature (i.e., the 'd-modsig'
* field). Therefore, notify the user if they have the 'modsig' field but not
* the 'd-modsig' field in the template.
*/
static void check_template_modsig(const struct ima_template_desc *template)
{
#define MSG "template with 'modsig' field also needs 'd-modsig' field\n"
bool has_modsig, has_dmodsig;
static bool checked;
int i;
/* We only need to notify the user once. */
if (checked)
return;
has_modsig = has_dmodsig = false;
for (i = 0; i < template->num_fields; i++) {
if (!strcmp(template->fields[i]->field_id, "modsig"))
has_modsig = true;
else if (!strcmp(template->fields[i]->field_id, "d-modsig"))
has_dmodsig = true;
}
if (has_modsig && !has_dmodsig)
pr_notice(MSG);
checked = true;
#undef MSG
}
static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
{ {
struct audit_buffer *ab; struct audit_buffer *ab;
@ -1187,6 +1222,12 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
else if (entry->action == APPRAISE) else if (entry->action == APPRAISE)
temp_ima_appraise |= ima_appraise_flag(entry->func); temp_ima_appraise |= ima_appraise_flag(entry->func);
if (!result && entry->flags & IMA_MODSIG_ALLOWED) {
template_desc = entry->template ? entry->template :
ima_template_desc_current();
check_template_modsig(template_desc);
}
audit_log_format(ab, "res=%d", !result); audit_log_format(ab, "res=%d", !result);
audit_log_end(ab); audit_log_end(ab);
return result; return result;

View File

@ -23,6 +23,7 @@ static struct ima_template_desc builtin_templates[] = {
{.name = "ima-ng", .fmt = "d-ng|n-ng"}, {.name = "ima-ng", .fmt = "d-ng|n-ng"},
{.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 = "", .fmt = ""}, /* placeholder for a custom format */ {.name = "", .fmt = ""}, /* placeholder for a custom format */
}; };
@ -42,6 +43,10 @@ static const struct ima_template_field supported_fields[] = {
.field_show = ima_show_template_sig}, .field_show = ima_show_template_sig},
{.field_id = "buf", .field_init = ima_eventbuf_init, {.field_id = "buf", .field_init = ima_eventbuf_init,
.field_show = ima_show_template_buf}, .field_show = ima_show_template_buf},
{.field_id = "d-modsig", .field_init = ima_eventdigest_modsig_init,
.field_show = ima_show_template_digest_ng},
{.field_id = "modsig", .field_init = ima_eventmodsig_init,
.field_show = ima_show_template_sig},
}; };
/* /*
@ -49,7 +54,7 @@ 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") #define MAX_TEMPLATE_NAME_LEN sizeof("d-ng|n-ng|sig|buf|d-modisg|modsig")
static struct ima_template_desc *ima_template; static struct ima_template_desc *ima_template;

View File

@ -225,7 +225,8 @@ int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
return 0; return 0;
} }
static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo, static int ima_eventdigest_init_common(const u8 *digest, u32 digestsize,
u8 hash_algo,
struct ima_field_data *field_data) struct ima_field_data *field_data)
{ {
/* /*
@ -328,6 +329,41 @@ int ima_eventdigest_ng_init(struct ima_event_data *event_data,
hash_algo, field_data); hash_algo, field_data);
} }
/*
* This function writes the digest of the file which is expected to match the
* digest contained in the file's appended signature.
*/
int ima_eventdigest_modsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
enum hash_algo hash_algo;
const u8 *cur_digest;
u32 cur_digestsize;
if (!event_data->modsig)
return 0;
if (event_data->violation) {
/* Recording a violation. */
hash_algo = HASH_ALGO_SHA1;
cur_digest = NULL;
cur_digestsize = 0;
} else {
int rc;
rc = ima_get_modsig_digest(event_data->modsig, &hash_algo,
&cur_digest, &cur_digestsize);
if (rc)
return rc;
else if (hash_algo == HASH_ALGO__LAST || cur_digestsize == 0)
/* There was some error collecting the digest. */
return -EINVAL;
}
return ima_eventdigest_init_common(cur_digest, cur_digestsize,
hash_algo, field_data);
}
static int ima_eventname_init_common(struct ima_event_data *event_data, static int ima_eventname_init_common(struct ima_event_data *event_data,
struct ima_field_data *field_data, struct ima_field_data *field_data,
bool size_limit) bool size_limit)
@ -406,3 +442,29 @@ int ima_eventbuf_init(struct ima_event_data *event_data,
event_data->buf_len, DATA_FMT_HEX, event_data->buf_len, DATA_FMT_HEX,
field_data); field_data);
} }
/*
* ima_eventmodsig_init - include the appended file signature as part of the
* template data
*/
int ima_eventmodsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
const void *data;
u32 data_len;
int rc;
if (!event_data->modsig)
return 0;
/*
* modsig is a runtime structure containing pointers. Get its raw data
* instead.
*/
rc = ima_get_raw_modsig(event_data->modsig, &data, &data_len);
if (rc)
return rc;
return ima_write_template_field_data(data, data_len, DATA_FMT_HEX,
field_data);
}

View File

@ -36,10 +36,14 @@ int ima_eventname_init(struct ima_event_data *event_data,
struct ima_field_data *field_data); struct ima_field_data *field_data);
int ima_eventdigest_ng_init(struct ima_event_data *event_data, int ima_eventdigest_ng_init(struct ima_event_data *event_data,
struct ima_field_data *field_data); struct ima_field_data *field_data);
int ima_eventdigest_modsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventname_ng_init(struct ima_event_data *event_data, int ima_eventname_ng_init(struct ima_event_data *event_data,
struct ima_field_data *field_data); struct ima_field_data *field_data);
int ima_eventsig_init(struct ima_event_data *event_data, int ima_eventsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data); struct ima_field_data *field_data);
int ima_eventbuf_init(struct ima_event_data *event_data, 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,
struct ima_field_data *field_data);
#endif /* __LINUX_IMA_TEMPLATE_LIB_H */ #endif /* __LINUX_IMA_TEMPLATE_LIB_H */