Skip to content

Commit 50b9774

Browse files
mjg59Mimi Zohar
authored andcommitted
EVM: Add support for portable signature format
The EVM signature includes the inode number and (optionally) the filesystem UUID, making it impractical to ship EVM signatures in packages. This patch adds a new portable format intended to allow distributions to include EVM signatures. It is identical to the existing format but hardcodes the inode and generation numbers to 0 and does not include the filesystem UUID even if the kernel is configured to do so. Removing the inode means that the metadata and signature from one file could be copied to another file without invalidating it. This is avoided by ensuring that an IMA xattr is present during EVM validation. Portable signatures are intended to be immutable - ie, they will never be transformed into HMACs. Based on earlier work by Dmitry Kasatkin and Mikhail Kurinnoi. Signed-off-by: Matthew Garrett <[email protected]> Cc: Dmitry Kasatkin <[email protected]> Cc: Mikhail Kurinnoi <[email protected]> Signed-off-by: Mimi Zohar <[email protected]>
1 parent ae1ba16 commit 50b9774

File tree

6 files changed

+92
-21
lines changed

6 files changed

+92
-21
lines changed

include/linux/integrity.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
enum integrity_status {
1616
INTEGRITY_PASS = 0,
17+
INTEGRITY_PASS_IMMUTABLE,
1718
INTEGRITY_FAIL,
1819
INTEGRITY_NOLABEL,
1920
INTEGRITY_NOXATTRS,

security/integrity/evm/evm.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
5454
size_t req_xattr_value_len, char *digest);
5555
int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
5656
const char *req_xattr_value,
57-
size_t req_xattr_value_len, char *digest);
57+
size_t req_xattr_value_len, char type, char *digest);
5858
int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
5959
char *hmac_val);
6060
int evm_init_secfs(void);

security/integrity/evm/evm_crypto.c

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ static struct shash_desc *init_desc(char type)
138138
* protection.)
139139
*/
140140
static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
141-
char *digest)
141+
char type, char *digest)
142142
{
143143
struct h_misc {
144144
unsigned long ino;
@@ -149,8 +149,13 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
149149
} hmac_misc;
150150

151151
memset(&hmac_misc, 0, sizeof(hmac_misc));
152-
hmac_misc.ino = inode->i_ino;
153-
hmac_misc.generation = inode->i_generation;
152+
/* Don't include the inode or generation number in portable
153+
* signatures
154+
*/
155+
if (type != EVM_XATTR_PORTABLE_DIGSIG) {
156+
hmac_misc.ino = inode->i_ino;
157+
hmac_misc.generation = inode->i_generation;
158+
}
154159
/* The hmac uid and gid must be encoded in the initial user
155160
* namespace (not the filesystems user namespace) as encoding
156161
* them in the filesystems user namespace allows an attack
@@ -163,7 +168,8 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
163168
hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
164169
hmac_misc.mode = inode->i_mode;
165170
crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
166-
if (evm_hmac_attrs & EVM_ATTR_FSUUID)
171+
if ((evm_hmac_attrs & EVM_ATTR_FSUUID) &&
172+
type != EVM_XATTR_PORTABLE_DIGSIG)
167173
crypto_shash_update(desc, &inode->i_sb->s_uuid.b[0],
168174
sizeof(inode->i_sb->s_uuid));
169175
crypto_shash_final(desc, digest);
@@ -189,6 +195,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
189195
char *xattr_value = NULL;
190196
int error;
191197
int size;
198+
bool ima_present = false;
192199

193200
if (!(inode->i_opflags & IOP_XATTR))
194201
return -EOPNOTSUPP;
@@ -199,11 +206,18 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
199206

200207
error = -ENODATA;
201208
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
209+
bool is_ima = false;
210+
211+
if (strcmp(*xattrname, XATTR_NAME_IMA) == 0)
212+
is_ima = true;
213+
202214
if ((req_xattr_name && req_xattr_value)
203215
&& !strcmp(*xattrname, req_xattr_name)) {
204216
error = 0;
205217
crypto_shash_update(desc, (const u8 *)req_xattr_value,
206218
req_xattr_value_len);
219+
if (is_ima)
220+
ima_present = true;
207221
continue;
208222
}
209223
size = vfs_getxattr_alloc(dentry, *xattrname,
@@ -218,9 +232,14 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
218232
error = 0;
219233
xattr_size = size;
220234
crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
235+
if (is_ima)
236+
ima_present = true;
221237
}
222-
hmac_add_misc(desc, inode, digest);
238+
hmac_add_misc(desc, inode, type, digest);
223239

240+
/* Portable EVM signatures must include an IMA hash */
241+
if (type == EVM_XATTR_PORTABLE_DIGSIG && !ima_present)
242+
return -EPERM;
224243
out:
225244
kfree(xattr_value);
226245
kfree(desc);
@@ -232,17 +251,45 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
232251
char *digest)
233252
{
234253
return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value,
235-
req_xattr_value_len, EVM_XATTR_HMAC, digest);
254+
req_xattr_value_len, EVM_XATTR_HMAC, digest);
236255
}
237256

238257
int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
239258
const char *req_xattr_value, size_t req_xattr_value_len,
240-
char *digest)
259+
char type, char *digest)
241260
{
242261
return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value,
243-
req_xattr_value_len, IMA_XATTR_DIGEST, digest);
262+
req_xattr_value_len, type, digest);
263+
}
264+
265+
static int evm_is_immutable(struct dentry *dentry, struct inode *inode)
266+
{
267+
const struct evm_ima_xattr_data *xattr_data = NULL;
268+
struct integrity_iint_cache *iint;
269+
int rc = 0;
270+
271+
iint = integrity_iint_find(inode);
272+
if (iint && (iint->flags & EVM_IMMUTABLE_DIGSIG))
273+
return 1;
274+
275+
/* Do this the hard way */
276+
rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0,
277+
GFP_NOFS);
278+
if (rc <= 0) {
279+
if (rc == -ENODATA)
280+
return 0;
281+
return rc;
282+
}
283+
if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG)
284+
rc = 1;
285+
else
286+
rc = 0;
287+
288+
kfree(xattr_data);
289+
return rc;
244290
}
245291

292+
246293
/*
247294
* Calculate the hmac and update security.evm xattr
248295
*
@@ -255,6 +302,16 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
255302
struct evm_ima_xattr_data xattr_data;
256303
int rc = 0;
257304

305+
/*
306+
* Don't permit any transformation of the EVM xattr if the signature
307+
* is of an immutable type
308+
*/
309+
rc = evm_is_immutable(dentry, inode);
310+
if (rc < 0)
311+
return rc;
312+
if (rc)
313+
return -EPERM;
314+
258315
rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
259316
xattr_value_len, xattr_data.digest);
260317
if (rc == 0) {
@@ -280,7 +337,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
280337
}
281338

282339
crypto_shash_update(desc, lsm_xattr->value, lsm_xattr->value_len);
283-
hmac_add_misc(desc, inode, hmac_val);
340+
hmac_add_misc(desc, inode, EVM_XATTR_HMAC, hmac_val);
284341
kfree(desc);
285342
return 0;
286343
}

security/integrity/evm/evm_main.c

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
int evm_initialized;
3232

3333
static char *integrity_status_msg[] = {
34-
"pass", "fail", "no_label", "no_xattrs", "unknown"
34+
"pass", "pass_immutable", "fail", "no_label", "no_xattrs", "unknown"
3535
};
3636
char *evm_hmac = "hmac(sha1)";
3737
char *evm_hash = "sha1";
@@ -128,7 +128,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
128128
enum integrity_status evm_status = INTEGRITY_PASS;
129129
int rc, xattr_len;
130130

131-
if (iint && iint->evm_status == INTEGRITY_PASS)
131+
if (iint && (iint->evm_status == INTEGRITY_PASS ||
132+
iint->evm_status == INTEGRITY_PASS_IMMUTABLE))
132133
return iint->evm_status;
133134

134135
/* if status is not PASS, try to check again - against -ENOMEM */
@@ -169,22 +170,26 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
169170
rc = -EINVAL;
170171
break;
171172
case EVM_IMA_XATTR_DIGSIG:
173+
case EVM_XATTR_PORTABLE_DIGSIG:
172174
rc = evm_calc_hash(dentry, xattr_name, xattr_value,
173-
xattr_value_len, calc.digest);
175+
xattr_value_len, xattr_data->type,
176+
calc.digest);
174177
if (rc)
175178
break;
176179
rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM,
177180
(const char *)xattr_data, xattr_len,
178181
calc.digest, sizeof(calc.digest));
179182
if (!rc) {
180-
/* Replace RSA with HMAC if not mounted readonly and
181-
* not immutable
182-
*/
183-
if (!IS_RDONLY(d_backing_inode(dentry)) &&
184-
!IS_IMMUTABLE(d_backing_inode(dentry)))
183+
if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) {
184+
if (iint)
185+
iint->flags |= EVM_IMMUTABLE_DIGSIG;
186+
evm_status = INTEGRITY_PASS_IMMUTABLE;
187+
} else if (!IS_RDONLY(d_backing_inode(dentry)) &&
188+
!IS_IMMUTABLE(d_backing_inode(dentry))) {
185189
evm_update_evmxattr(dentry, xattr_name,
186190
xattr_value,
187191
xattr_value_len);
192+
}
188193
}
189194
break;
190195
default:
@@ -285,7 +290,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
285290
* affect security.evm. An interesting side affect of writing posix xattr
286291
* acls is their modifying of the i_mode, which is included in security.evm.
287292
* For posix xattr acls only, permit security.evm, even if it currently
288-
* doesn't exist, to be updated.
293+
* doesn't exist, to be updated unless the EVM signature is immutable.
289294
*/
290295
static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
291296
const void *xattr_value, size_t xattr_value_len)
@@ -360,7 +365,8 @@ int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
360365
if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) {
361366
if (!xattr_value_len)
362367
return -EINVAL;
363-
if (xattr_data->type != EVM_IMA_XATTR_DIGSIG)
368+
if (xattr_data->type != EVM_IMA_XATTR_DIGSIG &&
369+
xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG)
364370
return -EPERM;
365371
}
366372
return evm_protect_xattr(dentry, xattr_name, xattr_value,
@@ -443,6 +449,9 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
443449
/**
444450
* evm_inode_setattr - prevent updating an invalid EVM extended attribute
445451
* @dentry: pointer to the affected dentry
452+
*
453+
* Permit update of file attributes when files have a valid EVM signature,
454+
* except in the case of them having an immutable portable signature.
446455
*/
447456
int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
448457
{

security/integrity/ima/ima_appraise.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,9 @@ int ima_appraise_measurement(enum ima_hooks func,
230230
}
231231

232232
status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint);
233-
if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) {
233+
if ((status != INTEGRITY_PASS) &&
234+
(status != INTEGRITY_PASS_IMMUTABLE) &&
235+
(status != INTEGRITY_UNKNOWN)) {
234236
if ((status == INTEGRITY_NOLABEL)
235237
|| (status == INTEGRITY_NOXATTRS))
236238
cause = "missing-HMAC";

security/integrity/integrity.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#define IMA_DIGSIG_REQUIRED 0x02000000
3434
#define IMA_PERMIT_DIRECTIO 0x04000000
3535
#define IMA_NEW_FILE 0x08000000
36+
#define EVM_IMMUTABLE_DIGSIG 0x10000000
3637

3738
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
3839
IMA_APPRAISE_SUBMASK)
@@ -58,6 +59,7 @@ enum evm_ima_xattr_type {
5859
EVM_XATTR_HMAC,
5960
EVM_IMA_XATTR_DIGSIG,
6061
IMA_XATTR_DIGEST_NG,
62+
EVM_XATTR_PORTABLE_DIGSIG,
6163
IMA_XATTR_LAST
6264
};
6365

0 commit comments

Comments
 (0)