Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+137
View File
@@ -0,0 +1,137 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config INTEGRITY
bool "Integrity subsystem"
depends on SECURITY
default y
help
This option enables the integrity subsystem, which is comprised
of a number of different components including the Integrity
Measurement Architecture (IMA), Extended Verification Module
(EVM), IMA-appraisal extension, digital signature verification
extension and audit measurement log support.
Each of these components can be enabled/disabled separately.
Refer to the individual components for additional details.
if INTEGRITY
config INTEGRITY_SIGNATURE
bool "Digital signature verification using multiple keyrings"
default n
select KEYS
select SIGNATURE
help
This option enables digital signature verification support
using multiple keyrings. It defines separate keyrings for each
of the different use cases - evm, ima, and modules.
Different keyrings improves search performance, but also allow
to "lock" certain keyring to prevent adding new keys.
This is useful for evm and module keyrings, when keys are
usually only added from initramfs.
config INTEGRITY_ASYMMETRIC_KEYS
bool "Enable asymmetric keys support"
depends on INTEGRITY_SIGNATURE
default n
select ASYMMETRIC_KEY_TYPE
select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
select CRYPTO
select CRYPTO_RSA
select X509_CERTIFICATE_PARSER
help
This option enables digital signature verification using
asymmetric keys.
config INTEGRITY_TRUSTED_KEYRING
bool "Require all keys on the integrity keyrings be signed"
depends on SYSTEM_TRUSTED_KEYRING
depends on INTEGRITY_ASYMMETRIC_KEYS
default y
help
This option requires that all keys added to the .ima and
.evm keyrings be signed by a key on the system trusted
keyring.
config INTEGRITY_PLATFORM_KEYRING
bool "Provide keyring for platform/firmware trusted keys"
depends on INTEGRITY_ASYMMETRIC_KEYS
depends on SYSTEM_BLACKLIST_KEYRING
help
Provide a separate, distinct keyring for platform trusted keys, which
the kernel automatically populates during initialization from values
provided by the platform for verifying the kexec'ed kerned image
and, possibly, the initramfs signature.
config INTEGRITY_MACHINE_KEYRING
bool "Provide a keyring to which Machine Owner Keys may be added"
depends on SECONDARY_TRUSTED_KEYRING
depends on INTEGRITY_ASYMMETRIC_KEYS
depends on SYSTEM_BLACKLIST_KEYRING
depends on LOAD_UEFI_KEYS || LOAD_PPC_KEYS
help
If set, provide a keyring to which Machine Owner Keys (MOK) may
be added. This keyring shall contain just MOK keys. Unlike keys
in the platform keyring, keys contained in the .machine keyring will
be trusted within the kernel.
config INTEGRITY_CA_MACHINE_KEYRING
bool "Enforce Machine Keyring CA Restrictions"
depends on INTEGRITY_MACHINE_KEYRING
default n
help
The .machine keyring can be configured to enforce CA restriction
on any key added to it. By default no restrictions are in place
and all Machine Owner Keys (MOK) are added to the machine keyring.
If enabled only CA keys are added to the machine keyring, all
other MOK keys load into the platform keyring.
config INTEGRITY_CA_MACHINE_KEYRING_MAX
bool "Only CA keys without DigitialSignature usage set"
depends on INTEGRITY_CA_MACHINE_KEYRING
default n
help
When selected, only load CA keys are loaded into the machine
keyring that contain the CA bit set along with the keyCertSign
Usage field. Keys containing the digitialSignature Usage field
will not be loaded. The remaining MOK keys are loaded into the
.platform keyring.
config LOAD_UEFI_KEYS
depends on INTEGRITY_PLATFORM_KEYRING
depends on EFI
def_bool y
config LOAD_IPL_KEYS
depends on INTEGRITY_PLATFORM_KEYRING
depends on S390
def_bool y
config LOAD_PPC_KEYS
bool "Enable loading of platform and blacklisted keys for POWER"
depends on INTEGRITY_PLATFORM_KEYRING
depends on PPC_SECURE_BOOT
default y
help
Enable loading of keys to the .platform keyring and blacklisted
hashes to the .blacklist keyring for powerpc based platforms.
config INTEGRITY_AUDIT
bool "Enables integrity auditing support "
depends on AUDIT
default y
help
In addition to enabling integrity auditing support, this
option adds a kernel parameter 'integrity_audit', which
controls the level of integrity auditing messages.
0 - basic integrity auditing messages (default)
1 - additional integrity auditing messages
Additional informational integrity auditing messages would
be enabled by specifying 'integrity_audit=1' on the kernel
command line.
source "security/integrity/ima/Kconfig"
source "security/integrity/evm/Kconfig"
endif # if INTEGRITY
+24
View File
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for caching inode integrity data (iint)
#
obj-$(CONFIG_INTEGRITY) += integrity.o
integrity-y := iint.o
integrity-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o
integrity-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
integrity-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) += platform_certs/platform_keyring.o
integrity-$(CONFIG_INTEGRITY_MACHINE_KEYRING) += platform_certs/machine_keyring.o
integrity-$(CONFIG_LOAD_UEFI_KEYS) += platform_certs/efi_parser.o \
platform_certs/load_uefi.o \
platform_certs/keyring_handler.o
integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o
integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \
platform_certs/load_powerpc.o \
platform_certs/keyring_handler.o
integrity-$(CONFIG_EFI) += efi_secureboot.o
# The relative order of the 'ima' and 'evm' LSMs depends on the order below.
obj-$(CONFIG_IMA) += ima/
obj-$(CONFIG_EVM) += evm/
+228
View File
@@ -0,0 +1,228 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Intel Corporation
*
* Author:
* Dmitry Kasatkin <dmitry.kasatkin@intel.com>
*/
#include <linux/err.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/cred.h>
#include <linux/kernel_read_file.h>
#include <linux/key-type.h>
#include <linux/digsig.h>
#include <linux/vmalloc.h>
#include <crypto/public_key.h>
#include <keys/system_keyring.h>
#include "integrity.h"
static struct key *keyring[INTEGRITY_KEYRING_MAX];
static const char * const keyring_name[INTEGRITY_KEYRING_MAX] = {
#ifndef CONFIG_INTEGRITY_TRUSTED_KEYRING
"_evm",
"_ima",
#else
".evm",
".ima",
#endif
".platform",
".machine",
};
#ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
#define restrict_link_to_ima restrict_link_by_digsig_builtin_and_secondary
#else
#define restrict_link_to_ima restrict_link_by_digsig_builtin
#endif
static struct key *integrity_keyring_from_id(const unsigned int id)
{
if (id >= INTEGRITY_KEYRING_MAX)
return ERR_PTR(-EINVAL);
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;
return ERR_PTR(err);
}
}
return keyring[id];
}
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
const char *digest, int digestlen, u8 algo)
{
struct key *keyring;
if (siglen < 2)
return -EINVAL;
keyring = integrity_keyring_from_id(id);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
switch (sig[1]) {
case 1:
/* v1 API expect signature without xattr type */
return digsig_verify(keyring, sig + 1, siglen - 1, digest,
digestlen);
case 2: /* regular file data hash based signature */
return asymmetric_verify(keyring, sig, siglen, digest,
digestlen);
case 3: /* struct ima_file_id data based signature */
return asymmetric_verify_v3(keyring, sig, siglen, digest,
digestlen, algo);
}
return -EOPNOTSUPP;
}
int integrity_modsig_verify(const unsigned int id, const struct modsig *modsig)
{
struct key *keyring;
keyring = integrity_keyring_from_id(id);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
return ima_modsig_verify(keyring, modsig);
}
static int __init __integrity_init_keyring(const unsigned int id,
key_perm_t perm,
struct key_restriction *restriction)
{
const struct cred *cred = current_cred();
int err = 0;
keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
KGIDT_INIT(0), cred, perm,
KEY_ALLOC_NOT_IN_QUOTA, restriction, NULL);
if (IS_ERR(keyring[id])) {
err = PTR_ERR(keyring[id]);
pr_info("Can't allocate %s keyring (%d)\n",
keyring_name[id], err);
keyring[id] = NULL;
} else {
if (id == INTEGRITY_KEYRING_PLATFORM)
set_platform_trusted_keys(keyring[id]);
if (id == INTEGRITY_KEYRING_MACHINE && imputed_trust_enabled())
set_machine_trusted_keys(keyring[id]);
if (id == INTEGRITY_KEYRING_IMA)
load_module_cert(keyring[id]);
}
return err;
}
int __init integrity_init_keyring(const unsigned int id)
{
struct key_restriction *restriction;
key_perm_t perm;
int ret;
perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW
| KEY_USR_READ | KEY_USR_SEARCH;
if (id == INTEGRITY_KEYRING_PLATFORM ||
(id == INTEGRITY_KEYRING_MACHINE &&
!IS_ENABLED(CONFIG_INTEGRITY_CA_MACHINE_KEYRING))) {
restriction = NULL;
goto out;
}
if (!IS_ENABLED(CONFIG_INTEGRITY_TRUSTED_KEYRING))
return 0;
restriction = kzalloc_obj(struct key_restriction);
if (!restriction)
return -ENOMEM;
if (id == INTEGRITY_KEYRING_MACHINE)
restriction->check = restrict_link_by_ca;
else
restriction->check = restrict_link_to_ima;
/*
* MOK keys can only be added through a read-only runtime services
* UEFI variable during boot. No additional keys shall be allowed to
* load into the machine keyring following init from userspace.
*/
if (id != INTEGRITY_KEYRING_MACHINE)
perm |= KEY_USR_WRITE;
out:
ret = __integrity_init_keyring(id, perm, restriction);
if (ret)
kfree(restriction);
return ret;
}
static int __init integrity_add_key(const unsigned int id, const void *data,
off_t size, key_perm_t perm)
{
key_ref_t key;
int rc = 0;
if (!keyring[id])
return -EINVAL;
key = key_create_or_update(make_key_ref(keyring[id], 1), "asymmetric",
NULL, data, size, perm,
KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(key)) {
rc = PTR_ERR(key);
if (id != INTEGRITY_KEYRING_MACHINE)
pr_err("Problem loading X.509 certificate %d\n", rc);
} else {
pr_notice("Loaded X.509 cert '%s'\n",
key_ref_to_ptr(key)->description);
key_ref_put(key);
}
return rc;
}
int __init integrity_load_x509(const unsigned int id, const char *path)
{
void *data = NULL;
size_t size;
int rc;
key_perm_t perm;
rc = kernel_read_file_from_path(path, 0, &data, INT_MAX, NULL,
READING_X509_CERTIFICATE);
if (rc < 0) {
pr_err("Unable to open file: %s (%d)", path, rc);
return rc;
}
size = rc;
perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ;
pr_info("Loading X.509 certificate: %s\n", path);
rc = integrity_add_key(id, (const void *)data, size, perm);
vfree(data);
return rc;
}
int __init integrity_load_cert(const unsigned int id, const char *source,
const void *data, size_t len, key_perm_t perm)
{
if (!data)
return -EINVAL;
pr_info("Loading X.509 certificate: %s\n", source);
return integrity_add_key(id, data, len, perm);
}
@@ -0,0 +1,302 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Intel Corporation
*
* Author:
* Dmitry Kasatkin <dmitry.kasatkin@intel.com>
*/
#include <linux/err.h>
#include <linux/ratelimit.h>
#include <linux/key-type.h>
#include <crypto/public_key.h>
#include <crypto/hash_info.h>
#include <keys/asymmetric-type.h>
#include <keys/system_keyring.h>
#include "integrity.h"
/*
* Request an asymmetric key.
*/
static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
{
struct key *key;
char name[12];
sprintf(name, "id:%08x", keyid);
pr_debug("key search: \"%s\"\n", name);
key = get_ima_blacklist_keyring();
if (key) {
key_ref_t kref;
kref = keyring_search(make_key_ref(key, 1),
&key_type_asymmetric, name, true);
if (!IS_ERR(kref)) {
pr_err("Key '%s' is in ima_blacklist_keyring\n", name);
return ERR_PTR(-EKEYREJECTED);
}
}
if (keyring) {
/* search in specific keyring */
key_ref_t kref;
kref = keyring_search(make_key_ref(keyring, 1),
&key_type_asymmetric, name, true);
if (IS_ERR(kref))
key = ERR_CAST(kref);
else
key = key_ref_to_ptr(kref);
} else {
key = request_key(&key_type_asymmetric, name, NULL);
}
if (IS_ERR(key)) {
if (keyring)
pr_err_ratelimited("Request for unknown key '%s' in '%s' keyring. err %ld\n",
name, keyring->description,
PTR_ERR(key));
else
pr_err_ratelimited("Request for unknown key '%s' err %ld\n",
name, PTR_ERR(key));
switch (PTR_ERR(key)) {
/* Hide some search errors */
case -EACCES:
case -ENOTDIR:
case -EAGAIN:
return ERR_PTR(-ENOKEY);
default:
return key;
}
}
pr_debug("%s() = 0 [%x]\n", __func__, key_serial(key));
return key;
}
/**
* asymmetric_verify_common -- sigv2 and sigv3 common verify function
* @key: The key to use for signature verification; caller must free it
* @pk: The associated public key; must not be NULL
* @sig: The xattr signature
* @siglen: The length of the xattr signature; must be at least
* sizeof(struct signature_v2_hdr)
* @data: The data to verify the signature on
* @datalen: Length of @data
*/
static int asymmetric_verify_common(const struct key *key,
const struct public_key *pk,
const char *sig, int siglen,
const char *data, int datalen)
{
struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
struct public_key_signature pks;
int ret;
siglen -= sizeof(*hdr);
if (siglen != be16_to_cpu(hdr->sig_size))
return -EBADMSG;
if (hdr->hash_algo >= HASH_ALGO__LAST)
return -ENOPKG;
memset(&pks, 0, sizeof(pks));
pks.hash_algo = hash_algo_name[hdr->hash_algo];
pks.pkey_algo = pk->pkey_algo;
if (!strcmp(pk->pkey_algo, "rsa")) {
pks.encoding = "pkcs1";
} else if (!strncmp(pk->pkey_algo, "ecdsa-", 6)) {
/* edcsa-nist-p192 etc. */
pks.encoding = "x962";
} else if (!strcmp(pk->pkey_algo, "ecrdsa")) {
pks.encoding = "raw";
} else {
ret = -ENOPKG;
goto out;
}
pks.m = (u8 *)data;
pks.m_size = datalen;
pks.s = hdr->sig;
pks.s_size = siglen;
ret = verify_signature(key, &pks);
out:
pr_debug("%s() = %d\n", __func__, ret);
return ret;
}
int asymmetric_verify(struct key *keyring, const char *sig,
int siglen, const char *data, int datalen)
{
struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
const struct public_key *pk;
struct key *key;
int ret;
if (siglen <= sizeof(*hdr))
return -EBADMSG;
key = request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid));
if (IS_ERR(key))
return PTR_ERR(key);
pk = asymmetric_key_public_key(key);
if (!pk) {
ret = -ENOKEY;
goto out;
}
ret = asymmetric_verify_common(key, pk, sig, siglen, data, datalen);
out:
key_put(key);
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]; caller must ensure valid value
* @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.
*
* 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_max_digest_data *hash)
{
struct ima_file_id file_id = {.hash_type = type, .hash_algorithm = algo};
size_t digest_size = hash_digest_size[algo];
struct crypto_shash *tfm;
size_t file_id_size;
int rc;
if (type != IMA_VERITY_DIGSIG && type != EVM_IMA_XATTR_DIGSIG &&
type != EVM_XATTR_PORTABLE_DIGSIG)
return -EINVAL;
tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0);
if (IS_ERR(tfm))
return PTR_ERR(tfm);
memcpy(file_id.hash, digest, digest_size);
/* Calculate the ima_file_id struct hash on the portion used. */
file_id_size = sizeof(file_id) - (HASH_MAX_DIGESTSIZE - digest_size);
hash->hdr.algo = algo;
hash->hdr.length = digest_size;
rc = crypto_shash_tfm_digest(tfm, (const u8 *)&file_id, file_id_size,
hash->digest);
crypto_free_shash(tfm);
return rc;
}
/**
* asymmetric_verify_v3_hashless - Use hashless signature verification on sigv3
* @key: The key to use for signature verification; caller must free it
* @pk: The associated public key; must not be NULL
* @encoding: The encoding the key type uses
* @sig: The xattr signature
* @siglen: The length of the xattr signature; must be at least
* sizeof(struct signature_v2_hdr)
* @algo: hash algorithm [enum hash_algo]; caller must ensure valid value
* @digest: The file digest
*
* Create an ima_file_id structure and use it for signature verification
* directly. This can be used for ML-DSA in pure mode for example.
*/
static int asymmetric_verify_v3_hashless(struct key *key,
const struct public_key *pk,
const char *encoding,
const char *sig, int siglen,
u8 algo,
const u8 *digest)
{
struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
struct ima_file_id file_id = {
.hash_type = hdr->type,
.hash_algorithm = algo,
};
size_t digest_size = hash_digest_size[algo];
struct public_key_signature pks = {
.m = (u8 *)&file_id,
.m_size = sizeof(file_id) - (HASH_MAX_DIGESTSIZE - digest_size),
.s = hdr->sig,
.s_size = siglen - sizeof(*hdr),
.pkey_algo = pk->pkey_algo,
.hash_algo = "none",
.encoding = encoding,
};
int ret;
if (hdr->type != IMA_VERITY_DIGSIG &&
hdr->type != EVM_IMA_XATTR_DIGSIG &&
hdr->type != EVM_XATTR_PORTABLE_DIGSIG)
return -EINVAL;
if (pks.s_size != be16_to_cpu(hdr->sig_size))
return -EBADMSG;
memcpy(file_id.hash, digest, digest_size);
ret = verify_signature(key, &pks);
pr_debug("%s() = %d\n", __func__, ret);
return ret;
}
int asymmetric_verify_v3(struct key *keyring, const char *sig, int siglen,
const char *data, int datalen, u8 algo)
{
struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
struct ima_max_digest_data hash;
const struct public_key *pk;
struct key *key;
int rc;
if (algo >= HASH_ALGO__LAST)
return -ENOPKG;
if (siglen <= sizeof(*hdr))
return -EBADMSG;
key = request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid));
if (IS_ERR(key))
return PTR_ERR(key);
pk = asymmetric_key_public_key(key);
if (!pk) {
rc = -ENOKEY;
goto out;
}
if (!strncmp(pk->pkey_algo, "mldsa", 5)) {
rc = asymmetric_verify_v3_hashless(key, pk, "raw",
sig, siglen, algo, data);
} else {
rc = calc_file_id_hash(hdr->type, algo, data, &hash);
if (rc) {
rc = -EINVAL;
goto out;
}
rc = asymmetric_verify_common(key, pk, sig, siglen, hash.digest,
hash.hdr.length);
}
out:
key_put(key);
return rc;
}
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-1.0+
/*
* Copyright (C) 2018 IBM Corporation
*/
#include <linux/efi.h>
#include <linux/secure_boot.h>
#include <asm/efi.h>
#ifndef arch_efi_boot_mode
#define arch_efi_boot_mode efi_secureboot_mode_unset
#endif
static enum efi_secureboot_mode get_sb_mode(void)
{
enum efi_secureboot_mode mode;
if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) {
pr_info("integrity: secureboot mode unknown, no efi\n");
return efi_secureboot_mode_unknown;
}
mode = efi_get_secureboot_mode(efi.get_variable);
if (mode == efi_secureboot_mode_disabled)
pr_info("integrity: secureboot mode disabled\n");
else if (mode == efi_secureboot_mode_unknown)
pr_info("integrity: secureboot mode unknown\n");
else
pr_info("integrity: secureboot mode enabled\n");
return mode;
}
/*
* Query secure boot status
*
* Note don't call this function too early e.g. in __setup hook otherwise the
* kernel may hang when calling efi_get_secureboot_mode.
*
*/
bool arch_get_secureboot(void)
{
static enum efi_secureboot_mode sb_mode;
static bool initialized;
if (!initialized && efi_enabled(EFI_BOOT)) {
sb_mode = arch_efi_boot_mode;
if (sb_mode == efi_secureboot_mode_unset)
sb_mode = get_sb_mode();
initialized = true;
}
if (sb_mode == efi_secureboot_mode_enabled)
return true;
else
return false;
}
@@ -0,0 +1,76 @@
# SPDX-License-Identifier: GPL-2.0-only
config EVM
bool "EVM support"
select KEYS
select ENCRYPTED_KEYS
select CRYPTO_HMAC
select CRYPTO_SHA1
select CRYPTO_HASH_INFO
select SECURITY_PATH
default n
help
EVM protects a file's security extended attributes against
integrity attacks.
If you are unsure how to answer this question, answer N.
config EVM_ATTR_FSUUID
bool "FSUUID (version 2)"
default y
depends on EVM
help
Include filesystem UUID for HMAC calculation.
Default value is 'selected', which is former version 2.
if 'not selected', it is former version 1
WARNING: changing the HMAC calculation method or adding
additional info to the calculation, requires existing EVM
labeled file systems to be relabeled.
config EVM_EXTRA_SMACK_XATTRS
bool "Additional SMACK xattrs"
depends on EVM && SECURITY_SMACK
default n
help
Include additional SMACK xattrs for HMAC calculation.
In addition to the original security xattrs (eg. security.selinux,
security.SMACK64, security.capability, and security.ima) included
in the HMAC calculation, enabling this option includes newly defined
Smack xattrs: security.SMACK64EXEC, security.SMACK64TRANSMUTE and
security.SMACK64MMAP.
WARNING: changing the HMAC calculation method or adding
additional info to the calculation, requires existing EVM
labeled file systems to be relabeled.
config EVM_ADD_XATTRS
bool "Add additional EVM extended attributes at runtime"
depends on EVM
default n
help
Allow userland to provide additional xattrs for HMAC calculation.
When this option is enabled, root can add additional xattrs to the
list used by EVM by writing them into
/sys/kernel/security/integrity/evm/evm_xattrs.
config EVM_LOAD_X509
bool "Load an X509 certificate onto the '.evm' trusted keyring"
depends on EVM && INTEGRITY_TRUSTED_KEYRING
default n
help
Load an X509 certificate onto the '.evm' trusted keyring.
This option enables X509 certificate loading from the kernel
onto the '.evm' trusted keyring. A public key can be used to
verify EVM integrity starting from the 'init' process. The
key must have digitalSignature usage set.
config EVM_X509_PATH
string "EVM X509 certificate path"
depends on EVM_LOAD_X509
default "/etc/keys/x509_evm.der"
help
This option defines X509 certificate path.
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for building the Extended Verification Module(EVM)
#
obj-$(CONFIG_EVM) += evm.o
evm-y := evm_main.o evm_crypto.o evm_secfs.o
evm-$(CONFIG_FS_POSIX_ACL) += evm_posix_acl.o
+89
View File
@@ -0,0 +1,89 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2005-2010 IBM Corporation
*
* Authors:
* Mimi Zohar <zohar@us.ibm.com>
* Kylene Hall <kjhall@us.ibm.com>
*
* File: evm.h
*/
#ifndef __INTEGRITY_EVM_H
#define __INTEGRITY_EVM_H
#include <linux/xattr.h>
#include <linux/security.h>
#include "../integrity.h"
#define EVM_INIT_HMAC 0x0001
#define EVM_INIT_X509 0x0002
#define EVM_ALLOW_METADATA_WRITES 0x0004
#define EVM_SIGV3_REQUIRED 0x0008
#define EVM_SETUP_COMPLETE 0x80000000 /* userland has signaled key load */
#define EVM_KEY_MASK (EVM_INIT_HMAC | EVM_INIT_X509)
#define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP_COMPLETE | \
EVM_ALLOW_METADATA_WRITES | EVM_SIGV3_REQUIRED)
struct xattr_list {
struct list_head list;
char *name;
bool enabled;
};
#define EVM_NEW_FILE 0x00000001
#define EVM_IMMUTABLE_DIGSIG 0x00000002
/* EVM integrity metadata associated with an inode */
struct evm_iint_cache {
unsigned long flags;
enum integrity_status evm_status:4;
struct integrity_inode_attributes metadata_inode;
};
extern struct lsm_blob_sizes evm_blob_sizes;
static inline struct evm_iint_cache *evm_iint_inode(const struct inode *inode)
{
if (unlikely(!inode->i_security))
return NULL;
return inode->i_security + evm_blob_sizes.lbs_inode;
}
extern int evm_initialized;
#define EVM_ATTR_FSUUID 0x0001
extern int evm_hmac_attrs;
/* List of EVM protected security xattrs */
extern struct list_head evm_config_xattrnames;
struct evm_digest {
struct ima_digest_data_hdr hdr;
char digest[IMA_MAX_DIGEST_SIZE];
} __packed;
int evm_protected_xattr(const char *req_xattr_name);
int evm_init_key(void);
int evm_update_evmxattr(struct dentry *dentry,
const char *req_xattr_name,
const char *req_xattr_value,
size_t req_xattr_value_len);
int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value,
size_t req_xattr_value_len, struct evm_digest *data,
struct evm_iint_cache *iint);
int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value,
size_t req_xattr_value_len, char type,
struct evm_digest *data, struct evm_iint_cache *iint);
int evm_init_hmac(struct inode *inode, const struct xattr *xattrs,
char *hmac_val);
int evm_init_secfs(void);
#endif
@@ -0,0 +1,459 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2005-2010 IBM Corporation
*
* Authors:
* Mimi Zohar <zohar@us.ibm.com>
* Kylene Hall <kjhall@us.ibm.com>
*
* File: evm_crypto.c
* Using root's kernel master key (kmk), calculate the HMAC
*/
#define pr_fmt(fmt) "EVM: "fmt
#include <linux/export.h>
#include <linux/hex.h>
#include <linux/crypto.h>
#include <linux/xattr.h>
#include <linux/evm.h>
#include <keys/encrypted-type.h>
#include <crypto/hash.h>
#include <crypto/hash_info.h>
#include "evm.h"
#define EVMKEY "evm-key"
#define MAX_KEY_SIZE 128
static unsigned char evmkey[MAX_KEY_SIZE];
static const int evmkey_len = MAX_KEY_SIZE;
static struct crypto_shash *hmac_tfm;
static struct crypto_shash *evm_tfm[HASH_ALGO__LAST];
static DEFINE_MUTEX(mutex);
#define EVM_SET_KEY_BUSY 0
static unsigned long evm_set_key_flags;
static const char evm_hmac[] = "hmac(sha1)";
/**
* evm_set_key() - set EVM HMAC key from the kernel
* @key: pointer to a buffer with the key data
* @keylen: length of the key data
*
* This function allows setting the EVM HMAC key from the kernel
* without using the "encrypted" key subsystem keys. It can be used
* by the crypto HW kernel module which has its own way of managing
* keys.
*
* key length should be between 32 and 128 bytes long
*/
int evm_set_key(void *key, size_t keylen)
{
int rc;
rc = -EBUSY;
if (test_and_set_bit(EVM_SET_KEY_BUSY, &evm_set_key_flags))
goto busy;
rc = -EINVAL;
if (keylen > MAX_KEY_SIZE)
goto inval;
memcpy(evmkey, key, keylen);
evm_initialized |= EVM_INIT_HMAC;
pr_info("key initialized\n");
return 0;
inval:
clear_bit(EVM_SET_KEY_BUSY, &evm_set_key_flags);
busy:
pr_err("key initialization failed\n");
return rc;
}
EXPORT_SYMBOL_GPL(evm_set_key);
static struct shash_desc *init_desc(char type, uint8_t hash_algo)
{
long rc;
const char *algo;
struct crypto_shash **tfm, *tmp_tfm;
struct shash_desc *desc;
if (type == EVM_XATTR_HMAC) {
if (!(evm_initialized & EVM_INIT_HMAC)) {
pr_err_once("HMAC key is not set\n");
return ERR_PTR(-ENOKEY);
}
tfm = &hmac_tfm;
algo = evm_hmac;
} else {
if (hash_algo >= HASH_ALGO__LAST)
return ERR_PTR(-EINVAL);
tfm = &evm_tfm[hash_algo];
algo = hash_algo_name[hash_algo];
}
if (*tfm)
goto alloc;
mutex_lock(&mutex);
if (*tfm)
goto unlock;
tmp_tfm = crypto_alloc_shash(algo, 0, CRYPTO_NOLOAD);
if (IS_ERR(tmp_tfm)) {
pr_err("Can not allocate %s (reason: %ld)\n", algo,
PTR_ERR(tmp_tfm));
mutex_unlock(&mutex);
return ERR_CAST(tmp_tfm);
}
if (type == EVM_XATTR_HMAC) {
rc = crypto_shash_setkey(tmp_tfm, evmkey, evmkey_len);
if (rc) {
crypto_free_shash(tmp_tfm);
mutex_unlock(&mutex);
return ERR_PTR(rc);
}
}
*tfm = tmp_tfm;
unlock:
mutex_unlock(&mutex);
alloc:
desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(*tfm),
GFP_KERNEL);
if (!desc)
return ERR_PTR(-ENOMEM);
desc->tfm = *tfm;
rc = crypto_shash_init(desc);
if (rc) {
kfree(desc);
return ERR_PTR(rc);
}
return desc;
}
/* Protect against 'cutting & pasting' security.evm xattr, include inode
* specific info.
*
* (Additional directory/file metadata needs to be added for more complete
* protection.)
*/
static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
char type, char *digest)
{
struct h_misc {
/*
* Although inode->i_ino is now u64, this field remains
* unsigned long to allow existing HMAC and signatures from
* 32-bit hosts to continue working when i_ino hasn't changed
* and fits in a u32.
*/
unsigned long ino;
__u32 generation;
uid_t uid;
gid_t gid;
umode_t mode;
} hmac_misc;
memset(&hmac_misc, 0, sizeof(hmac_misc));
/* Don't include the inode or generation number in portable
* signatures
*/
if (type != EVM_XATTR_PORTABLE_DIGSIG) {
hmac_misc.ino = inode->i_ino;
hmac_misc.generation = inode->i_generation;
}
/* The hmac uid and gid must be encoded in the initial user
* namespace (not the filesystems user namespace) as encoding
* them in the filesystems user namespace allows an attack
* where first they are written in an unprivileged fuse mount
* of a filesystem and then the system is tricked to mount the
* filesystem for real on next boot and trust it because
* everything is signed.
*/
hmac_misc.uid = from_kuid(&init_user_ns, inode->i_uid);
hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
hmac_misc.mode = inode->i_mode;
crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
if ((evm_hmac_attrs & EVM_ATTR_FSUUID) &&
type != EVM_XATTR_PORTABLE_DIGSIG)
crypto_shash_update(desc, (u8 *)&inode->i_sb->s_uuid, UUID_SIZE);
crypto_shash_final(desc, digest);
pr_debug("hmac_misc: (%zu) [%*phN]\n", sizeof(struct h_misc),
(int)sizeof(struct h_misc), &hmac_misc);
}
/*
* Dump large security xattr values as a continuous ascii hexadecimal string.
* (pr_debug is limited to 64 bytes.)
*/
static void dump_security_xattr_l(const char *prefix, const void *src,
size_t count)
{
#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
char *asciihex, *p;
p = asciihex = kmalloc(count * 2 + 1, GFP_KERNEL);
if (!asciihex)
return;
p = bin2hex(p, src, count);
*p = 0;
pr_debug("%s: (%zu) %.*s\n", prefix, count, (int)count * 2, asciihex);
kfree(asciihex);
#endif
}
static void dump_security_xattr(const char *name, const char *value,
size_t value_len)
{
if (value_len < 64)
pr_debug("%s: (%zu) [%*phN]\n", name, value_len,
(int)value_len, value);
else
dump_security_xattr_l(name, value, value_len);
}
/*
* Calculate the HMAC value across the set of protected security xattrs.
*
* Instead of retrieving the requested xattr, for performance, calculate
* the hmac using the requested xattr value. Don't alloc/free memory for
* each xattr, but attempt to re-use the previously allocated memory.
*/
static int evm_calc_hmac_or_hash(struct dentry *dentry,
const char *req_xattr_name,
const char *req_xattr_value,
size_t req_xattr_value_len,
uint8_t type, struct evm_digest *data,
struct evm_iint_cache *iint)
{
struct inode *inode = d_inode(d_real(dentry, D_REAL_METADATA));
struct xattr_list *xattr;
struct shash_desc *desc;
size_t xattr_size = 0;
char *xattr_value = NULL;
int error;
int size, user_space_size;
bool ima_present = false;
u64 i_version = 0;
if (!(inode->i_opflags & IOP_XATTR) ||
inode->i_sb->s_user_ns != &init_user_ns)
return -EOPNOTSUPP;
desc = init_desc(type, data->hdr.algo);
if (IS_ERR(desc))
return PTR_ERR(desc);
data->hdr.length = crypto_shash_digestsize(desc->tfm);
error = -ENODATA;
list_for_each_entry_lockless(xattr, &evm_config_xattrnames, list) {
bool is_ima = false;
if (strcmp(xattr->name, XATTR_NAME_IMA) == 0)
is_ima = true;
/*
* Skip non-enabled xattrs for locally calculated
* signatures/HMACs.
*/
if (type != EVM_XATTR_PORTABLE_DIGSIG && !xattr->enabled)
continue;
if ((req_xattr_name && req_xattr_value)
&& !strcmp(xattr->name, req_xattr_name)) {
error = 0;
crypto_shash_update(desc, (const u8 *)req_xattr_value,
req_xattr_value_len);
if (is_ima)
ima_present = true;
dump_security_xattr(req_xattr_name,
req_xattr_value,
req_xattr_value_len);
continue;
}
size = vfs_getxattr_alloc(&nop_mnt_idmap, dentry, xattr->name,
&xattr_value, xattr_size, GFP_NOFS);
if (size == -ENOMEM) {
error = -ENOMEM;
goto out;
}
if (size < 0)
continue;
user_space_size = vfs_getxattr(&nop_mnt_idmap, dentry,
xattr->name, NULL, 0);
if (user_space_size != size)
pr_debug("file %s: xattr %s size mismatch (kernel: %d, user: %d)\n",
dentry->d_name.name, xattr->name, size,
user_space_size);
error = 0;
xattr_size = size;
crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
if (is_ima)
ima_present = true;
dump_security_xattr(xattr->name, xattr_value, xattr_size);
}
hmac_add_misc(desc, inode, type, data->digest);
if (inode != d_backing_inode(dentry) && iint) {
if (IS_I_VERSION(inode))
i_version = inode_query_iversion(inode);
integrity_inode_attrs_store(&iint->metadata_inode, i_version,
inode);
}
/* Portable EVM signatures must include an IMA hash */
if (type == EVM_XATTR_PORTABLE_DIGSIG && !ima_present)
error = -EPERM;
out:
kfree(xattr_value);
kfree(desc);
return error;
}
int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, size_t req_xattr_value_len,
struct evm_digest *data, struct evm_iint_cache *iint)
{
return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value,
req_xattr_value_len, EVM_XATTR_HMAC, data,
iint);
}
int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, size_t req_xattr_value_len,
char type, struct evm_digest *data, struct evm_iint_cache *iint)
{
return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value,
req_xattr_value_len, type, data, iint);
}
static int evm_is_immutable(struct dentry *dentry, struct inode *inode)
{
const struct evm_ima_xattr_data *xattr_data = NULL;
struct evm_iint_cache *iint;
int rc = 0;
iint = evm_iint_inode(inode);
if (iint && (iint->flags & EVM_IMMUTABLE_DIGSIG))
return 1;
/* Do this the hard way */
rc = vfs_getxattr_alloc(&nop_mnt_idmap, dentry, XATTR_NAME_EVM,
(char **)&xattr_data, 0, GFP_NOFS);
if (rc <= 0) {
if (rc == -ENODATA)
rc = 0;
goto out;
}
if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG)
rc = 1;
else
rc = 0;
out:
kfree(xattr_data);
return rc;
}
/*
* Calculate the hmac and update security.evm xattr
*
* Expects to be called with i_mutex locked.
*/
int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
const char *xattr_value, size_t xattr_value_len)
{
struct inode *inode = d_backing_inode(dentry);
struct evm_iint_cache *iint = evm_iint_inode(inode);
struct evm_digest data;
int rc = 0;
/*
* Don't permit any transformation of the EVM xattr if the signature
* is of an immutable type
*/
rc = evm_is_immutable(dentry, inode);
if (rc < 0)
return rc;
if (rc)
return -EPERM;
data.hdr.algo = HASH_ALGO_SHA1;
rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, &data, iint);
if (rc == 0) {
data.hdr.xattr.sha1.type = EVM_XATTR_HMAC;
rc = __vfs_setxattr_noperm(&nop_mnt_idmap, dentry,
XATTR_NAME_EVM,
&data.hdr.xattr.data[1],
SHA1_DIGEST_SIZE + 1, 0);
} else if (rc == -ENODATA && (inode->i_opflags & IOP_XATTR)) {
rc = __vfs_removexattr(&nop_mnt_idmap, dentry, XATTR_NAME_EVM);
}
return rc;
}
int evm_init_hmac(struct inode *inode, const struct xattr *xattrs,
char *hmac_val)
{
struct shash_desc *desc;
const struct xattr *xattr;
struct xattr_list *xattr_entry;
desc = init_desc(EVM_XATTR_HMAC, HASH_ALGO_SHA1);
if (IS_ERR(desc)) {
pr_info("init_desc failed\n");
return PTR_ERR(desc);
}
list_for_each_entry_lockless(xattr_entry, &evm_config_xattrnames,
list) {
for (xattr = xattrs; xattr->name; xattr++) {
if (strcmp(xattr_entry->name +
XATTR_SECURITY_PREFIX_LEN, xattr->name) != 0)
continue;
crypto_shash_update(desc, xattr->value,
xattr->value_len);
}
}
hmac_add_misc(desc, inode, EVM_XATTR_HMAC, hmac_val);
kfree(desc);
return 0;
}
/*
* Get the key from the TPM for the SHA1-HMAC
*/
int evm_init_key(void)
{
struct key *evm_key;
struct encrypted_key_payload *ekp;
int rc;
evm_key = request_key(&key_type_encrypted, EVMKEY, NULL);
if (IS_ERR(evm_key))
return -ENOENT;
down_read(&evm_key->sem);
ekp = evm_key->payload.data[0];
rc = evm_set_key(ekp->decrypted_data, ekp->decrypted_datalen);
/* burn the original key contents */
memset(ekp->decrypted_data, 0, ekp->decrypted_datalen);
up_read(&evm_key->sem);
key_put(evm_key);
return rc;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 IBM Corporation
*
* Author:
* Mimi Zohar <zohar@us.ibm.com>
*/
#include <linux/xattr.h>
#include <linux/evm.h>
int posix_xattr_acl(const char *xattr)
{
int xattr_len = strlen(xattr);
if ((strlen(XATTR_NAME_POSIX_ACL_ACCESS) == xattr_len)
&& (strncmp(XATTR_NAME_POSIX_ACL_ACCESS, xattr, xattr_len) == 0))
return 1;
if ((strlen(XATTR_NAME_POSIX_ACL_DEFAULT) == xattr_len)
&& (strncmp(XATTR_NAME_POSIX_ACL_DEFAULT, xattr, xattr_len) == 0))
return 1;
return 0;
}
@@ -0,0 +1,346 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2010 IBM Corporation
*
* Authors:
* Mimi Zohar <zohar@us.ibm.com>
*
* File: evm_secfs.c
* - Used to signal when key is on keyring
* - Get the key and enable EVM
*/
#include <linux/audit.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include "evm.h"
static struct dentry *evm_dir;
static struct dentry *evm_symlink;
#ifdef CONFIG_EVM_ADD_XATTRS
static struct dentry *evm_xattrs;
static DEFINE_MUTEX(xattr_list_mutex);
static int evm_xattrs_locked;
#endif
/**
* evm_read_key - read() for <securityfs>/evm
*
* @filp: file pointer, not actually used
* @buf: where to put the result
* @count: maximum to send along
* @ppos: where to start
*
* Returns number of bytes read or error code, as appropriate
*/
static ssize_t evm_read_key(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
char temp[80];
ssize_t rc;
if (*ppos != 0)
return 0;
sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP_COMPLETE));
rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
return rc;
}
/**
* evm_write_key - write() for <securityfs>/evm
* @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
*
* Used to signal that key is on the kernel key ring.
* - get the integrity hmac key from the kernel key ring
* - create list of hmac protected extended attributes
* Returns number of bytes written or error code, as appropriate
*/
static ssize_t evm_write_key(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned int i;
int ret;
if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP_COMPLETE))
return -EPERM;
ret = kstrtouint_from_user(buf, count, 0, &i);
if (ret)
return ret;
/* Reject invalid values */
if (!i || (i & ~EVM_INIT_MASK) != 0)
return -EINVAL;
/*
* Don't allow a request to enable metadata writes if
* an HMAC key is loaded.
*/
if ((i & EVM_ALLOW_METADATA_WRITES) &&
(evm_initialized & EVM_INIT_HMAC) != 0)
return -EPERM;
if (i & EVM_INIT_HMAC) {
ret = evm_init_key();
if (ret != 0)
return ret;
/* Forbid further writes after the symmetric key is loaded */
i |= EVM_SETUP_COMPLETE;
}
evm_initialized |= i;
/* Don't allow protected metadata modification if a symmetric key
* is loaded
*/
if (evm_initialized & EVM_INIT_HMAC)
evm_initialized &= ~(EVM_ALLOW_METADATA_WRITES);
return count;
}
static const struct file_operations evm_key_ops = {
.read = evm_read_key,
.write = evm_write_key,
};
#ifdef CONFIG_EVM_ADD_XATTRS
/**
* evm_read_xattrs - read() for <securityfs>/evm_xattrs
*
* @filp: file pointer, not actually used
* @buf: where to put the result
* @count: maximum to send along
* @ppos: where to start
*
* Returns number of bytes read or error code, as appropriate
*/
static ssize_t evm_read_xattrs(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
char *temp;
size_t offset = 0, size = 0;
ssize_t rc;
struct xattr_list *xattr;
if (*ppos != 0)
return 0;
rc = mutex_lock_interruptible(&xattr_list_mutex);
if (rc)
return -ERESTARTSYS;
list_for_each_entry(xattr, &evm_config_xattrnames, list) {
if (!xattr->enabled)
continue;
size += strlen(xattr->name) + 1;
}
temp = kmalloc(size + 1, GFP_KERNEL);
if (!temp) {
mutex_unlock(&xattr_list_mutex);
return -ENOMEM;
}
temp[size] = '\0';
/*
* No truncation possible: size is computed over the same enabled
* xattrs under xattr_list_mutex, so offset never exceeds size.
*/
list_for_each_entry(xattr, &evm_config_xattrnames, list) {
if (!xattr->enabled)
continue;
offset += snprintf(temp + offset, size + 1 - offset, "%s\n",
xattr->name);
}
mutex_unlock(&xattr_list_mutex);
rc = simple_read_from_buffer(buf, count, ppos, temp, offset);
kfree(temp);
return rc;
}
/**
* evm_write_xattrs - write() for <securityfs>/evm_xattrs
* @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
*
* Returns number of bytes written or error code, as appropriate
*/
static ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
int len, err;
struct xattr_list *xattr, *tmp;
struct audit_buffer *ab;
struct iattr newattrs;
struct inode *inode;
if (!capable(CAP_SYS_ADMIN) || evm_xattrs_locked)
return -EPERM;
if (*ppos != 0)
return -EINVAL;
if (count > XATTR_NAME_MAX)
return -E2BIG;
ab = audit_log_start(audit_context(), GFP_KERNEL,
AUDIT_INTEGRITY_EVM_XATTR);
if (!ab && IS_ENABLED(CONFIG_AUDIT))
return -ENOMEM;
xattr = kmalloc_obj(struct xattr_list);
if (!xattr) {
err = -ENOMEM;
goto out;
}
xattr->enabled = true;
xattr->name = memdup_user_nul(buf, count);
if (IS_ERR(xattr->name)) {
err = PTR_ERR(xattr->name);
xattr->name = NULL;
goto out;
}
/* Remove any trailing newline */
len = strlen(xattr->name);
if (len && xattr->name[len-1] == '\n')
xattr->name[len-1] = '\0';
audit_log_format(ab, "xattr=");
audit_log_untrustedstring(ab, xattr->name);
if (strcmp(xattr->name, ".") == 0) {
evm_xattrs_locked = 1;
newattrs.ia_mode = S_IFREG | 0440;
newattrs.ia_valid = ATTR_MODE;
inode = evm_xattrs->d_inode;
inode_lock(inode);
err = simple_setattr(&nop_mnt_idmap, evm_xattrs, &newattrs);
inode_unlock(inode);
if (!err)
err = count;
goto out;
}
if (strncmp(xattr->name, XATTR_SECURITY_PREFIX,
XATTR_SECURITY_PREFIX_LEN) != 0) {
err = -EINVAL;
goto out;
}
/*
* xattr_list_mutex guards against races in evm_read_xattrs().
* Entries are only added to the evm_config_xattrnames list
* and never deleted. Therefore, the list is traversed
* using list_for_each_entry_lockless() without holding
* the mutex in evm_calc_hmac_or_hash(), evm_find_protected_xattrs()
* and evm_protected_xattr().
*/
mutex_lock(&xattr_list_mutex);
list_for_each_entry(tmp, &evm_config_xattrnames, list) {
if (strcmp(xattr->name, tmp->name) == 0) {
err = -EEXIST;
if (!tmp->enabled) {
tmp->enabled = true;
err = count;
}
mutex_unlock(&xattr_list_mutex);
goto out;
}
}
list_add_tail_rcu(&xattr->list, &evm_config_xattrnames);
mutex_unlock(&xattr_list_mutex);
audit_log_format(ab, " res=0");
audit_log_end(ab);
return count;
out:
audit_log_format(ab, " res=%d", (err < 0) ? err : 0);
audit_log_end(ab);
if (xattr) {
kfree(xattr->name);
kfree(xattr);
}
return err;
}
static const struct file_operations evm_xattr_ops = {
.read = evm_read_xattrs,
.write = evm_write_xattrs,
};
static int evm_init_xattrs(void)
{
evm_xattrs = securityfs_create_file("evm_xattrs", 0660, evm_dir, NULL,
&evm_xattr_ops);
if (IS_ERR(evm_xattrs))
return -EFAULT;
return 0;
}
#else
static int evm_init_xattrs(void)
{
return 0;
}
#endif
int __init evm_init_secfs(void)
{
int error = 0;
struct dentry *dentry;
error = integrity_fs_init();
if (error < 0)
return -EFAULT;
evm_dir = securityfs_create_dir("evm", integrity_dir);
if (IS_ERR(evm_dir)) {
error = -EFAULT;
goto out;
}
dentry = securityfs_create_file("evm", 0660,
evm_dir, NULL, &evm_key_ops);
if (IS_ERR(dentry)) {
error = -EFAULT;
goto out;
}
evm_symlink = securityfs_create_symlink("evm", NULL,
"integrity/evm/evm", NULL);
if (IS_ERR(evm_symlink)) {
error = -EFAULT;
goto out;
}
if (evm_init_xattrs() != 0) {
error = -EFAULT;
goto out;
}
return 0;
out:
securityfs_remove(evm_symlink);
securityfs_remove(evm_dir);
integrity_fs_fini();
return error;
}
+71
View File
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2008 IBM Corporation
*
* Authors:
* Mimi Zohar <zohar@us.ibm.com>
*
* File: integrity_iint.c
* - initialize the integrity directory in securityfs
* - load IMA and EVM keys
*/
#include <linux/security.h>
#include "integrity.h"
struct dentry *integrity_dir;
/*
* integrity_kernel_read - read data from the file
*
* This is a function for reading file content instead of kernel_read().
* It does not perform locking checks to ensure it cannot be blocked.
* It does not perform security checks because it is irrelevant for IMA.
*
*/
int integrity_kernel_read(struct file *file, loff_t offset,
void *addr, unsigned long count)
{
return __kernel_read(file, addr, count, &offset);
}
/*
* integrity_load_keys - load integrity keys hook
*
* Hooks is called from init/main.c:kernel_init_freeable()
* when rootfs is ready
*/
void __init integrity_load_keys(void)
{
ima_load_x509();
if (!IS_ENABLED(CONFIG_IMA_LOAD_X509))
evm_load_x509();
}
int __init integrity_fs_init(void)
{
if (integrity_dir)
return 0;
integrity_dir = securityfs_create_dir("integrity", NULL);
if (IS_ERR(integrity_dir)) {
int ret = PTR_ERR(integrity_dir);
if (ret != -ENODEV)
pr_err("Unable to create integrity sysfs dir: %d\n",
ret);
integrity_dir = NULL;
return ret;
}
return 0;
}
void __init integrity_fs_fini(void)
{
if (!integrity_dir || !simple_empty(integrity_dir))
return;
securityfs_remove(integrity_dir);
integrity_dir = NULL;
}
+350
View File
@@ -0,0 +1,350 @@
# SPDX-License-Identifier: GPL-2.0-only
# IBM Integrity Measurement Architecture
#
config IMA
bool "Integrity Measurement Architecture(IMA)"
select SECURITYFS
select CRYPTO
select CRYPTO_HMAC
select CRYPTO_SHA1
select CRYPTO_HASH_INFO
select SECURITY_PATH
select TCG_TPM if HAS_IOMEM
select TCG_TIS if TCG_TPM && X86
select TCG_CRB if TCG_TPM && ACPI
select TCG_IBMVTPM if TCG_TPM && PPC_PSERIES
select INTEGRITY_AUDIT if AUDIT
help
The Trusted Computing Group(TCG) runtime Integrity
Measurement Architecture(IMA) maintains a list of hash
values of executables and other sensitive system files,
as they are read or executed. If an attacker manages
to change the contents of an important system file
being measured, we can tell.
If your system has a TPM chip, then IMA also maintains
an aggregate integrity value over this list inside the
TPM hardware, so that the TPM can prove to a third party
whether or not critical system files have been modified.
Read <https://www.usenix.org/events/sec04/tech/sailer.html>
to learn more about IMA.
If unsure, say N.
if IMA
config IMA_KEXEC
bool "Enable carrying the IMA measurement list across a soft boot"
depends on TCG_TPM && HAVE_IMA_KEXEC
default n
help
TPM PCRs are only reset on a hard reboot. In order to validate
a TPM's quote after a soft boot, the IMA measurement list of the
running kernel must be saved and restored on boot.
Depending on the IMA policy, the measurement list can grow to
be very large.
config IMA_MEASURE_PCR_IDX
int
range 8 14
default 10
help
IMA_MEASURE_PCR_IDX determines the TPM PCR register index
that IMA uses to maintain the integrity aggregate of the
measurement list. If unsure, use the default 10.
config IMA_LSM_RULES
bool
depends on AUDIT && (SECURITY_SELINUX || SECURITY_SMACK || SECURITY_APPARMOR)
default y
help
Disabling this option will disregard LSM based policy rules.
choice
prompt "Default template"
default IMA_NG_TEMPLATE
help
Select the default IMA measurement template.
The original 'ima' measurement list template contains a
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. The configured default template can be replaced
by specifying "ima_template=" on the boot command line.
config IMA_NG_TEMPLATE
bool "ima-ng (default)"
config IMA_SIG_TEMPLATE
bool "ima-sig"
endchoice
config IMA_DEFAULT_TEMPLATE
string
default "ima-ng" if IMA_NG_TEMPLATE
default "ima-sig" if IMA_SIG_TEMPLATE
choice
prompt "Default integrity hash algorithm"
default IMA_DEFAULT_HASH_SHA1
help
Select the default hash algorithm used for the measurement
list, integrity appraisal and audit log. The compiled default
hash algorithm can be overwritten using the kernel command
line 'ima_hash=' option.
config IMA_DEFAULT_HASH_SHA1
bool "SHA1 (default)"
depends on CRYPTO_SHA1=y
config IMA_DEFAULT_HASH_SHA256
bool "SHA256"
depends on CRYPTO_SHA256=y
config IMA_DEFAULT_HASH_SHA512
bool "SHA512"
depends on CRYPTO_SHA512=y
config IMA_DEFAULT_HASH_WP512
bool "WP512"
depends on CRYPTO_WP512=y
config IMA_DEFAULT_HASH_SM3
bool "SM3"
depends on CRYPTO_SM3=y
endchoice
config IMA_DEFAULT_HASH
string
default "sha1" if IMA_DEFAULT_HASH_SHA1
default "sha256" if IMA_DEFAULT_HASH_SHA256
default "sha512" if IMA_DEFAULT_HASH_SHA512
default "wp512" if IMA_DEFAULT_HASH_WP512
default "sm3" if IMA_DEFAULT_HASH_SM3
config IMA_WRITE_POLICY
bool "Enable multiple writes to the IMA policy"
default n
help
IMA policy can now be updated multiple times. The new rules get
appended to the original policy. Have in mind that the rules are
scanned in FIFO order so be careful when you design and add new ones.
If unsure, say N.
config IMA_READ_POLICY
bool "Enable reading back the current IMA policy"
default y if IMA_WRITE_POLICY
default n if !IMA_WRITE_POLICY
help
It is often useful to be able to read back the IMA policy. It is
even more important after introducing CONFIG_IMA_WRITE_POLICY.
This option allows the root user to see the current policy rules.
config IMA_APPRAISE
bool "Appraise integrity measurements"
default n
help
This option enables local measurement integrity appraisal.
It requires the system to be labeled with a security extended
attribute containing the file hash measurement. To protect
the security extended attributes from offline attack, enable
and configure EVM.
For more information on integrity appraisal refer to:
<http://linux-ima.sourceforge.net>
If unsure, say N.
config IMA_ARCH_POLICY
bool "Enable loading an IMA architecture specific policy"
depends on (KEXEC_SIG && IMA) || IMA_APPRAISE \
&& INTEGRITY_ASYMMETRIC_KEYS
default n
help
This option enables loading an IMA architecture specific policy
based on run time secure boot flags.
config IMA_APPRAISE_BUILD_POLICY
bool "IMA build time configured policy rules"
depends on IMA_APPRAISE && INTEGRITY_ASYMMETRIC_KEYS
default n
help
This option defines an IMA appraisal policy at build time, which
is enforced at run time without having to specify a builtin
policy name on the boot command line. The build time appraisal
policy rules persist after loading a custom policy.
Depending on the rules configured, this policy may require kernel
modules, firmware, the kexec kernel image, and/or the IMA policy
to be signed. Unsigned files might prevent the system from
booting or applications from working properly.
config IMA_APPRAISE_REQUIRE_FIRMWARE_SIGS
bool "Appraise firmware signatures"
depends on IMA_APPRAISE_BUILD_POLICY
default n
help
This option defines a policy requiring all firmware to be signed,
including the regulatory.db. If both this option and
CFG80211_REQUIRE_SIGNED_REGDB are enabled, then both signature
verification methods are necessary.
config IMA_APPRAISE_REQUIRE_KEXEC_SIGS
bool "Appraise kexec kernel image signatures"
depends on IMA_APPRAISE_BUILD_POLICY
default n
help
Enabling this rule will require all kexec'ed kernel images to
be signed and verified by a public key on the trusted IMA
keyring.
Kernel image signatures can not be verified by the original
kexec_load syscall. Enabling this rule will prevent its
usage.
config IMA_APPRAISE_REQUIRE_MODULE_SIGS
bool "Appraise kernel modules signatures"
depends on IMA_APPRAISE_BUILD_POLICY
default n
help
Enabling this rule will require all kernel modules to be signed
and verified by a public key on the trusted IMA keyring.
Kernel module signatures can only be verified by IMA-appraisal,
via the finit_module syscall. Enabling this rule will prevent
the usage of the init_module syscall.
config IMA_APPRAISE_REQUIRE_POLICY_SIGS
bool "Appraise IMA policy signature"
depends on IMA_APPRAISE_BUILD_POLICY
default n
help
Enabling this rule will require the IMA policy to be signed and
and verified by a key on the trusted IMA keyring.
config IMA_APPRAISE_BOOTPARAM
bool "ima_appraise boot parameter"
depends on IMA_APPRAISE
default y
help
This option enables the different "ima_appraise=" modes
(eg. fix, log) from the boot command line.
config IMA_APPRAISE_MODSIG
bool "Support module-style signatures for appraisal"
depends on IMA_APPRAISE
depends on INTEGRITY_ASYMMETRIC_KEYS
select PKCS7_MESSAGE_PARSER
select MODULE_SIG_FORMAT
default n
help
Adds support for signatures appended to files. The format of the
appended signature is the same used for signed kernel modules.
The modsig keyword can be used in the IMA policy to allow a hook
to accept such signatures.
config IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
bool "Permit keys validly signed by a built-in, machine (if configured) or secondary"
depends on SYSTEM_TRUSTED_KEYRING
depends on SECONDARY_TRUSTED_KEYRING
depends on INTEGRITY_ASYMMETRIC_KEYS
select INTEGRITY_TRUSTED_KEYRING
default n
help
Keys may be added to the IMA or IMA blacklist keyrings, if the
key is validly signed by a CA cert in the system built-in,
machine (if configured), or secondary trusted keyrings. The
key must also have the digitalSignature usage set.
Intermediate keys between those the kernel has compiled in and the
IMA keys to be added may be added to the system secondary keyring,
provided they are validly signed by a key already resident in the
built-in, machine (if configured) or secondary trusted keyrings.
config IMA_BLACKLIST_KEYRING
bool "Create IMA machine owner blacklist keyrings (EXPERIMENTAL)"
depends on SYSTEM_TRUSTED_KEYRING
depends on INTEGRITY_TRUSTED_KEYRING
default n
help
This option creates an IMA blacklist keyring, which contains all
revoked IMA keys. It is consulted before any other keyring. If
the search is successful the requested operation is rejected and
an error is returned to the caller.
config IMA_LOAD_X509
bool "Load X509 certificate onto the '.ima' trusted keyring"
depends on INTEGRITY_TRUSTED_KEYRING
default n
help
File signature verification is based on the public keys
loaded on the .ima trusted keyring. These public keys are
X509 certificates signed by a trusted key on the
.system keyring. This option enables X509 certificate
loading from the kernel onto the '.ima' trusted keyring.
config IMA_X509_PATH
string "IMA X509 certificate path"
depends on IMA_LOAD_X509
default "/etc/keys/x509_ima.der"
help
This option defines IMA X509 certificate path.
config IMA_APPRAISE_SIGNED_INIT
bool "Require signed user-space initialization"
depends on IMA_LOAD_X509
default n
help
This option requires user-space init to be signed.
config IMA_MEASURE_ASYMMETRIC_KEYS
bool
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
default y
config IMA_QUEUE_EARLY_BOOT_KEYS
bool
depends on IMA_MEASURE_ASYMMETRIC_KEYS
depends on SYSTEM_TRUSTED_KEYRING
default y
config IMA_SECURE_AND_OR_TRUSTED_BOOT
bool
depends on IMA_ARCH_POLICY
help
This option is selected by architectures to enable secure and/or
trusted boot based on IMA runtime policies.
config IMA_DISABLE_HTABLE
bool "Disable htable to allow measurement of duplicate records"
default n
help
This option disables htable to allow measurement of duplicate records.
config IMA_KEXEC_EXTRA_MEMORY_KB
int "Extra memory for IMA measurements added during kexec soft reboot"
range 0 40
depends on IMA_KEXEC
default 0
help
IMA_KEXEC_EXTRA_MEMORY_KB determines the extra memory to be
allocated (in kb) for IMA measurements added during kexec soft reboot.
If set to the default value of 0, an extra half page of memory for those
additional measurements will be allocated.
config IMA_STAGING
bool "Support for staging the measurements list"
default n
help
Add support for staging the measurements list.
It allows user space to stage the measurements list for deletion and
to delete the staged measurements after confirmation.
Or, alternatively, it allows user space to specify N measurements
records to stage internally, so that they can be immediately deleted.
On kexec, staging is aborted and any staged measurement records are
copied to the secondary kernel.
endif
@@ -0,0 +1,20 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for building Trusted Computing Group's(TCG) runtime Integrity
# Measurement Architecture(IMA).
#
obj-$(CONFIG_IMA) += ima.o ima_iint.o
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
ima_policy.o ima_template.o ima_template_lib.o
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
ima-$(CONFIG_IMA_APPRAISE_MODSIG) += ima_modsig.o
ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
ima-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
ima-$(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) += ima_asymmetric_keys.o
ima-$(CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS) += ima_queue_keys.o
ifeq ($(CONFIG_EFI),y)
ima-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_efi.o
endif
+625
View File
@@ -0,0 +1,625 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2005,2006,2007,2008 IBM Corporation
*
* Authors:
* Reiner Sailer <sailer@watson.ibm.com>
* Mimi Zohar <zohar@us.ibm.com>
*
* File: ima.h
* internal Integrity Measurement Architecture (IMA) definitions
*/
#ifndef __LINUX_IMA_H
#define __LINUX_IMA_H
#include <linux/types.h>
#include <linux/crypto.h>
#include <linux/fs.h>
#include <linux/security.h>
#include <linux/hash.h>
#include <linux/tpm.h>
#include <linux/audit.h>
#include <crypto/hash_info.h>
#include "../integrity.h"
enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_BINARY_NO_FIELD_LEN,
IMA_SHOW_BINARY_OLD_STRING_FMT, IMA_SHOW_ASCII };
enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 };
/*
* BINARY: current binary measurements list
* BINARY_STAGED: staged binary measurements list
* BINARY_FULL: binary measurements list since IMA init (lost after kexec)
*/
enum binary_lists {
BINARY, BINARY_STAGED, BINARY_FULL, BINARY__LAST
};
/* digest size for IMA, fits SHA1 or MD5 */
#define IMA_DIGEST_SIZE SHA1_DIGEST_SIZE
#define IMA_EVENT_NAME_LEN_MAX 255
#define IMA_HASH_BITS 10
#define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
#define IMA_TEMPLATE_FIELD_ID_MAX_LEN 16
#define IMA_TEMPLATE_NUM_FIELDS_MAX 15
#define IMA_TEMPLATE_IMA_NAME "ima"
#define IMA_TEMPLATE_IMA_FMT "d|n"
#define NR_BANKS(chip) ((chip != NULL) ? chip->nr_allocated_banks : 0)
/* current content of the policy */
extern int ima_policy_flag;
/* bitset of digests algorithms allowed in the setxattr hook */
extern atomic_t ima_setxattr_allowed_hash_algorithms;
/* IMA hash algorithm description */
struct ima_algo_desc {
struct crypto_shash *tfm;
enum hash_algo algo;
unsigned int digest_size;
};
/* set during initialization */
extern int ima_hash_algo __ro_after_init;
extern int ima_sha1_idx __ro_after_init;
extern int ima_hash_algo_idx __ro_after_init;
extern int ima_extra_slots __ro_after_init;
extern struct ima_algo_desc *ima_algo_array __ro_after_init;
extern int ima_appraise;
extern struct tpm_chip *ima_tpm_chip;
extern const char boot_aggregate_name[];
/* IMA event related data */
struct ima_event_data {
struct ima_iint_cache *iint;
struct file *file;
const unsigned char *filename;
struct evm_ima_xattr_data *xattr_value;
int xattr_len;
const struct modsig *modsig;
const char *violation;
const void *buf;
int buf_len;
};
/* IMA template field data definition */
struct ima_field_data {
u8 *data;
u32 len;
};
/* IMA template field definition */
struct ima_template_field {
const char field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN];
int (*field_init)(struct ima_event_data *event_data,
struct ima_field_data *field_data);
void (*field_show)(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
};
/* IMA template descriptor definition */
struct ima_template_desc {
struct list_head list;
char *name;
char *fmt;
int num_fields;
const struct ima_template_field **fields;
};
struct ima_template_entry {
int pcr;
struct tpm_digest *digests;
struct ima_template_desc *template_desc; /* template descriptor */
u32 template_data_len;
struct ima_field_data template_data[]; /* template related data */
};
struct ima_queue_entry {
struct hlist_node hnext; /* place in hash collision list */
struct list_head later; /* place in ima_measurements list */
struct ima_template_entry *entry;
};
extern struct list_head ima_measurements; /* list of all measurements */
extern struct list_head ima_measurements_staged; /* list of staged meas. */
/* Some details preceding the binary serialized measurement list */
struct ima_kexec_hdr {
u16 version;
u16 _reserved0;
u32 _reserved1;
u64 buffer_size;
u64 count;
};
/* IMA iint action cache flags */
#define IMA_MEASURE 0x00000001
#define IMA_MEASURED 0x00000002
#define IMA_APPRAISE 0x00000004
#define IMA_APPRAISED 0x00000008
/*#define IMA_COLLECT 0x00000010 do not use this flag */
#define IMA_COLLECTED 0x00000020
#define IMA_AUDIT 0x00000040
#define IMA_AUDITED 0x00000080
#define IMA_HASH 0x00000100
#define IMA_HASHED 0x00000200
/* IMA iint policy rule cache flags */
#define IMA_NONACTION_FLAGS 0xff000000
#define IMA_DIGSIG_REQUIRED 0x01000000
#define IMA_PERMIT_DIRECTIO 0x02000000
#define IMA_NEW_FILE 0x04000000
#define IMA_SIGV3_REQUIRED 0x08000000
#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
#define IMA_MODSIG_ALLOWED 0x20000000
#define IMA_CHECK_BLACKLIST 0x40000000
#define IMA_VERITY_REQUIRED 0x80000000
/* Exclude non-action flags which are not rule-specific. */
#define IMA_NONACTION_RULE_FLAGS (IMA_NONACTION_FLAGS & ~IMA_NEW_FILE)
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
IMA_HASH | IMA_APPRAISE_SUBMASK)
#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
IMA_HASHED | IMA_COLLECTED | \
IMA_APPRAISED_SUBMASK)
/* IMA iint subaction appraise cache flags */
#define IMA_FILE_APPRAISE 0x00001000
#define IMA_FILE_APPRAISED 0x00002000
#define IMA_MMAP_APPRAISE 0x00004000
#define IMA_MMAP_APPRAISED 0x00008000
#define IMA_BPRM_APPRAISE 0x00010000
#define IMA_BPRM_APPRAISED 0x00020000
#define IMA_READ_APPRAISE 0x00040000
#define IMA_READ_APPRAISED 0x00080000
#define IMA_CREDS_APPRAISE 0x00100000
#define IMA_CREDS_APPRAISED 0x00200000
#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
IMA_BPRM_APPRAISE | IMA_READ_APPRAISE | \
IMA_CREDS_APPRAISE)
#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
IMA_BPRM_APPRAISED | IMA_READ_APPRAISED | \
IMA_CREDS_APPRAISED)
/*
* IMA iint cache atomic_flags
*
* IMA_CHANGE_ATTR - indicates that chATTR() was called (chmod, chown, chgrp)
* and file attributes have changed. On file open, it causes IMA to clear
* iint->flags to re-evaluate policy and perform IMA functions again.
*
* IMA_CHANGE_XATTR - indicates that setxattr or removexattr was called and
* extended attributes have changed. On file open, it causes IMA to clear
* iint->flags IMA_DONE_MASK to re-appraise.
*
* IMA_UPDATE_XATTR - indicates that security.ima needs to be updated. It is
* cleared if file policy changes and no update is needed.
*
* IMA_DIGSIG - indicates that file security.ima has signature and file
* security.ima must not update on file close.
*
* IMA_MAY_EMIT_TOMTOU - indicates to add Time-of-Measure-Time-of-Use (ToMToU)
* integrity violation (a file that is already opened for read is opened for
* write) to the measurement list and to also emit an audit message.
*
* IMA_EMITTED_OPENWRITERS - indicates to add open-writers integrity violation
* (a file that is already opened for write is opened for read) to the
* measurement list and to also emit an audit message.
*
*/
#define IMA_CHANGE_XATTR 0
#define IMA_UPDATE_XATTR 1
#define IMA_CHANGE_ATTR 2
#define IMA_DIGSIG 3
#define IMA_MAY_EMIT_TOMTOU 4
#define IMA_EMITTED_OPENWRITERS 5
/* IMA integrity metadata associated with an inode */
struct ima_iint_cache {
struct mutex mutex; /* protects: version, flags, digest */
struct integrity_inode_attributes real_inode;
unsigned long flags;
unsigned long measured_pcrs;
unsigned long atomic_flags;
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 ima_creds_status:4;
struct ima_digest_data *ima_hash;
};
extern struct lsm_blob_sizes ima_blob_sizes;
static inline struct ima_iint_cache *
ima_inode_get_iint(const struct inode *inode)
{
struct ima_iint_cache **iint_sec;
if (unlikely(!inode->i_security))
return NULL;
iint_sec = inode->i_security + ima_blob_sizes.lbs_inode;
return *iint_sec;
}
static inline void ima_inode_set_iint(const struct inode *inode,
struct ima_iint_cache *iint)
{
struct ima_iint_cache **iint_sec;
if (unlikely(!inode->i_security))
return;
iint_sec = inode->i_security + ima_blob_sizes.lbs_inode;
*iint_sec = iint;
}
struct ima_iint_cache *ima_iint_find(struct inode *inode);
struct ima_iint_cache *ima_inode_get(struct inode *inode);
void ima_inode_free_rcu(void *inode_security);
void __init ima_iintcache_init(void);
extern const int read_idmap[];
#ifdef CONFIG_HAVE_IMA_KEXEC
void ima_load_kexec_buffer(void);
#else
static inline void ima_load_kexec_buffer(void) {}
#endif /* CONFIG_HAVE_IMA_KEXEC */
#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS
void ima_post_key_create_or_update(struct key *keyring, struct key *key,
const void *payload, size_t plen,
unsigned long flags, bool create);
#endif
#ifdef CONFIG_IMA_KEXEC
void ima_measure_kexec_event(const char *event_name);
#else
static inline void ima_measure_kexec_event(const char *event_name) {}
#endif
/*
* The default binary_runtime_measurements list format is defined as the
* platform native format. The canonical format is defined as little-endian.
*/
extern bool ima_canonical_fmt;
/* Internal IMA function definitions */
int ima_init(void);
int ima_fs_init(void);
int ima_add_template_entry(struct ima_template_entry *entry, int violation,
const char *op, struct inode *inode,
const unsigned char *filename);
int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash);
int ima_calc_buffer_hash(const void *buf, loff_t len,
struct ima_digest_data *hash);
int ima_calc_field_array_hash(struct ima_field_data *field_data,
struct ima_template_entry *entry);
int ima_calc_boot_aggregate(struct ima_digest_data *hash);
void ima_add_violation(struct file *file, const unsigned char *filename,
struct ima_iint_cache *iint, const char *op,
const char *cause);
int ima_init_crypto(void);
void ima_putc(struct seq_file *m, void *data, int datalen);
void ima_print_digest(struct seq_file *m, u8 *digest, u32 size);
int template_desc_init_fields(const char *template_fmt,
const struct ima_template_field ***fields,
int *num_fields);
struct ima_template_desc *ima_template_desc_current(void);
struct ima_template_desc *ima_template_desc_buf(void);
struct ima_template_desc *lookup_template_desc(const char *name);
bool ima_template_has_modsig(const struct ima_template_desc *ima_template);
int ima_queue_stage(void);
int ima_queue_staged_delete_all(void);
int ima_queue_delete_partial(unsigned long req_value);
int ima_restore_measurement_entry(struct ima_template_entry *entry);
int ima_restore_measurement_list(loff_t bufsize, void *buf);
int ima_measurements_show(struct seq_file *m, void *v);
int __init ima_init_htable(void);
unsigned long ima_get_binary_runtime_size(enum binary_lists binary_list);
int ima_init_template(void);
void ima_init_template_list(void);
int __init ima_init_digests(void);
void __init ima_init_reboot_notifier(void);
int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
void *lsm_data);
/*
* used to protect h_table and sha_table
*/
extern spinlock_t ima_queue_lock;
/* Total number of measurement list records since hard boot. */
extern atomic_long_t ima_num_records[BINARY__LAST];
/* Total number of violations since hard boot. */
extern atomic_long_t ima_num_violations;
extern struct hlist_head __rcu *ima_htable;
extern bool ima_flush_htable;
static inline unsigned int ima_hash_key(u8 *digest)
{
/* there is no point in taking a hash of part of a digest */
return (digest[0] | digest[1] << 8) % IMA_MEASURE_HTABLE_SIZE;
}
#define __ima_hooks(hook) \
hook(NONE, none) \
hook(FILE_CHECK, file) \
hook(MMAP_CHECK, mmap) \
hook(MMAP_CHECK_REQPROT, mmap_reqprot) \
hook(BPRM_CHECK, bprm) \
hook(CREDS_CHECK, creds) \
hook(POST_SETATTR, post_setattr) \
hook(MODULE_CHECK, module) \
hook(FIRMWARE_CHECK, firmware) \
hook(KEXEC_KERNEL_CHECK, kexec_kernel) \
hook(KEXEC_INITRAMFS_CHECK, kexec_initramfs) \
hook(POLICY_CHECK, policy) \
hook(KEXEC_CMDLINE, kexec_cmdline) \
hook(KEY_CHECK, key) \
hook(CRITICAL_DATA, critical_data) \
hook(SETXATTR_CHECK, setxattr_check) \
hook(MAX_CHECK, none)
#define __ima_hook_enumify(ENUM, str) ENUM,
#define __ima_stringify(arg) (#arg)
#define __ima_hook_measuring_stringify(ENUM, str) \
(__ima_stringify(measuring_ ##str)),
enum ima_hooks {
__ima_hooks(__ima_hook_enumify)
};
static const char * const ima_hooks_measure_str[] = {
__ima_hooks(__ima_hook_measuring_stringify)
};
static inline const char *func_measure_str(enum ima_hooks func)
{
if (func >= MAX_CHECK)
return ima_hooks_measure_str[NONE];
return ima_hooks_measure_str[func];
}
extern const char *const func_tokens[];
struct modsig;
#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
/*
* To track keys that need to be measured.
*/
struct ima_key_entry {
struct list_head list;
void *payload;
size_t payload_len;
char *keyring_name;
};
void ima_init_key_queue(void);
bool ima_should_queue_key(void);
bool ima_queue_key(struct key *keyring, const void *payload,
size_t payload_len);
void ima_process_queued_keys(void);
#else
static inline void ima_init_key_queue(void) {}
static inline bool ima_should_queue_key(void) { return false; }
static inline bool ima_queue_key(struct key *keyring,
const void *payload,
size_t payload_len) { return false; }
static inline void ima_process_queued_keys(void) {}
#endif /* CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS */
/* LIM API function definitions */
int ima_get_action(struct mnt_idmap *idmap, struct inode *inode,
const struct cred *cred, struct lsm_prop *prop, int mask,
enum ima_hooks func, int *pcr,
struct ima_template_desc **template_desc,
const char *func_data, unsigned int *allowed_algos);
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
void *buf, loff_t size, enum hash_algo algo,
struct modsig *modsig);
void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr,
struct ima_template_desc *template_desc);
int process_buffer_measurement(struct mnt_idmap *idmap,
struct inode *inode, const void *buf, int size,
const char *eventname, enum ima_hooks func,
int pcr, const char *func_data,
bool buf_hash, u8 *digest, size_t digest_len);
void ima_audit_measurement(struct ima_iint_cache *iint,
const unsigned char *filename);
int ima_alloc_init_template(struct ima_event_data *event_data,
struct ima_template_entry **entry,
struct ima_template_desc *template_desc);
int ima_store_template(struct ima_template_entry *entry, int violation,
struct inode *inode,
const unsigned char *filename, int pcr);
void ima_free_template_entry(struct ima_template_entry *entry);
const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
/* IMA policy related functions */
int ima_match_policy(struct mnt_idmap *idmap, struct inode *inode,
const struct cred *cred, struct lsm_prop *prop,
enum ima_hooks func, int mask, int flags, int *pcr,
struct ima_template_desc **template_desc,
const char *func_data, unsigned int *allowed_algos);
void ima_init_policy(void);
void ima_update_policy(void);
void ima_update_policy_flags(void);
ssize_t ima_parse_add_rule(char *);
void ima_delete_rules(void);
int ima_check_policy(void);
void *ima_policy_start(struct seq_file *m, loff_t *pos);
void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
void ima_policy_stop(struct seq_file *m, void *v);
int ima_policy_show(struct seq_file *m, void *v);
/* Appraise integrity measurements */
#define IMA_APPRAISE_ENFORCE 0x01
#define IMA_APPRAISE_FIX 0x02
#define IMA_APPRAISE_LOG 0x04
#define IMA_APPRAISE_MODULES 0x08
#define IMA_APPRAISE_FIRMWARE 0x10
#define IMA_APPRAISE_POLICY 0x20
#define IMA_APPRAISE_KEXEC 0x40
#ifdef CONFIG_IMA_APPRAISE
int ima_check_blacklist(struct ima_iint_cache *iint,
const struct modsig *modsig, int pcr);
int ima_appraise_measurement(enum ima_hooks func, struct ima_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig,
bool bprm_is_check);
int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode,
int mask, enum ima_hooks func);
void ima_update_xattr(struct ima_iint_cache *iint, struct file *file);
enum integrity_status ima_get_cache_status(struct ima_iint_cache *iint,
enum ima_hooks func);
enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
int xattr_len);
int ima_read_xattr(struct dentry *dentry,
struct evm_ima_xattr_data **xattr_value, int xattr_len);
void __init init_ima_appraise_lsm(const struct lsm_id *lsmid);
#else
static inline int ima_check_blacklist(struct ima_iint_cache *iint,
const struct modsig *modsig, int pcr)
{
return 0;
}
static inline int ima_appraise_measurement(enum ima_hooks func,
struct ima_iint_cache *iint,
struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len,
const struct modsig *modsig,
bool bprm_is_check)
{
return INTEGRITY_UNKNOWN;
}
static inline int ima_must_appraise(struct mnt_idmap *idmap,
struct inode *inode, int mask,
enum ima_hooks func)
{
return 0;
}
static inline void ima_update_xattr(struct ima_iint_cache *iint,
struct file *file)
{
}
static inline enum integrity_status
ima_get_cache_status(struct ima_iint_cache *iint, enum ima_hooks func)
{
return INTEGRITY_UNKNOWN;
}
static inline enum hash_algo
ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len)
{
return ima_hash_algo;
}
static inline int ima_read_xattr(struct dentry *dentry,
struct evm_ima_xattr_data **xattr_value,
int xattr_len)
{
return 0;
}
static inline void __init init_ima_appraise_lsm(const struct lsm_id *lsmid)
{
}
#endif /* CONFIG_IMA_APPRAISE */
#ifdef CONFIG_IMA_APPRAISE_MODSIG
int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
struct modsig **modsig);
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);
#else
static inline int ima_read_modsig(enum ima_hooks func, const void *buf,
loff_t buf_len, struct modsig **modsig)
{
return -EOPNOTSUPP;
}
static inline void ima_collect_modsig(struct modsig *modsig, const void *buf,
loff_t size)
{
}
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)
{
}
#endif /* CONFIG_IMA_APPRAISE_MODSIG */
/* LSM based policy rules require audit */
#ifdef CONFIG_IMA_LSM_RULES
#define ima_filter_rule_init security_audit_rule_init
#define ima_filter_rule_free security_audit_rule_free
#define ima_filter_rule_match security_audit_rule_match
#else
static inline int ima_filter_rule_init(u32 field, u32 op, char *rulestr,
void **lsmrule, gfp_t gfp)
{
return -EINVAL;
}
static inline void ima_filter_rule_free(void *lsmrule)
{
}
static inline int ima_filter_rule_match(struct lsm_prop *prop, u32 field, u32 op,
void *lsmrule)
{
return -EINVAL;
}
#endif /* CONFIG_IMA_LSM_RULES */
#ifdef CONFIG_IMA_READ_POLICY
#define POLICY_FILE_FLAGS (S_IWUSR | S_IRUSR)
#else
#define POLICY_FILE_FLAGS S_IWUSR
#endif /* CONFIG_IMA_READ_POLICY */
#endif /* __LINUX_IMA_H */
@@ -0,0 +1,470 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2008 IBM Corporation
*
* Author: Mimi Zohar <zohar@us.ibm.com>
*
* File: ima_api.c
* Implements must_appraise_or_measure, collect_measurement,
* appraise_measurement, store_measurement and store_template.
*/
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/hex.h>
#include <linux/xattr.h>
#include <linux/evm.h>
#include <linux/fsverity.h>
#include "ima.h"
/*
* ima_free_template_entry - free an existing template entry
*/
void ima_free_template_entry(struct ima_template_entry *entry)
{
int i;
for (i = 0; i < entry->template_desc->num_fields; i++)
kfree(entry->template_data[i].data);
kfree(entry->digests);
kfree(entry);
}
/*
* ima_alloc_init_template - create and initialize a new template entry
*/
int ima_alloc_init_template(struct ima_event_data *event_data,
struct ima_template_entry **entry,
struct ima_template_desc *desc)
{
struct ima_template_desc *template_desc;
struct tpm_digest *digests;
int i, result = 0;
if (desc)
template_desc = desc;
else
template_desc = ima_template_desc_current();
*entry = kzalloc_flex(**entry, template_data, template_desc->num_fields,
GFP_NOFS);
if (!*entry)
return -ENOMEM;
digests = kzalloc_objs(*digests,
NR_BANKS(ima_tpm_chip) + ima_extra_slots,
GFP_NOFS);
if (!digests) {
kfree(*entry);
*entry = NULL;
return -ENOMEM;
}
(*entry)->digests = digests;
(*entry)->template_desc = template_desc;
for (i = 0; i < template_desc->num_fields; i++) {
const struct ima_template_field *field =
template_desc->fields[i];
u32 len;
result = field->field_init(event_data,
&((*entry)->template_data[i]));
if (result != 0)
goto out;
len = (*entry)->template_data[i].len;
(*entry)->template_data_len += sizeof(len);
(*entry)->template_data_len += len;
}
return 0;
out:
ima_free_template_entry(*entry);
*entry = NULL;
return result;
}
/*
* ima_store_template - store ima template measurements
*
* Calculate the hash of a template entry, add the template entry
* to an ordered list of measurement entries maintained inside the kernel,
* and also update the aggregate integrity value (maintained inside the
* configured TPM PCR) over the hashes of the current list of measurement
* entries.
*
* Applications retrieve the current kernel-held measurement list through
* the securityfs entries in /sys/kernel/security/ima. The signed aggregate
* TPM PCR (called quote) can be retrieved using a TPM user space library
* and is used to validate the measurement list.
*
* Returns 0 on success, error code otherwise
*/
int ima_store_template(struct ima_template_entry *entry,
int violation, struct inode *inode,
const unsigned char *filename, int pcr)
{
static const char op[] = "add_template_measure";
static const char audit_cause[] = "hashing_error";
char *template_name = entry->template_desc->name;
int result;
if (!violation) {
result = ima_calc_field_array_hash(&entry->template_data[0],
entry);
if (result < 0) {
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
template_name, op,
audit_cause, result, 0);
return result;
}
}
entry->pcr = pcr;
result = ima_add_template_entry(entry, violation, op, inode, filename);
return result;
}
/*
* ima_add_violation - add violation to measurement list.
*
* Violations are flagged in the measurement list with zero hash values.
* By extending the PCR with 0xFF's instead of with zeroes, the PCR
* value is invalidated.
*/
void ima_add_violation(struct file *file, const unsigned char *filename,
struct ima_iint_cache *iint, const char *op,
const char *cause)
{
struct ima_template_entry *entry;
struct inode *inode = file_inode(file);
struct ima_event_data event_data = { .iint = iint,
.file = file,
.filename = filename,
.violation = cause };
int violation = 1;
int result;
/* can overflow, only indicator */
atomic_long_inc(&ima_num_violations);
result = ima_alloc_init_template(&event_data, &entry, NULL);
if (result < 0) {
result = -ENOMEM;
goto err_out;
}
result = ima_store_template(entry, violation, inode,
filename, CONFIG_IMA_MEASURE_PCR_IDX);
if (result < 0)
ima_free_template_entry(entry);
err_out:
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
op, cause, result, 0);
}
/**
* ima_get_action - appraise & measure decision based on policy.
* @idmap: idmap of the mount the inode was found from
* @inode: pointer to the inode associated with the object being validated
* @cred: pointer to credentials structure to validate
* @prop: properties of the task being validated
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC,
* MAY_APPEND)
* @func: caller identifier
* @pcr: pointer filled in if matched measure policy sets pcr=
* @template_desc: pointer filled in if matched measure policy sets template=
* @func_data: func specific data, may be NULL
* @allowed_algos: allowlist of hash algorithms for the IMA xattr
*
* The policy is defined in terms of keypairs:
* subj=, obj=, type=, func=, mask=, fsmagic=
* subj,obj, and type: are LSM specific.
* func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK
* | KEXEC_CMDLINE | KEY_CHECK | CRITICAL_DATA | SETXATTR_CHECK
* | MMAP_CHECK_REQPROT
* mask: contains the permission mask
* fsmagic: hex value
*
* Returns IMA_MEASURE, IMA_APPRAISE mask.
*
*/
int ima_get_action(struct mnt_idmap *idmap, struct inode *inode,
const struct cred *cred, struct lsm_prop *prop, int mask,
enum ima_hooks func, int *pcr,
struct ima_template_desc **template_desc,
const char *func_data, unsigned int *allowed_algos)
{
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
flags &= ima_policy_flag;
return ima_match_policy(idmap, inode, cred, prop, func, mask,
flags, pcr, template_desc, func_data,
allowed_algos);
}
static bool ima_get_verity_digest(struct ima_iint_cache *iint,
struct inode *inode,
struct ima_max_digest_data *hash)
{
enum hash_algo alg;
int digest_len;
/*
* On failure, 'measure' policy rules will result in a file data
* hash containing 0's.
*/
digest_len = fsverity_get_digest(inode, hash->digest, NULL, &alg);
if (digest_len == 0)
return false;
/*
* 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 = alg;
hash->hdr.length = digest_len;
return true;
}
/*
* ima_collect_measurement - collect file measurement
*
* Calculate the file hash, if it doesn't already exist,
* storing the measurement and i_version in the iint.
*
* Must be called with iint->mutex held.
*
* Return 0 on success, error code otherwise
*/
int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
void *buf, loff_t size, enum hash_algo algo,
struct modsig *modsig)
{
const char *audit_cause = "failed";
struct inode *inode = file_inode(file);
struct inode *real_inode = d_real_inode(file_dentry(file));
struct ima_max_digest_data hash;
struct ima_digest_data *hash_hdr = container_of(&hash.hdr,
struct ima_digest_data, hdr);
struct name_snapshot filename;
struct kstat stat;
int result = 0;
int length;
void *tmpbuf;
u64 i_version = 0;
/*
* Always collect the modsig, because IMA might have already collected
* the file digest without collecting the modsig in a previous
* measurement rule.
*/
if (modsig)
ima_collect_modsig(modsig, buf, size);
if (iint->flags & IMA_COLLECTED)
goto out;
/*
* Detect file change based on STATX_CHANGE_COOKIE, when supported,
* and fallback to detecting file change based on i_version.
*
* On filesystems which did not support i_version, support was
* originally limited to an initial measurement/appraisal/audit,
* but was later modified to assume the file changed.
*/
result = vfs_getattr_nosec(&file->f_path, &stat, STATX_CHANGE_COOKIE,
AT_STATX_SYNC_AS_STAT);
if (!result && (stat.result_mask & STATX_CHANGE_COOKIE))
i_version = stat.change_cookie;
else if (IS_I_VERSION(real_inode))
i_version = inode_peek_iversion(real_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 (iint->flags & IMA_VERITY_REQUIRED) {
if (!ima_get_verity_digest(iint, inode, &hash)) {
audit_cause = "no-verity-digest";
result = -ENODATA;
}
} else if (buf) {
result = ima_calc_buffer_hash(buf, size, hash_hdr);
} else {
result = ima_calc_file_hash(file, hash_hdr);
}
if (result && result != -EBADF && result != -EINVAL)
goto out;
length = sizeof(hash.hdr) + hash.hdr.length;
tmpbuf = krealloc(iint->ima_hash, length, GFP_NOFS);
if (!tmpbuf) {
result = -ENOMEM;
goto out;
}
iint->ima_hash = tmpbuf;
memcpy(iint->ima_hash, &hash, length);
if (real_inode == inode)
iint->real_inode.version = i_version;
else
integrity_inode_attrs_store(&iint->real_inode, i_version,
real_inode);
/* Possibly temporary failure due to type of read (eg. O_DIRECT) */
if (!result)
iint->flags |= IMA_COLLECTED;
out:
if (result) {
if (file->f_flags & O_DIRECT)
audit_cause = "failed(directio)";
take_dentry_name_snapshot(&filename, file->f_path.dentry);
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
filename.name.name, "collect_data",
audit_cause, result, 0);
release_dentry_name_snapshot(&filename);
}
return result;
}
/*
* ima_store_measurement - store file measurement
*
* Create an "ima" template and then store the template by calling
* ima_store_template.
*
* We only get here if the inode has not already been measured,
* but the measurement could already exist:
* - multiple copies of the same file on either the same or
* different filesystems.
* - the inode was previously flushed as well as the iint info,
* containing the hashing info.
*
* Must be called with iint->mutex held.
*/
void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr,
struct ima_template_desc *template_desc)
{
static const char op[] = "add_template_measure";
static const char audit_cause[] = "ENOMEM";
int result = -ENOMEM;
struct inode *inode = file_inode(file);
struct ima_template_entry *entry;
struct ima_event_data event_data = { .iint = iint,
.file = file,
.filename = filename,
.xattr_value = xattr_value,
.xattr_len = xattr_len,
.modsig = modsig };
int violation = 0;
/*
* We still need to store the measurement in the case of MODSIG because
* we only have its contents to put in the list at the time of
* appraisal, but a file measurement from earlier might already exist in
* the measurement list.
*/
if (iint->measured_pcrs & (0x1 << pcr) && !modsig)
return;
result = ima_alloc_init_template(&event_data, &entry, template_desc);
if (result < 0) {
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
op, audit_cause, result, 0);
return;
}
result = ima_store_template(entry, violation, inode, filename, pcr);
if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) {
iint->flags |= IMA_MEASURED;
iint->measured_pcrs |= (0x1 << pcr);
}
if (result < 0)
ima_free_template_entry(entry);
}
void ima_audit_measurement(struct ima_iint_cache *iint,
const unsigned char *filename)
{
struct audit_buffer *ab;
char *hash;
const char *algo_name = hash_algo_name[iint->ima_hash->algo];
int i;
if (iint->flags & IMA_AUDITED)
return;
hash = kzalloc((iint->ima_hash->length * 2) + 1, GFP_KERNEL);
if (!hash)
return;
for (i = 0; i < iint->ima_hash->length; i++)
hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]);
hash[i * 2] = '\0';
ab = audit_log_start(audit_context(), GFP_KERNEL,
AUDIT_INTEGRITY_RULE);
if (!ab)
goto out;
audit_log_format(ab, "file=");
audit_log_untrustedstring(ab, filename);
audit_log_format(ab, " hash=\"%s:%s\"", algo_name, hash);
audit_log_task_info(ab);
audit_log_end(ab);
iint->flags |= IMA_AUDITED;
out:
kfree(hash);
return;
}
/*
* ima_d_path - return a pointer to the full pathname
*
* Attempt to return a pointer to the full pathname for use in the
* IMA measurement list, IMA audit records, and auditing logs.
*
* On failure, return a pointer to a copy of the filename, not dname.
* Returning a pointer to dname, could result in using the pointer
* after the memory has been freed.
*/
const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf)
{
struct name_snapshot filename;
char *pathname = NULL;
*pathbuf = __getname();
if (*pathbuf) {
pathname = d_absolute_path(path, *pathbuf, PATH_MAX);
if (IS_ERR(pathname)) {
__putname(*pathbuf);
*pathbuf = NULL;
pathname = NULL;
}
}
if (!pathname) {
take_dentry_name_snapshot(&filename, path->dentry);
strscpy(namebuf, filename.name.name, NAME_MAX);
release_dentry_name_snapshot(&filename);
pathname = namebuf;
}
return pathname;
}
@@ -0,0 +1,836 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 IBM Corporation
*
* Author:
* Mimi Zohar <zohar@us.ibm.com>
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/file.h>
#include <linux/binfmts.h>
#include <linux/fs.h>
#include <linux/xattr.h>
#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"
#ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
static char *ima_appraise_cmdline_default __initdata;
core_param(ima_appraise, ima_appraise_cmdline_default, charp, 0);
void __init ima_appraise_parse_cmdline(void)
{
const char *str = ima_appraise_cmdline_default;
bool sb_state = arch_get_secureboot();
int appraisal_state = ima_appraise;
if (!str)
return;
if (strncmp(str, "off", 3) == 0)
appraisal_state = 0;
else if (strncmp(str, "log", 3) == 0)
appraisal_state = IMA_APPRAISE_LOG;
else if (strncmp(str, "fix", 3) == 0)
appraisal_state = IMA_APPRAISE_FIX;
else if (strncmp(str, "enforce", 7) == 0)
appraisal_state = IMA_APPRAISE_ENFORCE;
else
pr_err("invalid \"%s\" appraise option", str);
/* If appraisal state was changed, but secure boot is enabled,
* keep its default */
if (sb_state) {
if (!(appraisal_state & IMA_APPRAISE_ENFORCE))
pr_info("Secure boot enabled: ignoring ima_appraise=%s option",
str);
} else {
ima_appraise = appraisal_state;
}
}
#endif
/*
* is_ima_appraise_enabled - return appraise status
*
* Only return enabled, if not in ima_appraise="fix" or "log" modes.
*/
bool is_ima_appraise_enabled(void)
{
return ima_appraise & IMA_APPRAISE_ENFORCE;
}
/*
* ima_must_appraise - set appraise flag
*
* Return 1 to appraise or hash
*/
int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode,
int mask, enum ima_hooks func)
{
struct lsm_prop prop;
if (!ima_appraise)
return 0;
security_current_getlsmprop_subj(&prop);
return ima_match_policy(idmap, inode, current_cred(), &prop,
func, mask, IMA_APPRAISE | IMA_HASH, NULL,
NULL, NULL, NULL);
}
static int ima_fix_xattr(struct dentry *dentry, struct ima_iint_cache *iint)
{
int rc, offset;
u8 algo = iint->ima_hash->algo;
if (IS_RDONLY(d_inode(dentry)))
return -EROFS;
if (IS_IMMUTABLE(d_inode(dentry)))
return -EPERM;
if (algo <= HASH_ALGO_SHA1) {
offset = 1;
iint->ima_hash->xattr.sha1.type = IMA_XATTR_DIGEST;
} else {
offset = 0;
iint->ima_hash->xattr.ng.type = IMA_XATTR_DIGEST_NG;
iint->ima_hash->xattr.ng.algo = algo;
}
rc = __vfs_setxattr_noperm(&nop_mnt_idmap, dentry, XATTR_NAME_IMA,
&iint->ima_hash->xattr.data[offset],
(sizeof(iint->ima_hash->xattr) - offset) +
iint->ima_hash->length, 0);
return rc;
}
/* Return specific func appraised cached result */
enum integrity_status ima_get_cache_status(struct ima_iint_cache *iint,
enum ima_hooks func)
{
switch (func) {
case MMAP_CHECK:
case MMAP_CHECK_REQPROT:
return iint->ima_mmap_status;
case BPRM_CHECK:
return iint->ima_bprm_status;
case CREDS_CHECK:
return iint->ima_creds_status;
case FILE_CHECK:
case POST_SETATTR:
return iint->ima_file_status;
case MODULE_CHECK ... MAX_CHECK - 1:
default:
return iint->ima_read_status;
}
}
static void ima_set_cache_status(struct ima_iint_cache *iint,
enum ima_hooks func,
enum integrity_status status)
{
switch (func) {
case MMAP_CHECK:
case MMAP_CHECK_REQPROT:
iint->ima_mmap_status = status;
break;
case BPRM_CHECK:
iint->ima_bprm_status = status;
break;
case CREDS_CHECK:
iint->ima_creds_status = status;
break;
case FILE_CHECK:
case POST_SETATTR:
iint->ima_file_status = status;
break;
case MODULE_CHECK ... MAX_CHECK - 1:
default:
iint->ima_read_status = status;
break;
}
}
static void ima_cache_flags(struct ima_iint_cache *iint, enum ima_hooks func)
{
switch (func) {
case MMAP_CHECK:
case MMAP_CHECK_REQPROT:
iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED);
break;
case BPRM_CHECK:
iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED);
break;
case CREDS_CHECK:
iint->flags |= (IMA_CREDS_APPRAISED | IMA_APPRAISED);
break;
case FILE_CHECK:
case POST_SETATTR:
iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED);
break;
case MODULE_CHECK ... MAX_CHECK - 1:
default:
iint->flags |= (IMA_READ_APPRAISED | IMA_APPRAISED);
break;
}
}
enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
int xattr_len)
{
struct signature_v2_hdr *sig;
enum hash_algo ret;
if (!xattr_value || xattr_len < 2)
/* return default hash algo */
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 && sig->version != 3) ||
xattr_len <= sizeof(*sig) ||
sig->hash_algo >= HASH_ALGO__LAST)
return ima_hash_algo;
return sig->hash_algo;
case IMA_XATTR_DIGEST_NG:
/* first byte contains algorithm id */
ret = xattr_value->data[0];
if (ret < HASH_ALGO__LAST)
return ret;
break;
case IMA_XATTR_DIGEST:
/* this is for backward compatibility */
if (xattr_len == 21) {
unsigned int zero = 0;
if (!memcmp(&xattr_value->data[16], &zero, 4))
return HASH_ALGO_MD5;
else
return HASH_ALGO_SHA1;
} else if (xattr_len == 17)
return HASH_ALGO_MD5;
break;
}
/* return default hash algo */
return ima_hash_algo;
}
int ima_read_xattr(struct dentry *dentry,
struct evm_ima_xattr_data **xattr_value, int xattr_len)
{
int ret;
ret = vfs_getxattr_alloc(&nop_mnt_idmap, dentry, XATTR_NAME_IMA,
(char **)xattr_value, xattr_len, GFP_NOFS);
if (ret == -EOPNOTSUPP)
ret = 0;
return ret;
}
/*
* xattr_verify - verify xattr digest or signature
*
* Verify whether the hash or signature matches the file contents.
*
* Return 0 on success, error code otherwise.
*/
static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
struct evm_ima_xattr_data *xattr_value, int xattr_len,
enum integrity_status *status, const char **cause)
{
struct signature_v2_hdr *sig;
int rc = -EINVAL, hash_start = 0;
int mask;
switch (xattr_value->type) {
case IMA_XATTR_DIGEST_NG:
/* first byte contains algorithm id */
hash_start = 1;
fallthrough;
case IMA_XATTR_DIGEST:
if (*status != INTEGRITY_PASS_IMMUTABLE) {
if (iint->flags & IMA_DIGSIG_REQUIRED) {
if (iint->flags & IMA_VERITY_REQUIRED)
*cause = "verity-signature-required";
else
*cause = "IMA-signature-required";
*status = INTEGRITY_FAIL;
break;
}
clear_bit(IMA_DIGSIG, &iint->atomic_flags);
} else {
set_bit(IMA_DIGSIG, &iint->atomic_flags);
}
/*
* Use addition, not subtraction: sizeof() forces unsigned
* math and a short xattr_len would wrap around, bypassing
* this bounds check.
*/
if (xattr_len >= (int)sizeof(xattr_value->type) + hash_start +
(int)iint->ima_hash->length)
/*
* xattr length may be longer. md5 hash in previous
* version occupied 20 bytes in xattr, instead of 16
*/
rc = memcmp(&xattr_value->data[hash_start],
iint->ima_hash->digest,
iint->ima_hash->length);
else
rc = -EINVAL;
if (rc) {
*cause = "invalid-hash";
*status = INTEGRITY_FAIL;
break;
}
*status = INTEGRITY_PASS;
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;
}
if ((iint->flags & IMA_SIGV3_REQUIRED) && sig->version != 3) {
*cause = "IMA-sigv3-required";
*status = INTEGRITY_FAIL;
break;
}
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
(const char *)xattr_value,
xattr_len,
iint->ima_hash->digest,
iint->ima_hash->length,
iint->ima_hash->algo);
if (rc == -EOPNOTSUPP) {
*status = INTEGRITY_UNKNOWN;
break;
}
if (IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING) && rc &&
func == KEXEC_KERNEL_CHECK)
rc = integrity_digsig_verify(INTEGRITY_KEYRING_PLATFORM,
(const char *)xattr_value,
xattr_len,
iint->ima_hash->digest,
iint->ima_hash->length,
iint->ima_hash->algo);
if (rc) {
*cause = "invalid-signature";
*status = INTEGRITY_FAIL;
} 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 = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
(const char *)xattr_value,
xattr_len,
iint->ima_hash->digest,
iint->ima_hash->length,
iint->ima_hash->algo);
if (rc == -EOPNOTSUPP) {
*status = INTEGRITY_UNKNOWN;
break;
} else if (rc) {
*cause = "invalid-verity-signature";
*status = INTEGRITY_FAIL;
} else {
*status = INTEGRITY_PASS;
}
break;
default:
*status = INTEGRITY_UNKNOWN;
*cause = "unknown-ima-data";
break;
}
return rc;
}
/*
* modsig_verify - verify modsig signature
*
* Verify whether the signature matches the file contents.
*
* Return 0 on success, error code otherwise.
*/
static int modsig_verify(enum ima_hooks func, const struct modsig *modsig,
enum integrity_status *status, const char **cause)
{
int rc;
rc = integrity_modsig_verify(INTEGRITY_KEYRING_IMA, modsig);
if (IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING) && rc &&
func == KEXEC_KERNEL_CHECK)
rc = integrity_modsig_verify(INTEGRITY_KEYRING_PLATFORM,
modsig);
if (rc) {
*cause = "invalid-signature";
*status = INTEGRITY_FAIL;
} else {
*status = INTEGRITY_PASS;
}
return rc;
}
/*
* ima_check_blacklist - determine if the binary is blacklisted.
*
* Add the hash of the blacklisted binary to the measurement list, based
* on policy.
*
* Returns -EPERM if the hash is blacklisted.
*/
int ima_check_blacklist(struct ima_iint_cache *iint,
const struct modsig *modsig, int pcr)
{
enum hash_algo hash_algo;
const u8 *digest = NULL;
u32 digestsize = 0;
int rc = 0;
if (!(iint->flags & IMA_CHECK_BLACKLIST))
return 0;
if (iint->flags & IMA_MODSIG_ALLOWED && modsig) {
ima_get_modsig_digest(modsig, &hash_algo, &digest, &digestsize);
rc = is_binary_blacklisted(digest, digestsize);
} else if (iint->flags & IMA_DIGSIG_REQUIRED && iint->ima_hash)
rc = is_binary_blacklisted(iint->ima_hash->digest, iint->ima_hash->length);
if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
process_buffer_measurement(&nop_mnt_idmap, NULL, digest, digestsize,
"blacklisted-hash", NONE,
pcr, NULL, false, NULL, 0);
return rc;
}
/*
* ima_appraise_measurement - appraise file measurement
*
* Call evm_verifyxattr() to verify the integrity of 'security.ima'.
* Assuming success, compare the xattr hash with the collected measurement.
*
* Return 0 on success, error code otherwise
*/
int ima_appraise_measurement(enum ima_hooks func, struct ima_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig,
bool bprm_is_check)
{
static const char op[] = "appraise_data";
int audit_msgno = AUDIT_INTEGRITY_DATA;
const char *cause = "unknown";
struct dentry *dentry = file_dentry(file);
struct inode *inode = d_backing_inode(dentry);
enum integrity_status status = INTEGRITY_UNKNOWN;
int rc = xattr_len;
bool try_modsig = iint->flags & IMA_MODSIG_ALLOWED && modsig;
/* If not appraising a modsig, we need an xattr. */
if (!(inode->i_opflags & IOP_XATTR) && !try_modsig)
return INTEGRITY_UNKNOWN;
/*
* Unlike any of the other LSM hooks where the kernel enforces file
* integrity, enforcing file integrity for the bprm_creds_for_exec()
* LSM hook with the AT_EXECVE_CHECK flag is left up to the discretion
* of the script interpreter(userspace). Differentiate kernel and
* userspace enforced integrity audit messages.
*/
if (bprm_is_check)
audit_msgno = AUDIT_INTEGRITY_USERSPACE;
/* If reading the xattr failed and there's no modsig, error out. */
if (rc <= 0 && !try_modsig) {
if (rc && rc != -ENODATA)
goto out;
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;
if ((iint->flags & IMA_NEW_FILE) &&
(!(iint->flags & IMA_DIGSIG_REQUIRED) ||
(inode->i_size == 0)))
status = INTEGRITY_PASS;
goto out;
}
status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value,
rc < 0 ? 0 : rc);
switch (status) {
case INTEGRITY_PASS:
case INTEGRITY_PASS_IMMUTABLE:
case INTEGRITY_UNKNOWN:
break;
case INTEGRITY_NOXATTRS: /* No EVM protected xattrs. */
/* It's fine not to have xattrs when using a modsig. */
if (try_modsig)
break;
fallthrough;
case INTEGRITY_NOLABEL: /* No security.evm xattr. */
cause = "missing-HMAC";
goto out;
case INTEGRITY_FAIL_IMMUTABLE:
set_bit(IMA_DIGSIG, &iint->atomic_flags);
cause = "invalid-fail-immutable";
goto out;
case INTEGRITY_FAIL: /* Invalid HMAC/signature. */
cause = "invalid-HMAC";
goto out;
default:
WARN_ONCE(true, "Unexpected integrity status %d\n", status);
}
if (xattr_value)
rc = xattr_verify(func, iint, xattr_value, xattr_len, &status,
&cause);
/*
* If we have a modsig and either no imasig or the imasig's key isn't
* known, then try verifying the modsig.
*/
if (try_modsig &&
(!xattr_value || xattr_value->type == IMA_XATTR_DIGEST_NG ||
rc == -ENOKEY))
rc = modsig_verify(func, modsig, &status, &cause);
out:
/*
* File signatures on some filesystems can not be properly verified.
* When such filesystems are mounted by an untrusted mounter or on a
* system not willing to accept such a risk, fail the file signature
* verification.
*/
if ((inode->i_sb->s_iflags & SB_I_IMA_UNVERIFIABLE_SIGNATURE) &&
((inode->i_sb->s_iflags & SB_I_UNTRUSTED_MOUNTER) ||
(iint->flags & IMA_FAIL_UNVERIFIABLE_SIGS))) {
status = INTEGRITY_FAIL;
cause = "unverifiable-signature";
integrity_audit_msg(audit_msgno, inode, filename,
op, cause, rc, 0);
} else if (status != INTEGRITY_PASS) {
/* Fix mode, but don't replace file signatures. */
if ((ima_appraise & IMA_APPRAISE_FIX) && !try_modsig &&
(!xattr_value ||
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
if (!ima_fix_xattr(dentry, iint))
status = INTEGRITY_PASS;
} else if (status == INTEGRITY_NOLABEL) {
if (!evm_fix_hmac(dentry, XATTR_NAME_IMA,
(const char *)xattr_value,
xattr_len))
status = INTEGRITY_PASS;
}
/*
* Permit new files with file/EVM portable signatures, but
* without data.
*/
if (inode->i_size == 0 && iint->flags & IMA_NEW_FILE &&
test_bit(IMA_DIGSIG, &iint->atomic_flags)) {
status = INTEGRITY_PASS;
}
integrity_audit_msg(audit_msgno, inode, filename,
op, cause, rc, 0);
} else {
ima_cache_flags(iint, func);
}
ima_set_cache_status(iint, func, status);
return status;
}
/*
* ima_update_xattr - update 'security.ima' hash value
*/
void ima_update_xattr(struct ima_iint_cache *iint, struct file *file)
{
struct dentry *dentry = file_dentry(file);
int rc = 0;
/* do not collect and update hash for digital signatures */
if (test_bit(IMA_DIGSIG, &iint->atomic_flags))
return;
if ((iint->ima_file_status != INTEGRITY_PASS) &&
!(iint->flags & IMA_HASH))
return;
rc = ima_collect_measurement(iint, file, NULL, 0, ima_hash_algo, NULL);
if (rc < 0)
return;
inode_lock(file_inode(file));
ima_fix_xattr(dentry, iint);
inode_unlock(file_inode(file));
}
/**
* ima_inode_post_setattr - reflect file metadata changes
* @idmap: idmap of the mount the inode was found from
* @dentry: pointer to the affected dentry
* @ia_valid: for the UID and GID status
*
* Changes to a dentry's metadata might result in needing to appraise.
*
* This function is called from notify_change(), which expects the caller
* to lock the inode's i_mutex.
*/
static void ima_inode_post_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, int ia_valid)
{
struct inode *inode = d_backing_inode(dentry);
struct ima_iint_cache *iint;
int action;
if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
|| !(inode->i_opflags & IOP_XATTR))
return;
action = ima_must_appraise(idmap, inode, MAY_ACCESS, POST_SETATTR);
iint = ima_iint_find(inode);
if (iint) {
set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
if (!action)
clear_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
}
}
/*
* ima_protect_xattr - protect 'security.ima'
*
* Ensure that not just anyone can modify or remove 'security.ima'.
*/
static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
{
if (strcmp(xattr_name, XATTR_NAME_IMA) == 0) {
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
return 1;
}
return 0;
}
/*
* ima_reset_appraise_flags - reset ima_iint_cache flags
*
* @digsig: whether to clear/set IMA_DIGSIG flag, tristate values
* 0: clear IMA_DIGSIG
* 1: set IMA_DIGSIG
* -1: don't change IMA_DIGSIG
*
*/
static void ima_reset_appraise_flags(struct inode *inode, int digsig)
{
struct ima_iint_cache *iint;
if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
return;
iint = ima_iint_find(inode);
if (!iint)
return;
iint->measured_pcrs = 0;
set_bit(IMA_CHANGE_XATTR, &iint->atomic_flags);
if (digsig == 1)
set_bit(IMA_DIGSIG, &iint->atomic_flags);
else if (digsig == 0)
clear_bit(IMA_DIGSIG, &iint->atomic_flags);
}
/**
* validate_hash_algo() - Block setxattr with unsupported hash algorithms
* @dentry: object of the setxattr()
* @xattr_value: userland supplied xattr value
* @xattr_value_len: length of xattr_value
*
* The xattr value is mapped to its hash algorithm, and this algorithm
* must be built in the kernel for the setxattr to be allowed.
*
* Emit an audit message when the algorithm is invalid.
*
* Return: 0 on success, else an error.
*/
static int validate_hash_algo(struct dentry *dentry,
const struct evm_ima_xattr_data *xattr_value,
size_t xattr_value_len)
{
char *path = NULL, *pathbuf = NULL;
enum hash_algo xattr_hash_algo;
const char *errmsg = "unavailable-hash-algorithm";
unsigned int allowed_hashes;
xattr_hash_algo = ima_get_hash_algo(xattr_value, xattr_value_len);
allowed_hashes = atomic_read(&ima_setxattr_allowed_hash_algorithms);
if (allowed_hashes) {
/* success if the algorithm is allowed in the ima policy */
if (allowed_hashes & (1U << xattr_hash_algo))
return 0;
/*
* We use a different audit message when the hash algorithm
* is denied by a policy rule, instead of not being built
* in the kernel image
*/
errmsg = "denied-hash-algorithm";
} else {
if (likely(xattr_hash_algo == ima_hash_algo))
return 0;
/* allow any xattr using an algorithm built in the kernel */
if (crypto_has_alg(hash_algo_name[xattr_hash_algo], 0, 0))
return 0;
}
pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
if (!pathbuf)
return -EACCES;
path = dentry_path(dentry, pathbuf, PATH_MAX);
integrity_audit_msg(AUDIT_INTEGRITY_DATA, d_inode(dentry), path,
"set_data", errmsg, -EACCES, 0);
kfree(pathbuf);
return -EACCES;
}
static int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
const char *xattr_name, const void *xattr_value,
size_t xattr_value_len, int flags)
{
const struct evm_ima_xattr_data *xvalue = xattr_value;
int digsig = 0;
int result;
int err;
result = ima_protect_xattr(dentry, xattr_name, xattr_value,
xattr_value_len);
if (result == 1) {
if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST))
return -EINVAL;
err = validate_hash_algo(dentry, xvalue, xattr_value_len);
if (err)
return err;
digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG);
} else if (!strcmp(xattr_name, XATTR_NAME_EVM) && xattr_value_len > 0) {
digsig = (xvalue->type == EVM_XATTR_PORTABLE_DIGSIG);
} else {
digsig = -1;
}
if (result == 1 || evm_revalidate_status(xattr_name)) {
ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
if (result == 1)
result = 0;
}
return result;
}
static int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
const char *acl_name, struct posix_acl *kacl)
{
if (evm_revalidate_status(acl_name))
ima_reset_appraise_flags(d_backing_inode(dentry), -1);
return 0;
}
static int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
const char *xattr_name)
{
int result, digsig = -1;
result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
if (result == 1 || evm_revalidate_status(xattr_name)) {
if (!strcmp(xattr_name, XATTR_NAME_IMA))
digsig = 0;
ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
if (result == 1)
result = 0;
}
return result;
}
static int ima_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
const char *acl_name)
{
return ima_inode_set_acl(idmap, dentry, acl_name, NULL);
}
static struct security_hook_list ima_appraise_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_post_setattr, ima_inode_post_setattr),
LSM_HOOK_INIT(inode_setxattr, ima_inode_setxattr),
LSM_HOOK_INIT(inode_set_acl, ima_inode_set_acl),
LSM_HOOK_INIT(inode_removexattr, ima_inode_removexattr),
LSM_HOOK_INIT(inode_remove_acl, ima_inode_remove_acl),
};
void __init init_ima_appraise_lsm(const struct lsm_id *lsmid)
{
security_add_hooks(ima_appraise_hooks, ARRAY_SIZE(ima_appraise_hooks),
lsmid);
}
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 Microsoft Corporation
*
* Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com)
*
* File: ima_asymmetric_keys.c
* Defines an IMA hook to measure asymmetric keys on key
* create or update.
*/
#include <keys/asymmetric-type.h>
#include <linux/user_namespace.h>
#include <linux/ima.h>
#include "ima.h"
/**
* ima_post_key_create_or_update - measure asymmetric keys
* @keyring: keyring to which the key is linked to
* @key: created or updated key
* @payload: The data used to instantiate or update the key.
* @payload_len: The length of @payload.
* @flags: key flags
* @create: flag indicating whether the key was created or updated
*
* Keys can only be measured, not appraised.
* The payload data used to instantiate or update the key is measured.
*/
void ima_post_key_create_or_update(struct key *keyring, struct key *key,
const void *payload, size_t payload_len,
unsigned long flags, bool create)
{
bool queued = false;
/* Only asymmetric keys are handled by this hook. */
if (key->type != &key_type_asymmetric)
return;
if (!payload || (payload_len == 0))
return;
if (ima_should_queue_key())
queued = ima_queue_key(keyring, payload, payload_len);
if (queued)
return;
/*
* keyring->description points to the name of the keyring
* (such as ".builtin_trusted_keys", ".ima", etc.) to
* which the given key is linked to.
*
* The name of the keyring is passed in the "eventname"
* parameter to process_buffer_measurement() and is set
* in the "eventname" field in ima_event_data for
* the key measurement IMA event.
*
* The name of the keyring is also passed in the "keyring"
* parameter to process_buffer_measurement() to check
* if the IMA policy is configured to measure a key linked
* to the given keyring.
*/
process_buffer_measurement(&nop_mnt_idmap, NULL, payload, payload_len,
keyring->description, KEY_CHECK, 0,
keyring->description, false, NULL, 0);
}
@@ -0,0 +1,519 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2005,2006,2007,2008 IBM Corporation
*
* Authors:
* Mimi Zohar <zohar@us.ibm.com>
* Kylene Hall <kjhall@us.ibm.com>
*
* File: ima_crypto.c
* Calculates md5/sha1 file hash, template hash, boot-aggreate hash
*/
#include <linux/kernel.h>
#include <linux/file.h>
#include <linux/crypto.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <crypto/hash.h>
#include "ima.h"
static struct crypto_shash *ima_shash_tfm;
int ima_sha1_idx __ro_after_init;
int ima_hash_algo_idx __ro_after_init;
/*
* Additional number of slots reserved, as needed, for SHA1
* and IMA default algo.
*/
int ima_extra_slots __ro_after_init;
struct ima_algo_desc *ima_algo_array __ro_after_init;
static int __init ima_init_ima_crypto(void)
{
long rc;
ima_shash_tfm = crypto_alloc_shash(hash_algo_name[ima_hash_algo], 0, 0);
if (IS_ERR(ima_shash_tfm)) {
rc = PTR_ERR(ima_shash_tfm);
pr_err("Can not allocate %s (reason: %ld)\n",
hash_algo_name[ima_hash_algo], rc);
return rc;
}
pr_info("Allocated hash algorithm: %s\n",
hash_algo_name[ima_hash_algo]);
return 0;
}
static struct crypto_shash *ima_alloc_tfm(enum hash_algo algo)
{
struct crypto_shash *tfm = ima_shash_tfm;
int rc, i;
if (algo < 0 || algo >= HASH_ALGO__LAST)
algo = ima_hash_algo;
if (algo == ima_hash_algo)
return tfm;
for (i = 0; i < NR_BANKS(ima_tpm_chip) + ima_extra_slots; i++)
if (ima_algo_array[i].tfm && ima_algo_array[i].algo == algo)
return ima_algo_array[i].tfm;
tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0);
if (IS_ERR(tfm)) {
rc = PTR_ERR(tfm);
pr_err("Can not allocate %s (reason: %d)\n",
hash_algo_name[algo], rc);
}
return tfm;
}
int __init ima_init_crypto(void)
{
unsigned int digest_size;
enum hash_algo algo;
long rc;
int i;
rc = ima_init_ima_crypto();
if (rc)
return rc;
ima_sha1_idx = -1;
ima_hash_algo_idx = -1;
for (i = 0; i < NR_BANKS(ima_tpm_chip); i++) {
algo = ima_tpm_chip->allocated_banks[i].crypto_id;
if (algo == HASH_ALGO_SHA1)
ima_sha1_idx = i;
if (algo == ima_hash_algo)
ima_hash_algo_idx = i;
}
if (ima_sha1_idx < 0) {
ima_sha1_idx = NR_BANKS(ima_tpm_chip) + ima_extra_slots++;
if (ima_hash_algo == HASH_ALGO_SHA1)
ima_hash_algo_idx = ima_sha1_idx;
}
if (ima_hash_algo_idx < 0)
ima_hash_algo_idx = NR_BANKS(ima_tpm_chip) + ima_extra_slots++;
ima_algo_array = kzalloc_objs(*ima_algo_array,
NR_BANKS(ima_tpm_chip) + ima_extra_slots);
if (!ima_algo_array) {
rc = -ENOMEM;
goto out;
}
for (i = 0; i < NR_BANKS(ima_tpm_chip); i++) {
algo = ima_tpm_chip->allocated_banks[i].crypto_id;
digest_size = ima_tpm_chip->allocated_banks[i].digest_size;
ima_algo_array[i].algo = algo;
ima_algo_array[i].digest_size = digest_size;
/* unknown TPM algorithm */
if (algo == HASH_ALGO__LAST)
continue;
if (algo == ima_hash_algo) {
ima_algo_array[i].tfm = ima_shash_tfm;
continue;
}
ima_algo_array[i].tfm = ima_alloc_tfm(algo);
if (IS_ERR(ima_algo_array[i].tfm)) {
if (algo == HASH_ALGO_SHA1) {
rc = PTR_ERR(ima_algo_array[i].tfm);
ima_algo_array[i].tfm = NULL;
goto out_array;
}
ima_algo_array[i].tfm = NULL;
}
}
if (ima_sha1_idx >= NR_BANKS(ima_tpm_chip)) {
if (ima_hash_algo == HASH_ALGO_SHA1) {
ima_algo_array[ima_sha1_idx].tfm = ima_shash_tfm;
} else {
ima_algo_array[ima_sha1_idx].tfm =
ima_alloc_tfm(HASH_ALGO_SHA1);
if (IS_ERR(ima_algo_array[ima_sha1_idx].tfm)) {
rc = PTR_ERR(ima_algo_array[ima_sha1_idx].tfm);
goto out_array;
}
}
ima_algo_array[ima_sha1_idx].algo = HASH_ALGO_SHA1;
ima_algo_array[ima_sha1_idx].digest_size = SHA1_DIGEST_SIZE;
}
if (ima_hash_algo_idx >= NR_BANKS(ima_tpm_chip) &&
ima_hash_algo_idx != ima_sha1_idx) {
digest_size = hash_digest_size[ima_hash_algo];
ima_algo_array[ima_hash_algo_idx].tfm = ima_shash_tfm;
ima_algo_array[ima_hash_algo_idx].algo = ima_hash_algo;
ima_algo_array[ima_hash_algo_idx].digest_size = digest_size;
}
return 0;
out_array:
for (i = 0; i < NR_BANKS(ima_tpm_chip) + ima_extra_slots; i++) {
if (!ima_algo_array[i].tfm ||
ima_algo_array[i].tfm == ima_shash_tfm)
continue;
crypto_free_shash(ima_algo_array[i].tfm);
}
kfree(ima_algo_array);
out:
crypto_free_shash(ima_shash_tfm);
return rc;
}
static void ima_free_tfm(struct crypto_shash *tfm)
{
int i;
if (tfm == ima_shash_tfm)
return;
for (i = 0; i < NR_BANKS(ima_tpm_chip) + ima_extra_slots; i++)
if (ima_algo_array[i].tfm == tfm)
return;
crypto_free_shash(tfm);
}
static int ima_calc_file_hash_tfm(struct file *file,
struct ima_digest_data *hash,
struct crypto_shash *tfm)
{
loff_t i_size, offset = 0;
char *rbuf;
int rc;
SHASH_DESC_ON_STACK(shash, tfm);
shash->tfm = tfm;
hash->length = crypto_shash_digestsize(tfm);
rc = crypto_shash_init(shash);
if (rc != 0)
return rc;
i_size = i_size_read(file_inode(file));
if (i_size == 0)
goto out;
rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!rbuf)
return -ENOMEM;
while (offset < i_size) {
int rbuf_len;
rbuf_len = integrity_kernel_read(file, offset, rbuf, PAGE_SIZE);
if (rbuf_len < 0) {
rc = rbuf_len;
break;
}
if (rbuf_len == 0) { /* unexpected EOF */
rc = -EINVAL;
break;
}
offset += rbuf_len;
rc = crypto_shash_update(shash, rbuf, rbuf_len);
if (rc)
break;
}
kfree(rbuf);
out:
if (!rc)
rc = crypto_shash_final(shash, hash->digest);
return rc;
}
/*
* ima_calc_file_hash - calculate file hash
*/
int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash)
{
int rc;
struct file *f = file;
bool new_file_instance = false;
struct crypto_shash *tfm;
/*
* For consistency, fail file's opened with the O_DIRECT flag on
* filesystems mounted with/without DAX option.
*/
if (file->f_flags & O_DIRECT) {
hash->length = hash_digest_size[ima_hash_algo];
hash->algo = ima_hash_algo;
return -EINVAL;
}
/* Open a new file instance in O_RDONLY if we cannot read */
if (!(file->f_mode & FMODE_READ)) {
int flags = file->f_flags & ~(O_WRONLY | O_APPEND |
O_TRUNC | O_CREAT | O_NOCTTY | O_EXCL);
flags |= O_RDONLY;
f = dentry_open(&file->f_path, flags, file->f_cred);
if (IS_ERR(f))
return PTR_ERR(f);
new_file_instance = true;
}
tfm = ima_alloc_tfm(hash->algo);
if (IS_ERR(tfm)) {
rc = PTR_ERR(tfm);
} else {
rc = ima_calc_file_hash_tfm(f, hash, tfm);
ima_free_tfm(tfm);
}
if (new_file_instance)
fput(f);
return rc;
}
/*
* Calculate the hash of template data
*/
static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data,
struct ima_template_entry *entry,
int tfm_idx)
{
SHASH_DESC_ON_STACK(shash, ima_algo_array[tfm_idx].tfm);
struct ima_template_desc *td = entry->template_desc;
int num_fields = entry->template_desc->num_fields;
int rc, i;
shash->tfm = ima_algo_array[tfm_idx].tfm;
rc = crypto_shash_init(shash);
if (rc != 0)
return rc;
for (i = 0; i < num_fields; i++) {
u8 buffer[IMA_EVENT_NAME_LEN_MAX + 1] = { 0 };
u8 *data_to_hash = field_data[i].data;
u32 datalen = field_data[i].len;
u32 datalen_to_hash = !ima_canonical_fmt ?
datalen : (__force u32)cpu_to_le32(datalen);
if (strcmp(td->name, IMA_TEMPLATE_IMA_NAME) != 0) {
rc = crypto_shash_update(shash,
(const u8 *) &datalen_to_hash,
sizeof(datalen_to_hash));
if (rc)
break;
} else if (strcmp(td->fields[i]->field_id, "n") == 0) {
memcpy(buffer, data_to_hash, datalen);
data_to_hash = buffer;
datalen = IMA_EVENT_NAME_LEN_MAX + 1;
}
rc = crypto_shash_update(shash, data_to_hash, datalen);
if (rc)
break;
}
if (!rc)
rc = crypto_shash_final(shash, entry->digests[tfm_idx].digest);
return rc;
}
int ima_calc_field_array_hash(struct ima_field_data *field_data,
struct ima_template_entry *entry)
{
u16 alg_id;
int rc, i;
rc = ima_calc_field_array_hash_tfm(field_data, entry, ima_sha1_idx);
if (rc)
return rc;
entry->digests[ima_sha1_idx].alg_id = TPM_ALG_SHA1;
for (i = 0; i < NR_BANKS(ima_tpm_chip) + ima_extra_slots; i++) {
if (i == ima_sha1_idx)
continue;
if (i < NR_BANKS(ima_tpm_chip)) {
alg_id = ima_tpm_chip->allocated_banks[i].alg_id;
entry->digests[i].alg_id = alg_id;
}
/* for unmapped TPM algorithms digest is still a padded SHA1 */
if (!ima_algo_array[i].tfm) {
memcpy(entry->digests[i].digest,
entry->digests[ima_sha1_idx].digest,
TPM_DIGEST_SIZE);
continue;
}
rc = ima_calc_field_array_hash_tfm(field_data, entry, i);
if (rc)
return rc;
}
return rc;
}
static int calc_buffer_shash_tfm(const void *buf, loff_t size,
struct ima_digest_data *hash,
struct crypto_shash *tfm)
{
SHASH_DESC_ON_STACK(shash, tfm);
unsigned int len;
int rc;
shash->tfm = tfm;
hash->length = crypto_shash_digestsize(tfm);
rc = crypto_shash_init(shash);
if (rc != 0)
return rc;
while (size) {
len = size < PAGE_SIZE ? size : PAGE_SIZE;
rc = crypto_shash_update(shash, buf, len);
if (rc)
break;
buf += len;
size -= len;
}
if (!rc)
rc = crypto_shash_final(shash, hash->digest);
return rc;
}
int ima_calc_buffer_hash(const void *buf, loff_t len,
struct ima_digest_data *hash)
{
struct crypto_shash *tfm;
int rc;
tfm = ima_alloc_tfm(hash->algo);
if (IS_ERR(tfm))
return PTR_ERR(tfm);
rc = calc_buffer_shash_tfm(buf, len, hash, tfm);
ima_free_tfm(tfm);
return rc;
}
static void ima_pcrread(u32 idx, struct tpm_digest *d)
{
if (!ima_tpm_chip)
return;
if (tpm_pcr_read(ima_tpm_chip, idx, d) != 0)
pr_err("Error Communicating to TPM chip\n");
}
/*
* The boot_aggregate is a cumulative hash over TPM registers 0 - 7. With
* TPM 1.2 the boot_aggregate was based on reading the SHA1 PCRs, but with
* TPM 2.0 hash agility, TPM chips could support multiple TPM PCR banks,
* allowing firmware to configure and enable different banks.
*
* Knowing which TPM bank is read to calculate the boot_aggregate digest
* needs to be conveyed to a verifier. For this reason, use the same
* hash algorithm for reading the TPM PCRs as for calculating the boot
* aggregate digest as stored in the measurement list.
*/
static int ima_calc_boot_aggregate_tfm(char *digest, u16 alg_id,
struct crypto_shash *tfm)
{
struct tpm_digest d = { .alg_id = alg_id, .digest = {0} };
int rc;
u32 i;
SHASH_DESC_ON_STACK(shash, tfm);
shash->tfm = tfm;
pr_devel("calculating the boot-aggregate based on TPM bank: %04x\n",
d.alg_id);
rc = crypto_shash_init(shash);
if (rc != 0)
return rc;
/* cumulative digest over TPM registers 0-7 */
for (i = TPM_PCR0; i < TPM_PCR8; i++) {
ima_pcrread(i, &d);
/* now accumulate with current aggregate */
rc = crypto_shash_update(shash, d.digest,
crypto_shash_digestsize(tfm));
if (rc != 0)
return rc;
}
/*
* Extend cumulative digest over TPM registers 8-9, which contain
* measurement for the kernel command line (reg. 8) and image (reg. 9)
* in a typical PCR allocation. Registers 8-9 are only included in
* non-SHA1 boot_aggregate digests to avoid ambiguity.
*/
if (alg_id != TPM_ALG_SHA1) {
for (i = TPM_PCR8; i < TPM_PCR10; i++) {
ima_pcrread(i, &d);
rc = crypto_shash_update(shash, d.digest,
crypto_shash_digestsize(tfm));
}
}
if (!rc)
rc = crypto_shash_final(shash, digest);
return rc;
}
int ima_calc_boot_aggregate(struct ima_digest_data *hash)
{
struct crypto_shash *tfm;
u16 crypto_id, alg_id;
int rc, i, bank_idx = -1;
for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++) {
crypto_id = ima_tpm_chip->allocated_banks[i].crypto_id;
if (crypto_id == hash->algo) {
bank_idx = i;
break;
}
if (crypto_id == HASH_ALGO_SHA256)
bank_idx = i;
if (bank_idx == -1 && crypto_id == HASH_ALGO_SHA1)
bank_idx = i;
}
if (bank_idx == -1) {
pr_err("No suitable TPM algorithm for boot aggregate\n");
return 0;
}
hash->algo = ima_tpm_chip->allocated_banks[bank_idx].crypto_id;
tfm = ima_alloc_tfm(hash->algo);
if (IS_ERR(tfm))
return PTR_ERR(tfm);
hash->length = crypto_shash_digestsize(tfm);
alg_id = ima_tpm_chip->allocated_banks[bank_idx].alg_id;
rc = ima_calc_boot_aggregate_tfm(hash->digest, alg_id, tfm);
ima_free_tfm(tfm);
return rc;
}
@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2018 IBM Corporation
*/
#include <linux/module.h>
#include <linux/ima.h>
#include <linux/secure_boot.h>
/* secureboot arch rules */
static const char * const sb_arch_rules[] = {
#if !IS_ENABLED(CONFIG_KEXEC_SIG)
"appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig",
#endif /* CONFIG_KEXEC_SIG */
"measure func=KEXEC_KERNEL_CHECK",
#if !IS_ENABLED(CONFIG_MODULE_SIG)
"appraise func=MODULE_CHECK appraise_type=imasig",
#endif
#if IS_ENABLED(CONFIG_INTEGRITY_MACHINE_KEYRING) && IS_ENABLED(CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY)
"appraise func=POLICY_CHECK appraise_type=imasig",
#endif
"measure func=MODULE_CHECK",
NULL
};
const char * const *arch_get_ima_policy(void)
{
if (IS_ENABLED(CONFIG_IMA_ARCH_POLICY) && arch_get_secureboot()) {
set_module_sig_enforced();
set_kexec_sig_enforced();
return sb_arch_rules;
}
return NULL;
}
+849
View File
@@ -0,0 +1,849 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2005,2006,2007,2008 IBM Corporation
*
* Authors:
* Kylene Hall <kjhall@us.ibm.com>
* Reiner Sailer <sailer@us.ibm.com>
* Mimi Zohar <zohar@us.ibm.com>
*
* File: ima_fs.c
* implemenents security file system for reporting
* current measurement list and IMA statistics
*/
#include <linux/fcntl.h>
#include <linux/kernel_read_file.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/seq_file.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/parser.h>
#include <linux/vmalloc.h>
#include "ima.h"
/*
* Requests:
* 'A\n': stage the entire measurements list
* 'D\n': delete all staged measurements
* '[1, ULONG_MAX]\n' delete N measurements records
*/
#define STAGED_REQ_LENGTH 21
static DEFINE_MUTEX(ima_write_mutex);
static DEFINE_MUTEX(ima_measure_mutex);
static long ima_measure_users;
static struct task_struct *measure_writer;
static long measure_writer_extra_writes;
bool ima_canonical_fmt;
static int __init default_canonical_fmt_setup(char *str)
{
#ifdef __BIG_ENDIAN
ima_canonical_fmt = true;
#endif
return 1;
}
__setup("ima_canonical_fmt", default_canonical_fmt_setup);
static int valid_policy = 1;
static ssize_t ima_show_counter(char __user *buf, size_t count, loff_t *ppos,
atomic_long_t *val)
{
char tmpbuf[32]; /* greater than largest 'long' string value */
ssize_t len;
len = scnprintf(tmpbuf, sizeof(tmpbuf), "%li\n", atomic_long_read(val));
return simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
}
static ssize_t ima_show_num_violations(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
return ima_show_counter(buf, count, ppos, &ima_num_violations);
}
static const struct file_operations ima_num_violations_ops = {
.read = ima_show_num_violations,
.llseek = generic_file_llseek,
};
static ssize_t ima_show_measurements_count(struct file *filp,
char __user *buf,
size_t count, loff_t *ppos)
{
return ima_show_counter(buf, count, ppos, &ima_num_records[BINARY]);
}
static const struct file_operations ima_measurements_count_ops = {
.read = ima_show_measurements_count,
.llseek = generic_file_llseek,
};
/* returns pointer to hlist_node */
static void *_ima_measurements_start(struct seq_file *m, loff_t *pos,
struct list_head *head)
{
loff_t l = *pos;
struct ima_queue_entry *qe;
/* we need a lock since pos could point beyond last element */
rcu_read_lock();
list_for_each_entry_rcu(qe, head, later) {
if (!l--) {
rcu_read_unlock();
return qe;
}
}
rcu_read_unlock();
return NULL;
}
static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
{
return _ima_measurements_start(m, pos, &ima_measurements);
}
static void *ima_measurements_staged_start(struct seq_file *m, loff_t *pos)
{
return _ima_measurements_start(m, pos, &ima_measurements_staged);
}
static void *_ima_measurements_next(struct seq_file *m, void *v, loff_t *pos,
struct list_head *head)
{
struct ima_queue_entry *qe = v;
/* lock protects when reading beyond last element
* against concurrent list-extension
*/
rcu_read_lock();
qe = list_entry_rcu(qe->later.next, struct ima_queue_entry, later);
rcu_read_unlock();
(*pos)++;
return (&qe->later == head) ? NULL : qe;
}
static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
{
return _ima_measurements_next(m, v, pos, &ima_measurements);
}
static void *ima_measurements_staged_next(struct seq_file *m, void *v,
loff_t *pos)
{
return _ima_measurements_next(m, v, pos, &ima_measurements_staged);
}
static void ima_measurements_stop(struct seq_file *m, void *v)
{
}
void ima_putc(struct seq_file *m, void *data, int datalen)
{
while (datalen--)
seq_putc(m, *(char *)data++);
}
/* print format:
* 32bit-le=pcr#
* char[n]=template digest
* 32bit-le=template name size
* char[n]=template name
* [eventdata length]
* eventdata[n]=template specific data
*/
int ima_measurements_show(struct seq_file *m, void *v)
{
/* the list never shrinks, so we don't need a lock here */
struct ima_queue_entry *qe = v;
struct ima_template_entry *e;
char *template_name;
u32 pcr, namelen, template_data_len; /* temporary fields */
bool is_ima_template = false;
int i, algo_idx;
algo_idx = ima_sha1_idx;
if (m->file != NULL)
algo_idx = (unsigned long)file_inode(m->file)->i_private;
/* get entry */
e = qe->entry;
if (e == NULL)
return -1;
template_name = (e->template_desc->name[0] != '\0') ?
e->template_desc->name : e->template_desc->fmt;
/*
* 1st: PCRIndex
* PCR used defaults to the same (config option) in
* little-endian format, unless set in policy
*/
pcr = !ima_canonical_fmt ? e->pcr : (__force u32)cpu_to_le32(e->pcr);
ima_putc(m, &pcr, sizeof(e->pcr));
/* 2nd: template digest */
ima_putc(m, e->digests[algo_idx].digest,
ima_algo_array[algo_idx].digest_size);
/* 3rd: template name size */
namelen = !ima_canonical_fmt ? strlen(template_name) :
(__force u32)cpu_to_le32(strlen(template_name));
ima_putc(m, &namelen, sizeof(namelen));
/* 4th: template name */
ima_putc(m, template_name, strlen(template_name));
/* 5th: template length (except for 'ima' template) */
if (strcmp(template_name, IMA_TEMPLATE_IMA_NAME) == 0)
is_ima_template = true;
if (!is_ima_template) {
template_data_len = !ima_canonical_fmt ? e->template_data_len :
(__force u32)cpu_to_le32(e->template_data_len);
ima_putc(m, &template_data_len, sizeof(e->template_data_len));
}
/* 6th: template specific data */
for (i = 0; i < e->template_desc->num_fields; i++) {
enum ima_show_type show = IMA_SHOW_BINARY;
const struct ima_template_field *field =
e->template_desc->fields[i];
if (is_ima_template && strcmp(field->field_id, "d") == 0)
show = IMA_SHOW_BINARY_NO_FIELD_LEN;
if (is_ima_template && strcmp(field->field_id, "n") == 0)
show = IMA_SHOW_BINARY_OLD_STRING_FMT;
field->field_show(m, show, &e->template_data[i]);
}
return 0;
}
static const struct seq_operations ima_measurments_seqops = {
.start = ima_measurements_start,
.next = ima_measurements_next,
.stop = ima_measurements_stop,
.show = ima_measurements_show
};
static const struct seq_operations ima_measurments_staged_seqops = {
.start = ima_measurements_staged_start,
.next = ima_measurements_staged_next,
.stop = ima_measurements_stop,
.show = ima_measurements_show
};
static int ima_measure_lock(bool write)
{
mutex_lock(&ima_measure_mutex);
/* Overflow check. */
if (!write && ima_measure_users == LONG_MAX) {
mutex_unlock(&ima_measure_mutex);
return -ENFILE;
}
/* Same writer can do additional writes or read/writes. */
if (write && current == measure_writer) {
measure_writer_extra_writes++;
mutex_unlock(&ima_measure_mutex);
return 0;
}
/*
* ima_measure_users: > 0 open readers
* ima_measure_users: == -1 open writer
*/
if ((write && ima_measure_users != 0) ||
(!write && ima_measure_users < 0)) {
mutex_unlock(&ima_measure_mutex);
return -EBUSY;
}
if (write) {
ima_measure_users--;
/* Pointer valid, no reuse while the file descriptor is open. */
measure_writer = current;
} else {
ima_measure_users++;
}
mutex_unlock(&ima_measure_mutex);
return 0;
}
static void ima_measure_unlock(bool write)
{
mutex_lock(&ima_measure_mutex);
/* Decrement additional writes or read/writes. */
if (write && current == measure_writer &&
measure_writer_extra_writes != 0) {
measure_writer_extra_writes--;
mutex_unlock(&ima_measure_mutex);
return;
}
if (write) {
ima_measure_users++;
measure_writer = NULL;
} else {
ima_measure_users--;
}
mutex_unlock(&ima_measure_mutex);
}
static int _ima_measurements_open(struct inode *inode, struct file *file,
const struct seq_operations *seq_ops)
{
bool write = (file->f_mode & FMODE_WRITE);
int ret;
if (write && !capable(CAP_SYS_ADMIN))
return -EPERM;
ret = ima_measure_lock(write);
if (ret < 0)
return ret;
ret = seq_open(file, seq_ops);
if (ret < 0)
ima_measure_unlock(write);
return ret;
}
static int ima_measurements_open(struct inode *inode, struct file *file)
{
return _ima_measurements_open(inode, file, &ima_measurments_seqops);
}
static int ima_measurements_release(struct inode *inode, struct file *file)
{
bool write = (file->f_mode & FMODE_WRITE);
int ret;
/* seq_release() always returns zero. */
ret = seq_release(inode, file);
ima_measure_unlock(write);
return ret;
}
static int ima_measurements_staged_open(struct inode *inode, struct file *file)
{
return _ima_measurements_open(inode, file,
&ima_measurments_staged_seqops);
}
static ssize_t _ima_measurements_write(struct file *file,
const char __user *buf, size_t datalen,
loff_t *ppos, bool staged_interface)
{
char req[STAGED_REQ_LENGTH];
unsigned long req_value;
int ret;
if (datalen < 2 || datalen > STAGED_REQ_LENGTH)
return -EINVAL;
if (copy_from_user(req, buf, datalen) != 0)
return -EFAULT;
if (req[datalen - 1] != '\n')
return -EINVAL;
req[datalen - 1] = '\0';
switch (req[0]) {
case 'A':
if (datalen != 2 || !staged_interface)
return -EINVAL;
ret = ima_queue_stage();
break;
case 'D':
if (datalen != 2 || !staged_interface)
return -EINVAL;
ret = ima_queue_staged_delete_all();
break;
default:
if (staged_interface)
return -EINVAL;
if (ima_flush_htable) {
pr_debug("Deleting staged N measurements not supported when flushing the hash table is requested\n");
return -EINVAL;
}
ret = kstrtoul(req, 10, &req_value);
if (ret < 0)
return ret;
if (req_value == 0) {
pr_debug("Must delete at least one entry\n");
return -EINVAL;
}
ret = ima_queue_delete_partial(req_value);
}
if (ret < 0)
return ret;
return datalen;
}
static ssize_t ima_measurements_write(struct file *file, const char __user *buf,
size_t datalen, loff_t *ppos)
{
return _ima_measurements_write(file, buf, datalen, ppos, false);
}
static ssize_t ima_measurements_staged_write(struct file *file,
const char __user *buf,
size_t datalen, loff_t *ppos)
{
return _ima_measurements_write(file, buf, datalen, ppos, true);
}
static const struct file_operations ima_measurements_ops = {
.open = ima_measurements_open,
.read = seq_read,
.write = ima_measurements_write,
.llseek = seq_lseek,
.release = ima_measurements_release,
};
static const struct file_operations ima_measurements_staged_ops = {
.open = ima_measurements_staged_open,
.read = seq_read,
.write = ima_measurements_staged_write,
.llseek = seq_lseek,
.release = ima_measurements_release,
};
void ima_print_digest(struct seq_file *m, u8 *digest, u32 size)
{
u32 i;
for (i = 0; i < size; i++)
seq_printf(m, "%02x", *(digest + i));
}
/* print in ascii */
static int ima_ascii_measurements_show(struct seq_file *m, void *v)
{
/* the list never shrinks, so we don't need a lock here */
struct ima_queue_entry *qe = v;
struct ima_template_entry *e;
char *template_name;
int i, algo_idx;
algo_idx = ima_sha1_idx;
if (m->file != NULL)
algo_idx = (unsigned long)file_inode(m->file)->i_private;
/* get entry */
e = qe->entry;
if (e == NULL)
return -1;
template_name = (e->template_desc->name[0] != '\0') ?
e->template_desc->name : e->template_desc->fmt;
/* 1st: PCR used (config option) */
seq_printf(m, "%2d ", e->pcr);
/* 2nd: template hash */
ima_print_digest(m, e->digests[algo_idx].digest,
ima_algo_array[algo_idx].digest_size);
/* 3th: template name */
seq_printf(m, " %s", template_name);
/* 4th: template specific data */
for (i = 0; i < e->template_desc->num_fields; i++) {
seq_puts(m, " ");
if (e->template_data[i].len == 0)
continue;
e->template_desc->fields[i]->field_show(m, IMA_SHOW_ASCII,
&e->template_data[i]);
}
seq_puts(m, "\n");
return 0;
}
static const struct seq_operations ima_ascii_measurements_seqops = {
.start = ima_measurements_start,
.next = ima_measurements_next,
.stop = ima_measurements_stop,
.show = ima_ascii_measurements_show
};
static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
{
return _ima_measurements_open(inode, file,
&ima_ascii_measurements_seqops);
}
static const struct file_operations ima_ascii_measurements_ops = {
.open = ima_ascii_measurements_open,
.read = seq_read,
.write = ima_measurements_write,
.llseek = seq_lseek,
.release = ima_measurements_release,
};
static const struct seq_operations ima_ascii_measurements_staged_seqops = {
.start = ima_measurements_staged_start,
.next = ima_measurements_staged_next,
.stop = ima_measurements_stop,
.show = ima_ascii_measurements_show
};
static int ima_ascii_measurements_staged_open(struct inode *inode,
struct file *file)
{
return _ima_measurements_open(inode, file,
&ima_ascii_measurements_staged_seqops);
}
static const struct file_operations ima_ascii_measurements_staged_ops = {
.open = ima_ascii_measurements_staged_open,
.read = seq_read,
.write = ima_measurements_staged_write,
.llseek = seq_lseek,
.release = ima_measurements_release,
};
static ssize_t ima_read_policy(char *path)
{
void *data = NULL;
char *datap;
size_t size;
int rc, pathlen = strlen(path);
char *p;
/* remove \n */
datap = path;
strsep(&datap, "\n");
rc = kernel_read_file_from_path(path, 0, &data, INT_MAX, NULL,
READING_POLICY);
if (rc < 0) {
pr_err("Unable to open file: %s (%d)", path, rc);
return rc;
}
size = rc;
rc = 0;
datap = data;
while (size > 0 && (p = strsep(&datap, "\n"))) {
pr_debug("rule: %s\n", p);
rc = ima_parse_add_rule(p);
if (rc < 0)
break;
size -= rc;
}
vfree(data);
if (rc < 0)
return rc;
else if (size)
return -EINVAL;
else
return pathlen;
}
static ssize_t ima_write_policy(struct file *file, const char __user *buf,
size_t datalen, loff_t *ppos)
{
char *data;
ssize_t result;
if (datalen >= PAGE_SIZE)
datalen = PAGE_SIZE - 1;
/* No partial writes. */
result = -EINVAL;
if (*ppos != 0)
goto out;
data = memdup_user_nul(buf, datalen);
if (IS_ERR(data)) {
result = PTR_ERR(data);
goto out;
}
result = mutex_lock_interruptible(&ima_write_mutex);
if (result < 0)
goto out_free;
if (data[0] == '/') {
result = ima_read_policy(data);
} else if (ima_appraise & IMA_APPRAISE_POLICY) {
pr_err("signed policy file (specified as an absolute pathname) required\n");
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
"policy_update", "signed policy required",
1, 0);
result = -EACCES;
} else {
result = ima_parse_add_rule(data);
}
mutex_unlock(&ima_write_mutex);
out_free:
kfree(data);
out:
if (result < 0)
valid_policy = 0;
return result;
}
static struct dentry *ima_dir;
static struct dentry *ima_symlink;
enum ima_fs_flags {
IMA_FS_BUSY,
};
static unsigned long ima_fs_flags;
#ifdef CONFIG_IMA_READ_POLICY
static const struct seq_operations ima_policy_seqops = {
.start = ima_policy_start,
.next = ima_policy_next,
.stop = ima_policy_stop,
.show = ima_policy_show,
};
#endif
static int __init create_securityfs_measurement_lists(bool staging)
{
const struct file_operations *ascii_ops = &ima_ascii_measurements_ops;
const struct file_operations *binary_ops = &ima_measurements_ops;
umode_t permissions = (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP);
const char *file_suffix = "";
int count = NR_BANKS(ima_tpm_chip);
if (staging) {
ascii_ops = &ima_ascii_measurements_staged_ops;
binary_ops = &ima_measurements_staged_ops;
file_suffix = "_staged";
}
if (ima_sha1_idx >= NR_BANKS(ima_tpm_chip))
count++;
for (int i = 0; i < count; i++) {
u16 algo = ima_algo_array[i].algo;
char file_name[NAME_MAX + 1];
struct dentry *dentry;
if (algo == HASH_ALGO__LAST)
snprintf(file_name, sizeof(file_name),
"ascii_runtime_measurements_tpm_alg_%x%s",
ima_tpm_chip->allocated_banks[i].alg_id,
file_suffix);
else
snprintf(file_name, sizeof(file_name),
"ascii_runtime_measurements_%s%s",
hash_algo_name[algo], file_suffix);
dentry = securityfs_create_file(file_name, permissions,
ima_dir, (void *)(uintptr_t)i,
ascii_ops);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
if (algo == HASH_ALGO__LAST)
snprintf(file_name, sizeof(file_name),
"binary_runtime_measurements_tpm_alg_%x%s",
ima_tpm_chip->allocated_banks[i].alg_id,
file_suffix);
else
snprintf(file_name, sizeof(file_name),
"binary_runtime_measurements_%s%s",
hash_algo_name[algo], file_suffix);
dentry = securityfs_create_file(file_name, permissions,
ima_dir, (void *)(uintptr_t)i,
binary_ops);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
}
return 0;
}
static int __init create_securityfs_staging_links(void)
{
struct dentry *dentry;
dentry = securityfs_create_symlink("binary_runtime_measurements_staged",
ima_dir, "binary_runtime_measurements_sha1_staged", NULL);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
dentry = securityfs_create_symlink("ascii_runtime_measurements_staged",
ima_dir, "ascii_runtime_measurements_sha1_staged", NULL);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
return 0;
}
/*
* ima_open_policy: sequentialize access to the policy file
*/
static int ima_open_policy(struct inode *inode, struct file *filp)
{
if (!(filp->f_flags & O_WRONLY)) {
#ifndef CONFIG_IMA_READ_POLICY
return -EACCES;
#else
if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
return -EACCES;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
return seq_open(filp, &ima_policy_seqops);
#endif
}
if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags))
return -EBUSY;
return 0;
}
/*
* ima_release_policy - start using the new measure policy rules.
*
* Initially, ima_measure points to the default policy rules, now
* point to the new policy rules, and remove the securityfs policy file,
* assuming a valid policy.
*/
static int ima_release_policy(struct inode *inode, struct file *file)
{
const char *cause = valid_policy ? "completed" : "failed";
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
return seq_release(inode, file);
if (valid_policy && ima_check_policy() < 0) {
cause = "failed";
valid_policy = 0;
}
pr_info("policy update %s\n", cause);
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
"policy_update", cause, !valid_policy, 0);
if (!valid_policy) {
ima_delete_rules();
valid_policy = 1;
clear_bit(IMA_FS_BUSY, &ima_fs_flags);
return 0;
}
ima_update_policy();
#if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
securityfs_remove(file->f_path.dentry);
#elif defined(CONFIG_IMA_WRITE_POLICY)
clear_bit(IMA_FS_BUSY, &ima_fs_flags);
#elif defined(CONFIG_IMA_READ_POLICY)
inode->i_mode &= ~S_IWUSR;
#endif
return 0;
}
static const struct file_operations ima_measure_policy_ops = {
.open = ima_open_policy,
.write = ima_write_policy,
.read = seq_read,
.release = ima_release_policy,
.llseek = generic_file_llseek,
};
int __init ima_fs_init(void)
{
struct dentry *dentry;
int ret;
ret = integrity_fs_init();
if (ret < 0)
return ret;
ima_dir = securityfs_create_dir("ima", integrity_dir);
if (IS_ERR(ima_dir)) {
ret = PTR_ERR(ima_dir);
goto out;
}
ima_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima",
NULL);
if (IS_ERR(ima_symlink)) {
ret = PTR_ERR(ima_symlink);
goto out;
}
ret = create_securityfs_measurement_lists(false);
if (ret == 0 && IS_ENABLED(CONFIG_IMA_STAGING)) {
ret = create_securityfs_measurement_lists(true);
if (ret == 0)
ret = create_securityfs_staging_links();
}
if (ret != 0)
goto out;
dentry = securityfs_create_symlink("binary_runtime_measurements", ima_dir,
"binary_runtime_measurements_sha1", NULL);
if (IS_ERR(dentry)) {
ret = PTR_ERR(dentry);
goto out;
}
dentry = securityfs_create_symlink("ascii_runtime_measurements", ima_dir,
"ascii_runtime_measurements_sha1", NULL);
if (IS_ERR(dentry)) {
ret = PTR_ERR(dentry);
goto out;
}
dentry = securityfs_create_file("runtime_measurements_count",
S_IRUSR | S_IRGRP, ima_dir, NULL,
&ima_measurements_count_ops);
if (IS_ERR(dentry)) {
ret = PTR_ERR(dentry);
goto out;
}
dentry = securityfs_create_file("violations", S_IRUSR | S_IRGRP,
ima_dir, NULL, &ima_num_violations_ops);
if (IS_ERR(dentry)) {
ret = PTR_ERR(dentry);
goto out;
}
dentry = securityfs_create_file("policy", POLICY_FILE_FLAGS,
ima_dir, NULL,
&ima_measure_policy_ops);
if (IS_ERR(dentry)) {
ret = PTR_ERR(dentry);
goto out;
}
return 0;
out:
securityfs_remove(ima_symlink);
securityfs_remove(ima_dir);
integrity_fs_fini();
return ret;
}
@@ -0,0 +1,138 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2008 IBM Corporation
*
* Authors:
* Mimi Zohar <zohar@us.ibm.com>
*
* File: ima_iint.c
* - implements the IMA hook: ima_inode_free
* - cache integrity information in the inode security blob
*/
#include <linux/slab.h>
#include "ima.h"
static struct kmem_cache *ima_iint_cache __ro_after_init;
/**
* ima_iint_find - Return the iint associated with an inode
* @inode: Pointer to the inode
*
* Return the IMA integrity information (iint) associated with an inode, if the
* inode was processed by IMA.
*
* Return: Found iint or NULL.
*/
struct ima_iint_cache *ima_iint_find(struct inode *inode)
{
if (!IS_IMA(inode))
return NULL;
return ima_inode_get_iint(inode);
}
#define IMA_MAX_NESTING (FILESYSTEM_MAX_STACK_DEPTH + 1)
/*
* It is not clear that IMA should be nested at all, but as long is it measures
* files both on overlayfs and on underlying fs, we need to annotate the iint
* mutex to avoid lockdep false positives related to IMA + overlayfs.
* See ovl_lockdep_annotate_inode_mutex_key() for more details.
*/
static inline void ima_iint_lockdep_annotate(struct ima_iint_cache *iint,
struct inode *inode)
{
#ifdef CONFIG_LOCKDEP
static struct lock_class_key ima_iint_mutex_key[IMA_MAX_NESTING];
int depth = inode->i_sb->s_stack_depth;
if (WARN_ON_ONCE(depth < 0 || depth >= IMA_MAX_NESTING))
depth = 0;
lockdep_set_class(&iint->mutex, &ima_iint_mutex_key[depth]);
#endif
}
static void ima_iint_init_always(struct ima_iint_cache *iint,
struct inode *inode)
{
iint->ima_hash = NULL;
iint->real_inode.version = 0;
iint->flags = 0UL;
iint->atomic_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->ima_creds_status = INTEGRITY_UNKNOWN;
iint->measured_pcrs = 0;
mutex_init(&iint->mutex);
ima_iint_lockdep_annotate(iint, inode);
}
static void ima_iint_free(struct ima_iint_cache *iint)
{
kfree(iint->ima_hash);
mutex_destroy(&iint->mutex);
kmem_cache_free(ima_iint_cache, iint);
}
/**
* ima_inode_get - Find or allocate an iint associated with an inode
* @inode: Pointer to the inode
*
* Find an iint associated with an inode, and allocate a new one if not found.
* Caller must lock i_mutex.
*
* Return: An iint on success, NULL on error.
*/
struct ima_iint_cache *ima_inode_get(struct inode *inode)
{
struct ima_iint_cache *iint;
iint = ima_iint_find(inode);
if (iint)
return iint;
iint = kmem_cache_alloc(ima_iint_cache, GFP_NOFS);
if (!iint)
return NULL;
ima_iint_init_always(iint, inode);
inode->i_flags |= S_IMA;
ima_inode_set_iint(inode, iint);
return iint;
}
/**
* ima_inode_free_rcu - Called to free an inode via a RCU callback
* @inode_security: The inode->i_security pointer
*
* Free the IMA data associated with an inode.
*/
void ima_inode_free_rcu(void *inode_security)
{
struct ima_iint_cache **iint_p = inode_security + ima_blob_sizes.lbs_inode;
/* *iint_p should be NULL if !IS_IMA(inode) */
if (*iint_p)
ima_iint_free(*iint_p);
}
static void ima_iint_init_once(void *foo)
{
struct ima_iint_cache *iint = (struct ima_iint_cache *)foo;
memset(iint, 0, sizeof(*iint));
}
void __init ima_iintcache_init(void)
{
ima_iint_cache =
kmem_cache_create("ima_iint_cache", sizeof(struct ima_iint_cache),
0, SLAB_PANIC, ima_iint_init_once);
}
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2005,2006,2007,2008 IBM Corporation
*
* Authors:
* Reiner Sailer <sailer@watson.ibm.com>
* Leendert van Doorn <leendert@watson.ibm.com>
* Mimi Zohar <zohar@us.ibm.com>
*
* File: ima_init.c
* initialization and cleanup functions
*/
#include <linux/init.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/ima.h>
#include <generated/utsrelease.h>
#include "ima.h"
/* name for boot aggregate entry */
const char boot_aggregate_name[] = "boot_aggregate";
struct tpm_chip *ima_tpm_chip;
/* Add the boot aggregate to the IMA measurement list and extend
* the PCR register.
*
* Calculate the boot aggregate, a hash over tpm registers 0-7,
* assuming a TPM chip exists, and zeroes if the TPM chip does not
* exist. Add the boot aggregate measurement to the measurement
* list and extend the PCR register.
*
* If a tpm chip does not exist, indicate the core root of trust is
* not hardware based by invalidating the aggregate PCR value.
* (The aggregate PCR value is invalidated by adding one value to
* the measurement list and extending the aggregate PCR value with
* a different value.) Violations add a zero entry to the measurement
* list and extend the aggregate PCR value with ff...ff's.
*/
static int __init ima_add_boot_aggregate(void)
{
static const char op[] = "add_boot_aggregate";
const char *audit_cause = "ENOMEM";
struct ima_template_entry *entry;
struct ima_iint_cache tmp_iint, *iint = &tmp_iint;
struct ima_event_data event_data = { .iint = iint,
.filename = boot_aggregate_name };
struct ima_max_digest_data hash;
struct ima_digest_data *hash_hdr = container_of(&hash.hdr,
struct ima_digest_data, hdr);
int result = -ENOMEM;
int violation = 0;
memset(iint, 0, sizeof(*iint));
memset(&hash, 0, sizeof(hash));
iint->ima_hash = hash_hdr;
iint->ima_hash->algo = ima_hash_algo;
iint->ima_hash->length = hash_digest_size[ima_hash_algo];
/*
* With TPM 2.0 hash agility, TPM chips could support multiple TPM
* PCR banks, allowing firmware to configure and enable different
* banks. The SHA1 bank is not necessarily enabled.
*
* Use the same hash algorithm for reading the TPM PCRs as for
* calculating the boot aggregate digest. Preference is given to
* the configured IMA default hash algorithm. Otherwise, use the
* TCG required banks - SHA256 for TPM 2.0, SHA1 for TPM 1.2.
* Ultimately select SHA1 also for TPM 2.0 if the SHA256 PCR bank
* is not found.
*/
if (ima_tpm_chip) {
result = ima_calc_boot_aggregate(hash_hdr);
if (result < 0) {
audit_cause = "hashing_error";
goto err_out;
}
}
result = ima_alloc_init_template(&event_data, &entry, NULL);
if (result < 0) {
audit_cause = "alloc_entry";
goto err_out;
}
result = ima_store_template(entry, violation, NULL,
boot_aggregate_name,
CONFIG_IMA_MEASURE_PCR_IDX);
if (result < 0) {
ima_free_template_entry(entry);
audit_cause = "store_entry";
goto err_out;
}
return 0;
err_out:
integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op,
audit_cause, result, 0);
return result;
}
#ifdef CONFIG_IMA_LOAD_X509
void __init ima_load_x509(void)
{
int unset_flags = ima_policy_flag & IMA_APPRAISE;
ima_policy_flag &= ~unset_flags;
integrity_load_x509(INTEGRITY_KEYRING_IMA, CONFIG_IMA_X509_PATH);
/* load also EVM key to avoid appraisal */
evm_load_x509();
ima_policy_flag |= unset_flags;
}
#endif
int __init ima_init(void)
{
int rc;
ima_tpm_chip = tpm_default_chip();
if (!ima_tpm_chip)
pr_info("No TPM chip found, activating TPM-bypass!\n");
rc = integrity_init_keyring(INTEGRITY_KEYRING_IMA);
if (rc)
return rc;
rc = ima_init_crypto();
if (rc)
return rc;
rc = ima_init_template();
if (rc != 0)
return rc;
/* It can be called before ima_init_digests(), it does not use TPM. */
ima_load_kexec_buffer();
rc = ima_init_digests();
if (rc != 0)
return rc;
rc = ima_init_htable();
if (rc != 0)
return rc;
rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */
if (rc != 0)
return rc;
ima_init_policy();
rc = ima_fs_init();
if (rc != 0)
return rc;
ima_init_key_queue();
ima_init_reboot_notifier();
ima_measure_critical_data("kernel_info", "kernel_version",
UTS_RELEASE, strlen(UTS_RELEASE), false,
NULL, 0);
return rc;
}
@@ -0,0 +1,351 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2016 IBM Corporation
*
* Authors:
* Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
* Mimi Zohar <zohar@linux.vnet.ibm.com>
*/
#include <linux/seq_file.h>
#include <linux/vmalloc.h>
#include <linux/kexec.h>
#include <linux/of.h>
#include <linux/ima.h>
#include <linux/mm.h>
#include <linux/overflow.h>
#include <linux/reboot.h>
#include <asm/page.h>
#include "ima.h"
#ifdef CONFIG_IMA_KEXEC
#define IMA_KEXEC_EVENT_LEN 256
static bool ima_kexec_update_registered;
static struct seq_file ima_kexec_file;
static size_t kexec_segment_size;
static void *ima_kexec_buffer;
static void ima_free_kexec_file_buf(struct seq_file *sf)
{
vfree(sf->buf);
sf->buf = NULL;
sf->size = 0;
sf->read_pos = 0;
sf->count = 0;
}
void ima_measure_kexec_event(const char *event_name)
{
char ima_kexec_event[IMA_KEXEC_EVENT_LEN];
size_t buf_size = 0;
long len;
int n;
buf_size = ima_get_binary_runtime_size(BINARY_FULL);
len = atomic_long_read(&ima_num_records[BINARY_FULL]);
n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN,
"kexec_segment_size=%lu;ima_binary_runtime_size=%lu;"
"ima_runtime_measurements_count=%ld;",
kexec_segment_size, buf_size, len);
ima_measure_critical_data("ima_kexec", event_name, ima_kexec_event, n, false, NULL, 0);
}
static int ima_alloc_kexec_file_buf(size_t segment_size)
{
/*
* kexec 'load' may be called multiple times.
* Free and realloc the buffer only if the segment_size is
* changed from the previous kexec 'load' call.
*/
if (ima_kexec_file.buf && ima_kexec_file.size == segment_size)
goto out;
ima_free_kexec_file_buf(&ima_kexec_file);
/* segment size can't change between kexec load and execute */
ima_kexec_file.buf = vmalloc(segment_size);
if (!ima_kexec_file.buf)
return -ENOMEM;
ima_kexec_file.size = segment_size;
out:
ima_kexec_file.read_pos = 0;
ima_kexec_file.count = sizeof(struct ima_kexec_hdr); /* reserved space */
ima_measure_kexec_event("kexec_load");
return 0;
}
static int ima_dump_measurement(struct ima_kexec_hdr *khdr,
struct ima_queue_entry *qe)
{
if (ima_kexec_file.count >= ima_kexec_file.size)
return -EINVAL;
khdr->count++;
ima_measurements_show(&ima_kexec_file, qe);
return 0;
}
static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
unsigned long segment_size)
{
struct ima_queue_entry *qe;
struct ima_kexec_hdr khdr;
int ret = 0;
/* segment size can't change between kexec load and execute */
if (!ima_kexec_file.buf) {
pr_err("Kexec file buf not allocated\n");
return -EINVAL;
}
memset(&khdr, 0, sizeof(khdr));
khdr.version = 1;
/*
* Lockless walks possible due to strict ordering of the reboot
* notifiers, suspending measurement before dump, and forbidding
* staging/deleting (list mutations) after suspend.
*/
list_for_each_entry(qe, &ima_measurements_staged, later) {
ret = ima_dump_measurement(&khdr, qe);
if (ret < 0)
break;
}
list_for_each_entry(qe, &ima_measurements, later) {
if (!ret)
ret = ima_dump_measurement(&khdr, qe);
if (ret < 0)
break;
}
/*
* fill in reserved space with some buffer details
* (eg. version, buffer size, number of measurements)
*/
khdr.buffer_size = ima_kexec_file.count;
if (ima_canonical_fmt) {
khdr.version = cpu_to_le16(khdr.version);
khdr.count = cpu_to_le64(khdr.count);
khdr.buffer_size = cpu_to_le64(khdr.buffer_size);
}
memcpy(ima_kexec_file.buf, &khdr, sizeof(khdr));
print_hex_dump_debug("ima dump: ", DUMP_PREFIX_NONE, 16, 1,
ima_kexec_file.buf, ima_kexec_file.count < 100 ?
ima_kexec_file.count : 100,
true);
*buffer_size = ima_kexec_file.count;
*buffer = ima_kexec_file.buf;
return ret;
}
/*
* Called during kexec_file_load so that IMA can add a segment to the kexec
* image for the measurement list for the next kernel.
*
* This function assumes that kexec_lock is held.
*/
void ima_add_kexec_buffer(struct kimage *image)
{
struct kexec_buf kbuf = { .image = image, .buf_align = PAGE_SIZE,
.buf_min = 0, .buf_max = ULONG_MAX,
.top_down = true };
unsigned long binary_runtime_size;
unsigned long extra_memory;
/* use more understandable variable names than defined in kbuf */
size_t kexec_buffer_size = 0;
void *kexec_buffer = NULL;
int ret;
if (image->type == KEXEC_TYPE_CRASH)
return;
/*
* Reserve extra memory for measurements added during kexec.
*/
if (CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB <= 0)
extra_memory = PAGE_SIZE / 2;
else
extra_memory = CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB * 1024;
binary_runtime_size = ima_get_binary_runtime_size(BINARY) +
ima_get_binary_runtime_size(BINARY_STAGED) +
extra_memory;
if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE)
kexec_segment_size = ULONG_MAX;
else
kexec_segment_size = ALIGN(binary_runtime_size, PAGE_SIZE);
if ((kexec_segment_size == ULONG_MAX) ||
((kexec_segment_size >> PAGE_SHIFT) > totalram_pages() / 2)) {
pr_err("Binary measurement list too large.\n");
return;
}
ret = ima_alloc_kexec_file_buf(kexec_segment_size);
if (ret < 0) {
pr_err("Not enough memory for the kexec measurement buffer.\n");
return;
}
kbuf.buffer = kexec_buffer;
kbuf.bufsz = kexec_buffer_size;
kbuf.memsz = kexec_segment_size;
image->is_ima_segment_index_set = false;
ret = kexec_add_buffer(&kbuf);
if (ret) {
pr_err("Error passing over kexec measurement buffer.\n");
vfree(kexec_buffer);
return;
}
image->ima_buffer_addr = kbuf.mem;
image->ima_buffer_size = kexec_segment_size;
image->ima_buffer = kexec_buffer;
image->ima_segment_index = image->nr_segments - 1;
image->is_ima_segment_index_set = true;
kexec_dprintk("kexec measurement buffer for the loaded kernel at 0x%lx.\n",
kbuf.mem);
}
/*
* Called during kexec execute so that IMA can update the measurement list.
*/
static int ima_update_kexec_buffer(struct notifier_block *self,
unsigned long action, void *data)
{
size_t buf_size = 0;
int ret = NOTIFY_OK;
void *buf = NULL;
if (!kexec_in_progress) {
pr_info("No kexec in progress.\n");
return ret;
}
if (!ima_kexec_buffer) {
pr_err("Kexec buffer not set.\n");
return ret;
}
ret = ima_dump_measurement_list(&buf_size, &buf, kexec_segment_size);
if (ret)
pr_err("Dump measurements failed. Error:%d\n", ret);
if (buf_size != 0)
memcpy(ima_kexec_buffer, buf, buf_size);
kimage_unmap_segment(ima_kexec_buffer);
ima_kexec_buffer = NULL;
return ret;
}
static struct notifier_block update_buffer_nb = {
.notifier_call = ima_update_kexec_buffer,
.priority = INT_MIN
};
/*
* Create a mapping for the source pages that contain the IMA buffer
* so we can update it later.
*/
void ima_kexec_post_load(struct kimage *image)
{
if (ima_kexec_buffer) {
kimage_unmap_segment(ima_kexec_buffer);
ima_kexec_buffer = NULL;
}
if (!image->ima_buffer_addr)
return;
ima_kexec_buffer = kimage_map_segment(image, image->ima_segment_index);
if (!ima_kexec_buffer) {
pr_err("Could not map measurements buffer.\n");
return;
}
if (!ima_kexec_update_registered) {
register_reboot_notifier(&update_buffer_nb);
ima_kexec_update_registered = true;
}
}
#endif /* IMA_KEXEC */
/*
* Restore the measurement list from the previous kernel.
*/
void __init ima_load_kexec_buffer(void)
{
void *kexec_buffer = NULL;
size_t kexec_buffer_size = 0;
int rc;
rc = ima_get_kexec_buffer(&kexec_buffer, &kexec_buffer_size);
switch (rc) {
case 0:
rc = ima_restore_measurement_list(kexec_buffer_size,
kexec_buffer);
if (rc != 0)
pr_err("Failed to restore the measurement list: %d\n",
rc);
ima_free_kexec_buffer();
break;
case -ENOTSUPP:
pr_debug("Restoring the measurement list not supported\n");
break;
case -ENOENT:
pr_debug("No measurement list to restore\n");
break;
default:
pr_debug("Error restoring the measurement list: %d\n", rc);
}
}
/*
* ima_validate_range - verify a physical buffer lies in addressable RAM
* @phys: physical start address of the buffer from previous kernel
* @size: size of the buffer
*
* On success return 0. On failure returns -EINVAL so callers can skip
* restoring.
*/
int ima_validate_range(phys_addr_t phys, size_t size)
{
unsigned long start_pfn, end_pfn;
phys_addr_t end_phys;
if (check_add_overflow(phys, (phys_addr_t)size - 1, &end_phys))
return -EINVAL;
start_pfn = PHYS_PFN(phys);
end_pfn = PHYS_PFN(end_phys);
#ifdef CONFIG_X86
if (!pfn_range_is_mapped(start_pfn, end_pfn))
#else
if (!page_is_ram(start_pfn) || !page_is_ram(end_pfn))
#endif
{
pr_warn("IMA: previous kernel measurement buffer %pa (size 0x%zx) lies outside available memory\n",
&phys, size);
return -EINVAL;
}
return 0;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,151 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* IMA support for appraising module-style appended signatures.
*
* Copyright (C) 2019 IBM Corporation
*
* Author:
* Thiago Jung Bauermann <bauerman@linux.ibm.com>
*/
#include <linux/types.h>
#include <linux/module_signature.h>
#include <keys/asymmetric-type.h>
#include <crypto/pkcs7.h>
#include "ima.h"
struct modsig {
struct pkcs7_message *pkcs7_msg;
enum hash_algo hash_algo;
/* This digest will go in the 'd-modsig' field of the IMA template. */
const u8 *digest;
u32 digest_size;
/*
* This is what will go to the measurement list if the template requires
* storing the signature.
*/
int raw_pkcs7_len;
u8 raw_pkcs7[] __counted_by(raw_pkcs7_len);
};
/*
* ima_read_modsig - Read modsig from buf.
*
* Return: 0 on success, error code otherwise.
*/
int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
struct modsig **modsig)
{
const size_t marker_len = strlen(MODULE_SIGNATURE_MARKER);
const struct module_signature *sig;
struct modsig *hdr;
size_t sig_len;
const void *p;
int rc;
if (buf_len <= marker_len + sizeof(*sig))
return -ENOENT;
p = buf + buf_len - marker_len;
if (memcmp(p, MODULE_SIGNATURE_MARKER, marker_len))
return -ENOENT;
buf_len -= marker_len;
sig = (const struct module_signature *)(p - sizeof(*sig));
rc = mod_check_sig(sig, buf_len, func_tokens[func]);
if (rc)
return rc;
sig_len = be32_to_cpu(sig->sig_len);
buf_len -= sig_len + sizeof(*sig);
/* Allocate sig_len additional bytes to hold the raw PKCS#7 data. */
hdr = kzalloc_flex(*hdr, raw_pkcs7, sig_len);
if (!hdr)
return -ENOMEM;
hdr->raw_pkcs7_len = sig_len;
hdr->pkcs7_msg = pkcs7_parse_message(buf + buf_len, sig_len);
if (IS_ERR(hdr->pkcs7_msg)) {
rc = PTR_ERR(hdr->pkcs7_msg);
kfree(hdr);
return rc;
}
memcpy(hdr->raw_pkcs7, buf + buf_len, sig_len);
/* We don't know the hash algorithm yet. */
hdr->hash_algo = HASH_ALGO__LAST;
*modsig = hdr;
return 0;
}
/**
* ima_collect_modsig - Calculate the file hash without the appended signature.
* @modsig: parsed module signature
* @buf: data to verify the signature on
* @size: data size
*
* Since the modsig is part of the file contents, the hash used in its signature
* isn't the same one ordinarily calculated by IMA. Therefore PKCS7 code
* calculates a separate one for signature verification.
*/
void ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size)
{
int rc;
/*
* Provide the file contents (minus the appended sig) so that the PKCS7
* code can calculate the file hash.
*/
size -= modsig->raw_pkcs7_len + strlen(MODULE_SIGNATURE_MARKER) +
sizeof(struct module_signature);
rc = pkcs7_supply_detached_data(modsig->pkcs7_msg, buf, size);
if (rc)
return;
/* Ask the PKCS7 code to calculate the file hash. */
rc = pkcs7_get_digest(modsig->pkcs7_msg, &modsig->digest,
&modsig->digest_size, &modsig->hash_algo);
}
int ima_modsig_verify(struct key *keyring, const struct modsig *modsig)
{
return verify_pkcs7_message_sig(NULL, 0, modsig->pkcs7_msg, keyring,
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)
{
if (!modsig)
return;
pkcs7_free_message(modsig->pkcs7_msg);
kfree(modsig);
}
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2015 Juniper Networks, Inc.
*
* Author:
* Petko Manolov <petko.manolov@konsulko.com>
*/
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <keys/system_keyring.h>
struct key *ima_blacklist_keyring;
/*
* Allocate the IMA blacklist keyring
*/
static __init int ima_mok_init(void)
{
struct key_restriction *restriction;
pr_notice("Allocating IMA blacklist keyring.\n");
restriction = kzalloc_obj(struct key_restriction);
if (!restriction)
panic("Can't allocate IMA blacklist restriction.");
restriction->check = restrict_link_by_builtin_trusted;
ima_blacklist_keyring = keyring_alloc(".ima_blacklist",
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ |
KEY_USR_WRITE | KEY_USR_SEARCH,
KEY_ALLOC_NOT_IN_QUOTA |
KEY_ALLOC_SET_KEEP,
restriction, NULL);
if (IS_ERR(ima_blacklist_keyring))
panic("Can't allocate IMA blacklist keyring.");
return 0;
}
device_initcall(ima_mok_init);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,577 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2005,2006,2007,2008 IBM Corporation
*
* Authors:
* Serge Hallyn <serue@us.ibm.com>
* Reiner Sailer <sailer@watson.ibm.com>
* Mimi Zohar <zohar@us.ibm.com>
*
* File: ima_queue.c
* Implements queues that store template measurements and
* maintains aggregate over the stored measurements
* in the pre-configured TPM PCR (if available).
* The measurement list is append-only. No entry is
* ever removed or changed during the boot-cycle.
*/
#include <linux/rculist.h>
#include <linux/reboot.h>
#include <linux/slab.h>
#include "ima.h"
#define AUDIT_CAUSE_LEN_MAX 32
bool ima_flush_htable;
static int __init ima_flush_htable_setup(char *str)
{
if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) {
pr_warn("Hash table not enabled, ignoring request to flush\n");
return 1;
}
ima_flush_htable = true;
return 1;
}
__setup("ima_flush_htable", ima_flush_htable_setup);
/* pre-allocated array of tpm_digest structures to extend a PCR */
static struct tpm_digest *digests;
LIST_HEAD(ima_measurements); /* list of all measurements */
LIST_HEAD(ima_measurements_staged); /* list of staged measurements */
#ifdef CONFIG_IMA_KEXEC
static unsigned long binary_runtime_size[BINARY__LAST];
#else
static unsigned long binary_runtime_size[BINARY__LAST] = {
[0 ... BINARY__LAST - 1] = ULONG_MAX
};
#endif
atomic_long_t ima_num_records[BINARY__LAST] = {
[0 ... BINARY__LAST - 1] = ATOMIC_LONG_INIT(0)
};
atomic_long_t ima_num_violations = ATOMIC_LONG_INIT(0);
/* key: inode (before secure-hashing a file) */
struct hlist_head __rcu *ima_htable;
/* mutex protects atomicity of extending and staging measurement list
* and extending the TPM PCR aggregate. Since tpm_extend can take
* long (and the tpm driver uses a mutex), we can't use the spinlock.
*/
static DEFINE_MUTEX(ima_extend_list_mutex);
/*
* Used internally by the kernel to suspend measurements.
* Protected by ima_extend_list_mutex.
*/
static bool ima_measurements_suspended;
/* Callers must call synchronize_rcu() and free the hash table. */
static struct hlist_head *ima_alloc_replace_htable(void)
{
struct hlist_head *old_htable, *new_htable;
/* Initializing to zeros is equivalent to call HLIST_HEAD_INIT. */
new_htable = kcalloc(IMA_MEASURE_HTABLE_SIZE, sizeof(struct hlist_head),
GFP_KERNEL);
if (!new_htable)
return ERR_PTR(-ENOMEM);
old_htable = rcu_replace_pointer(ima_htable, new_htable,
lockdep_is_held(&ima_extend_list_mutex));
return old_htable;
}
int __init ima_init_htable(void)
{
struct hlist_head *old_htable;
mutex_lock(&ima_extend_list_mutex);
old_htable = ima_alloc_replace_htable();
mutex_unlock(&ima_extend_list_mutex);
if (IS_ERR(old_htable))
return PTR_ERR(old_htable);
/* Synchronize_rcu() and kfree() not necessary, only for robustness. */
synchronize_rcu();
kfree(old_htable);
return 0;
}
/* lookup up the digest value in the hash table, and return the entry */
static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
int pcr)
{
struct ima_queue_entry *qe, *ret = NULL;
struct hlist_head *htable;
unsigned int key;
int rc;
key = ima_hash_key(digest_value);
rcu_read_lock();
htable = rcu_dereference(ima_htable);
hlist_for_each_entry_rcu(qe, &htable[key], hnext) {
rc = memcmp(qe->entry->digests[ima_hash_algo_idx].digest,
digest_value, hash_digest_size[ima_hash_algo]);
if ((rc == 0) && (qe->entry->pcr == pcr)) {
ret = qe;
break;
}
}
rcu_read_unlock();
return ret;
}
/*
* Calculate the memory required for serializing a single
* binary_runtime_measurement list entry, which contains a
* couple of variable length fields (e.g template name and data).
*/
static int get_binary_runtime_size(struct ima_template_entry *entry)
{
int size = 0;
size += sizeof(u32); /* pcr */
size += TPM_DIGEST_SIZE;
size += sizeof(int); /* template name size field */
size += strlen(entry->template_desc->name);
size += sizeof(entry->template_data_len);
size += entry->template_data_len;
return size;
}
static void ima_update_binary_runtime_size(struct ima_template_entry *entry,
enum binary_lists binary_list)
{
int size;
if (binary_runtime_size[binary_list] == ULONG_MAX)
return;
size = get_binary_runtime_size(entry);
binary_runtime_size[binary_list] =
(binary_runtime_size[binary_list] < ULONG_MAX - size) ?
binary_runtime_size[binary_list] + size : ULONG_MAX;
}
/* ima_add_template_entry helper function:
* - Add template entry to the measurement list and hash table, for
* all entries except those carried across kexec.
*
* (Called with ima_extend_list_mutex held.)
*/
static int ima_add_digest_entry(struct ima_template_entry *entry,
bool update_htable)
{
struct ima_queue_entry *qe;
struct hlist_head *htable;
unsigned int key;
qe = kmalloc_obj(*qe);
if (qe == NULL) {
pr_err("OUT OF MEMORY ERROR creating queue entry\n");
return -ENOMEM;
}
qe->entry = entry;
INIT_LIST_HEAD(&qe->later);
list_add_tail_rcu(&qe->later, &ima_measurements);
htable = rcu_dereference_protected(ima_htable,
lockdep_is_held(&ima_extend_list_mutex));
atomic_long_inc(&ima_num_records[BINARY]);
atomic_long_inc(&ima_num_records[BINARY_FULL]);
if (update_htable) {
key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest);
hlist_add_head_rcu(&qe->hnext, &htable[key]);
}
ima_update_binary_runtime_size(entry, BINARY);
ima_update_binary_runtime_size(entry, BINARY_FULL);
return 0;
}
/*
* Return the amount of memory required for serializing the
* entire binary_runtime_measurement list, including the ima_kexec_hdr
* structure.
*/
unsigned long ima_get_binary_runtime_size(enum binary_lists binary_list)
{
unsigned long val;
mutex_lock(&ima_extend_list_mutex);
val = binary_runtime_size[binary_list];
mutex_unlock(&ima_extend_list_mutex);
if (val >= (ULONG_MAX - sizeof(struct ima_kexec_hdr)))
return ULONG_MAX;
else
return val + sizeof(struct ima_kexec_hdr);
}
static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr)
{
int result = 0;
if (!ima_tpm_chip)
return result;
result = tpm_pcr_extend(ima_tpm_chip, pcr, digests_arg);
if (result != 0)
pr_err("Error Communicating to TPM chip, result: %d\n", result);
return result;
}
/*
* Add template entry to the measurement list and hash table, and
* extend the pcr.
*
* On systems which support carrying the IMA measurement list across
* kexec, maintain the total memory size required for serializing the
* binary_runtime_measurements.
*/
int ima_add_template_entry(struct ima_template_entry *entry, int violation,
const char *op, struct inode *inode,
const unsigned char *filename)
{
u8 *digest = entry->digests[ima_hash_algo_idx].digest;
struct tpm_digest *digests_arg = entry->digests;
const char *audit_cause = "hash_added";
char tpm_audit_cause[AUDIT_CAUSE_LEN_MAX];
int audit_info = 1;
int result = 0, tpmresult = 0;
mutex_lock(&ima_extend_list_mutex);
/*
* Avoid appending to the measurement log when the TPM subsystem has
* been shut down while preparing for system reboot.
*/
if (ima_measurements_suspended) {
audit_cause = "measurements_suspended";
audit_info = 0;
result = -ENODEV;
goto out;
}
if (!violation && !IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) {
if (ima_lookup_digest_entry(digest, entry->pcr)) {
audit_cause = "hash_exists";
result = -EEXIST;
goto out;
}
}
result = ima_add_digest_entry(entry,
!IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE));
if (result < 0) {
audit_cause = "ENOMEM";
audit_info = 0;
goto out;
}
if (violation) /* invalidate pcr */
digests_arg = digests;
tpmresult = ima_pcr_extend(digests_arg, entry->pcr);
if (tpmresult != 0) {
snprintf(tpm_audit_cause, AUDIT_CAUSE_LEN_MAX, "TPM_error(%d)",
tpmresult);
audit_cause = tpm_audit_cause;
audit_info = 0;
}
out:
mutex_unlock(&ima_extend_list_mutex);
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
op, audit_cause, result, audit_info);
return result;
}
/**
* ima_queue_stage - Stage all measurements
*
* If the staged measurements list is empty, the current measurements list is
* not empty, and measurement is not suspended, move the measurements from the
* current list to the staged one, and update the number of records and binary
* run-time size accordingly.
*
* Do not allow staging after measurement is suspended, so that dumping
* measurements can be done in a lockless way.
*
* Return: Zero on success, a negative value otherwise.
*/
int ima_queue_stage(void)
{
int ret = 0;
mutex_lock(&ima_extend_list_mutex);
if (!list_empty(&ima_measurements_staged)) {
ret = -EEXIST;
goto out_unlock;
}
if (list_empty(&ima_measurements)) {
ret = -ENOENT;
goto out_unlock;
}
if (ima_measurements_suspended) {
ret = -EACCES;
goto out_unlock;
}
list_replace(&ima_measurements, &ima_measurements_staged);
INIT_LIST_HEAD(&ima_measurements);
atomic_long_set(&ima_num_records[BINARY_STAGED],
atomic_long_read(&ima_num_records[BINARY]));
atomic_long_set(&ima_num_records[BINARY], 0);
if (IS_ENABLED(CONFIG_IMA_KEXEC)) {
binary_runtime_size[BINARY_STAGED] =
binary_runtime_size[BINARY];
binary_runtime_size[BINARY] = 0;
}
out_unlock:
mutex_unlock(&ima_extend_list_mutex);
return ret;
}
static void ima_queue_delete(struct list_head *head, bool flush_htable);
/**
* ima_queue_staged_delete_all - Delete staged measurements
*
* Move staged measurements to a temporary list, ima_measurements_trim, update
* the number of records and the binary run-time size accordingly. Finally,
* delete measurements in the temporary list.
*
* Refuse to delete staged measurements if measurement is suspended, so that
* dump can be done in a lockless way and user space is notified about staged
* measurements being carried over to the secondary kernel, so that it does not
* save them twice.
*
* Return: Zero on success, a negative value otherwise.
*/
int ima_queue_staged_delete_all(void)
{
struct hlist_head *old_queue = NULL;
LIST_HEAD(ima_measurements_trim);
mutex_lock(&ima_extend_list_mutex);
if (list_empty(&ima_measurements_staged)) {
mutex_unlock(&ima_extend_list_mutex);
return -ENOENT;
}
if (ima_measurements_suspended) {
mutex_unlock(&ima_extend_list_mutex);
return -ESTALE;
}
list_replace(&ima_measurements_staged, &ima_measurements_trim);
INIT_LIST_HEAD(&ima_measurements_staged);
atomic_long_set(&ima_num_records[BINARY_STAGED], 0);
if (IS_ENABLED(CONFIG_IMA_KEXEC))
binary_runtime_size[BINARY_STAGED] = 0;
if (ima_flush_htable) {
old_queue = ima_alloc_replace_htable();
if (IS_ERR(old_queue)) {
mutex_unlock(&ima_extend_list_mutex);
return PTR_ERR(old_queue);
}
}
mutex_unlock(&ima_extend_list_mutex);
if (ima_flush_htable) {
synchronize_rcu();
kfree(old_queue);
}
ima_queue_delete(&ima_measurements_trim, ima_flush_htable);
return 0;
}
/**
* ima_queue_delete_partial - Delete current measurements
* @req_value: Number of measurements to delete
*
* Delete the requested number of measurements from the current measurements
* list, and update the number of records and the binary run-time size
* accordingly.
*
* Refuse to delete current measurements if measurement is suspended, so that
* dump can be done in a lockless way and user space is notified about current
* measurements being carried over to the secondary kernel, so that it does not
* save them twice.
*
* Return: Zero on success, a negative value otherwise.
*/
int ima_queue_delete_partial(unsigned long req_value)
{
unsigned long req_value_copy = req_value;
unsigned long size_to_remove = 0, num_to_remove = 0;
LIST_HEAD(ima_measurements_trim);
struct ima_queue_entry *qe;
int ret = 0;
/*
* list_for_each_entry_rcu() without rcu_read_lock() is fine because
* only list append can happen concurrently. No list replace due to the
* staging/delete writers mutual exclusion.
*/
list_for_each_entry_rcu(qe, &ima_measurements, later, true) {
size_to_remove += get_binary_runtime_size(qe->entry);
num_to_remove++;
if (--req_value_copy == 0)
break;
}
/* Not enough records to delete. */
if (req_value_copy > 0)
return -ENOENT;
mutex_lock(&ima_extend_list_mutex);
if (ima_measurements_suspended) {
mutex_unlock(&ima_extend_list_mutex);
return -ESTALE;
}
/*
* qe remains valid because ima_fs.c enforces single-writer exclusion.
*/
__list_cut_position(&ima_measurements_trim, &ima_measurements,
&qe->later);
atomic_long_sub(num_to_remove, &ima_num_records[BINARY]);
if (IS_ENABLED(CONFIG_IMA_KEXEC))
binary_runtime_size[BINARY] -= size_to_remove;
mutex_unlock(&ima_extend_list_mutex);
ima_queue_delete(&ima_measurements_trim, false);
return ret;
}
/**
* ima_queue_delete - Delete measurements
* @head: List head measurements are deleted from
* @flush_htable: Whether or not the hash table is being flushed
*
* Delete the measurements from the passed list head completely if the
* hash table is not enabled or is being flushed, or partially (only the
* template data), if the hash table is used.
*/
static void ima_queue_delete(struct list_head *head, bool flush_htable)
{
struct ima_queue_entry *qe, *qe_tmp;
unsigned int i;
list_for_each_entry_safe(qe, qe_tmp, head, later) {
/*
* Safe to free template_data here without synchronize_rcu()
* because the only htable reader, ima_lookup_digest_entry(),
* accesses only entry->digests, not template_data. If new
* htable readers are added that access template_data, a
* synchronize_rcu() is required here.
*/
for (i = 0; i < qe->entry->template_desc->num_fields; i++) {
kfree(qe->entry->template_data[i].data);
qe->entry->template_data[i].data = NULL;
qe->entry->template_data[i].len = 0;
}
list_del(&qe->later);
/* No leak if condition is false, referenced by ima_htable. */
if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE) || flush_htable) {
kfree(qe->entry->digests);
kfree(qe->entry);
kfree(qe);
}
}
}
int ima_restore_measurement_entry(struct ima_template_entry *entry)
{
int result = 0;
mutex_lock(&ima_extend_list_mutex);
result = ima_add_digest_entry(entry, 0);
mutex_unlock(&ima_extend_list_mutex);
return result;
}
static void ima_measurements_suspend(void)
{
mutex_lock(&ima_extend_list_mutex);
ima_measurements_suspended = true;
mutex_unlock(&ima_extend_list_mutex);
}
static int ima_reboot_notifier(struct notifier_block *nb,
unsigned long action,
void *data)
{
#ifdef CONFIG_IMA_KEXEC
if (action == SYS_RESTART && data && !strcmp(data, "kexec reboot"))
ima_measure_kexec_event("kexec_execute");
#endif
ima_measurements_suspend();
return NOTIFY_DONE;
}
static struct notifier_block ima_reboot_nb = {
.notifier_call = ima_reboot_notifier,
};
void __init ima_init_reboot_notifier(void)
{
register_reboot_notifier(&ima_reboot_nb);
}
int __init ima_init_digests(void)
{
u16 digest_size;
u16 crypto_id;
int i;
if (!ima_tpm_chip)
return 0;
digests = kzalloc_objs(*digests, ima_tpm_chip->nr_allocated_banks,
GFP_NOFS);
if (!digests)
return -ENOMEM;
for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++) {
digests[i].alg_id = ima_tpm_chip->allocated_banks[i].alg_id;
digest_size = ima_tpm_chip->allocated_banks[i].digest_size;
crypto_id = ima_tpm_chip->allocated_banks[i].crypto_id;
/* for unmapped TPM algorithms digest is still a padded SHA1 */
if (crypto_id == HASH_ALGO__LAST)
digest_size = SHA1_DIGEST_SIZE;
memset(digests[i].digest, 0xff, digest_size);
}
return 0;
}
@@ -0,0 +1,177 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 Microsoft Corporation
*
* Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com)
*
* File: ima_queue_keys.c
* Enables deferred processing of keys
*/
#include <linux/user_namespace.h>
#include <linux/workqueue.h>
#include <keys/asymmetric-type.h>
#include "ima.h"
/*
* Flag to indicate whether a key can be processed
* right away or should be queued for processing later.
*/
static bool ima_process_keys;
/*
* To synchronize access to the list of keys that need to be measured
*/
static DEFINE_MUTEX(ima_keys_lock);
static LIST_HEAD(ima_keys);
/*
* If custom IMA policy is not loaded then keys queued up
* for measurement should be freed. This worker is used
* for handling this scenario.
*/
static long ima_key_queue_timeout = 300000; /* 5 Minutes */
static void ima_keys_handler(struct work_struct *work);
static DECLARE_DELAYED_WORK(ima_keys_delayed_work, ima_keys_handler);
static bool timer_expired;
/*
* This worker function frees keys that may still be
* queued up in case custom IMA policy was not loaded.
*/
static void ima_keys_handler(struct work_struct *work)
{
timer_expired = true;
ima_process_queued_keys();
}
/*
* This function sets up a worker to free queued keys in case
* custom IMA policy was never loaded.
*/
void ima_init_key_queue(void)
{
schedule_delayed_work(&ima_keys_delayed_work,
msecs_to_jiffies(ima_key_queue_timeout));
}
static void ima_free_key_entry(struct ima_key_entry *entry)
{
if (entry) {
kfree(entry->payload);
kfree(entry->keyring_name);
kfree(entry);
}
}
static struct ima_key_entry *ima_alloc_key_entry(struct key *keyring,
const void *payload,
size_t payload_len)
{
int rc = 0;
const char *audit_cause = "ENOMEM";
struct ima_key_entry *entry;
entry = kzalloc_obj(*entry);
if (entry) {
entry->payload = kmemdup(payload, payload_len, GFP_KERNEL);
entry->keyring_name = kstrdup(keyring->description,
GFP_KERNEL);
entry->payload_len = payload_len;
}
if ((entry == NULL) || (entry->payload == NULL) ||
(entry->keyring_name == NULL)) {
rc = -ENOMEM;
goto out;
}
INIT_LIST_HEAD(&entry->list);
out:
if (rc) {
integrity_audit_message(AUDIT_INTEGRITY_PCR, NULL,
keyring->description,
func_measure_str(KEY_CHECK),
audit_cause, rc, 0, rc);
ima_free_key_entry(entry);
entry = NULL;
}
return entry;
}
bool ima_queue_key(struct key *keyring, const void *payload,
size_t payload_len)
{
bool queued = false;
struct ima_key_entry *entry;
entry = ima_alloc_key_entry(keyring, payload, payload_len);
if (!entry)
return false;
mutex_lock(&ima_keys_lock);
if (!ima_process_keys) {
list_add_tail(&entry->list, &ima_keys);
queued = true;
}
mutex_unlock(&ima_keys_lock);
if (!queued)
ima_free_key_entry(entry);
return queued;
}
/*
* ima_process_queued_keys() - process keys queued for measurement
*
* This function sets ima_process_keys to true and processes queued keys.
* From here on keys will be processed right away (not queued).
*/
void ima_process_queued_keys(void)
{
struct ima_key_entry *entry, *tmp;
bool process = false;
if (ima_process_keys)
return;
/*
* Since ima_process_keys is set to true, any new key will be
* processed immediately and not be queued to ima_keys list.
* First one setting the ima_process_keys flag to true will
* process the queued keys.
*/
mutex_lock(&ima_keys_lock);
if (!ima_process_keys) {
ima_process_keys = true;
process = true;
}
mutex_unlock(&ima_keys_lock);
if (!process)
return;
if (!timer_expired)
cancel_delayed_work_sync(&ima_keys_delayed_work);
list_for_each_entry_safe(entry, tmp, &ima_keys, list) {
if (!timer_expired)
process_buffer_measurement(&nop_mnt_idmap, NULL,
entry->payload,
entry->payload_len,
entry->keyring_name,
KEY_CHECK, 0,
entry->keyring_name,
false, NULL, 0);
list_del(&entry->list);
ima_free_key_entry(entry);
}
}
inline bool ima_should_queue_key(void)
{
return !ima_process_keys;
}
@@ -0,0 +1,537 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Politecnico di Torino, Italy
* TORSEC group -- https://security.polito.it
*
* Author: Roberto Sassu <roberto.sassu@polito.it>
*
* File: ima_template.c
* Helpers to manage template descriptors.
*/
#include <linux/rculist.h>
#include "ima.h"
#include "ima_template_lib.h"
enum header_fields { HDR_PCR, HDR_DIGEST, HDR_TEMPLATE_NAME,
HDR_TEMPLATE_DATA, HDR__LAST };
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",
.fmt = "d-ng|n-ng|evmsig|xattrnames|xattrlengths|xattrvalues|iuid|igid|imode"},
{.name = "", .fmt = ""}, /* placeholder for a custom format */
};
static LIST_HEAD(defined_templates);
static DEFINE_SPINLOCK(template_list);
static int template_setup_done;
static const struct ima_template_field supported_fields[] = {
{.field_id = "d", .field_init = ima_eventdigest_init,
.field_show = ima_show_template_digest},
{.field_id = "n", .field_init = ima_eventname_init,
.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,
.field_show = ima_show_template_sig},
{.field_id = "buf", .field_init = ima_eventbuf_init,
.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},
{.field_id = "evmsig", .field_init = ima_eventevmsig_init,
.field_show = ima_show_template_sig},
{.field_id = "iuid", .field_init = ima_eventinodeuid_init,
.field_show = ima_show_template_uint},
{.field_id = "igid", .field_init = ima_eventinodegid_init,
.field_show = ima_show_template_uint},
{.field_id = "imode", .field_init = ima_eventinodemode_init,
.field_show = ima_show_template_uint},
{.field_id = "xattrnames",
.field_init = ima_eventinodexattrnames_init,
.field_show = ima_show_template_string},
{.field_id = "xattrlengths",
.field_init = ima_eventinodexattrlengths_init,
.field_show = ima_show_template_sig},
{.field_id = "xattrvalues",
.field_init = ima_eventinodexattrvalues_init,
.field_show = ima_show_template_sig},
};
/*
* Used when restoring measurements carried over from a kexec. 'd' and 'n' don't
* need to be accounted for since they shouldn't be defined in the same template
* description as 'd-ng' and 'n-ng' respectively.
*/
#define MAX_TEMPLATE_NAME_LEN \
sizeof("d-ng|n-ng|evmsig|xattrnames|xattrlengths|xattrvalues|iuid|igid|imode")
static struct ima_template_desc *ima_template;
static struct ima_template_desc *ima_buf_template;
/**
* ima_template_has_modsig - Check whether template has modsig-related fields.
* @ima_template: IMA template to check.
*
* Tells whether the given template has fields referencing a file's appended
* signature.
*/
bool ima_template_has_modsig(const struct ima_template_desc *ima_template)
{
int i;
for (i = 0; i < ima_template->num_fields; i++)
if (!strcmp(ima_template->fields[i]->field_id, "modsig") ||
!strcmp(ima_template->fields[i]->field_id, "d-modsig"))
return true;
return false;
}
static int __init ima_template_setup(char *str)
{
struct ima_template_desc *template_desc;
int template_len = strlen(str);
if (template_setup_done)
return 1;
if (!ima_template)
ima_init_template_list();
/*
* Verify that a template with the supplied name exists.
* If not, use CONFIG_IMA_DEFAULT_TEMPLATE.
*/
template_desc = lookup_template_desc(str);
if (!template_desc) {
pr_err("template %s not found, using %s\n",
str, CONFIG_IMA_DEFAULT_TEMPLATE);
return 1;
}
/*
* Verify whether the current hash algorithm is supported
* by the 'ima' template.
*/
if (template_len == 3 && strcmp(str, IMA_TEMPLATE_IMA_NAME) == 0 &&
ima_hash_algo != HASH_ALGO_SHA1 && ima_hash_algo != HASH_ALGO_MD5) {
pr_err("template does not support hash alg\n");
return 1;
}
ima_template = template_desc;
template_setup_done = 1;
return 1;
}
__setup("ima_template=", ima_template_setup);
static int __init ima_template_fmt_setup(char *str)
{
int num_templates = ARRAY_SIZE(builtin_templates);
if (template_setup_done)
return 1;
if (template_desc_init_fields(str, NULL, NULL) < 0) {
pr_err("format string '%s' not valid, using template %s\n",
str, CONFIG_IMA_DEFAULT_TEMPLATE);
return 1;
}
builtin_templates[num_templates - 1].fmt = str;
ima_template = builtin_templates + num_templates - 1;
template_setup_done = 1;
return 1;
}
__setup("ima_template_fmt=", ima_template_fmt_setup);
struct ima_template_desc *lookup_template_desc(const char *name)
{
struct ima_template_desc *template_desc;
int found = 0;
rcu_read_lock();
list_for_each_entry_rcu(template_desc, &defined_templates, list) {
if ((strcmp(template_desc->name, name) == 0) ||
(strcmp(template_desc->fmt, name) == 0)) {
found = 1;
break;
}
}
rcu_read_unlock();
return found ? template_desc : NULL;
}
static const struct ima_template_field *
lookup_template_field(const char *field_id)
{
int i;
for (i = 0; i < ARRAY_SIZE(supported_fields); i++)
if (strncmp(supported_fields[i].field_id, field_id,
IMA_TEMPLATE_FIELD_ID_MAX_LEN) == 0)
return &supported_fields[i];
return NULL;
}
static int template_fmt_size(const char *template_fmt)
{
char c;
int template_fmt_len = strlen(template_fmt);
int i = 0, j = 0;
while (i < template_fmt_len) {
c = template_fmt[i];
if (c == '|')
j++;
i++;
}
return j + 1;
}
int template_desc_init_fields(const char *template_fmt,
const struct ima_template_field ***fields,
int *num_fields)
{
const char *template_fmt_ptr;
const struct ima_template_field *found_fields[IMA_TEMPLATE_NUM_FIELDS_MAX];
int template_num_fields;
int i, len;
if (num_fields && *num_fields > 0) /* already initialized? */
return 0;
template_num_fields = template_fmt_size(template_fmt);
if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX) {
pr_err("format string '%s' contains too many fields\n",
template_fmt);
return -EINVAL;
}
for (i = 0, template_fmt_ptr = template_fmt; i < template_num_fields;
i++, template_fmt_ptr += len + 1) {
char tmp_field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN + 1];
len = strchrnul(template_fmt_ptr, '|') - template_fmt_ptr;
if (len == 0 || len > IMA_TEMPLATE_FIELD_ID_MAX_LEN) {
pr_err("Invalid field with length %d\n", len);
return -EINVAL;
}
memcpy(tmp_field_id, template_fmt_ptr, len);
tmp_field_id[len] = '\0';
found_fields[i] = lookup_template_field(tmp_field_id);
if (!found_fields[i]) {
pr_err("field '%s' not found\n", tmp_field_id);
return -ENOENT;
}
}
if (fields && num_fields) {
*fields = kmalloc_objs(**fields, i);
if (*fields == NULL)
return -ENOMEM;
memcpy(*fields, found_fields, i * sizeof(**fields));
*num_fields = i;
}
return 0;
}
void ima_init_template_list(void)
{
int i;
if (!list_empty(&defined_templates))
return;
spin_lock(&template_list);
for (i = 0; i < ARRAY_SIZE(builtin_templates); i++) {
list_add_tail_rcu(&builtin_templates[i].list,
&defined_templates);
}
spin_unlock(&template_list);
}
struct ima_template_desc *ima_template_desc_current(void)
{
if (!ima_template) {
ima_init_template_list();
ima_template =
lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE);
}
return ima_template;
}
struct ima_template_desc *ima_template_desc_buf(void)
{
if (!ima_buf_template) {
ima_init_template_list();
ima_buf_template = lookup_template_desc("ima-buf");
}
return ima_buf_template;
}
int __init ima_init_template(void)
{
struct ima_template_desc *template = ima_template_desc_current();
int result;
result = template_desc_init_fields(template->fmt,
&(template->fields),
&(template->num_fields));
if (result < 0) {
pr_err("template %s init failed, result: %d\n",
(strlen(template->name) ?
template->name : template->fmt), result);
return result;
}
template = ima_template_desc_buf();
if (!template) {
pr_err("Failed to get ima-buf template\n");
return -EINVAL;
}
result = template_desc_init_fields(template->fmt,
&(template->fields),
&(template->num_fields));
if (result < 0)
pr_err("template %s init failed, result: %d\n",
(strlen(template->name) ?
template->name : template->fmt), result);
return result;
}
static struct ima_template_desc *restore_template_fmt(char *template_name)
{
struct ima_template_desc *template_desc = NULL;
int ret;
ret = template_desc_init_fields(template_name, NULL, NULL);
if (ret < 0) {
pr_err("attempting to initialize the template \"%s\" failed\n",
template_name);
goto out;
}
template_desc = kzalloc_obj(*template_desc);
if (!template_desc)
goto out;
template_desc->name = "";
template_desc->fmt = kstrdup(template_name, GFP_KERNEL);
if (!template_desc->fmt) {
kfree(template_desc);
template_desc = NULL;
goto out;
}
spin_lock(&template_list);
list_add_tail_rcu(&template_desc->list, &defined_templates);
spin_unlock(&template_list);
out:
return template_desc;
}
static int ima_restore_template_data(struct ima_template_desc *template_desc,
void *template_data,
int template_data_size,
struct ima_template_entry **entry)
{
struct tpm_digest *digests;
int ret = 0;
int i;
*entry = kzalloc_flex(**entry, template_data, template_desc->num_fields,
GFP_NOFS);
if (!*entry)
return -ENOMEM;
digests = kzalloc_objs(*digests,
NR_BANKS(ima_tpm_chip) + ima_extra_slots,
GFP_NOFS);
if (!digests) {
kfree(*entry);
return -ENOMEM;
}
(*entry)->digests = digests;
ret = ima_parse_buf(template_data, template_data + template_data_size,
NULL, template_desc->num_fields,
(*entry)->template_data, NULL, NULL,
ENFORCE_FIELDS | ENFORCE_BUFEND, "template data");
if (ret < 0) {
kfree((*entry)->digests);
kfree(*entry);
return ret;
}
(*entry)->template_desc = template_desc;
for (i = 0; i < template_desc->num_fields; i++) {
struct ima_field_data *field_data = &(*entry)->template_data[i];
u8 *data = field_data->data;
(*entry)->template_data[i].data =
kzalloc(field_data->len + 1, GFP_KERNEL);
if (!(*entry)->template_data[i].data) {
ret = -ENOMEM;
break;
}
memcpy((*entry)->template_data[i].data, data, field_data->len);
(*entry)->template_data_len += sizeof(field_data->len);
(*entry)->template_data_len += field_data->len;
}
if (ret < 0) {
ima_free_template_entry(*entry);
*entry = NULL;
}
return ret;
}
/* Restore the serialized binary measurement list without extending PCRs. */
int ima_restore_measurement_list(loff_t size, void *buf)
{
char template_name[MAX_TEMPLATE_NAME_LEN];
unsigned char zero[TPM_DIGEST_SIZE] = { 0 };
struct ima_kexec_hdr *khdr = buf;
struct ima_field_data hdr[HDR__LAST] = {
[HDR_PCR] = {.len = sizeof(u32)},
[HDR_DIGEST] = {.len = TPM_DIGEST_SIZE},
};
void *bufp = buf + sizeof(*khdr);
void *bufendp;
struct ima_template_entry *entry;
struct ima_template_desc *template_desc;
DECLARE_BITMAP(hdr_mask, HDR__LAST);
unsigned long count = 0;
int ret = 0;
if (!buf || size < sizeof(*khdr))
return 0;
if (ima_canonical_fmt) {
khdr->version = le16_to_cpu((__force __le16)khdr->version);
khdr->count = le64_to_cpu((__force __le64)khdr->count);
khdr->buffer_size = le64_to_cpu((__force __le64)khdr->buffer_size);
}
if (khdr->version != 1) {
pr_err("attempting to restore a incompatible measurement list");
return -EINVAL;
}
if (khdr->count > ULONG_MAX - 1) {
pr_err("attempting to restore too many measurements");
return -EINVAL;
}
bitmap_zero(hdr_mask, HDR__LAST);
bitmap_set(hdr_mask, HDR_PCR, 1);
bitmap_set(hdr_mask, HDR_DIGEST, 1);
/*
* ima kexec buffer prefix: version, buffer size, count
* v1 format: pcr, digest, template-name-len, template-name,
* template-data-size, template-data
*/
bufendp = buf + khdr->buffer_size;
while ((bufp < bufendp) && (count++ < khdr->count)) {
int enforce_mask = ENFORCE_FIELDS;
enforce_mask |= (count == khdr->count) ? ENFORCE_BUFEND : 0;
ret = ima_parse_buf(bufp, bufendp, &bufp, HDR__LAST, hdr, NULL,
hdr_mask, enforce_mask, "entry header");
if (ret < 0)
break;
if (hdr[HDR_TEMPLATE_NAME].len >= MAX_TEMPLATE_NAME_LEN) {
pr_err("attempting to restore a template name that is too long\n");
ret = -EINVAL;
break;
}
/* template name is not null terminated */
memcpy(template_name, hdr[HDR_TEMPLATE_NAME].data,
hdr[HDR_TEMPLATE_NAME].len);
template_name[hdr[HDR_TEMPLATE_NAME].len] = 0;
if (strcmp(template_name, "ima") == 0) {
pr_err("attempting to restore an unsupported template \"%s\" failed\n",
template_name);
ret = -EINVAL;
break;
}
template_desc = lookup_template_desc(template_name);
if (!template_desc) {
template_desc = restore_template_fmt(template_name);
if (!template_desc)
break;
}
/*
* Only the running system's template format is initialized
* on boot. As needed, initialize the other template formats.
*/
ret = template_desc_init_fields(template_desc->fmt,
&(template_desc->fields),
&(template_desc->num_fields));
if (ret < 0) {
pr_err("attempting to restore the template fmt \"%s\" failed\n",
template_desc->fmt);
ret = -EINVAL;
break;
}
ret = ima_restore_template_data(template_desc,
hdr[HDR_TEMPLATE_DATA].data,
hdr[HDR_TEMPLATE_DATA].len,
&entry);
if (ret < 0)
break;
if (memcmp(hdr[HDR_DIGEST].data, zero, sizeof(zero))) {
ret = ima_calc_field_array_hash(
&entry->template_data[0],
entry);
if (ret < 0) {
pr_err("cannot calculate template digest\n");
ret = -EINVAL;
break;
}
}
entry->pcr = !ima_canonical_fmt ? *(u32 *)(hdr[HDR_PCR].data) :
le32_to_cpu(*(__le32 *)(hdr[HDR_PCR].data));
ret = ima_restore_measurement_entry(entry);
if (ret < 0)
break;
}
return ret;
}
@@ -0,0 +1,764 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Politecnico di Torino, Italy
* TORSEC group -- https://security.polito.it
*
* Author: Roberto Sassu <roberto.sassu@polito.it>
*
* File: ima_template_lib.c
* Library of supported template fields.
*/
#include "ima_template_lib.h"
#include <linux/xattr.h>
#include <linux/evm.h>
static bool ima_template_hash_algo_allowed(u8 algo)
{
if (algo == HASH_ALGO_SHA1 || algo == HASH_ALGO_MD5)
return true;
return false;
}
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)
{
u8 *buf, *buf_ptr;
u32 buflen = datalen;
if (datafmt == DATA_FMT_STRING)
buflen = datalen + 1;
buf = kzalloc(buflen, GFP_KERNEL);
if (!buf)
return -ENOMEM;
memcpy(buf, data, datalen);
/*
* Replace all space characters with underscore for event names and
* strings. This avoid that, during the parsing of a measurements list,
* filenames with spaces or that end with the suffix ' (deleted)' are
* split into multiple template fields (the space is the delimitator
* character for measurements lists in ASCII format).
*/
if (datafmt == DATA_FMT_STRING) {
for (buf_ptr = buf; buf_ptr - buf < datalen; buf_ptr++)
if (*buf_ptr == ' ')
*buf_ptr = '_';
}
field_data->data = buf;
field_data->len = buflen;
return 0;
}
static void ima_show_template_data_ascii(struct seq_file *m,
enum ima_show_type show,
enum data_formats datafmt,
struct ima_field_data *field_data)
{
u8 *buf_ptr = field_data->data;
u32 buflen = field_data->len;
switch (datafmt) {
case DATA_FMT_DIGEST_WITH_TYPE_AND_ALGO:
case DATA_FMT_DIGEST_WITH_ALGO:
buf_ptr = strrchr(field_data->data, ':');
if (buf_ptr != field_data->data)
seq_printf(m, "%s", field_data->data);
/* skip ':' and '\0' */
buf_ptr += 2;
buflen -= buf_ptr - field_data->data;
fallthrough;
case DATA_FMT_DIGEST:
case DATA_FMT_HEX:
if (!buflen)
break;
ima_print_digest(m, buf_ptr, buflen);
break;
case DATA_FMT_STRING:
seq_printf(m, "%s", buf_ptr);
break;
case DATA_FMT_UINT:
switch (field_data->len) {
case sizeof(u8):
seq_printf(m, "%u", *(u8 *)buf_ptr);
break;
case sizeof(u16):
if (ima_canonical_fmt)
seq_printf(m, "%u",
le16_to_cpu(*(__le16 *)buf_ptr));
else
seq_printf(m, "%u", *(u16 *)buf_ptr);
break;
case sizeof(u32):
if (ima_canonical_fmt)
seq_printf(m, "%u",
le32_to_cpu(*(__le32 *)buf_ptr));
else
seq_printf(m, "%u", *(u32 *)buf_ptr);
break;
case sizeof(u64):
if (ima_canonical_fmt)
seq_printf(m, "%llu",
le64_to_cpu(*(__le64 *)buf_ptr));
else
seq_printf(m, "%llu", *(u64 *)buf_ptr);
break;
default:
break;
}
break;
default:
break;
}
}
static void ima_show_template_data_binary(struct seq_file *m,
enum ima_show_type show,
enum data_formats datafmt,
struct ima_field_data *field_data)
{
u32 len = (show == IMA_SHOW_BINARY_OLD_STRING_FMT) ?
strlen(field_data->data) : field_data->len;
if (show != IMA_SHOW_BINARY_NO_FIELD_LEN) {
u32 field_len = !ima_canonical_fmt ?
len : (__force u32)cpu_to_le32(len);
ima_putc(m, &field_len, sizeof(field_len));
}
if (!len)
return;
ima_putc(m, field_data->data, len);
}
static void ima_show_template_field_data(struct seq_file *m,
enum ima_show_type show,
enum data_formats datafmt,
struct ima_field_data *field_data)
{
switch (show) {
case IMA_SHOW_ASCII:
ima_show_template_data_ascii(m, show, datafmt, field_data);
break;
case IMA_SHOW_BINARY:
case IMA_SHOW_BINARY_NO_FIELD_LEN:
case IMA_SHOW_BINARY_OLD_STRING_FMT:
ima_show_template_data_binary(m, show, datafmt, field_data);
break;
default:
break;
}
}
void ima_show_template_digest(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, field_data);
}
void ima_show_template_digest_ng(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_ALGO,
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)
{
ima_show_template_field_data(m, show, DATA_FMT_STRING, field_data);
}
void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data)
{
ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data);
}
void ima_show_template_buf(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data)
{
ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data);
}
void ima_show_template_uint(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data)
{
ima_show_template_field_data(m, show, DATA_FMT_UINT, field_data);
}
/**
* ima_parse_buf() - Parses lengths and data from an input buffer
* @bufstartp: Buffer start address.
* @bufendp: Buffer end address.
* @bufcurp: Pointer to remaining (non-parsed) data.
* @maxfields: Length of fields array.
* @fields: Array containing lengths and pointers of parsed data.
* @curfields: Number of array items containing parsed data.
* @len_mask: Bitmap (if bit is set, data length should not be parsed).
* @enforce_mask: Check if curfields == maxfields and/or bufcurp == bufendp.
* @bufname: String identifier of the input buffer.
*
* Return: 0 on success, -EINVAL on error.
*/
int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
int maxfields, struct ima_field_data *fields, int *curfields,
unsigned long *len_mask, int enforce_mask, char *bufname)
{
void *bufp = bufstartp;
int i;
for (i = 0; i < maxfields; i++) {
if (len_mask == NULL || !test_bit(i, len_mask)) {
if (bufp > (bufendp - sizeof(u32)))
break;
if (ima_canonical_fmt)
fields[i].len = le32_to_cpu(*(__le32 *)bufp);
else
fields[i].len = *(u32 *)bufp;
bufp += sizeof(u32);
}
if (bufp > (bufendp - fields[i].len))
break;
fields[i].data = bufp;
bufp += fields[i].len;
}
if ((enforce_mask & ENFORCE_FIELDS) && i != maxfields) {
pr_err("%s: nr of fields mismatch: expected: %d, current: %d\n",
bufname, maxfields, i);
return -EINVAL;
}
if ((enforce_mask & ENFORCE_BUFEND) && bufp != bufendp) {
pr_err("%s: buf end mismatch: expected: %p, current: %p\n",
bufname, bufendp, bufp);
return -EINVAL;
}
if (curfields)
*curfields = i;
if (bufcurp)
*bufcurp = bufp;
return 0;
}
static int ima_eventdigest_init_common(const u8 *digest, u32 digestsize,
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,
* - 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[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 (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 += 1 + sprintf(buffer, "%s:",
hash_algo_name[hash_algo]);
}
if (digest) {
memcpy(buffer + offset, digest, digestsize);
} else {
/*
* If digest is NULL, the event being recorded is a violation.
* Make room for the digest by increasing the offset by the
* hash algorithm digest size. If the hash algorithm is not
* specified increase the offset by IMA_DIGEST_SIZE which
* fits SHA1 or MD5
*/
if (hash_algo < HASH_ALGO__LAST)
offset += hash_digest_size[hash_algo];
else
offset += IMA_DIGEST_SIZE;
}
return ima_write_template_field_data(buffer, offset + digestsize,
fmt, field_data);
}
/*
* This function writes the digest of an event (with size limit).
*/
int ima_eventdigest_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
struct ima_max_digest_data hash;
struct ima_digest_data *hash_hdr = container_of(&hash.hdr,
struct ima_digest_data, hdr);
u8 *cur_digest = NULL;
u32 cur_digestsize = 0;
struct inode *inode;
int result;
memset(&hash, 0, sizeof(hash));
if (event_data->violation) /* recording a violation. */
goto out;
if (ima_template_hash_algo_allowed(event_data->iint->ima_hash->algo)) {
cur_digest = event_data->iint->ima_hash->digest;
cur_digestsize = event_data->iint->ima_hash->length;
goto out;
}
if ((const char *)event_data->filename == boot_aggregate_name) {
if (ima_tpm_chip) {
hash.hdr.algo = HASH_ALGO_SHA1;
result = ima_calc_boot_aggregate(hash_hdr);
/* algo can change depending on available PCR banks */
if (!result && hash.hdr.algo != HASH_ALGO_SHA1)
result = -EINVAL;
if (result < 0)
memset(&hash, 0, sizeof(hash));
}
cur_digest = hash_hdr->digest;
cur_digestsize = hash_digest_size[HASH_ALGO_SHA1];
goto out;
}
if (!event_data->file) /* missing info to re-calculate the digest */
return -EINVAL;
inode = file_inode(event_data->file);
hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ?
ima_hash_algo : HASH_ALGO_SHA1;
result = ima_calc_file_hash(event_data->file, hash_hdr);
if (result) {
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
event_data->filename, "collect_data",
"failed", result, 0);
return result;
}
cur_digest = hash_hdr->digest;
cur_digestsize = hash.hdr.length;
out:
return ima_eventdigest_init_common(cur_digest, cur_digestsize,
DIGEST_TYPE__LAST, HASH_ALGO__LAST,
field_data);
}
/*
* This function writes the digest of an event (without size limit).
*/
int ima_eventdigest_ng_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;
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;
out:
return ima_eventdigest_init_common(cur_digest, cur_digestsize,
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);
}
/*
* 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,
DIGEST_TYPE__LAST, hash_algo,
field_data);
}
static int ima_eventname_init_common(struct ima_event_data *event_data,
struct ima_field_data *field_data,
bool size_limit)
{
const char *cur_filename = NULL;
struct name_snapshot filename;
u32 cur_filename_len = 0;
bool snapshot = false;
int ret;
BUG_ON(event_data->filename == NULL && event_data->file == NULL);
if (event_data->filename) {
cur_filename = event_data->filename;
cur_filename_len = strlen(event_data->filename);
if (!size_limit || cur_filename_len <= IMA_EVENT_NAME_LEN_MAX)
goto out;
}
if (event_data->file) {
take_dentry_name_snapshot(&filename,
event_data->file->f_path.dentry);
snapshot = true;
cur_filename = filename.name.name;
cur_filename_len = strlen(cur_filename);
} else
/*
* Truncate filename if the latter is too long and
* the file descriptor is not available.
*/
cur_filename_len = IMA_EVENT_NAME_LEN_MAX;
out:
ret = ima_write_template_field_data(cur_filename, cur_filename_len,
DATA_FMT_STRING, field_data);
if (snapshot)
release_dentry_name_snapshot(&filename);
return ret;
}
/*
* This function writes the name of an event (with size limit).
*/
int ima_eventname_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
return ima_eventname_init_common(event_data, field_data, true);
}
/*
* This function writes the name of an event (without size limit).
*/
int ima_eventname_ng_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
return ima_eventname_init_common(event_data, field_data, false);
}
/*
* ima_eventsig_init - include the file signature as part of the template data
*/
int ima_eventsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
struct evm_ima_xattr_data *xattr_value = event_data->xattr_value;
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,
DATA_FMT_HEX, field_data);
}
/*
* ima_eventbuf_init - include the buffer(kexec-cmldine) as part of the
* template data.
*/
int ima_eventbuf_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
if ((!event_data->buf) || (event_data->buf_len == 0))
return 0;
return ima_write_template_field_data(event_data->buf,
event_data->buf_len, DATA_FMT_HEX,
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);
}
/*
* ima_eventevmsig_init - include the EVM portable signature as part of the
* template data
*/
int ima_eventevmsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
struct evm_ima_xattr_data *xattr_data = NULL;
int rc = 0;
if (!event_data->file)
return 0;
rc = vfs_getxattr_alloc(&nop_mnt_idmap, file_dentry(event_data->file),
XATTR_NAME_EVM, (char **)&xattr_data, 0,
GFP_NOFS);
if (rc <= 0 || xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG) {
rc = 0;
goto out;
}
rc = ima_write_template_field_data((char *)xattr_data, rc, DATA_FMT_HEX,
field_data);
out:
kfree(xattr_data);
return rc;
}
static int ima_eventinodedac_init_common(struct ima_event_data *event_data,
struct ima_field_data *field_data,
bool get_uid)
{
unsigned int id;
if (!event_data->file)
return 0;
if (get_uid)
id = i_uid_read(file_inode(event_data->file));
else
id = i_gid_read(file_inode(event_data->file));
if (ima_canonical_fmt) {
if (sizeof(id) == sizeof(u16))
id = (__force u16)cpu_to_le16(id);
else
id = (__force u32)cpu_to_le32(id);
}
return ima_write_template_field_data((void *)&id, sizeof(id),
DATA_FMT_UINT, field_data);
}
/*
* ima_eventinodeuid_init - include the inode UID as part of the template
* data
*/
int ima_eventinodeuid_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
return ima_eventinodedac_init_common(event_data, field_data, true);
}
/*
* ima_eventinodegid_init - include the inode GID as part of the template
* data
*/
int ima_eventinodegid_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
return ima_eventinodedac_init_common(event_data, field_data, false);
}
/*
* ima_eventinodemode_init - include the inode mode as part of the template
* data
*/
int ima_eventinodemode_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
struct inode *inode;
u16 mode;
if (!event_data->file)
return 0;
inode = file_inode(event_data->file);
mode = inode->i_mode;
if (ima_canonical_fmt)
mode = (__force u16)cpu_to_le16(mode);
return ima_write_template_field_data((char *)&mode, sizeof(mode),
DATA_FMT_UINT, field_data);
}
static int ima_eventinodexattrs_init_common(struct ima_event_data *event_data,
struct ima_field_data *field_data,
char type)
{
u8 *buffer = NULL;
int rc;
if (!event_data->file)
return 0;
rc = evm_read_protected_xattrs(file_dentry(event_data->file), NULL, 0,
type, ima_canonical_fmt);
if (rc < 0)
return 0;
buffer = kmalloc(rc, GFP_KERNEL);
if (!buffer)
return 0;
rc = evm_read_protected_xattrs(file_dentry(event_data->file), buffer,
rc, type, ima_canonical_fmt);
if (rc < 0) {
rc = 0;
goto out;
}
rc = ima_write_template_field_data((char *)buffer, rc, DATA_FMT_HEX,
field_data);
out:
kfree(buffer);
return rc;
}
/*
* ima_eventinodexattrnames_init - include a list of xattr names as part of the
* template data
*/
int ima_eventinodexattrnames_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
return ima_eventinodexattrs_init_common(event_data, field_data, 'n');
}
/*
* ima_eventinodexattrlengths_init - include a list of xattr lengths as part of
* the template data
*/
int ima_eventinodexattrlengths_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
return ima_eventinodexattrs_init_common(event_data, field_data, 'l');
}
/*
* ima_eventinodexattrvalues_init - include a list of xattr values as part of
* the template data
*/
int ima_eventinodexattrvalues_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
return ima_eventinodexattrs_init_common(event_data, field_data, 'v');
}
@@ -0,0 +1,69 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2013 Politecnico di Torino, Italy
* TORSEC group -- https://security.polito.it
*
* Author: Roberto Sassu <roberto.sassu@polito.it>
*
* File: ima_template_lib.h
* Header for the library of supported template fields.
*/
#ifndef __LINUX_IMA_TEMPLATE_LIB_H
#define __LINUX_IMA_TEMPLATE_LIB_H
#include <linux/seq_file.h>
#include "ima.h"
#define ENFORCE_FIELDS 0x00000001
#define ENFORCE_BUFEND 0x00000002
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,
struct ima_field_data *field_data);
void ima_show_template_buf(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
void ima_show_template_uint(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
int maxfields, struct ima_field_data *fields, int *curfields,
unsigned long *len_mask, int enforce_mask, char *bufname);
int ima_eventdigest_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
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,
struct ima_field_data *field_data);
int ima_eventsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventbuf_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventmodsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventevmsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventinodeuid_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventinodegid_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventinodemode_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventinodexattrnames_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventinodexattrlengths_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventinodexattrvalues_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
#endif /* __LINUX_IMA_TEMPLATE_LIB_H */
+281
View File
@@ -0,0 +1,281 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2009-2010 IBM Corporation
*
* Authors:
* Mimi Zohar <zohar@us.ibm.com>
*/
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/types.h>
#include <linux/integrity.h>
#include <linux/secure_boot.h>
#include <crypto/sha1.h>
#include <crypto/hash.h>
#include <linux/key.h>
#include <linux/audit.h>
#include <linux/lsm_hooks.h>
enum evm_ima_xattr_type {
IMA_XATTR_DIGEST = 0x01,
EVM_XATTR_HMAC,
EVM_IMA_XATTR_DIGSIG,
IMA_XATTR_DIGEST_NG,
EVM_XATTR_PORTABLE_DIGSIG,
IMA_VERITY_DIGSIG,
IMA_XATTR_LAST
};
struct evm_ima_xattr_data {
/* New members must be added within the __struct_group() macro below. */
__struct_group(evm_ima_xattr_data_hdr, hdr, __packed,
u8 type;
);
u8 data[];
} __packed;
static_assert(offsetof(struct evm_ima_xattr_data, data) == sizeof(struct evm_ima_xattr_data_hdr),
"struct member likely outside of __struct_group()");
/* Only used in the EVM HMAC code. */
struct evm_xattr {
struct evm_ima_xattr_data_hdr data;
u8 digest[SHA1_DIGEST_SIZE];
} __packed;
#define IMA_MAX_DIGEST_SIZE HASH_MAX_DIGESTSIZE
struct ima_digest_data {
/* New members must be added within the __struct_group() macro below. */
__struct_group(ima_digest_data_hdr, hdr, __packed,
u8 algo;
u8 length;
union {
struct {
u8 unused;
u8 type;
} sha1;
struct {
u8 type;
u8 algo;
} ng;
u8 data[2];
} xattr;
);
u8 digest[];
} __packed;
static_assert(offsetof(struct ima_digest_data, digest) == sizeof(struct ima_digest_data_hdr),
"struct member likely outside of __struct_group()");
/*
* Instead of wrapping the ima_digest_data struct inside a local structure
* with the maximum hash size, define ima_max_digest_data struct.
*/
struct ima_max_digest_data {
struct ima_digest_data_hdr hdr;
u8 digest[HASH_MAX_DIGESTSIZE];
} __packed;
/*
* 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 */
uint8_t version; /* signature format version */
uint8_t hash_algo; /* Digest algorithm [enum hash_algo] */
__be32 keyid; /* IMA key identifier - not X509/PGP specific */
__be16 sig_size; /* signature size */
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;
int integrity_kernel_read(struct file *file, loff_t offset,
void *addr, unsigned long count);
int __init integrity_fs_init(void);
void __init integrity_fs_fini(void);
#define INTEGRITY_KEYRING_EVM 0
#define INTEGRITY_KEYRING_IMA 1
#define INTEGRITY_KEYRING_PLATFORM 2
#define INTEGRITY_KEYRING_MACHINE 3
#define INTEGRITY_KEYRING_MAX 4
extern struct dentry *integrity_dir;
struct modsig;
#ifdef CONFIG_INTEGRITY_SIGNATURE
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
const char *digest, int digestlen, u8 algo);
int integrity_modsig_verify(unsigned int id, const struct modsig *modsig);
int __init integrity_init_keyring(const unsigned int id);
int __init integrity_load_x509(const unsigned int id, const char *path);
int __init integrity_load_cert(const unsigned int id, const char *source,
const void *data, size_t len, key_perm_t perm);
#else
static inline int integrity_digsig_verify(const unsigned int id,
const char *sig, int siglen,
const char *digest, int digestlen,
u8 algo)
{
return -EOPNOTSUPP;
}
static inline int integrity_modsig_verify(unsigned int id,
const struct modsig *modsig)
{
return -EOPNOTSUPP;
}
static inline int integrity_init_keyring(const unsigned int id)
{
return 0;
}
static inline int __init integrity_load_cert(const unsigned int id,
const char *source,
const void *data, size_t len,
key_perm_t perm)
{
return 0;
}
#endif /* CONFIG_INTEGRITY_SIGNATURE */
#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
int asymmetric_verify(struct key *keyring, const char *sig,
int siglen, const char *data, int datalen);
int asymmetric_verify_v3(struct key *keyring, const char *sig,
int siglen, const char *data, int datalen, u8 algo);
#else
static inline int asymmetric_verify(struct key *keyring, const char *sig,
int siglen, const char *data, int datalen)
{
return -EOPNOTSUPP;
}
static inline int asymmetric_verify_v3(struct key *keyring,
const char *sig, int siglen,
const char *data, int datalen, u8 algo)
{
return -EOPNOTSUPP;
}
#endif
#ifdef CONFIG_IMA_APPRAISE_MODSIG
int ima_modsig_verify(struct key *keyring, const struct modsig *modsig);
#else
static inline int ima_modsig_verify(struct key *keyring,
const struct modsig *modsig)
{
return -EOPNOTSUPP;
}
#endif
#ifdef CONFIG_IMA_LOAD_X509
void __init ima_load_x509(void);
#else
static inline void ima_load_x509(void)
{
}
#endif
#ifdef CONFIG_EVM_LOAD_X509
void __init evm_load_x509(void);
#else
static inline void evm_load_x509(void)
{
}
#endif
#ifdef CONFIG_INTEGRITY_AUDIT
/* declarations */
void integrity_audit_msg(int audit_msgno, struct inode *inode,
const unsigned char *fname, const char *op,
const char *cause, int result, int info);
void integrity_audit_message(int audit_msgno, struct inode *inode,
const unsigned char *fname, const char *op,
const char *cause, int result, int info,
int errno);
static inline struct audit_buffer *
integrity_audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type)
{
return audit_log_start(ctx, gfp_mask, type);
}
#else
static inline void integrity_audit_msg(int audit_msgno, struct inode *inode,
const unsigned char *fname,
const char *op, const char *cause,
int result, int info)
{
}
static inline void integrity_audit_message(int audit_msgno,
struct inode *inode,
const unsigned char *fname,
const char *op, const char *cause,
int result, int info, int errno)
{
}
static inline struct audit_buffer *
integrity_audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type)
{
return NULL;
}
#endif
#ifdef CONFIG_INTEGRITY_PLATFORM_KEYRING
void __init add_to_platform_keyring(const char *source, const void *data,
size_t len);
#else
static inline void __init add_to_platform_keyring(const char *source,
const void *data, size_t len)
{
}
#endif
#ifdef CONFIG_INTEGRITY_MACHINE_KEYRING
void __init add_to_machine_keyring(const char *source, const void *data, size_t len);
bool __init imputed_trust_enabled(void);
#else
static inline void __init add_to_machine_keyring(const char *source,
const void *data, size_t len)
{
}
static inline bool __init imputed_trust_enabled(void)
{
return false;
}
#endif
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2008 IBM Corporation
* Author: Mimi Zohar <zohar@us.ibm.com>
*
* File: integrity_audit.c
* Audit calls for the integrity subsystem
*/
#include <linux/fs.h>
#include <linux/gfp.h>
#include <linux/audit.h>
#include "integrity.h"
static int integrity_audit_info;
/* ima_audit_setup - enable informational auditing messages */
static int __init integrity_audit_setup(char *str)
{
unsigned long audit;
if (!kstrtoul(str, 0, &audit))
integrity_audit_info = audit ? 1 : 0;
return 1;
}
__setup("integrity_audit=", integrity_audit_setup);
void integrity_audit_msg(int audit_msgno, struct inode *inode,
const unsigned char *fname, const char *op,
const char *cause, int result, int audit_info)
{
integrity_audit_message(audit_msgno, inode, fname, op, cause,
result, audit_info, 0);
}
void integrity_audit_message(int audit_msgno, struct inode *inode,
const unsigned char *fname, const char *op,
const char *cause, int result, int audit_info,
int errno)
{
struct audit_buffer *ab;
char name[TASK_COMM_LEN];
if (!integrity_audit_info && audit_info == 1) /* Skip info messages */
return;
ab = audit_log_start(audit_context(), GFP_KERNEL, audit_msgno);
if (!ab)
return;
audit_log_format(ab, "pid=%d uid=%u auid=%u ses=%u",
task_pid_nr(current),
from_kuid(&init_user_ns, current_uid()),
from_kuid(&init_user_ns, audit_get_loginuid(current)),
audit_get_sessionid(current));
audit_log_task_context(ab);
audit_log_format(ab, " op=%s cause=%s comm=", op, cause);
audit_log_untrustedstring(ab, get_task_comm(name, current));
if (fname) {
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, fname);
}
if (inode) {
audit_log_format(ab, " dev=");
audit_log_untrustedstring(ab, inode->i_sb->s_id);
audit_log_format(ab, " ino=%llu", inode->i_ino);
}
audit_log_format(ab, " res=%d errno=%d", !result, errno);
audit_log_end(ab);
}
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: GPL-2.0+
/* EFI signature/key/certificate list parser
*
* Copyright (C) 2012, 2016 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#define pr_fmt(fmt) "EFI: "fmt
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/err.h>
#include <linux/efi.h>
/**
* parse_efi_signature_list - Parse an EFI signature list for certificates
* @source: The source of the key
* @data: The data blob to parse
* @size: The size of the data blob
* @get_handler_for_guid: Get the handler func for the sig type (or NULL)
*
* Parse an EFI signature list looking for elements of interest. A list is
* made up of a series of sublists, where all the elements in a sublist are of
* the same type, but sublists can be of different types.
*
* For each sublist encountered, the @get_handler_for_guid function is called
* with the type specifier GUID and returns either a pointer to a function to
* handle elements of that type or NULL if the type is not of interest.
*
* If the sublist is of interest, each element is passed to the handler
* function in turn.
*
* Error EBADMSG is returned if the list doesn't parse correctly and 0 is
* returned if the list was parsed correctly. No error can be returned from
* the @get_handler_for_guid function or the element handler function it
* returns.
*/
int __init parse_efi_signature_list(
const char *source,
const void *data, size_t size,
efi_element_handler_t (*get_handler_for_guid)(const efi_guid_t *))
{
efi_element_handler_t handler;
unsigned int offs = 0;
pr_devel("-->%s(,%zu)\n", __func__, size);
while (size > 0) {
const efi_signature_data_t *elem;
efi_signature_list_t list;
size_t lsize, esize, hsize, elsize;
if (size < sizeof(list))
return -EBADMSG;
memcpy(&list, data, sizeof(list));
pr_devel("LIST[%04x] guid=%pUl ls=%x hs=%x ss=%x\n",
offs,
&list.signature_type, list.signature_list_size,
list.signature_header_size, list.signature_size);
lsize = list.signature_list_size;
hsize = list.signature_header_size;
esize = list.signature_size;
elsize = lsize - sizeof(list) - hsize;
if (lsize > size) {
pr_devel("<--%s() = -EBADMSG [overrun @%x]\n",
__func__, offs);
return -EBADMSG;
}
if (lsize < sizeof(list) ||
lsize - sizeof(list) < hsize ||
esize < sizeof(*elem) ||
elsize < esize ||
elsize % esize != 0) {
pr_devel("- bad size combo @%x\n", offs);
return -EBADMSG;
}
handler = get_handler_for_guid(&list.signature_type);
if (!handler) {
data += lsize;
size -= lsize;
offs += lsize;
continue;
}
data += sizeof(list) + hsize;
size -= sizeof(list) + hsize;
offs += sizeof(list) + hsize;
for (; elsize > 0; elsize -= esize) {
elem = data;
pr_devel("ELEM[%04x]\n", offs);
handler(source,
&elem->signature_data,
esize - sizeof(*elem));
data += esize;
size -= esize;
offs += esize;
}
}
return 0;
}
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/efi.h>
#include <linux/slab.h>
#include <keys/asymmetric-type.h>
#include <keys/system_keyring.h>
#include "../integrity.h"
#include "keyring_handler.h"
static efi_guid_t efi_cert_x509_guid __initdata = EFI_CERT_X509_GUID;
static efi_guid_t efi_cert_x509_sha256_guid __initdata =
EFI_CERT_X509_SHA256_GUID;
static efi_guid_t efi_cert_sha256_guid __initdata = EFI_CERT_SHA256_GUID;
/*
* Blacklist an X509 TBS hash.
*/
static __init void uefi_blacklist_x509_tbs(const char *source,
const void *data, size_t len)
{
mark_hash_blacklisted(data, len, BLACKLIST_HASH_X509_TBS);
}
/*
* Blacklist the hash of an executable.
*/
static __init void uefi_blacklist_binary(const char *source,
const void *data, size_t len)
{
mark_hash_blacklisted(data, len, BLACKLIST_HASH_BINARY);
}
/*
* Add an X509 cert to the revocation list.
*/
static __init void uefi_revocation_list_x509(const char *source,
const void *data, size_t len)
{
add_key_to_revocation_list(data, len);
}
/*
* Return the appropriate handler for particular signature list types found in
* the UEFI db tables.
*/
__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 NULL;
}
/*
* Return the appropriate handler for particular signature list types found in
* the MokListRT tables.
*/
__init efi_element_handler_t get_handler_for_mok(const efi_guid_t *sig_type)
{
if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0) {
if (IS_ENABLED(CONFIG_INTEGRITY_MACHINE_KEYRING) &&
imputed_trust_enabled())
return add_to_machine_keyring;
else
return add_to_platform_keyring;
}
return NULL;
}
__init efi_element_handler_t get_handler_for_ca_keys(const efi_guid_t *sig_type)
{
if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0)
return add_to_machine_keyring;
return NULL;
}
__init efi_element_handler_t get_handler_for_code_signing_keys(const efi_guid_t *sig_type)
{
if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0)
return add_to_secondary_keyring;
return NULL;
}
/*
* Return the appropriate handler for particular signature list types found in
* the UEFI dbx and MokListXRT tables.
*/
__init efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type)
{
if (efi_guidcmp(*sig_type, efi_cert_x509_sha256_guid) == 0)
return uefi_blacklist_x509_tbs;
if (efi_guidcmp(*sig_type, efi_cert_sha256_guid) == 0)
return uefi_blacklist_binary;
if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0)
return uefi_revocation_list_x509;
return NULL;
}
@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef PLATFORM_CERTS_INTERNAL_H
#define PLATFORM_CERTS_INTERNAL_H
#include <linux/efi.h>
void blacklist_hash(const char *source, const void *data,
size_t len, const char *type,
size_t type_len);
/*
* Blacklist an X509 TBS hash.
*/
void blacklist_x509_tbs(const char *source, const void *data, size_t len);
/*
* Blacklist the hash of an executable.
*/
void blacklist_binary(const char *source, const void *data, size_t len);
/*
* Return the handler for particular signature list types found in the db.
*/
efi_element_handler_t get_handler_for_db(const efi_guid_t *sig_type);
/*
* Return the handler for particular signature list types found in the mok.
*/
efi_element_handler_t get_handler_for_mok(const efi_guid_t *sig_type);
/*
* Return the handler for particular signature list types for CA keys.
*/
efi_element_handler_t get_handler_for_ca_keys(const efi_guid_t *sig_type);
/*
* Return the handler for particular signature list types for code signing keys.
*/
efi_element_handler_t get_handler_for_code_signing_keys(const efi_guid_t *sig_type);
/*
* Return the handler for particular signature list types found in the dbx.
*/
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
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/efi.h>
#include <linux/slab.h>
#include <keys/asymmetric-type.h>
#include <keys/system_keyring.h>
#include <asm/boot_data.h>
#include "../integrity.h"
/*
* Load the certs contained in the IPL report created by the machine loader
* into the platform trusted keyring.
*/
static int __init load_ipl_certs(void)
{
void *ptr, *end;
unsigned int len;
if (!ipl_cert_list_addr)
return 0;
/* Copy the certificates to the platform keyring */
ptr = __va(ipl_cert_list_addr);
end = ptr + ipl_cert_list_size;
while ((void *) ptr < end) {
len = *(unsigned int *) ptr;
ptr += sizeof(unsigned int);
add_to_platform_keyring("IPL:db", ptr, len);
ptr += len;
}
return 0;
}
late_initcall(load_ipl_certs);
@@ -0,0 +1,160 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 IBM Corporation
* Author: Nayna Jain
*
* - loads keys and hashes stored and controlled by the firmware.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <asm/secure_boot.h>
#include <asm/secvar.h>
#include "keyring_handler.h"
#include "../integrity.h"
#define extract_esl(db, data, size, offset) \
do { db = data + offset; size = size - offset; } while (0)
/*
* Get a certificate list blob from the named secure variable.
*
* Returns:
* - a pointer to a kmalloc'd buffer containing the cert list on success
* - NULL if the key does not exist
* - an ERR_PTR on error
*/
static __init void *get_cert_list(u8 *key, unsigned long keylen, u64 *size)
{
int rc;
void *db;
rc = secvar_ops->get(key, keylen, NULL, size);
if (rc) {
if (rc == -ENOENT)
return NULL;
return ERR_PTR(rc);
}
db = kmalloc(*size, GFP_KERNEL);
if (!db)
return ERR_PTR(-ENOMEM);
rc = secvar_ops->get(key, keylen, db, size);
if (rc) {
kfree(db);
return ERR_PTR(rc);
}
return db;
}
/*
* Load the certs contained in the keys databases into the platform trusted
* keyring and the blacklisted X.509 cert SHA256 hashes into the blacklist
* keyring.
*/
static int __init load_powerpc_certs(void)
{
void *db = NULL, *dbx = NULL, *data = NULL;
void *trustedca;
void *moduledb;
u64 dsize = 0;
u64 offset = 0;
int rc = 0;
ssize_t len;
char buf[32];
if (!secvar_ops)
return -ENODEV;
len = secvar_ops->format(buf, sizeof(buf));
if (len <= 0)
return -ENODEV;
// Check for known secure boot implementations from OPAL or PLPKS
if (strcmp("ibm,edk2-compat-v1", buf) && strcmp("ibm,plpks-sb-v1", buf) &&
strcmp("ibm,plpks-sb-v0", buf)) {
pr_err("Unsupported secvar implementation \"%s\", not loading certs\n", buf);
return -ENODEV;
}
if (strcmp("ibm,plpks-sb-v1", buf) == 0 || strcmp("ibm,plpks-sb-v0", buf) == 0)
/* PLPKS authenticated variables ESL data is prefixed with 8 bytes of timestamp */
offset = 8;
/*
* Get db, and dbx. They might not exist, so it isn't an error if we
* can't get them.
*/
data = get_cert_list("db", 3, &dsize);
if (!data) {
pr_info("Couldn't get db list from firmware\n");
} else if (IS_ERR(data)) {
rc = PTR_ERR(data);
pr_err("Error reading db from firmware: %d\n", rc);
return rc;
} else {
extract_esl(db, data, dsize, offset);
rc = parse_efi_signature_list("powerpc:db", db, dsize,
get_handler_for_db);
if (rc)
pr_err("Couldn't parse db signatures: %d\n", rc);
kfree(data);
}
data = get_cert_list("dbx", 4, &dsize);
if (!data) {
pr_info("Couldn't get dbx list from firmware\n");
} else if (IS_ERR(data)) {
rc = PTR_ERR(data);
pr_err("Error reading dbx from firmware: %d\n", rc);
return rc;
} else {
extract_esl(dbx, data, dsize, offset);
rc = parse_efi_signature_list("powerpc:dbx", dbx, dsize,
get_handler_for_dbx);
if (rc)
pr_err("Couldn't parse dbx signatures: %d\n", rc);
kfree(data);
}
data = get_cert_list("trustedcadb", 12, &dsize);
if (!data) {
pr_info("Couldn't get trustedcadb list from firmware\n");
} else if (IS_ERR(data)) {
rc = PTR_ERR(data);
pr_err("Error reading trustedcadb from firmware: %d\n", rc);
} else {
extract_esl(trustedca, data, dsize, offset);
rc = parse_efi_signature_list("powerpc:trustedca", trustedca, dsize,
get_handler_for_ca_keys);
if (rc)
pr_err("Couldn't parse trustedcadb signatures: %d\n", rc);
kfree(data);
}
data = get_cert_list("moduledb", 9, &dsize);
if (!data) {
pr_info("Couldn't get moduledb list from firmware\n");
} else if (IS_ERR(data)) {
rc = PTR_ERR(data);
pr_err("Error reading moduledb from firmware: %d\n", rc);
} else {
extract_esl(moduledb, data, dsize, offset);
rc = parse_efi_signature_list("powerpc:moduledb", moduledb, dsize,
get_handler_for_code_signing_keys);
if (rc)
pr_err("Couldn't parse moduledb signatures: %d\n", rc);
kfree(data);
}
return rc;
}
late_initcall(load_powerpc_certs);
@@ -0,0 +1,238 @@
// SPDX-License-Identifier: GPL-2.0
#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>
#include <linux/ima.h>
#include <keys/asymmetric-type.h>
#include <keys/system_keyring.h>
#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") },
{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "iMacPro1,1") },
{ }
};
/*
* Look to see if a UEFI variable called MokIgnoreDB exists and return true if
* it does.
*
* This UEFI variable is set by the shim if a user tells the shim to not use
* the certs/hashes in the UEFI db variable for verification purposes. If it
* is set, we should ignore the db variable also and the true return indicates
* this.
*/
static __init bool uefi_check_ignore_db(void)
{
efi_status_t status;
unsigned int db = 0;
unsigned long size = sizeof(db);
efi_guid_t guid = EFI_SHIM_LOCK_GUID;
status = efi.get_variable(L"MokIgnoreDB", &guid, NULL, &size, &db);
return status == EFI_SUCCESS;
}
/*
* Get a certificate list blob from the named EFI variable.
*/
static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid,
unsigned long *size, efi_status_t *status)
{
unsigned long lsize = 4;
unsigned long tmpdb[4];
void *db;
*status = efi.get_variable(name, guid, NULL, &lsize, &tmpdb);
if (*status == EFI_NOT_FOUND)
return NULL;
if (*status != EFI_BUFFER_TOO_SMALL) {
pr_err("Couldn't get size: 0x%lx\n", *status);
return NULL;
}
db = kmalloc(lsize, GFP_KERNEL);
if (!db)
return NULL;
*status = efi.get_variable(name, guid, NULL, &lsize, db);
if (*status != EFI_SUCCESS) {
kfree(db);
pr_err("Error reading db var: 0x%lx\n", *status);
return NULL;
}
*size = lsize;
return db;
}
/*
* load_moklist_certs() - Load MokList certs
*
* Load the certs contained in the UEFI MokListRT database into the
* platform trusted keyring.
*
* This routine checks the EFI MOK config table first. If and only if
* that fails, this routine uses the MokListRT ordinary UEFI variable.
*
* Return: Status
*/
static int __init load_moklist_certs(void)
{
struct efi_mokvar_table_entry *mokvar_entry;
efi_guid_t mok_var = EFI_SHIM_LOCK_GUID;
void *mok;
unsigned long moksize;
efi_status_t status;
int rc;
/* First try to load certs from the EFI MOKvar config table.
* It's not an error if the MOKvar config table doesn't exist
* or the MokListRT entry is not found in it.
*/
mokvar_entry = efi_mokvar_entry_find("MokListRT");
if (mokvar_entry) {
rc = parse_efi_signature_list("UEFI:MokListRT (MOKvar table)",
mokvar_entry->data,
mokvar_entry->data_size,
get_handler_for_mok);
/* All done if that worked. */
if (!rc)
return rc;
pr_err("Couldn't parse MokListRT signatures from EFI MOKvar config table: %d\n",
rc);
}
/* Get MokListRT. It might not exist, so it isn't an error
* if we can't get it.
*/
mok = get_cert_list(L"MokListRT", &mok_var, &moksize, &status);
if (mok) {
rc = parse_efi_signature_list("UEFI:MokListRT",
mok, moksize, get_handler_for_mok);
kfree(mok);
if (rc)
pr_err("Couldn't parse MokListRT signatures: %d\n", rc);
return rc;
}
if (status == EFI_NOT_FOUND)
pr_debug("MokListRT variable wasn't found\n");
else
pr_info("Couldn't get UEFI MokListRT\n");
return 0;
}
/*
* load_uefi_certs() - Load certs from UEFI sources
*
* Load the certs contained in the UEFI databases into the platform trusted
* keyring and the UEFI blacklisted X.509 cert SHA256 hashes into the blacklist
* keyring.
*/
static int __init load_uefi_certs(void)
{
efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
efi_guid_t mok_var = EFI_SHIM_LOCK_GUID;
void *db = NULL, *dbx = NULL, *mokx = NULL;
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;
/* Get db and dbx. They might not exist, so it isn't an error
* if we can't get them.
*/
if (!uefi_check_ignore_db()) {
db = get_cert_list(L"db", &secure_var, &dbsize, &status);
if (!db) {
if (status == EFI_NOT_FOUND)
pr_debug("MODSIGN: db variable wasn't found\n");
else
pr_err("MODSIGN: Couldn't get UEFI db list\n");
} else {
rc = parse_efi_signature_list("UEFI:db",
db, dbsize, get_handler_for_db);
if (rc)
pr_err("Couldn't parse db signatures: %d\n",
rc);
kfree(db);
}
}
dbx = get_cert_list(L"dbx", &secure_var, &dbxsize, &status);
if (!dbx) {
if (status == EFI_NOT_FOUND)
pr_debug("dbx variable wasn't found\n");
else
pr_info("Couldn't get UEFI dbx list\n");
} else {
rc = parse_efi_signature_list("UEFI:dbx",
dbx, dbxsize,
get_handler_for_dbx);
if (rc)
pr_err("Couldn't parse dbx signatures: %d\n", rc);
kfree(dbx);
}
/* the MOK/MOKx can not be trusted when secure boot is disabled */
if (!arch_get_secureboot())
return 0;
mokx = get_cert_list(L"MokListXRT", &mok_var, &mokxsize, &status);
if (!mokx) {
if (status == EFI_NOT_FOUND)
pr_debug("mokx variable wasn't found\n");
else
pr_info("Couldn't get mokx list\n");
} else {
rc = parse_efi_signature_list("UEFI:MokListXRT",
mokx, mokxsize,
get_handler_for_dbx);
if (rc)
pr_err("Couldn't parse mokx signatures %d\n", rc);
kfree(mokx);
}
/* Load the MokListRT certs */
rc = load_moklist_certs();
return rc;
}
late_initcall(load_uefi_certs);
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Machine keyring routines.
*
* Copyright (c) 2021, Oracle and/or its affiliates.
*/
#include <linux/efi.h>
#include "../integrity.h"
static __init int machine_keyring_init(void)
{
int rc;
rc = integrity_init_keyring(INTEGRITY_KEYRING_MACHINE);
if (rc)
return rc;
pr_notice("Machine keyring initialized\n");
return 0;
}
device_initcall(machine_keyring_init);
void __init add_to_machine_keyring(const char *source, const void *data, size_t len)
{
key_perm_t perm;
int rc;
perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW;
rc = integrity_load_cert(INTEGRITY_KEYRING_MACHINE, source, data, len, perm);
/*
* Some MOKList keys may not pass the machine keyring restrictions.
* If the restriction check does not pass and the platform keyring
* is configured, try to add it into that keyring instead.
*/
if (rc && efi_enabled(EFI_BOOT) &&
IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING))
rc = integrity_load_cert(INTEGRITY_KEYRING_PLATFORM, source,
data, len, perm);
if (rc)
pr_info("Error adding keys to machine keyring %s\n", source);
}
/*
* Try to load the MokListTrustedRT MOK variable to see if we should trust
* the MOK keys within the kernel. It is not an error if this variable
* does not exist. If it does not exist, MOK keys should not be trusted
* within the machine keyring.
*/
static __init bool uefi_check_trust_mok_keys(void)
{
struct efi_mokvar_table_entry *mokvar_entry;
mokvar_entry = efi_mokvar_entry_find("MokListTrustedRT");
if (mokvar_entry)
return true;
return false;
}
static bool __init trust_moklist(void)
{
static bool initialized;
static bool trust_mok;
if (!initialized) {
initialized = true;
trust_mok = false;
if (uefi_check_trust_mok_keys())
trust_mok = true;
}
return trust_mok;
}
/*
* Provides platform specific check for trusting imputed keys before loading
* on .machine keyring. UEFI systems enable this trust based on a variable,
* and for other platforms, it is always enabled.
*/
bool __init imputed_trust_enabled(void)
{
if (efi_enabled(EFI_BOOT))
return trust_moklist();
return true;
}
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Platform keyring for firmware/platform keys
*
* Copyright IBM Corporation, 2018
* Author(s): Nayna Jain <nayna@linux.ibm.com>
*/
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/slab.h>
#include "../integrity.h"
/**
* add_to_platform_keyring - Add to platform keyring without validation.
* @source: Source of key
* @data: The blob holding the key
* @len: The length of the data blob
*
* Add a key to the platform keyring without checking its trust chain. This
* is available only during kernel initialisation.
*/
void __init add_to_platform_keyring(const char *source, const void *data,
size_t len)
{
key_perm_t perm;
int rc;
perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW;
rc = integrity_load_cert(INTEGRITY_KEYRING_PLATFORM, source, data, len,
perm);
if (rc)
pr_info("Error adding keys to platform keyring %s\n", source);
}
/*
* Create the trusted keyrings.
*/
static __init int platform_keyring_init(void)
{
int rc;
rc = integrity_init_keyring(INTEGRITY_KEYRING_PLATFORM);
if (rc)
return rc;
pr_notice("Platform Keyring initialized\n");
return 0;
}
/*
* Must be initialised before we try and load the keys into the keyring.
*/
device_initcall(platform_keyring_init);