Skip to content

Commit 398c42e

Browse files
committed
ima: support fs-verity file digest based version 3 signatures
IMA may verify a file's integrity against a "good" value stored in the 'security.ima' xattr or as an appended signature, based on policy. When the "good value" is stored in the xattr, the xattr may contain a file hash or signature. In either case, the "good" value is preceded by a header. The first byte of the xattr header indicates the type of data - hash, signature - stored in the xattr. To support storing fs-verity signatures in the 'security.ima' xattr requires further differentiating the fs-verity signature from the existing IMA signature. In addition the signatures stored in 'security.ima' xattr, need to be disambiguated. Instead of directly signing the fs-verity digest, a new signature format version 3 is defined as the hash of the ima_file_id structure, which identifies the type of signature and the digest. The IMA policy defines "which" files are to be measured, verified, and/or audited. For those files being verified, the policy rules indicate "how" the file should be verified. For example to require a file be signed, the appraise policy rule must include the 'appraise_type' option. appraise_type:= [imasig] | [imasig|modsig] | [sigv3] where 'imasig' is the original or signature format v2 (default), where 'modsig' is an appended signature, where 'sigv3' is the signature format v3. The policy rule must also indicate the type of digest, if not the IMA default, by first specifying the digest type: digest_type:= [verity] The following policy rule requires fsverity signatures. The rule may be constrained, for example based on a fsuuid or LSM label. appraise func=BPRM_CHECK digest_type=verity appraise_type=sigv3 Acked-by: Stefan Berger <stefanb@linux.ibm.com> Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
1 parent 54f0391 commit 398c42e

7 files changed

Lines changed: 209 additions & 19 deletions

File tree

Documentation/ABI/testing/ima_policy

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,15 @@ Description:
4848
fgroup:= decimal value
4949
lsm: are LSM specific
5050
option:
51-
appraise_type:= [imasig] [imasig|modsig]
51+
appraise_type:= [imasig] | [imasig|modsig] | [sigv3]
52+
where 'imasig' is the original or the signature
53+
format v2.
54+
where 'modsig' is an appended signature,
55+
where 'sigv3' is the signature format v3. (Currently
56+
limited to fsverity digest based signatures
57+
stored in security.ima xattr. Requires
58+
specifying "digest_type=verity" first.)
59+
5260
appraise_flag:= [check_blacklist]
5361
Currently, blacklist check is only for files signed with appended
5462
signature.
@@ -159,3 +167,24 @@ Description:
159167

160168
measure func=FILE_CHECK digest_type=verity \
161169
template=ima-ngv2
170+
171+
Example of 'measure' and 'appraise' rules requiring fs-verity
172+
signatures (format version 3) stored in security.ima xattr.
173+
174+
The 'measure' rule specifies the 'ima-sigv3' template option,
175+
which includes the indication of type of digest and the file
176+
signature in the measurement list.
177+
178+
measure func=BPRM_CHECK digest_type=verity \
179+
template=ima-sigv3
180+
181+
182+
The 'appraise' rule specifies the type and signature format
183+
version (sigv3) required.
184+
185+
appraise func=BPRM_CHECK digest_type=verity \
186+
appraise_type=sigv3
187+
188+
All of these policy rules could, for example, be constrained
189+
either based on a filesystem's UUID (fsuuid) or based on LSM
190+
labels.

Documentation/security/IMA-templates.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ descriptors by adding their identifier to the format string
7171
(field format: <digest type>:<hash algo>:digest);
7272
- 'd-modsig': the digest of the event without the appended modsig;
7373
- 'n-ng': the name of the event, without size limitations;
74-
- 'sig': the file signature, or the EVM portable signature if the file
75-
signature is not found;
74+
- 'sig': the file signature, based on either the file's/fsverity's digest[1],
75+
or the EVM portable signature, if 'security.ima' contains a file hash.
7676
- 'modsig' the appended file signature;
7777
- 'buf': the buffer data that was used to generate the hash without size limitations;
7878
- 'evmsig': the EVM portable signature;

security/integrity/digsig.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
7575
/* v1 API expect signature without xattr type */
7676
return digsig_verify(keyring, sig + 1, siglen - 1, digest,
7777
digestlen);
78-
case 2:
78+
case 2: /* regular file data hash based signature */
79+
case 3: /* struct ima_file_id data based signature */
7980
return asymmetric_verify(keyring, sig, siglen, digest,
8081
digestlen);
8182
}

security/integrity/ima/ima_appraise.c

Lines changed: 110 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
#include <linux/magic.h>
1414
#include <linux/ima.h>
1515
#include <linux/evm.h>
16+
#include <linux/fsverity.h>
1617
#include <keys/system_keyring.h>
18+
#include <uapi/linux/fsverity.h>
1719

1820
#include "ima.h"
1921

@@ -183,13 +185,18 @@ enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
183185
return ima_hash_algo;
184186

185187
switch (xattr_value->type) {
188+
case IMA_VERITY_DIGSIG:
189+
sig = (typeof(sig))xattr_value;
190+
if (sig->version != 3 || xattr_len <= sizeof(*sig) ||
191+
sig->hash_algo >= HASH_ALGO__LAST)
192+
return ima_hash_algo;
193+
return sig->hash_algo;
186194
case EVM_IMA_XATTR_DIGSIG:
187195
sig = (typeof(sig))xattr_value;
188196
if (sig->version != 2 || xattr_len <= sizeof(*sig)
189197
|| sig->hash_algo >= HASH_ALGO__LAST)
190198
return ima_hash_algo;
191199
return sig->hash_algo;
192-
break;
193200
case IMA_XATTR_DIGEST_NG:
194201
/* first byte contains algorithm id */
195202
ret = xattr_value->data[0];
@@ -225,6 +232,40 @@ int ima_read_xattr(struct dentry *dentry,
225232
return ret;
226233
}
227234

235+
/*
236+
* calc_file_id_hash - calculate the hash of the ima_file_id struct data
237+
* @type: xattr type [enum evm_ima_xattr_type]
238+
* @algo: hash algorithm [enum hash_algo]
239+
* @digest: pointer to the digest to be hashed
240+
* @hash: (out) pointer to the hash
241+
*
242+
* IMA signature version 3 disambiguates the data that is signed by
243+
* indirectly signing the hash of the ima_file_id structure data.
244+
*
245+
* Signing the ima_file_id struct is currently only supported for
246+
* IMA_VERITY_DIGSIG type xattrs.
247+
*
248+
* Return 0 on success, error code otherwise.
249+
*/
250+
static int calc_file_id_hash(enum evm_ima_xattr_type type,
251+
enum hash_algo algo, const u8 *digest,
252+
struct ima_digest_data *hash)
253+
{
254+
struct ima_file_id file_id = {
255+
.hash_type = IMA_VERITY_DIGSIG, .hash_algorithm = algo};
256+
unsigned int unused = HASH_MAX_DIGESTSIZE - hash_digest_size[algo];
257+
258+
if (type != IMA_VERITY_DIGSIG)
259+
return -EINVAL;
260+
261+
memcpy(file_id.hash, digest, hash_digest_size[algo]);
262+
263+
hash->algo = algo;
264+
hash->length = hash_digest_size[algo];
265+
266+
return ima_calc_buffer_hash(&file_id, sizeof(file_id) - unused, hash);
267+
}
268+
228269
/*
229270
* xattr_verify - verify xattr digest or signature
230271
*
@@ -236,7 +277,10 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
236277
struct evm_ima_xattr_data *xattr_value, int xattr_len,
237278
enum integrity_status *status, const char **cause)
238279
{
280+
struct ima_max_digest_data hash;
281+
struct signature_v2_hdr *sig;
239282
int rc = -EINVAL, hash_start = 0;
283+
int mask;
240284

241285
switch (xattr_value->type) {
242286
case IMA_XATTR_DIGEST_NG:
@@ -246,7 +290,10 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
246290
case IMA_XATTR_DIGEST:
247291
if (*status != INTEGRITY_PASS_IMMUTABLE) {
248292
if (iint->flags & IMA_DIGSIG_REQUIRED) {
249-
*cause = "IMA-signature-required";
293+
if (iint->flags & IMA_VERITY_REQUIRED)
294+
*cause = "verity-signature-required";
295+
else
296+
*cause = "IMA-signature-required";
250297
*status = INTEGRITY_FAIL;
251298
break;
252299
}
@@ -274,6 +321,20 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
274321
break;
275322
case EVM_IMA_XATTR_DIGSIG:
276323
set_bit(IMA_DIGSIG, &iint->atomic_flags);
324+
325+
mask = IMA_DIGSIG_REQUIRED | IMA_VERITY_REQUIRED;
326+
if ((iint->flags & mask) == mask) {
327+
*cause = "verity-signature-required";
328+
*status = INTEGRITY_FAIL;
329+
break;
330+
}
331+
332+
sig = (typeof(sig))xattr_value;
333+
if (sig->version >= 3) {
334+
*cause = "invalid-signature-version";
335+
*status = INTEGRITY_FAIL;
336+
break;
337+
}
277338
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
278339
(const char *)xattr_value,
279340
xattr_len,
@@ -296,6 +357,44 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
296357
} else {
297358
*status = INTEGRITY_PASS;
298359
}
360+
break;
361+
case IMA_VERITY_DIGSIG:
362+
set_bit(IMA_DIGSIG, &iint->atomic_flags);
363+
364+
if (iint->flags & IMA_DIGSIG_REQUIRED) {
365+
if (!(iint->flags & IMA_VERITY_REQUIRED)) {
366+
*cause = "IMA-signature-required";
367+
*status = INTEGRITY_FAIL;
368+
break;
369+
}
370+
}
371+
372+
sig = (typeof(sig))xattr_value;
373+
if (sig->version != 3) {
374+
*cause = "invalid-signature-version";
375+
*status = INTEGRITY_FAIL;
376+
break;
377+
}
378+
379+
rc = calc_file_id_hash(IMA_VERITY_DIGSIG, iint->ima_hash->algo,
380+
iint->ima_hash->digest, &hash.hdr);
381+
if (rc) {
382+
*cause = "sigv3-hashing-error";
383+
*status = INTEGRITY_FAIL;
384+
break;
385+
}
386+
387+
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
388+
(const char *)xattr_value,
389+
xattr_len, hash.digest,
390+
hash.hdr.length);
391+
if (rc) {
392+
*cause = "invalid-verity-signature";
393+
*status = INTEGRITY_FAIL;
394+
} else {
395+
*status = INTEGRITY_PASS;
396+
}
397+
299398
break;
300399
default:
301400
*status = INTEGRITY_UNKNOWN;
@@ -396,8 +495,15 @@ int ima_appraise_measurement(enum ima_hooks func,
396495
if (rc && rc != -ENODATA)
397496
goto out;
398497

399-
cause = iint->flags & IMA_DIGSIG_REQUIRED ?
400-
"IMA-signature-required" : "missing-hash";
498+
if (iint->flags & IMA_DIGSIG_REQUIRED) {
499+
if (iint->flags & IMA_VERITY_REQUIRED)
500+
cause = "verity-signature-required";
501+
else
502+
cause = "IMA-signature-required";
503+
} else {
504+
cause = "missing-hash";
505+
}
506+
401507
status = INTEGRITY_NOLABEL;
402508
if (file->f_mode & FMODE_CREATED)
403509
iint->flags |= IMA_NEW_FILE;

security/integrity/ima/ima_policy.c

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,18 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
13101310
!(entry->flags & IMA_MODSIG_ALLOWED))
13111311
return false;
13121312

1313+
/*
1314+
* Unlike for regular IMA 'appraise' policy rules where security.ima
1315+
* xattr may contain either a file hash or signature, the security.ima
1316+
* xattr for fsverity must contain a file signature (sigv3). Ensure
1317+
* that 'appraise' rules for fsverity require file signatures by
1318+
* checking the IMA_DIGSIG_REQUIRED flag is set.
1319+
*/
1320+
if (entry->action == APPRAISE &&
1321+
(entry->flags & IMA_VERITY_REQUIRED) &&
1322+
!(entry->flags & IMA_DIGSIG_REQUIRED))
1323+
return false;
1324+
13131325
return true;
13141326
}
13151327

@@ -1727,21 +1739,37 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
17271739
break;
17281740
case Opt_digest_type:
17291741
ima_log_string(ab, "digest_type", args[0].from);
1730-
if ((strcmp(args[0].from, "verity")) == 0)
1742+
if (entry->flags & IMA_DIGSIG_REQUIRED)
1743+
result = -EINVAL;
1744+
else if ((strcmp(args[0].from, "verity")) == 0)
17311745
entry->flags |= IMA_VERITY_REQUIRED;
17321746
else
17331747
result = -EINVAL;
17341748
break;
17351749
case Opt_appraise_type:
17361750
ima_log_string(ab, "appraise_type", args[0].from);
1737-
if ((strcmp(args[0].from, "imasig")) == 0)
1738-
entry->flags |= IMA_DIGSIG_REQUIRED;
1739-
else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
1740-
strcmp(args[0].from, "imasig|modsig") == 0)
1741-
entry->flags |= IMA_DIGSIG_REQUIRED |
1751+
1752+
if ((strcmp(args[0].from, "imasig")) == 0) {
1753+
if (entry->flags & IMA_VERITY_REQUIRED)
1754+
result = -EINVAL;
1755+
else
1756+
entry->flags |= IMA_DIGSIG_REQUIRED;
1757+
} else if (strcmp(args[0].from, "sigv3") == 0) {
1758+
/* Only fsverity supports sigv3 for now */
1759+
if (entry->flags & IMA_VERITY_REQUIRED)
1760+
entry->flags |= IMA_DIGSIG_REQUIRED;
1761+
else
1762+
result = -EINVAL;
1763+
} else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
1764+
strcmp(args[0].from, "imasig|modsig") == 0) {
1765+
if (entry->flags & IMA_VERITY_REQUIRED)
1766+
result = -EINVAL;
1767+
else
1768+
entry->flags |= IMA_DIGSIG_REQUIRED |
17421769
IMA_MODSIG_ALLOWED;
1743-
else
1770+
} else {
17441771
result = -EINVAL;
1772+
}
17451773
break;
17461774
case Opt_appraise_flag:
17471775
ima_log_string(ab, "appraise_flag", args[0].from);
@@ -2183,7 +2211,9 @@ int ima_policy_show(struct seq_file *m, void *v)
21832211
if (entry->template)
21842212
seq_printf(m, "template=%s ", entry->template->name);
21852213
if (entry->flags & IMA_DIGSIG_REQUIRED) {
2186-
if (entry->flags & IMA_MODSIG_ALLOWED)
2214+
if (entry->flags & IMA_VERITY_REQUIRED)
2215+
seq_puts(m, "appraise_type=sigv3 ");
2216+
else if (entry->flags & IMA_MODSIG_ALLOWED)
21872217
seq_puts(m, "appraise_type=imasig|modsig ");
21882218
else
21892219
seq_puts(m, "appraise_type=imasig ");

security/integrity/ima/ima_template_lib.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,9 @@ int ima_eventsig_init(struct ima_event_data *event_data,
535535
{
536536
struct evm_ima_xattr_data *xattr_value = event_data->xattr_value;
537537

538-
if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG))
538+
if (!xattr_value ||
539+
(xattr_value->type != EVM_IMA_XATTR_DIGSIG &&
540+
xattr_value->type != IMA_VERITY_DIGSIG))
539541
return ima_eventevmsig_init(event_data, field_data);
540542

541543
return ima_write_template_field_data(xattr_value, event_data->xattr_len,

security/integrity/integrity.h

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ enum evm_ima_xattr_type {
7979
EVM_IMA_XATTR_DIGSIG,
8080
IMA_XATTR_DIGEST_NG,
8181
EVM_XATTR_PORTABLE_DIGSIG,
82+
IMA_VERITY_DIGSIG,
8283
IMA_XATTR_LAST
8384
};
8485

@@ -93,7 +94,7 @@ struct evm_xattr {
9394
u8 digest[SHA1_DIGEST_SIZE];
9495
} __packed;
9596

96-
#define IMA_MAX_DIGEST_SIZE 64
97+
#define IMA_MAX_DIGEST_SIZE HASH_MAX_DIGESTSIZE
9798

9899
struct ima_digest_data {
99100
u8 algo;
@@ -122,7 +123,14 @@ struct ima_max_digest_data {
122123
} __packed;
123124

124125
/*
125-
* signature format v2 - for using with asymmetric keys
126+
* signature header format v2 - for using with asymmetric keys
127+
*
128+
* The signature_v2_hdr struct includes a signature format version
129+
* to simplify defining new signature formats.
130+
*
131+
* signature format:
132+
* version 2: regular file data hash based signature
133+
* version 3: struct ima_file_id data based signature
126134
*/
127135
struct signature_v2_hdr {
128136
uint8_t type; /* xattr type */
@@ -133,6 +141,20 @@ struct signature_v2_hdr {
133141
uint8_t sig[]; /* signature payload */
134142
} __packed;
135143

144+
/*
145+
* IMA signature version 3 disambiguates the data that is signed, by
146+
* indirectly signing the hash of the ima_file_id structure data,
147+
* containing either the fsverity_descriptor struct digest or, in the
148+
* future, the regular IMA file hash.
149+
*
150+
* (The hash of the ima_file_id structure is only of the portion used.)
151+
*/
152+
struct ima_file_id {
153+
__u8 hash_type; /* xattr type [enum evm_ima_xattr_type] */
154+
__u8 hash_algorithm; /* Digest algorithm [enum hash_algo] */
155+
__u8 hash[HASH_MAX_DIGESTSIZE];
156+
} __packed;
157+
136158
/* integrity data associated with an inode */
137159
struct integrity_iint_cache {
138160
struct rb_node rb_node; /* rooted in integrity_iint_tree */

0 commit comments

Comments
 (0)