mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-29 17:22:07 +00:00
integrity-v5.19
-----BEGIN PGP SIGNATURE----- iIoEABYIADIWIQQdXVVFGN5XqKr1Hj7LwZzRsCrn5QUCYo0tOhQcem9oYXJAbGlu dXguaWJtLmNvbQAKCRDLwZzRsCrn5QJfAP47Ym9vacLc1m8/MUaRA/QjbJ/8t3TX h/4McK8kiRudxgD/RiPHII6gJ8q+qpBrYWJZ4ZZaHE8v0oA1viuZfbuN2wc= =KQYi -----END PGP SIGNATURE----- Merge tag 'integrity-v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity Pull IMA updates from Mimi Zohar: "New is IMA support for including fs-verity file digests and signatures in the IMA measurement list as well as verifying the fs-verity file digest based signatures, both based on policy. In addition, are two bug fixes: - avoid reading UEFI variables, which cause a page fault, on Apple Macs with T2 chips. - remove the original "ima" template Kconfig option to address a boot command line ordering issue. The rest is a mixture of code/documentation cleanup" * tag 'integrity-v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity: integrity: Fix sparse warnings in keyring_handler evm: Clean up some variables evm: Return INTEGRITY_PASS for enum integrity_status value '0' efi: Do not import certificates from UEFI Secure Boot for T2 Macs fsverity: update the documentation ima: support fs-verity file digest based version 3 signatures ima: permit fsverity's file digests in the IMA measurement list ima: define a new template field named 'd-ngv2' and templates fs-verity: define a function to return the integrity protected file digest ima: use IMA default hash algorithm for integrity violations ima: fix 'd-ng' comments and documentation ima: remove the IMA_TEMPLATE Kconfig option ima: remove redundant initialization of pointer 'file'.
This commit is contained in:
commit
0350785b0a
@ -27,8 +27,9 @@ Description:
|
||||
[fowner=] [fgroup=]]
|
||||
lsm: [[subj_user=] [subj_role=] [subj_type=]
|
||||
[obj_user=] [obj_role=] [obj_type=]]
|
||||
option: [[appraise_type=]] [template=] [permit_directio]
|
||||
[appraise_flag=] [appraise_algos=] [keyrings=]
|
||||
option: [digest_type=] [template=] [permit_directio]
|
||||
[appraise_type=] [appraise_flag=]
|
||||
[appraise_algos=] [keyrings=]
|
||||
base:
|
||||
func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
|
||||
[FIRMWARE_CHECK]
|
||||
@ -47,10 +48,21 @@ Description:
|
||||
fgroup:= decimal value
|
||||
lsm: are LSM specific
|
||||
option:
|
||||
appraise_type:= [imasig] [imasig|modsig]
|
||||
appraise_type:= [imasig] | [imasig|modsig] | [sigv3]
|
||||
where 'imasig' is the original or the signature
|
||||
format v2.
|
||||
where 'modsig' is an appended signature,
|
||||
where 'sigv3' is the signature format v3. (Currently
|
||||
limited to fsverity digest based signatures
|
||||
stored in security.ima xattr. Requires
|
||||
specifying "digest_type=verity" first.)
|
||||
|
||||
appraise_flag:= [check_blacklist]
|
||||
Currently, blacklist check is only for files signed with appended
|
||||
signature.
|
||||
digest_type:= verity
|
||||
Require fs-verity's file digest instead of the
|
||||
regular IMA file hash.
|
||||
keyrings:= list of keyrings
|
||||
(eg, .builtin_trusted_keys|.ima). Only valid
|
||||
when action is "measure" and func is KEY_CHECK.
|
||||
@ -149,3 +161,30 @@ Description:
|
||||
security.ima xattr of a file:
|
||||
|
||||
appraise func=SETXATTR_CHECK appraise_algos=sha256,sha384,sha512
|
||||
|
||||
Example of a 'measure' rule requiring fs-verity's digests
|
||||
with indication of type of digest in the measurement list.
|
||||
|
||||
measure func=FILE_CHECK digest_type=verity \
|
||||
template=ima-ngv2
|
||||
|
||||
Example of 'measure' and 'appraise' rules requiring fs-verity
|
||||
signatures (format version 3) stored in security.ima xattr.
|
||||
|
||||
The 'measure' rule specifies the 'ima-sigv3' template option,
|
||||
which includes the indication of type of digest and the file
|
||||
signature in the measurement list.
|
||||
|
||||
measure func=BPRM_CHECK digest_type=verity \
|
||||
template=ima-sigv3
|
||||
|
||||
|
||||
The 'appraise' rule specifies the type and signature format
|
||||
version (sigv3) required.
|
||||
|
||||
appraise func=BPRM_CHECK digest_type=verity \
|
||||
appraise_type=sigv3
|
||||
|
||||
All of these policy rules could, for example, be constrained
|
||||
either based on a filesystem's UUID (fsuuid) or based on LSM
|
||||
labels.
|
||||
|
@ -1914,7 +1914,8 @@
|
||||
|
||||
ima_template= [IMA]
|
||||
Select one of defined IMA measurements template formats.
|
||||
Formats: { "ima" | "ima-ng" | "ima-sig" }
|
||||
Formats: { "ima" | "ima-ng" | "ima-ngv2" | "ima-sig" |
|
||||
"ima-sigv2" }
|
||||
Default: "ima-ng"
|
||||
|
||||
ima_template_fmt=
|
||||
|
@ -70,12 +70,23 @@ must live on a read-write filesystem because they are independently
|
||||
updated and potentially user-installed, so dm-verity cannot be used.
|
||||
|
||||
The base fs-verity feature is a hashing mechanism only; actually
|
||||
authenticating the files is up to userspace. However, to meet some
|
||||
users' needs, fs-verity optionally supports a simple signature
|
||||
verification mechanism where users can configure the kernel to require
|
||||
that all fs-verity files be signed by a key loaded into a keyring; see
|
||||
`Built-in signature verification`_. Support for fs-verity file hashes
|
||||
in IMA (Integrity Measurement Architecture) policies is also planned.
|
||||
authenticating the files may be done by:
|
||||
|
||||
* Userspace-only
|
||||
|
||||
* Builtin signature verification + userspace policy
|
||||
|
||||
fs-verity optionally supports a simple signature verification
|
||||
mechanism where users can configure the kernel to require that
|
||||
all fs-verity files be signed by a key loaded into a keyring;
|
||||
see `Built-in signature verification`_.
|
||||
|
||||
* Integrity Measurement Architecture (IMA)
|
||||
|
||||
IMA supports including fs-verity file digests and signatures in the
|
||||
IMA measurement list and verifying fs-verity based file signatures
|
||||
stored as security.ima xattrs, based on policy.
|
||||
|
||||
|
||||
User API
|
||||
========
|
||||
@ -653,12 +664,12 @@ weren't already directly answered in other parts of this document.
|
||||
hashed and what to do with those hashes, such as log them,
|
||||
authenticate them, or add them to a measurement list.
|
||||
|
||||
IMA is planned to support the fs-verity hashing mechanism as an
|
||||
alternative to doing full file hashes, for people who want the
|
||||
performance and security benefits of the Merkle tree based hash.
|
||||
But it doesn't make sense to force all uses of fs-verity to be
|
||||
through IMA. As a standalone filesystem feature, fs-verity
|
||||
already meets many users' needs, and it's testable like other
|
||||
IMA supports the fs-verity hashing mechanism as an alternative
|
||||
to full file hashes, for those who want the performance and
|
||||
security benefits of the Merkle tree based hash. However, it
|
||||
doesn't make sense to force all uses of fs-verity to be through
|
||||
IMA. fs-verity already meets many users' needs even as a
|
||||
standalone filesystem feature, and it's testable like other
|
||||
filesystem features e.g. with xfstests.
|
||||
|
||||
:Q: Isn't fs-verity useless because the attacker can just modify the
|
||||
|
@ -66,12 +66,13 @@ descriptors by adding their identifier to the format string
|
||||
calculated with the SHA1 or MD5 hash algorithm;
|
||||
- 'n': the name of the event (i.e. the file name), with size up to 255 bytes;
|
||||
- 'd-ng': the digest of the event, calculated with an arbitrary hash
|
||||
algorithm (field format: [<hash algo>:]digest, where the digest
|
||||
prefix is shown only if the hash algorithm is not SHA1 or MD5);
|
||||
algorithm (field format: <hash algo>:digest);
|
||||
- 'd-ngv2': same as d-ng, but prefixed with the "ima" or "verity" digest type
|
||||
(field format: <digest type>:<hash algo>:digest);
|
||||
- 'd-modsig': the digest of the event without the appended modsig;
|
||||
- 'n-ng': the name of the event, without size limitations;
|
||||
- 'sig': the file signature, or the EVM portable signature if the file
|
||||
signature is not found;
|
||||
- 'sig': the file signature, based on either the file's/fsverity's digest[1],
|
||||
or the EVM portable signature, if 'security.ima' contains a file hash.
|
||||
- 'modsig' the appended file signature;
|
||||
- 'buf': the buffer data that was used to generate the hash without size limitations;
|
||||
- 'evmsig': the EVM portable signature;
|
||||
@ -88,7 +89,9 @@ Below, there is the list of defined template descriptors:
|
||||
|
||||
- "ima": its format is ``d|n``;
|
||||
- "ima-ng" (default): its format is ``d-ng|n-ng``;
|
||||
- "ima-ngv2": its format is ``d-ngv2|n-ng``;
|
||||
- "ima-sig": its format is ``d-ng|n-ng|sig``;
|
||||
- "ima-sigv2": its format is ``d-ngv2|n-ng|sig``;
|
||||
- "ima-buf": its format is ``d-ng|n-ng|buf``;
|
||||
- "ima-modsig": its format is ``d-ng|n-ng|sig|d-modsig|modsig``;
|
||||
- "evm-sig": its format is ``d-ng|n-ng|evmsig|xattrnames|xattrlengths|xattrvalues|iuid|igid|imode``;
|
||||
|
@ -3,6 +3,7 @@
|
||||
config FS_VERITY
|
||||
bool "FS Verity (read-only file-based authenticity protection)"
|
||||
select CRYPTO
|
||||
select CRYPTO_HASH_INFO
|
||||
# SHA-256 is implied as it's intended to be the default hash algorithm.
|
||||
# To avoid bloat, other wanted algorithms must be selected explicitly.
|
||||
# Note that CRYPTO_SHA256 denotes the generic C implementation, but
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
#define pr_fmt(fmt) "fs-verity: " fmt
|
||||
|
||||
#include <crypto/sha2.h>
|
||||
#include <linux/fsverity.h>
|
||||
#include <linux/mempool.h>
|
||||
|
||||
@ -26,12 +25,6 @@ struct ahash_request;
|
||||
*/
|
||||
#define FS_VERITY_MAX_LEVELS 8
|
||||
|
||||
/*
|
||||
* Largest digest size among all hash algorithms supported by fs-verity.
|
||||
* Currently assumed to be <= size of fsverity_descriptor::root_hash.
|
||||
*/
|
||||
#define FS_VERITY_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE
|
||||
|
||||
/* A hash algorithm supported by fs-verity */
|
||||
struct fsverity_hash_alg {
|
||||
struct crypto_ahash *tfm; /* hash tfm, allocated on demand */
|
||||
|
@ -57,3 +57,46 @@ int fsverity_ioctl_measure(struct file *filp, void __user *_uarg)
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsverity_ioctl_measure);
|
||||
|
||||
/**
|
||||
* fsverity_get_digest() - get a verity file's digest
|
||||
* @inode: inode to get digest of
|
||||
* @digest: (out) pointer to the digest
|
||||
* @alg: (out) pointer to the hash algorithm enumeration
|
||||
*
|
||||
* Return the file hash algorithm and digest of an fsverity protected file.
|
||||
* Assumption: before calling fsverity_get_digest(), the file must have been
|
||||
* opened.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
int fsverity_get_digest(struct inode *inode,
|
||||
u8 digest[FS_VERITY_MAX_DIGEST_SIZE],
|
||||
enum hash_algo *alg)
|
||||
{
|
||||
const struct fsverity_info *vi;
|
||||
const struct fsverity_hash_alg *hash_alg;
|
||||
int i;
|
||||
|
||||
vi = fsverity_get_info(inode);
|
||||
if (!vi)
|
||||
return -ENODATA; /* not a verity file */
|
||||
|
||||
hash_alg = vi->tree_params.hash_alg;
|
||||
memset(digest, 0, FS_VERITY_MAX_DIGEST_SIZE);
|
||||
|
||||
/* convert the verity hash algorithm name to a hash_algo_name enum */
|
||||
i = match_string(hash_algo_name, HASH_ALGO__LAST, hash_alg->name);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
*alg = i;
|
||||
|
||||
if (WARN_ON_ONCE(hash_alg->digest_size != hash_digest_size[*alg]))
|
||||
return -EINVAL;
|
||||
memcpy(digest, vi->file_digest, hash_alg->digest_size);
|
||||
|
||||
pr_debug("file digest %s:%*phN\n", hash_algo_name[*alg],
|
||||
hash_digest_size[*alg], digest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -12,8 +12,16 @@
|
||||
#define _LINUX_FSVERITY_H
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <crypto/hash_info.h>
|
||||
#include <crypto/sha2.h>
|
||||
#include <uapi/linux/fsverity.h>
|
||||
|
||||
/*
|
||||
* Largest digest size among all hash algorithms supported by fs-verity.
|
||||
* Currently assumed to be <= size of fsverity_descriptor::root_hash.
|
||||
*/
|
||||
#define FS_VERITY_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE
|
||||
|
||||
/* Verity operations for filesystems */
|
||||
struct fsverity_operations {
|
||||
|
||||
@ -131,6 +139,9 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *arg);
|
||||
/* measure.c */
|
||||
|
||||
int fsverity_ioctl_measure(struct file *filp, void __user *arg);
|
||||
int fsverity_get_digest(struct inode *inode,
|
||||
u8 digest[FS_VERITY_MAX_DIGEST_SIZE],
|
||||
enum hash_algo *alg);
|
||||
|
||||
/* open.c */
|
||||
|
||||
@ -170,6 +181,13 @@ static inline int fsverity_ioctl_measure(struct file *filp, void __user *arg)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int fsverity_get_digest(struct inode *inode,
|
||||
u8 digest[FS_VERITY_MAX_DIGEST_SIZE],
|
||||
enum hash_algo *alg)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* open.c */
|
||||
|
||||
static inline int fsverity_file_open(struct inode *inode, struct file *filp)
|
||||
|
@ -75,7 +75,8 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
/* v1 API expect signature without xattr type */
|
||||
return digsig_verify(keyring, sig + 1, siglen - 1, digest,
|
||||
digestlen);
|
||||
case 2:
|
||||
case 2: /* regular file data hash based signature */
|
||||
case 3: /* struct ima_file_id data based signature */
|
||||
return asymmetric_verify(keyring, sig, siglen, digest,
|
||||
digestlen);
|
||||
}
|
||||
|
@ -38,9 +38,6 @@ extern int evm_initialized;
|
||||
|
||||
extern int evm_hmac_attrs;
|
||||
|
||||
extern struct crypto_shash *hmac_tfm;
|
||||
extern struct crypto_shash *hash_tfm;
|
||||
|
||||
/* List of EVM protected security xattrs */
|
||||
extern struct list_head evm_config_xattrnames;
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
static unsigned char evmkey[MAX_KEY_SIZE];
|
||||
static const int evmkey_len = MAX_KEY_SIZE;
|
||||
|
||||
struct crypto_shash *hmac_tfm;
|
||||
static struct crypto_shash *hmac_tfm;
|
||||
static struct crypto_shash *evm_tfm[HASH_ALGO__LAST];
|
||||
|
||||
static DEFINE_MUTEX(mutex);
|
||||
|
@ -436,7 +436,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
|
||||
struct inode *inode = d_backing_inode(dentry);
|
||||
|
||||
if (!evm_key_loaded() || !S_ISREG(inode->i_mode) || evm_fixmode)
|
||||
return 0;
|
||||
return INTEGRITY_PASS;
|
||||
return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
|
@ -69,10 +69,9 @@ choice
|
||||
hash, defined as 20 bytes, and a null terminated pathname,
|
||||
limited to 255 characters. The 'ima-ng' measurement list
|
||||
template permits both larger hash digests and longer
|
||||
pathnames.
|
||||
pathnames. The configured default template can be replaced
|
||||
by specifying "ima_template=" on the boot command line.
|
||||
|
||||
config IMA_TEMPLATE
|
||||
bool "ima"
|
||||
config IMA_NG_TEMPLATE
|
||||
bool "ima-ng (default)"
|
||||
config IMA_SIG_TEMPLATE
|
||||
@ -82,7 +81,6 @@ endchoice
|
||||
config IMA_DEFAULT_TEMPLATE
|
||||
string
|
||||
depends on IMA
|
||||
default "ima" if IMA_TEMPLATE
|
||||
default "ima-ng" if IMA_NG_TEMPLATE
|
||||
default "ima-sig" if IMA_SIG_TEMPLATE
|
||||
|
||||
@ -102,19 +100,19 @@ choice
|
||||
|
||||
config IMA_DEFAULT_HASH_SHA256
|
||||
bool "SHA256"
|
||||
depends on CRYPTO_SHA256=y && !IMA_TEMPLATE
|
||||
depends on CRYPTO_SHA256=y
|
||||
|
||||
config IMA_DEFAULT_HASH_SHA512
|
||||
bool "SHA512"
|
||||
depends on CRYPTO_SHA512=y && !IMA_TEMPLATE
|
||||
depends on CRYPTO_SHA512=y
|
||||
|
||||
config IMA_DEFAULT_HASH_WP512
|
||||
bool "WP512"
|
||||
depends on CRYPTO_WP512=y && !IMA_TEMPLATE
|
||||
depends on CRYPTO_WP512=y
|
||||
|
||||
config IMA_DEFAULT_HASH_SM3
|
||||
bool "SM3"
|
||||
depends on CRYPTO_SM3=y && !IMA_TEMPLATE
|
||||
depends on CRYPTO_SM3=y
|
||||
endchoice
|
||||
|
||||
config IMA_DEFAULT_HASH
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/evm.h>
|
||||
#include <linux/iversion.h>
|
||||
#include <linux/fsverity.h>
|
||||
|
||||
#include "ima.h"
|
||||
|
||||
@ -200,6 +201,32 @@ int ima_get_action(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
allowed_algos);
|
||||
}
|
||||
|
||||
static int ima_get_verity_digest(struct integrity_iint_cache *iint,
|
||||
struct ima_max_digest_data *hash)
|
||||
{
|
||||
enum hash_algo verity_alg;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* On failure, 'measure' policy rules will result in a file data
|
||||
* hash containing 0's.
|
||||
*/
|
||||
ret = fsverity_get_digest(iint->inode, hash->digest, &verity_alg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Unlike in the case of actually calculating the file hash, in
|
||||
* the fsverity case regardless of the hash algorithm, return
|
||||
* the verity digest to be included in the measurement list. A
|
||||
* mismatch between the verity algorithm and the xattr signature
|
||||
* algorithm, if one exists, will be detected later.
|
||||
*/
|
||||
hash->hdr.algo = verity_alg;
|
||||
hash->hdr.length = hash_digest_size[verity_alg];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_collect_measurement - collect file measurement
|
||||
*
|
||||
@ -242,16 +269,30 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||
*/
|
||||
i_version = inode_query_iversion(inode);
|
||||
hash.hdr.algo = algo;
|
||||
hash.hdr.length = hash_digest_size[algo];
|
||||
|
||||
/* Initialize hash digest to 0's in case of failure */
|
||||
memset(&hash.digest, 0, sizeof(hash.digest));
|
||||
|
||||
if (buf)
|
||||
if (iint->flags & IMA_VERITY_REQUIRED) {
|
||||
result = ima_get_verity_digest(iint, &hash);
|
||||
switch (result) {
|
||||
case 0:
|
||||
break;
|
||||
case -ENODATA:
|
||||
audit_cause = "no-verity-digest";
|
||||
break;
|
||||
default:
|
||||
audit_cause = "invalid-verity-digest";
|
||||
break;
|
||||
}
|
||||
} else if (buf) {
|
||||
result = ima_calc_buffer_hash(buf, size, &hash.hdr);
|
||||
else
|
||||
} else {
|
||||
result = ima_calc_file_hash(file, &hash.hdr);
|
||||
}
|
||||
|
||||
if (result && result != -EBADF && result != -EINVAL)
|
||||
if (result == -ENOMEM)
|
||||
goto out;
|
||||
|
||||
length = sizeof(hash.hdr) + hash.hdr.length;
|
||||
|
@ -13,7 +13,9 @@
|
||||
#include <linux/magic.h>
|
||||
#include <linux/ima.h>
|
||||
#include <linux/evm.h>
|
||||
#include <linux/fsverity.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include <uapi/linux/fsverity.h>
|
||||
|
||||
#include "ima.h"
|
||||
|
||||
@ -183,13 +185,18 @@ enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
|
||||
return ima_hash_algo;
|
||||
|
||||
switch (xattr_value->type) {
|
||||
case IMA_VERITY_DIGSIG:
|
||||
sig = (typeof(sig))xattr_value;
|
||||
if (sig->version != 3 || xattr_len <= sizeof(*sig) ||
|
||||
sig->hash_algo >= HASH_ALGO__LAST)
|
||||
return ima_hash_algo;
|
||||
return sig->hash_algo;
|
||||
case EVM_IMA_XATTR_DIGSIG:
|
||||
sig = (typeof(sig))xattr_value;
|
||||
if (sig->version != 2 || xattr_len <= sizeof(*sig)
|
||||
|| sig->hash_algo >= HASH_ALGO__LAST)
|
||||
return ima_hash_algo;
|
||||
return sig->hash_algo;
|
||||
break;
|
||||
case IMA_XATTR_DIGEST_NG:
|
||||
/* first byte contains algorithm id */
|
||||
ret = xattr_value->data[0];
|
||||
@ -225,6 +232,40 @@ int ima_read_xattr(struct dentry *dentry,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* calc_file_id_hash - calculate the hash of the ima_file_id struct data
|
||||
* @type: xattr type [enum evm_ima_xattr_type]
|
||||
* @algo: hash algorithm [enum hash_algo]
|
||||
* @digest: pointer to the digest to be hashed
|
||||
* @hash: (out) pointer to the hash
|
||||
*
|
||||
* IMA signature version 3 disambiguates the data that is signed by
|
||||
* indirectly signing the hash of the ima_file_id structure data.
|
||||
*
|
||||
* Signing the ima_file_id struct is currently only supported for
|
||||
* IMA_VERITY_DIGSIG type xattrs.
|
||||
*
|
||||
* Return 0 on success, error code otherwise.
|
||||
*/
|
||||
static int calc_file_id_hash(enum evm_ima_xattr_type type,
|
||||
enum hash_algo algo, const u8 *digest,
|
||||
struct ima_digest_data *hash)
|
||||
{
|
||||
struct ima_file_id file_id = {
|
||||
.hash_type = IMA_VERITY_DIGSIG, .hash_algorithm = algo};
|
||||
unsigned int unused = HASH_MAX_DIGESTSIZE - hash_digest_size[algo];
|
||||
|
||||
if (type != IMA_VERITY_DIGSIG)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(file_id.hash, digest, hash_digest_size[algo]);
|
||||
|
||||
hash->algo = algo;
|
||||
hash->length = hash_digest_size[algo];
|
||||
|
||||
return ima_calc_buffer_hash(&file_id, sizeof(file_id) - unused, hash);
|
||||
}
|
||||
|
||||
/*
|
||||
* xattr_verify - verify xattr digest or signature
|
||||
*
|
||||
@ -236,7 +277,10 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
|
||||
struct evm_ima_xattr_data *xattr_value, int xattr_len,
|
||||
enum integrity_status *status, const char **cause)
|
||||
{
|
||||
struct ima_max_digest_data hash;
|
||||
struct signature_v2_hdr *sig;
|
||||
int rc = -EINVAL, hash_start = 0;
|
||||
int mask;
|
||||
|
||||
switch (xattr_value->type) {
|
||||
case IMA_XATTR_DIGEST_NG:
|
||||
@ -246,7 +290,10 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
|
||||
case IMA_XATTR_DIGEST:
|
||||
if (*status != INTEGRITY_PASS_IMMUTABLE) {
|
||||
if (iint->flags & IMA_DIGSIG_REQUIRED) {
|
||||
*cause = "IMA-signature-required";
|
||||
if (iint->flags & IMA_VERITY_REQUIRED)
|
||||
*cause = "verity-signature-required";
|
||||
else
|
||||
*cause = "IMA-signature-required";
|
||||
*status = INTEGRITY_FAIL;
|
||||
break;
|
||||
}
|
||||
@ -274,6 +321,20 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
|
||||
break;
|
||||
case EVM_IMA_XATTR_DIGSIG:
|
||||
set_bit(IMA_DIGSIG, &iint->atomic_flags);
|
||||
|
||||
mask = IMA_DIGSIG_REQUIRED | IMA_VERITY_REQUIRED;
|
||||
if ((iint->flags & mask) == mask) {
|
||||
*cause = "verity-signature-required";
|
||||
*status = INTEGRITY_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
sig = (typeof(sig))xattr_value;
|
||||
if (sig->version >= 3) {
|
||||
*cause = "invalid-signature-version";
|
||||
*status = INTEGRITY_FAIL;
|
||||
break;
|
||||
}
|
||||
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
|
||||
(const char *)xattr_value,
|
||||
xattr_len,
|
||||
@ -296,6 +357,44 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
|
||||
} else {
|
||||
*status = INTEGRITY_PASS;
|
||||
}
|
||||
break;
|
||||
case IMA_VERITY_DIGSIG:
|
||||
set_bit(IMA_DIGSIG, &iint->atomic_flags);
|
||||
|
||||
if (iint->flags & IMA_DIGSIG_REQUIRED) {
|
||||
if (!(iint->flags & IMA_VERITY_REQUIRED)) {
|
||||
*cause = "IMA-signature-required";
|
||||
*status = INTEGRITY_FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sig = (typeof(sig))xattr_value;
|
||||
if (sig->version != 3) {
|
||||
*cause = "invalid-signature-version";
|
||||
*status = INTEGRITY_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = calc_file_id_hash(IMA_VERITY_DIGSIG, iint->ima_hash->algo,
|
||||
iint->ima_hash->digest, &hash.hdr);
|
||||
if (rc) {
|
||||
*cause = "sigv3-hashing-error";
|
||||
*status = INTEGRITY_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
|
||||
(const char *)xattr_value,
|
||||
xattr_len, hash.digest,
|
||||
hash.hdr.length);
|
||||
if (rc) {
|
||||
*cause = "invalid-verity-signature";
|
||||
*status = INTEGRITY_FAIL;
|
||||
} else {
|
||||
*status = INTEGRITY_PASS;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
*status = INTEGRITY_UNKNOWN;
|
||||
@ -396,8 +495,15 @@ int ima_appraise_measurement(enum ima_hooks func,
|
||||
if (rc && rc != -ENODATA)
|
||||
goto out;
|
||||
|
||||
cause = iint->flags & IMA_DIGSIG_REQUIRED ?
|
||||
"IMA-signature-required" : "missing-hash";
|
||||
if (iint->flags & IMA_DIGSIG_REQUIRED) {
|
||||
if (iint->flags & IMA_VERITY_REQUIRED)
|
||||
cause = "verity-signature-required";
|
||||
else
|
||||
cause = "IMA-signature-required";
|
||||
} else {
|
||||
cause = "missing-hash";
|
||||
}
|
||||
|
||||
status = INTEGRITY_NOLABEL;
|
||||
if (file->f_mode & FMODE_CREATED)
|
||||
iint->flags |= IMA_NEW_FILE;
|
||||
|
@ -335,7 +335,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
|
||||
hash_algo = ima_get_hash_algo(xattr_value, xattr_len);
|
||||
|
||||
rc = ima_collect_measurement(iint, file, buf, size, hash_algo, modsig);
|
||||
if (rc != 0 && rc != -EBADF && rc != -EINVAL)
|
||||
if (rc == -ENOMEM)
|
||||
goto out_locked;
|
||||
|
||||
if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */
|
||||
@ -432,7 +432,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
|
||||
int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
|
||||
{
|
||||
struct ima_template_desc *template = NULL;
|
||||
struct file *file = vma->vm_file;
|
||||
struct file *file;
|
||||
char filename[NAME_MAX];
|
||||
char *pathbuf = NULL;
|
||||
const char *pathname = NULL;
|
||||
|
@ -1023,6 +1023,7 @@ enum policy_opt {
|
||||
Opt_fowner_gt, Opt_fgroup_gt,
|
||||
Opt_uid_lt, Opt_euid_lt, Opt_gid_lt, Opt_egid_lt,
|
||||
Opt_fowner_lt, Opt_fgroup_lt,
|
||||
Opt_digest_type,
|
||||
Opt_appraise_type, Opt_appraise_flag, Opt_appraise_algos,
|
||||
Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings,
|
||||
Opt_label, Opt_err
|
||||
@ -1065,6 +1066,7 @@ static const match_table_t policy_tokens = {
|
||||
{Opt_egid_lt, "egid<%s"},
|
||||
{Opt_fowner_lt, "fowner<%s"},
|
||||
{Opt_fgroup_lt, "fgroup<%s"},
|
||||
{Opt_digest_type, "digest_type=%s"},
|
||||
{Opt_appraise_type, "appraise_type=%s"},
|
||||
{Opt_appraise_flag, "appraise_flag=%s"},
|
||||
{Opt_appraise_algos, "appraise_algos=%s"},
|
||||
@ -1172,6 +1174,21 @@ static void check_template_modsig(const struct ima_template_desc *template)
|
||||
#undef MSG
|
||||
}
|
||||
|
||||
/*
|
||||
* Warn if the template does not contain the given field.
|
||||
*/
|
||||
static void check_template_field(const struct ima_template_desc *template,
|
||||
const char *field, const char *msg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < template->num_fields; i++)
|
||||
if (!strcmp(template->fields[i]->field_id, field))
|
||||
return;
|
||||
|
||||
pr_notice_once("%s", msg);
|
||||
}
|
||||
|
||||
static bool ima_validate_rule(struct ima_rule_entry *entry)
|
||||
{
|
||||
/* Ensure that the action is set and is compatible with the flags */
|
||||
@ -1214,7 +1231,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
|
||||
IMA_INMASK | IMA_EUID | IMA_PCR |
|
||||
IMA_FSNAME | IMA_GID | IMA_EGID |
|
||||
IMA_FGROUP | IMA_DIGSIG_REQUIRED |
|
||||
IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS))
|
||||
IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS |
|
||||
IMA_VERITY_REQUIRED))
|
||||
return false;
|
||||
|
||||
break;
|
||||
@ -1292,6 +1310,18 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
|
||||
!(entry->flags & IMA_MODSIG_ALLOWED))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Unlike for regular IMA 'appraise' policy rules where security.ima
|
||||
* xattr may contain either a file hash or signature, the security.ima
|
||||
* xattr for fsverity must contain a file signature (sigv3). Ensure
|
||||
* that 'appraise' rules for fsverity require file signatures by
|
||||
* checking the IMA_DIGSIG_REQUIRED flag is set.
|
||||
*/
|
||||
if (entry->action == APPRAISE &&
|
||||
(entry->flags & IMA_VERITY_REQUIRED) &&
|
||||
!(entry->flags & IMA_DIGSIG_REQUIRED))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1707,17 +1737,40 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
LSM_SUBJ_TYPE,
|
||||
AUDIT_SUBJ_TYPE);
|
||||
break;
|
||||
case Opt_appraise_type:
|
||||
ima_log_string(ab, "appraise_type", args[0].from);
|
||||
if ((strcmp(args[0].from, "imasig")) == 0)
|
||||
entry->flags |= IMA_DIGSIG_REQUIRED;
|
||||
else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
|
||||
strcmp(args[0].from, "imasig|modsig") == 0)
|
||||
entry->flags |= IMA_DIGSIG_REQUIRED |
|
||||
IMA_MODSIG_ALLOWED;
|
||||
case Opt_digest_type:
|
||||
ima_log_string(ab, "digest_type", args[0].from);
|
||||
if (entry->flags & IMA_DIGSIG_REQUIRED)
|
||||
result = -EINVAL;
|
||||
else if ((strcmp(args[0].from, "verity")) == 0)
|
||||
entry->flags |= IMA_VERITY_REQUIRED;
|
||||
else
|
||||
result = -EINVAL;
|
||||
break;
|
||||
case Opt_appraise_type:
|
||||
ima_log_string(ab, "appraise_type", args[0].from);
|
||||
|
||||
if ((strcmp(args[0].from, "imasig")) == 0) {
|
||||
if (entry->flags & IMA_VERITY_REQUIRED)
|
||||
result = -EINVAL;
|
||||
else
|
||||
entry->flags |= IMA_DIGSIG_REQUIRED;
|
||||
} else if (strcmp(args[0].from, "sigv3") == 0) {
|
||||
/* Only fsverity supports sigv3 for now */
|
||||
if (entry->flags & IMA_VERITY_REQUIRED)
|
||||
entry->flags |= IMA_DIGSIG_REQUIRED;
|
||||
else
|
||||
result = -EINVAL;
|
||||
} else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
|
||||
strcmp(args[0].from, "imasig|modsig") == 0) {
|
||||
if (entry->flags & IMA_VERITY_REQUIRED)
|
||||
result = -EINVAL;
|
||||
else
|
||||
entry->flags |= IMA_DIGSIG_REQUIRED |
|
||||
IMA_MODSIG_ALLOWED;
|
||||
} else {
|
||||
result = -EINVAL;
|
||||
}
|
||||
break;
|
||||
case Opt_appraise_flag:
|
||||
ima_log_string(ab, "appraise_flag", args[0].from);
|
||||
if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
|
||||
@ -1797,6 +1850,15 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
check_template_modsig(template_desc);
|
||||
}
|
||||
|
||||
/* d-ngv2 template field recommended for unsigned fs-verity digests */
|
||||
if (!result && entry->action == MEASURE &&
|
||||
entry->flags & IMA_VERITY_REQUIRED) {
|
||||
template_desc = entry->template ? entry->template :
|
||||
ima_template_desc_current();
|
||||
check_template_field(template_desc, "d-ngv2",
|
||||
"verity rules should include d-ngv2");
|
||||
}
|
||||
|
||||
audit_log_format(ab, "res=%d", !result);
|
||||
audit_log_end(ab);
|
||||
return result;
|
||||
@ -2149,11 +2211,15 @@ int ima_policy_show(struct seq_file *m, void *v)
|
||||
if (entry->template)
|
||||
seq_printf(m, "template=%s ", entry->template->name);
|
||||
if (entry->flags & IMA_DIGSIG_REQUIRED) {
|
||||
if (entry->flags & IMA_MODSIG_ALLOWED)
|
||||
if (entry->flags & IMA_VERITY_REQUIRED)
|
||||
seq_puts(m, "appraise_type=sigv3 ");
|
||||
else if (entry->flags & IMA_MODSIG_ALLOWED)
|
||||
seq_puts(m, "appraise_type=imasig|modsig ");
|
||||
else
|
||||
seq_puts(m, "appraise_type=imasig ");
|
||||
}
|
||||
if (entry->flags & IMA_VERITY_REQUIRED)
|
||||
seq_puts(m, "digest_type=verity ");
|
||||
if (entry->flags & IMA_CHECK_BLACKLIST)
|
||||
seq_puts(m, "appraise_flag=check_blacklist ");
|
||||
if (entry->flags & IMA_PERMIT_DIRECTIO)
|
||||
|
@ -20,6 +20,8 @@ static struct ima_template_desc builtin_templates[] = {
|
||||
{.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT},
|
||||
{.name = "ima-ng", .fmt = "d-ng|n-ng"},
|
||||
{.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
|
||||
{.name = "ima-ngv2", .fmt = "d-ngv2|n-ng"},
|
||||
{.name = "ima-sigv2", .fmt = "d-ngv2|n-ng|sig"},
|
||||
{.name = "ima-buf", .fmt = "d-ng|n-ng|buf"},
|
||||
{.name = "ima-modsig", .fmt = "d-ng|n-ng|sig|d-modsig|modsig"},
|
||||
{.name = "evm-sig",
|
||||
@ -38,6 +40,8 @@ static const struct ima_template_field supported_fields[] = {
|
||||
.field_show = ima_show_template_string},
|
||||
{.field_id = "d-ng", .field_init = ima_eventdigest_ng_init,
|
||||
.field_show = ima_show_template_digest_ng},
|
||||
{.field_id = "d-ngv2", .field_init = ima_eventdigest_ngv2_init,
|
||||
.field_show = ima_show_template_digest_ngv2},
|
||||
{.field_id = "n-ng", .field_init = ima_eventname_ng_init,
|
||||
.field_show = ima_show_template_string},
|
||||
{.field_id = "sig", .field_init = ima_eventsig_init,
|
||||
|
@ -24,11 +24,24 @@ static bool ima_template_hash_algo_allowed(u8 algo)
|
||||
enum data_formats {
|
||||
DATA_FMT_DIGEST = 0,
|
||||
DATA_FMT_DIGEST_WITH_ALGO,
|
||||
DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO,
|
||||
DATA_FMT_STRING,
|
||||
DATA_FMT_HEX,
|
||||
DATA_FMT_UINT
|
||||
};
|
||||
|
||||
enum digest_type {
|
||||
DIGEST_TYPE_IMA,
|
||||
DIGEST_TYPE_VERITY,
|
||||
DIGEST_TYPE__LAST
|
||||
};
|
||||
|
||||
#define DIGEST_TYPE_NAME_LEN_MAX 7 /* including NUL */
|
||||
static const char * const digest_type_name[DIGEST_TYPE__LAST] = {
|
||||
[DIGEST_TYPE_IMA] = "ima",
|
||||
[DIGEST_TYPE_VERITY] = "verity"
|
||||
};
|
||||
|
||||
static int ima_write_template_field_data(const void *data, const u32 datalen,
|
||||
enum data_formats datafmt,
|
||||
struct ima_field_data *field_data)
|
||||
@ -72,8 +85,9 @@ static void ima_show_template_data_ascii(struct seq_file *m,
|
||||
u32 buflen = field_data->len;
|
||||
|
||||
switch (datafmt) {
|
||||
case DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO:
|
||||
case DATA_FMT_DIGEST_WITH_ALGO:
|
||||
buf_ptr = strnchr(field_data->data, buflen, ':');
|
||||
buf_ptr = strrchr(field_data->data, ':');
|
||||
if (buf_ptr != field_data->data)
|
||||
seq_printf(m, "%s", field_data->data);
|
||||
|
||||
@ -178,6 +192,14 @@ void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show,
|
||||
field_data);
|
||||
}
|
||||
|
||||
void ima_show_template_digest_ngv2(struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
ima_show_template_field_data(m, show,
|
||||
DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO,
|
||||
field_data);
|
||||
}
|
||||
|
||||
void ima_show_template_string(struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
@ -265,26 +287,35 @@ int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
|
||||
}
|
||||
|
||||
static int ima_eventdigest_init_common(const u8 *digest, u32 digestsize,
|
||||
u8 hash_algo,
|
||||
u8 digest_type, u8 hash_algo,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
/*
|
||||
* digest formats:
|
||||
* - DATA_FMT_DIGEST: digest
|
||||
* - DATA_FMT_DIGEST_WITH_ALGO: [<hash algo>] + ':' + '\0' + digest,
|
||||
* where <hash algo> is provided if the hash algorithm is not
|
||||
* SHA1 or MD5
|
||||
* - DATA_FMT_DIGEST_WITH_ALGO: <hash algo> + ':' + '\0' + digest,
|
||||
* - DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO:
|
||||
* <digest type> + ':' + <hash algo> + ':' + '\0' + digest,
|
||||
*
|
||||
* where 'DATA_FMT_DIGEST' is the original digest format ('d')
|
||||
* with a hash size limitation of 20 bytes,
|
||||
* where <digest type> is either "ima" or "verity",
|
||||
* where <hash algo> is the hash_algo_name[] string.
|
||||
*/
|
||||
u8 buffer[CRYPTO_MAX_ALG_NAME + 2 + IMA_MAX_DIGEST_SIZE] = { 0 };
|
||||
u8 buffer[DIGEST_TYPE_NAME_LEN_MAX + CRYPTO_MAX_ALG_NAME + 2 +
|
||||
IMA_MAX_DIGEST_SIZE] = { 0 };
|
||||
enum data_formats fmt = DATA_FMT_DIGEST;
|
||||
u32 offset = 0;
|
||||
|
||||
if (hash_algo < HASH_ALGO__LAST) {
|
||||
if (digest_type < DIGEST_TYPE__LAST && hash_algo < HASH_ALGO__LAST) {
|
||||
fmt = DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO;
|
||||
offset += 1 + sprintf(buffer, "%s:%s:",
|
||||
digest_type_name[digest_type],
|
||||
hash_algo_name[hash_algo]);
|
||||
} else if (hash_algo < HASH_ALGO__LAST) {
|
||||
fmt = DATA_FMT_DIGEST_WITH_ALGO;
|
||||
offset += snprintf(buffer, CRYPTO_MAX_ALG_NAME + 1, "%s",
|
||||
hash_algo_name[hash_algo]);
|
||||
buffer[offset] = ':';
|
||||
offset += 2;
|
||||
offset += 1 + sprintf(buffer, "%s:",
|
||||
hash_algo_name[hash_algo]);
|
||||
}
|
||||
|
||||
if (digest)
|
||||
@ -359,7 +390,8 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
|
||||
cur_digestsize = hash.hdr.length;
|
||||
out:
|
||||
return ima_eventdigest_init_common(cur_digest, cur_digestsize,
|
||||
HASH_ALGO__LAST, field_data);
|
||||
DIGEST_TYPE__LAST, HASH_ALGO__LAST,
|
||||
field_data);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -368,7 +400,7 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
|
||||
int ima_eventdigest_ng_init(struct ima_event_data *event_data,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
u8 *cur_digest = NULL, hash_algo = HASH_ALGO_SHA1;
|
||||
u8 *cur_digest = NULL, hash_algo = ima_hash_algo;
|
||||
u32 cur_digestsize = 0;
|
||||
|
||||
if (event_data->violation) /* recording a violation. */
|
||||
@ -380,7 +412,34 @@ int ima_eventdigest_ng_init(struct ima_event_data *event_data,
|
||||
hash_algo = event_data->iint->ima_hash->algo;
|
||||
out:
|
||||
return ima_eventdigest_init_common(cur_digest, cur_digestsize,
|
||||
hash_algo, field_data);
|
||||
DIGEST_TYPE__LAST, hash_algo,
|
||||
field_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function writes the digest of an event (without size limit),
|
||||
* prefixed with both the digest type and hash algorithm.
|
||||
*/
|
||||
int ima_eventdigest_ngv2_init(struct ima_event_data *event_data,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
u8 *cur_digest = NULL, hash_algo = ima_hash_algo;
|
||||
u32 cur_digestsize = 0;
|
||||
u8 digest_type = DIGEST_TYPE_IMA;
|
||||
|
||||
if (event_data->violation) /* recording a violation. */
|
||||
goto out;
|
||||
|
||||
cur_digest = event_data->iint->ima_hash->digest;
|
||||
cur_digestsize = event_data->iint->ima_hash->length;
|
||||
|
||||
hash_algo = event_data->iint->ima_hash->algo;
|
||||
if (event_data->iint->flags & IMA_VERITY_REQUIRED)
|
||||
digest_type = DIGEST_TYPE_VERITY;
|
||||
out:
|
||||
return ima_eventdigest_init_common(cur_digest, cur_digestsize,
|
||||
digest_type, hash_algo,
|
||||
field_data);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -415,7 +474,8 @@ int ima_eventdigest_modsig_init(struct ima_event_data *event_data,
|
||||
}
|
||||
|
||||
return ima_eventdigest_init_common(cur_digest, cur_digestsize,
|
||||
hash_algo, field_data);
|
||||
DIGEST_TYPE__LAST, hash_algo,
|
||||
field_data);
|
||||
}
|
||||
|
||||
static int ima_eventname_init_common(struct ima_event_data *event_data,
|
||||
@ -475,7 +535,9 @@ int ima_eventsig_init(struct ima_event_data *event_data,
|
||||
{
|
||||
struct evm_ima_xattr_data *xattr_value = event_data->xattr_value;
|
||||
|
||||
if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG))
|
||||
if (!xattr_value ||
|
||||
(xattr_value->type != EVM_IMA_XATTR_DIGSIG &&
|
||||
xattr_value->type != IMA_VERITY_DIGSIG))
|
||||
return ima_eventevmsig_init(event_data, field_data);
|
||||
|
||||
return ima_write_template_field_data(xattr_value, event_data->xattr_len,
|
||||
|
@ -21,6 +21,8 @@ void ima_show_template_digest(struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data);
|
||||
void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data);
|
||||
void ima_show_template_digest_ngv2(struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data);
|
||||
void ima_show_template_string(struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data);
|
||||
void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
|
||||
@ -38,6 +40,8 @@ int ima_eventname_init(struct ima_event_data *event_data,
|
||||
struct ima_field_data *field_data);
|
||||
int ima_eventdigest_ng_init(struct ima_event_data *event_data,
|
||||
struct ima_field_data *field_data);
|
||||
int ima_eventdigest_ngv2_init(struct ima_event_data *event_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,
|
||||
|
@ -40,6 +40,7 @@
|
||||
#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
|
||||
#define IMA_MODSIG_ALLOWED 0x20000000
|
||||
#define IMA_CHECK_BLACKLIST 0x40000000
|
||||
#define IMA_VERITY_REQUIRED 0x80000000
|
||||
|
||||
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
|
||||
IMA_HASH | IMA_APPRAISE_SUBMASK)
|
||||
@ -78,6 +79,7 @@ enum evm_ima_xattr_type {
|
||||
EVM_IMA_XATTR_DIGSIG,
|
||||
IMA_XATTR_DIGEST_NG,
|
||||
EVM_XATTR_PORTABLE_DIGSIG,
|
||||
IMA_VERITY_DIGSIG,
|
||||
IMA_XATTR_LAST
|
||||
};
|
||||
|
||||
@ -92,7 +94,7 @@ struct evm_xattr {
|
||||
u8 digest[SHA1_DIGEST_SIZE];
|
||||
} __packed;
|
||||
|
||||
#define IMA_MAX_DIGEST_SIZE 64
|
||||
#define IMA_MAX_DIGEST_SIZE HASH_MAX_DIGESTSIZE
|
||||
|
||||
struct ima_digest_data {
|
||||
u8 algo;
|
||||
@ -121,7 +123,14 @@ struct ima_max_digest_data {
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* signature format v2 - for using with asymmetric keys
|
||||
* signature header format v2 - for using with asymmetric keys
|
||||
*
|
||||
* The signature_v2_hdr struct includes a signature format version
|
||||
* to simplify defining new signature formats.
|
||||
*
|
||||
* signature format:
|
||||
* version 2: regular file data hash based signature
|
||||
* version 3: struct ima_file_id data based signature
|
||||
*/
|
||||
struct signature_v2_hdr {
|
||||
uint8_t type; /* xattr type */
|
||||
@ -132,6 +141,20 @@ struct signature_v2_hdr {
|
||||
uint8_t sig[]; /* signature payload */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* IMA signature version 3 disambiguates the data that is signed, by
|
||||
* indirectly signing the hash of the ima_file_id structure data,
|
||||
* containing either the fsverity_descriptor struct digest or, in the
|
||||
* future, the regular IMA file hash.
|
||||
*
|
||||
* (The hash of the ima_file_id structure is only of the portion used.)
|
||||
*/
|
||||
struct ima_file_id {
|
||||
__u8 hash_type; /* xattr type [enum evm_ima_xattr_type] */
|
||||
__u8 hash_algorithm; /* Digest algorithm [enum hash_algo] */
|
||||
__u8 hash[HASH_MAX_DIGESTSIZE];
|
||||
} __packed;
|
||||
|
||||
/* integrity data associated with an inode */
|
||||
struct integrity_iint_cache {
|
||||
struct rb_node rb_node; /* rooted in integrity_iint_tree */
|
||||
|
@ -51,7 +51,7 @@ __init efi_element_handler_t get_handler_for_db(const efi_guid_t *sig_type)
|
||||
{
|
||||
if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0)
|
||||
return add_to_platform_keyring;
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -66,7 +66,7 @@ __init efi_element_handler_t get_handler_for_mok(const efi_guid_t *sig_type)
|
||||
else
|
||||
return add_to_platform_keyring;
|
||||
}
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -81,5 +81,5 @@ __init efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type)
|
||||
return uefi_blacklist_binary;
|
||||
if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0)
|
||||
return uefi_revocation_list_x509;
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -35,3 +35,11 @@ efi_element_handler_t get_handler_for_mok(const efi_guid_t *sig_type);
|
||||
efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type);
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef UEFI_QUIRK_SKIP_CERT
|
||||
#define UEFI_QUIRK_SKIP_CERT(vendor, product) \
|
||||
.matches = { \
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, product), \
|
||||
},
|
||||
#endif
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/slab.h>
|
||||
@ -12,6 +13,31 @@
|
||||
#include "../integrity.h"
|
||||
#include "keyring_handler.h"
|
||||
|
||||
/*
|
||||
* On T2 Macs reading the db and dbx efi variables to load UEFI Secure Boot
|
||||
* certificates causes occurrence of a page fault in Apple's firmware and
|
||||
* a crash disabling EFI runtime services. The following quirk skips reading
|
||||
* these variables.
|
||||
*/
|
||||
static const struct dmi_system_id uefi_skip_cert[] = {
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,1") },
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,2") },
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,3") },
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,4") },
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,1") },
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,2") },
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,3") },
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,4") },
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir8,1") },
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir8,2") },
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir9,1") },
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacMini8,1") },
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacPro7,1") },
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "iMac20,1") },
|
||||
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "iMac20,2") },
|
||||
{ }
|
||||
};
|
||||
|
||||
/*
|
||||
* Look to see if a UEFI variable called MokIgnoreDB exists and return true if
|
||||
* it does.
|
||||
@ -138,6 +164,13 @@ static int __init load_uefi_certs(void)
|
||||
unsigned long dbsize = 0, dbxsize = 0, mokxsize = 0;
|
||||
efi_status_t status;
|
||||
int rc = 0;
|
||||
const struct dmi_system_id *dmi_id;
|
||||
|
||||
dmi_id = dmi_first_match(uefi_skip_cert);
|
||||
if (dmi_id) {
|
||||
pr_err("Reading UEFI Secure Boot Certs is not supported on T2 Macs.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
|
||||
return false;
|
||||
|
Loading…
Reference in New Issue
Block a user