Merge branch 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity
Pull integrity updates from Mimi Zohar: "The major feature in this time is IMA support for measuring and appraising appended file signatures. In addition are a couple of bug fixes and code cleanup to use struct_size(). In addition to the PE/COFF and IMA xattr signatures, the kexec kernel image may be signed with an appended signature, using the same scripts/sign-file tool that is used to sign kernel modules. Similarly, the initramfs may contain an appended signature. This contained a lot of refactoring of the existing appended signature verification code, so that IMA could retain the existing framework of calculating the file hash once, storing it in the IMA measurement list and extending the TPM, verifying the file's integrity based on a file hash or signature (eg. xattrs), and adding an audit record containing the file hash, all based on policy. (The IMA support for appended signatures patch set was posted and reviewed 11 times.) The support for appended signature paves the way for adding other signature verification methods, such as fs-verity, based on a single system-wide policy. The file hash used for verifying the signature and the signature, itself, can be included in the IMA measurement list" * 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity: ima: ima_api: Use struct_size() in kzalloc() ima: use struct_size() in kzalloc() sefltest/ima: support appended signatures (modsig) ima: Fix use after free in ima_read_modsig() MODSIGN: make new include file self contained ima: fix freeing ongoing ahash_request ima: always return negative code for error ima: Store the measurement again when appraising a modsig ima: Define ima-modsig template ima: Collect modsig ima: Implement support for module-style appended signatures ima: Factor xattr_verify() out of ima_appraise_measurement() ima: Add modsig appraise_type option for module-style appended signatures integrity: Select CONFIG_KEYS instead of depending on it PKCS#7: Introduce pkcs7_get_digest() PKCS#7: Refactor verify_pkcs7_signature() MODSIGN: Export module signature definitions ima: initialize the "template" field with the default template
This commit is contained in:
commit
f1f2f614d5
|
@ -37,7 +37,7 @@ Description:
|
||||||
euid:= decimal value
|
euid:= decimal value
|
||||||
fowner:= decimal value
|
fowner:= decimal value
|
||||||
lsm: are LSM specific
|
lsm: are LSM specific
|
||||||
option: appraise_type:= [imasig]
|
option: appraise_type:= [imasig] [imasig|modsig]
|
||||||
template:= name of a defined IMA template type
|
template:= name of a defined IMA template type
|
||||||
(eg, ima-ng). Only valid when action is "measure".
|
(eg, ima-ng). Only valid when action is "measure".
|
||||||
pcr:= decimal value
|
pcr:= decimal value
|
||||||
|
@ -105,3 +105,7 @@ Description:
|
||||||
|
|
||||||
measure func=KEXEC_KERNEL_CHECK pcr=4
|
measure func=KEXEC_KERNEL_CHECK pcr=4
|
||||||
measure func=KEXEC_INITRAMFS_CHECK pcr=5
|
measure func=KEXEC_INITRAMFS_CHECK pcr=5
|
||||||
|
|
||||||
|
Example of appraise rule allowing modsig appended signatures:
|
||||||
|
|
||||||
|
appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig|modsig
|
||||||
|
|
|
@ -68,8 +68,10 @@ descriptors by adding their identifier to the format string
|
||||||
- 'd-ng': the digest of the event, calculated with an arbitrary hash
|
- 'd-ng': the digest of the event, calculated with an arbitrary hash
|
||||||
algorithm (field format: [<hash algo>:]digest, where the digest
|
algorithm (field format: [<hash algo>:]digest, where the digest
|
||||||
prefix is shown only if the hash algorithm is not SHA1 or MD5);
|
prefix is shown only if the hash algorithm is not SHA1 or MD5);
|
||||||
|
- 'd-modsig': the digest of the event without the appended modsig;
|
||||||
- 'n-ng': the name of the event, without size limitations;
|
- 'n-ng': the name of the event, without size limitations;
|
||||||
- 'sig': the file signature;
|
- 'sig': the file signature;
|
||||||
|
- 'modsig' the appended file signature;
|
||||||
- 'buf': the buffer data that was used to generate the hash without size limitations;
|
- 'buf': the buffer data that was used to generate the hash without size limitations;
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,6 +81,7 @@ Below, there is the list of defined template descriptors:
|
||||||
- "ima-ng" (default): its format is ``d-ng|n-ng``;
|
- "ima-ng" (default): its format is ``d-ng|n-ng``;
|
||||||
- "ima-sig": its format is ``d-ng|n-ng|sig``;
|
- "ima-sig": its format is ``d-ng|n-ng|sig``;
|
||||||
- "ima-buf": its format is ``d-ng|n-ng|buf``;
|
- "ima-buf": its format is ``d-ng|n-ng|buf``;
|
||||||
|
- "ima-modsig": its format is ``d-ng|n-ng|sig|d-modsig|modsig``;
|
||||||
|
|
||||||
|
|
||||||
Use
|
Use
|
||||||
|
|
|
@ -556,7 +556,7 @@ config ARCH_HAS_KEXEC_PURGATORY
|
||||||
|
|
||||||
config KEXEC_VERIFY_SIG
|
config KEXEC_VERIFY_SIG
|
||||||
bool "Verify kernel signature during kexec_file_load() syscall"
|
bool "Verify kernel signature during kexec_file_load() syscall"
|
||||||
depends on KEXEC_FILE && SYSTEM_DATA_VERIFICATION
|
depends on KEXEC_FILE && MODULE_SIG_FORMAT
|
||||||
help
|
help
|
||||||
This option makes kernel signature verification mandatory for
|
This option makes kernel signature verification mandatory for
|
||||||
the kexec_file_load() syscall.
|
the kexec_file_load() syscall.
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <linux/elf.h>
|
#include <linux/elf.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/kexec.h>
|
#include <linux/kexec.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module_signature.h>
|
||||||
#include <linux/verification.h>
|
#include <linux/verification.h>
|
||||||
#include <asm/boot_data.h>
|
#include <asm/boot_data.h>
|
||||||
#include <asm/ipl.h>
|
#include <asm/ipl.h>
|
||||||
|
@ -23,28 +23,6 @@ const struct kexec_file_ops * const kexec_file_loaders[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_KEXEC_VERIFY_SIG
|
#ifdef CONFIG_KEXEC_VERIFY_SIG
|
||||||
/*
|
|
||||||
* Module signature information block.
|
|
||||||
*
|
|
||||||
* The constituents of the signature section are, in order:
|
|
||||||
*
|
|
||||||
* - Signer's name
|
|
||||||
* - Key identifier
|
|
||||||
* - Signature data
|
|
||||||
* - Information block
|
|
||||||
*/
|
|
||||||
struct module_signature {
|
|
||||||
u8 algo; /* Public-key crypto algorithm [0] */
|
|
||||||
u8 hash; /* Digest algorithm [0] */
|
|
||||||
u8 id_type; /* Key identifier type [PKEY_ID_PKCS7] */
|
|
||||||
u8 signer_len; /* Length of signer's name [0] */
|
|
||||||
u8 key_id_len; /* Length of key identifier [0] */
|
|
||||||
u8 __pad[3];
|
|
||||||
__be32 sig_len; /* Length of signature data */
|
|
||||||
};
|
|
||||||
|
|
||||||
#define PKEY_ID_PKCS7 2
|
|
||||||
|
|
||||||
int s390_verify_sig(const char *kernel, unsigned long kernel_len)
|
int s390_verify_sig(const char *kernel, unsigned long kernel_len)
|
||||||
{
|
{
|
||||||
const unsigned long marker_len = sizeof(MODULE_SIG_STRING) - 1;
|
const unsigned long marker_len = sizeof(MODULE_SIG_STRING) - 1;
|
||||||
|
|
|
@ -190,33 +190,27 @@ late_initcall(load_system_certificate_list);
|
||||||
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
|
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* verify_pkcs7_signature - Verify a PKCS#7-based signature on system data.
|
* verify_pkcs7_message_sig - Verify a PKCS#7-based signature on system data.
|
||||||
* @data: The data to be verified (NULL if expecting internal data).
|
* @data: The data to be verified (NULL if expecting internal data).
|
||||||
* @len: Size of @data.
|
* @len: Size of @data.
|
||||||
* @raw_pkcs7: The PKCS#7 message that is the signature.
|
* @pkcs7: The PKCS#7 message that is the signature.
|
||||||
* @pkcs7_len: The size of @raw_pkcs7.
|
|
||||||
* @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only,
|
* @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only,
|
||||||
* (void *)1UL for all trusted keys).
|
* (void *)1UL for all trusted keys).
|
||||||
* @usage: The use to which the key is being put.
|
* @usage: The use to which the key is being put.
|
||||||
* @view_content: Callback to gain access to content.
|
* @view_content: Callback to gain access to content.
|
||||||
* @ctx: Context for callback.
|
* @ctx: Context for callback.
|
||||||
*/
|
*/
|
||||||
int verify_pkcs7_signature(const void *data, size_t len,
|
int verify_pkcs7_message_sig(const void *data, size_t len,
|
||||||
const void *raw_pkcs7, size_t pkcs7_len,
|
struct pkcs7_message *pkcs7,
|
||||||
struct key *trusted_keys,
|
struct key *trusted_keys,
|
||||||
enum key_being_used_for usage,
|
enum key_being_used_for usage,
|
||||||
int (*view_content)(void *ctx,
|
int (*view_content)(void *ctx,
|
||||||
const void *data, size_t len,
|
const void *data, size_t len,
|
||||||
size_t asn1hdrlen),
|
size_t asn1hdrlen),
|
||||||
void *ctx)
|
void *ctx)
|
||||||
{
|
{
|
||||||
struct pkcs7_message *pkcs7;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len);
|
|
||||||
if (IS_ERR(pkcs7))
|
|
||||||
return PTR_ERR(pkcs7);
|
|
||||||
|
|
||||||
/* The data should be detached - so we need to supply it. */
|
/* The data should be detached - so we need to supply it. */
|
||||||
if (data && pkcs7_supply_detached_data(pkcs7, data, len) < 0) {
|
if (data && pkcs7_supply_detached_data(pkcs7, data, len) < 0) {
|
||||||
pr_err("PKCS#7 signature with non-detached data\n");
|
pr_err("PKCS#7 signature with non-detached data\n");
|
||||||
|
@ -269,6 +263,41 @@ int verify_pkcs7_signature(const void *data, size_t len,
|
||||||
}
|
}
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* verify_pkcs7_signature - Verify a PKCS#7-based signature on system data.
|
||||||
|
* @data: The data to be verified (NULL if expecting internal data).
|
||||||
|
* @len: Size of @data.
|
||||||
|
* @raw_pkcs7: The PKCS#7 message that is the signature.
|
||||||
|
* @pkcs7_len: The size of @raw_pkcs7.
|
||||||
|
* @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only,
|
||||||
|
* (void *)1UL for all trusted keys).
|
||||||
|
* @usage: The use to which the key is being put.
|
||||||
|
* @view_content: Callback to gain access to content.
|
||||||
|
* @ctx: Context for callback.
|
||||||
|
*/
|
||||||
|
int verify_pkcs7_signature(const void *data, size_t len,
|
||||||
|
const void *raw_pkcs7, size_t pkcs7_len,
|
||||||
|
struct key *trusted_keys,
|
||||||
|
enum key_being_used_for usage,
|
||||||
|
int (*view_content)(void *ctx,
|
||||||
|
const void *data, size_t len,
|
||||||
|
size_t asn1hdrlen),
|
||||||
|
void *ctx)
|
||||||
|
{
|
||||||
|
struct pkcs7_message *pkcs7;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len);
|
||||||
|
if (IS_ERR(pkcs7))
|
||||||
|
return PTR_ERR(pkcs7);
|
||||||
|
|
||||||
|
ret = verify_pkcs7_message_sig(data, len, pkcs7, trusted_keys, usage,
|
||||||
|
view_content, ctx);
|
||||||
|
|
||||||
pkcs7_free_message(pkcs7);
|
pkcs7_free_message(pkcs7);
|
||||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/asn1.h>
|
#include <linux/asn1.h>
|
||||||
#include <crypto/hash.h>
|
#include <crypto/hash.h>
|
||||||
|
#include <crypto/hash_info.h>
|
||||||
#include <crypto/public_key.h>
|
#include <crypto/public_key.h>
|
||||||
#include "pkcs7_parser.h"
|
#include "pkcs7_parser.h"
|
||||||
|
|
||||||
|
@ -29,6 +30,10 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
|
||||||
|
|
||||||
kenter(",%u,%s", sinfo->index, sinfo->sig->hash_algo);
|
kenter(",%u,%s", sinfo->index, sinfo->sig->hash_algo);
|
||||||
|
|
||||||
|
/* The digest was calculated already. */
|
||||||
|
if (sig->digest)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (!sinfo->sig->hash_algo)
|
if (!sinfo->sig->hash_algo)
|
||||||
return -ENOPKG;
|
return -ENOPKG;
|
||||||
|
|
||||||
|
@ -117,6 +122,34 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int pkcs7_get_digest(struct pkcs7_message *pkcs7, const u8 **buf, u32 *len,
|
||||||
|
enum hash_algo *hash_algo)
|
||||||
|
{
|
||||||
|
struct pkcs7_signed_info *sinfo = pkcs7->signed_infos;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function doesn't support messages with more than one signature.
|
||||||
|
*/
|
||||||
|
if (sinfo == NULL || sinfo->next != NULL)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
ret = pkcs7_digest(pkcs7, sinfo);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
*buf = sinfo->sig->digest;
|
||||||
|
*len = sinfo->sig->digest_size;
|
||||||
|
|
||||||
|
for (i = 0; i < HASH_ALGO__LAST; i++)
|
||||||
|
if (!strcmp(hash_algo_name[i], sinfo->sig->hash_algo)) {
|
||||||
|
*hash_algo = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7
|
* Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7
|
||||||
* uses the issuer's name and the issuing certificate serial number for
|
* uses the issuer's name and the issuing certificate serial number for
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#define _CRYPTO_PKCS7_H
|
#define _CRYPTO_PKCS7_H
|
||||||
|
|
||||||
#include <linux/verification.h>
|
#include <linux/verification.h>
|
||||||
|
#include <linux/hash_info.h>
|
||||||
#include <crypto/public_key.h>
|
#include <crypto/public_key.h>
|
||||||
|
|
||||||
struct key;
|
struct key;
|
||||||
|
@ -40,4 +41,7 @@ extern int pkcs7_verify(struct pkcs7_message *pkcs7,
|
||||||
extern int pkcs7_supply_detached_data(struct pkcs7_message *pkcs7,
|
extern int pkcs7_supply_detached_data(struct pkcs7_message *pkcs7,
|
||||||
const void *data, size_t datalen);
|
const void *data, size_t datalen);
|
||||||
|
|
||||||
|
extern int pkcs7_get_digest(struct pkcs7_message *pkcs7, const u8 **buf,
|
||||||
|
u32 *len, enum hash_algo *hash_algo);
|
||||||
|
|
||||||
#endif /* _CRYPTO_PKCS7_H */
|
#endif /* _CRYPTO_PKCS7_H */
|
||||||
|
|
|
@ -26,9 +26,6 @@
|
||||||
#include <linux/percpu.h>
|
#include <linux/percpu.h>
|
||||||
#include <asm/module.h>
|
#include <asm/module.h>
|
||||||
|
|
||||||
/* In stripped ARM and x86-64 modules, ~ is surprisingly rare. */
|
|
||||||
#define MODULE_SIG_STRING "~Module signature appended~\n"
|
|
||||||
|
|
||||||
/* Not Yet Implemented */
|
/* Not Yet Implemented */
|
||||||
#define MODULE_SUPPORTED_DEVICE(name)
|
#define MODULE_SUPPORTED_DEVICE(name)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||||
|
/*
|
||||||
|
* Module signature handling.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
||||||
|
* Written by David Howells (dhowells@redhat.com)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LINUX_MODULE_SIGNATURE_H
|
||||||
|
#define _LINUX_MODULE_SIGNATURE_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
/* In stripped ARM and x86-64 modules, ~ is surprisingly rare. */
|
||||||
|
#define MODULE_SIG_STRING "~Module signature appended~\n"
|
||||||
|
|
||||||
|
enum pkey_id_type {
|
||||||
|
PKEY_ID_PGP, /* OpenPGP generated key ID */
|
||||||
|
PKEY_ID_X509, /* X.509 arbitrary subjectKeyIdentifier */
|
||||||
|
PKEY_ID_PKCS7, /* Signature in PKCS#7 message */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Module signature information block.
|
||||||
|
*
|
||||||
|
* The constituents of the signature section are, in order:
|
||||||
|
*
|
||||||
|
* - Signer's name
|
||||||
|
* - Key identifier
|
||||||
|
* - Signature data
|
||||||
|
* - Information block
|
||||||
|
*/
|
||||||
|
struct module_signature {
|
||||||
|
u8 algo; /* Public-key crypto algorithm [0] */
|
||||||
|
u8 hash; /* Digest algorithm [0] */
|
||||||
|
u8 id_type; /* Key identifier type [PKEY_ID_PKCS7] */
|
||||||
|
u8 signer_len; /* Length of signer's name [0] */
|
||||||
|
u8 key_id_len; /* Length of key identifier [0] */
|
||||||
|
u8 __pad[3];
|
||||||
|
__be32 sig_len; /* Length of signature data */
|
||||||
|
};
|
||||||
|
|
||||||
|
int mod_check_sig(const struct module_signature *ms, size_t file_len,
|
||||||
|
const char *name);
|
||||||
|
|
||||||
|
#endif /* _LINUX_MODULE_SIGNATURE_H */
|
|
@ -32,6 +32,7 @@ extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR];
|
||||||
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
|
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
|
||||||
|
|
||||||
struct key;
|
struct key;
|
||||||
|
struct pkcs7_message;
|
||||||
|
|
||||||
extern int verify_pkcs7_signature(const void *data, size_t len,
|
extern int verify_pkcs7_signature(const void *data, size_t len,
|
||||||
const void *raw_pkcs7, size_t pkcs7_len,
|
const void *raw_pkcs7, size_t pkcs7_len,
|
||||||
|
@ -41,6 +42,15 @@ extern int verify_pkcs7_signature(const void *data, size_t len,
|
||||||
const void *data, size_t len,
|
const void *data, size_t len,
|
||||||
size_t asn1hdrlen),
|
size_t asn1hdrlen),
|
||||||
void *ctx);
|
void *ctx);
|
||||||
|
extern int verify_pkcs7_message_sig(const void *data, size_t len,
|
||||||
|
struct pkcs7_message *pkcs7,
|
||||||
|
struct key *trusted_keys,
|
||||||
|
enum key_being_used_for usage,
|
||||||
|
int (*view_content)(void *ctx,
|
||||||
|
const void *data,
|
||||||
|
size_t len,
|
||||||
|
size_t asn1hdrlen),
|
||||||
|
void *ctx);
|
||||||
|
|
||||||
#ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION
|
#ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION
|
||||||
extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
||||||
|
|
|
@ -1963,6 +1963,10 @@ config BASE_SMALL
|
||||||
default 0 if BASE_FULL
|
default 0 if BASE_FULL
|
||||||
default 1 if !BASE_FULL
|
default 1 if !BASE_FULL
|
||||||
|
|
||||||
|
config MODULE_SIG_FORMAT
|
||||||
|
def_bool n
|
||||||
|
select SYSTEM_DATA_VERIFICATION
|
||||||
|
|
||||||
menuconfig MODULES
|
menuconfig MODULES
|
||||||
bool "Enable loadable module support"
|
bool "Enable loadable module support"
|
||||||
option modules
|
option modules
|
||||||
|
@ -2047,7 +2051,7 @@ config MODULE_SRCVERSION_ALL
|
||||||
|
|
||||||
config MODULE_SIG
|
config MODULE_SIG
|
||||||
bool "Module signature verification"
|
bool "Module signature verification"
|
||||||
select SYSTEM_DATA_VERIFICATION
|
select MODULE_SIG_FORMAT
|
||||||
help
|
help
|
||||||
Check modules for valid signatures upon load: the signature
|
Check modules for valid signatures upon load: the signature
|
||||||
is simply appended to the module. For more information see
|
is simply appended to the module. For more information see
|
||||||
|
|
|
@ -58,6 +58,7 @@ endif
|
||||||
obj-$(CONFIG_UID16) += uid16.o
|
obj-$(CONFIG_UID16) += uid16.o
|
||||||
obj-$(CONFIG_MODULES) += module.o
|
obj-$(CONFIG_MODULES) += module.o
|
||||||
obj-$(CONFIG_MODULE_SIG) += module_signing.o
|
obj-$(CONFIG_MODULE_SIG) += module_signing.o
|
||||||
|
obj-$(CONFIG_MODULE_SIG_FORMAT) += module_signature.o
|
||||||
obj-$(CONFIG_KALLSYMS) += kallsyms.o
|
obj-$(CONFIG_KALLSYMS) += kallsyms.o
|
||||||
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
|
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
|
||||||
obj-$(CONFIG_CRASH_CORE) += crash_core.o
|
obj-$(CONFIG_CRASH_CORE) += crash_core.o
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/extable.h>
|
#include <linux/extable.h>
|
||||||
#include <linux/moduleloader.h>
|
#include <linux/moduleloader.h>
|
||||||
|
#include <linux/module_signature.h>
|
||||||
#include <linux/trace_events.h>
|
#include <linux/trace_events.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kallsyms.h>
|
#include <linux/kallsyms.h>
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Module signature checker
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
||||||
|
* Written by David Howells (dhowells@redhat.com)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/module_signature.h>
|
||||||
|
#include <asm/byteorder.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mod_check_sig - check that the given signature is sane
|
||||||
|
*
|
||||||
|
* @ms: Signature to check.
|
||||||
|
* @file_len: Size of the file to which @ms is appended.
|
||||||
|
* @name: What is being checked. Used for error messages.
|
||||||
|
*/
|
||||||
|
int mod_check_sig(const struct module_signature *ms, size_t file_len,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
if (be32_to_cpu(ms->sig_len) >= file_len - sizeof(*ms))
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
if (ms->id_type != PKEY_ID_PKCS7) {
|
||||||
|
pr_err("%s: Module is not signed with expected PKCS#7 message\n",
|
||||||
|
name);
|
||||||
|
return -ENOPKG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ms->algo != 0 ||
|
||||||
|
ms->hash != 0 ||
|
||||||
|
ms->signer_len != 0 ||
|
||||||
|
ms->key_id_len != 0 ||
|
||||||
|
ms->__pad[0] != 0 ||
|
||||||
|
ms->__pad[1] != 0 ||
|
||||||
|
ms->__pad[2] != 0) {
|
||||||
|
pr_err("%s: PKCS#7 signature info has unexpected non-zero params\n",
|
||||||
|
name);
|
||||||
|
return -EBADMSG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -7,37 +7,13 @@
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/module_signature.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/verification.h>
|
#include <linux/verification.h>
|
||||||
#include <crypto/public_key.h>
|
#include <crypto/public_key.h>
|
||||||
#include "module-internal.h"
|
#include "module-internal.h"
|
||||||
|
|
||||||
enum pkey_id_type {
|
|
||||||
PKEY_ID_PGP, /* OpenPGP generated key ID */
|
|
||||||
PKEY_ID_X509, /* X.509 arbitrary subjectKeyIdentifier */
|
|
||||||
PKEY_ID_PKCS7, /* Signature in PKCS#7 message */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Module signature information block.
|
|
||||||
*
|
|
||||||
* The constituents of the signature section are, in order:
|
|
||||||
*
|
|
||||||
* - Signer's name
|
|
||||||
* - Key identifier
|
|
||||||
* - Signature data
|
|
||||||
* - Information block
|
|
||||||
*/
|
|
||||||
struct module_signature {
|
|
||||||
u8 algo; /* Public-key crypto algorithm [0] */
|
|
||||||
u8 hash; /* Digest algorithm [0] */
|
|
||||||
u8 id_type; /* Key identifier type [PKEY_ID_PKCS7] */
|
|
||||||
u8 signer_len; /* Length of signer's name [0] */
|
|
||||||
u8 key_id_len; /* Length of key identifier [0] */
|
|
||||||
u8 __pad[3];
|
|
||||||
__be32 sig_len; /* Length of signature data */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Verify the signature on a module.
|
* Verify the signature on a module.
|
||||||
*/
|
*/
|
||||||
|
@ -45,6 +21,7 @@ int mod_verify_sig(const void *mod, struct load_info *info)
|
||||||
{
|
{
|
||||||
struct module_signature ms;
|
struct module_signature ms;
|
||||||
size_t sig_len, modlen = info->len;
|
size_t sig_len, modlen = info->len;
|
||||||
|
int ret;
|
||||||
|
|
||||||
pr_devel("==>%s(,%zu)\n", __func__, modlen);
|
pr_devel("==>%s(,%zu)\n", __func__, modlen);
|
||||||
|
|
||||||
|
@ -52,32 +29,15 @@ int mod_verify_sig(const void *mod, struct load_info *info)
|
||||||
return -EBADMSG;
|
return -EBADMSG;
|
||||||
|
|
||||||
memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms));
|
memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms));
|
||||||
modlen -= sizeof(ms);
|
|
||||||
|
ret = mod_check_sig(&ms, modlen, info->name);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
sig_len = be32_to_cpu(ms.sig_len);
|
sig_len = be32_to_cpu(ms.sig_len);
|
||||||
if (sig_len >= modlen)
|
modlen -= sig_len + sizeof(ms);
|
||||||
return -EBADMSG;
|
|
||||||
modlen -= sig_len;
|
|
||||||
info->len = modlen;
|
info->len = modlen;
|
||||||
|
|
||||||
if (ms.id_type != PKEY_ID_PKCS7) {
|
|
||||||
pr_err("%s: Module is not signed with expected PKCS#7 message\n",
|
|
||||||
info->name);
|
|
||||||
return -ENOPKG;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ms.algo != 0 ||
|
|
||||||
ms.hash != 0 ||
|
|
||||||
ms.signer_len != 0 ||
|
|
||||||
ms.key_id_len != 0 ||
|
|
||||||
ms.__pad[0] != 0 ||
|
|
||||||
ms.__pad[1] != 0 ||
|
|
||||||
ms.__pad[2] != 0) {
|
|
||||||
pr_err("%s: PKCS#7 signature info has unexpected non-zero params\n",
|
|
||||||
info->name);
|
|
||||||
return -EBADMSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
|
return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
|
||||||
VERIFY_USE_SECONDARY_KEYRING,
|
VERIFY_USE_SECONDARY_KEYRING,
|
||||||
VERIFYING_MODULE_SIGNATURE,
|
VERIFYING_MODULE_SIGNATURE,
|
||||||
|
|
|
@ -17,7 +17,7 @@ hostprogs-$(CONFIG_VT) += conmakehash
|
||||||
hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
|
hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
|
||||||
hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable
|
hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable
|
||||||
hostprogs-$(CONFIG_ASN1) += asn1_compiler
|
hostprogs-$(CONFIG_ASN1) += asn1_compiler
|
||||||
hostprogs-$(CONFIG_MODULE_SIG) += sign-file
|
hostprogs-$(CONFIG_MODULE_SIG_FORMAT) += sign-file
|
||||||
hostprogs-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += extract-cert
|
hostprogs-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += extract-cert
|
||||||
hostprogs-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE) += insert-sys-cert
|
hostprogs-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE) += insert-sys-cert
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,8 @@ if INTEGRITY
|
||||||
|
|
||||||
config INTEGRITY_SIGNATURE
|
config INTEGRITY_SIGNATURE
|
||||||
bool "Digital signature verification using multiple keyrings"
|
bool "Digital signature verification using multiple keyrings"
|
||||||
depends on KEYS
|
|
||||||
default n
|
default n
|
||||||
|
select KEYS
|
||||||
select SIGNATURE
|
select SIGNATURE
|
||||||
help
|
help
|
||||||
This option enables digital signature verification support
|
This option enables digital signature verification support
|
||||||
|
|
|
@ -39,11 +39,10 @@ static const char * const keyring_name[INTEGRITY_KEYRING_MAX] = {
|
||||||
#define restrict_link_to_ima restrict_link_by_builtin_trusted
|
#define restrict_link_to_ima restrict_link_by_builtin_trusted
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
static struct key *integrity_keyring_from_id(const unsigned int id)
|
||||||
const char *digest, int digestlen)
|
|
||||||
{
|
{
|
||||||
if (id >= INTEGRITY_KEYRING_MAX || siglen < 2)
|
if (id >= INTEGRITY_KEYRING_MAX)
|
||||||
return -EINVAL;
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
if (!keyring[id]) {
|
if (!keyring[id]) {
|
||||||
keyring[id] =
|
keyring[id] =
|
||||||
|
@ -52,23 +51,49 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||||
int err = PTR_ERR(keyring[id]);
|
int err = PTR_ERR(keyring[id]);
|
||||||
pr_err("no %s keyring: %d\n", keyring_name[id], err);
|
pr_err("no %s keyring: %d\n", keyring_name[id], err);
|
||||||
keyring[id] = NULL;
|
keyring[id] = NULL;
|
||||||
return err;
|
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)
|
||||||
|
{
|
||||||
|
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]) {
|
switch (sig[1]) {
|
||||||
case 1:
|
case 1:
|
||||||
/* v1 API expect signature without xattr type */
|
/* v1 API expect signature without xattr type */
|
||||||
return digsig_verify(keyring[id], sig + 1, siglen - 1,
|
return digsig_verify(keyring, sig + 1, siglen - 1, digest,
|
||||||
digest, digestlen);
|
digestlen);
|
||||||
case 2:
|
case 2:
|
||||||
return asymmetric_verify(keyring[id], sig, siglen,
|
return asymmetric_verify(keyring, sig, siglen, digest,
|
||||||
digest, digestlen);
|
digestlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
return -EOPNOTSUPP;
|
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,
|
static int __init __integrity_init_keyring(const unsigned int id,
|
||||||
key_perm_t perm,
|
key_perm_t perm,
|
||||||
struct key_restriction *restriction)
|
struct key_restriction *restriction)
|
||||||
|
|
|
@ -233,6 +233,19 @@ config IMA_APPRAISE_BOOTPARAM
|
||||||
This option enables the different "ima_appraise=" modes
|
This option enables the different "ima_appraise=" modes
|
||||||
(eg. fix, log) from the boot command line.
|
(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_TRUSTED_KEYRING
|
config IMA_TRUSTED_KEYRING
|
||||||
bool "Require all keys on the .ima keyring be signed (deprecated)"
|
bool "Require all keys on the .ima keyring be signed (deprecated)"
|
||||||
depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING
|
depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING
|
||||||
|
|
|
@ -9,5 +9,6 @@ obj-$(CONFIG_IMA) += ima.o
|
||||||
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.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_policy.o ima_template.o ima_template_lib.o
|
||||||
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.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_HAVE_IMA_KEXEC) += ima_kexec.o
|
||||||
obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
|
obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
|
||||||
|
|
|
@ -60,6 +60,7 @@ struct ima_event_data {
|
||||||
const unsigned char *filename;
|
const unsigned char *filename;
|
||||||
struct evm_ima_xattr_data *xattr_value;
|
struct evm_ima_xattr_data *xattr_value;
|
||||||
int xattr_len;
|
int xattr_len;
|
||||||
|
const struct modsig *modsig;
|
||||||
const char *violation;
|
const char *violation;
|
||||||
const void *buf;
|
const void *buf;
|
||||||
int buf_len;
|
int buf_len;
|
||||||
|
@ -149,6 +150,7 @@ int template_desc_init_fields(const char *template_fmt,
|
||||||
int *num_fields);
|
int *num_fields);
|
||||||
struct ima_template_desc *ima_template_desc_current(void);
|
struct ima_template_desc *ima_template_desc_current(void);
|
||||||
struct ima_template_desc *lookup_template_desc(const char *name);
|
struct ima_template_desc *lookup_template_desc(const char *name);
|
||||||
|
bool ima_template_has_modsig(const struct ima_template_desc *ima_template);
|
||||||
int ima_restore_measurement_entry(struct ima_template_entry *entry);
|
int ima_restore_measurement_entry(struct ima_template_entry *entry);
|
||||||
int ima_restore_measurement_list(loff_t bufsize, void *buf);
|
int ima_restore_measurement_list(loff_t bufsize, void *buf);
|
||||||
int ima_measurements_show(struct seq_file *m, void *v);
|
int ima_measurements_show(struct seq_file *m, void *v);
|
||||||
|
@ -196,6 +198,10 @@ enum ima_hooks {
|
||||||
__ima_hooks(__ima_hook_enumify)
|
__ima_hooks(__ima_hook_enumify)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern const char *const func_tokens[];
|
||||||
|
|
||||||
|
struct modsig;
|
||||||
|
|
||||||
/* LIM API function definitions */
|
/* LIM API function definitions */
|
||||||
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
|
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
|
||||||
int mask, enum ima_hooks func, int *pcr,
|
int mask, enum ima_hooks func, int *pcr,
|
||||||
|
@ -203,11 +209,11 @@ int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
|
||||||
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
|
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
|
||||||
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||||
struct file *file, void *buf, loff_t size,
|
struct file *file, void *buf, loff_t size,
|
||||||
enum hash_algo algo);
|
enum hash_algo algo, struct modsig *modsig);
|
||||||
void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
|
void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
|
||||||
const unsigned char *filename,
|
const unsigned char *filename,
|
||||||
struct evm_ima_xattr_data *xattr_value,
|
struct evm_ima_xattr_data *xattr_value,
|
||||||
int xattr_len, int pcr,
|
int xattr_len, const struct modsig *modsig, int pcr,
|
||||||
struct ima_template_desc *template_desc);
|
struct ima_template_desc *template_desc);
|
||||||
void ima_audit_measurement(struct integrity_iint_cache *iint,
|
void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||||
const unsigned char *filename);
|
const unsigned char *filename);
|
||||||
|
@ -249,7 +255,7 @@ int ima_appraise_measurement(enum ima_hooks func,
|
||||||
struct integrity_iint_cache *iint,
|
struct integrity_iint_cache *iint,
|
||||||
struct file *file, const unsigned char *filename,
|
struct file *file, const unsigned char *filename,
|
||||||
struct evm_ima_xattr_data *xattr_value,
|
struct evm_ima_xattr_data *xattr_value,
|
||||||
int xattr_len);
|
int xattr_len, const struct modsig *modsig);
|
||||||
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);
|
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);
|
||||||
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
|
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
|
||||||
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
|
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
|
||||||
|
@ -265,7 +271,8 @@ static inline int ima_appraise_measurement(enum ima_hooks func,
|
||||||
struct file *file,
|
struct file *file,
|
||||||
const unsigned char *filename,
|
const unsigned char *filename,
|
||||||
struct evm_ima_xattr_data *xattr_value,
|
struct evm_ima_xattr_data *xattr_value,
|
||||||
int xattr_len)
|
int xattr_len,
|
||||||
|
const struct modsig *modsig)
|
||||||
{
|
{
|
||||||
return INTEGRITY_UNKNOWN;
|
return INTEGRITY_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
@ -302,6 +309,51 @@ static inline int ima_read_xattr(struct dentry *dentry,
|
||||||
|
|
||||||
#endif /* CONFIG_IMA_APPRAISE */
|
#endif /* CONFIG_IMA_APPRAISE */
|
||||||
|
|
||||||
|
#ifdef CONFIG_IMA_APPRAISE_MODSIG
|
||||||
|
bool ima_hook_supports_modsig(enum ima_hooks func);
|
||||||
|
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 bool ima_hook_supports_modsig(enum ima_hooks func)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
/* LSM based policy rules require audit */
|
||||||
#ifdef CONFIG_IMA_LSM_RULES
|
#ifdef CONFIG_IMA_LSM_RULES
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,8 @@ int ima_alloc_init_template(struct ima_event_data *event_data,
|
||||||
else
|
else
|
||||||
template_desc = ima_template_desc_current();
|
template_desc = ima_template_desc_current();
|
||||||
|
|
||||||
*entry = kzalloc(sizeof(**entry) + template_desc->num_fields *
|
*entry = kzalloc(struct_size(*entry, template_data,
|
||||||
sizeof(struct ima_field_data), GFP_NOFS);
|
template_desc->num_fields), GFP_NOFS);
|
||||||
if (!*entry)
|
if (!*entry)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
|
||||||
*/
|
*/
|
||||||
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||||
struct file *file, void *buf, loff_t size,
|
struct file *file, void *buf, loff_t size,
|
||||||
enum hash_algo algo)
|
enum hash_algo algo, struct modsig *modsig)
|
||||||
{
|
{
|
||||||
const char *audit_cause = "failed";
|
const char *audit_cause = "failed";
|
||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
|
@ -219,6 +219,14 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||||
char digest[IMA_MAX_DIGEST_SIZE];
|
char digest[IMA_MAX_DIGEST_SIZE];
|
||||||
} hash;
|
} hash;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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)
|
if (iint->flags & IMA_COLLECTED)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -285,7 +293,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||||
void ima_store_measurement(struct integrity_iint_cache *iint,
|
void ima_store_measurement(struct integrity_iint_cache *iint,
|
||||||
struct file *file, const unsigned char *filename,
|
struct file *file, const unsigned char *filename,
|
||||||
struct evm_ima_xattr_data *xattr_value,
|
struct evm_ima_xattr_data *xattr_value,
|
||||||
int xattr_len, int pcr,
|
int xattr_len, const struct modsig *modsig, int pcr,
|
||||||
struct ima_template_desc *template_desc)
|
struct ima_template_desc *template_desc)
|
||||||
{
|
{
|
||||||
static const char op[] = "add_template_measure";
|
static const char op[] = "add_template_measure";
|
||||||
|
@ -297,10 +305,17 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
|
||||||
.file = file,
|
.file = file,
|
||||||
.filename = filename,
|
.filename = filename,
|
||||||
.xattr_value = xattr_value,
|
.xattr_value = xattr_value,
|
||||||
.xattr_len = xattr_len };
|
.xattr_len = xattr_len,
|
||||||
|
.modsig = modsig };
|
||||||
int violation = 0;
|
int violation = 0;
|
||||||
|
|
||||||
if (iint->measured_pcrs & (0x1 << pcr))
|
/*
|
||||||
|
* 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;
|
return;
|
||||||
|
|
||||||
result = ima_alloc_init_template(&event_data, &entry, template_desc);
|
result = ima_alloc_init_template(&event_data, &entry, template_desc);
|
||||||
|
|
|
@ -199,6 +199,110 @@ int ima_read_xattr(struct dentry *dentry,
|
||||||
return ret;
|
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 integrity_iint_cache *iint,
|
||||||
|
struct evm_ima_xattr_data *xattr_value, int xattr_len,
|
||||||
|
enum integrity_status *status, const char **cause)
|
||||||
|
{
|
||||||
|
int rc = -EINVAL, hash_start = 0;
|
||||||
|
|
||||||
|
switch (xattr_value->type) {
|
||||||
|
case IMA_XATTR_DIGEST_NG:
|
||||||
|
/* first byte contains algorithm id */
|
||||||
|
hash_start = 1;
|
||||||
|
/* fall through */
|
||||||
|
case IMA_XATTR_DIGEST:
|
||||||
|
if (iint->flags & IMA_DIGSIG_REQUIRED) {
|
||||||
|
*cause = "IMA-signature-required";
|
||||||
|
*status = INTEGRITY_FAIL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
clear_bit(IMA_DIGSIG, &iint->atomic_flags);
|
||||||
|
if (xattr_len - sizeof(xattr_value->type) - hash_start >=
|
||||||
|
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);
|
||||||
|
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
|
||||||
|
(const char *)xattr_value,
|
||||||
|
xattr_len,
|
||||||
|
iint->ima_hash->digest,
|
||||||
|
iint->ima_hash->length);
|
||||||
|
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);
|
||||||
|
if (rc) {
|
||||||
|
*cause = "invalid-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_appraise_measurement - appraise file measurement
|
* ima_appraise_measurement - appraise file measurement
|
||||||
*
|
*
|
||||||
|
@ -211,19 +315,22 @@ int ima_appraise_measurement(enum ima_hooks func,
|
||||||
struct integrity_iint_cache *iint,
|
struct integrity_iint_cache *iint,
|
||||||
struct file *file, const unsigned char *filename,
|
struct file *file, const unsigned char *filename,
|
||||||
struct evm_ima_xattr_data *xattr_value,
|
struct evm_ima_xattr_data *xattr_value,
|
||||||
int xattr_len)
|
int xattr_len, const struct modsig *modsig)
|
||||||
{
|
{
|
||||||
static const char op[] = "appraise_data";
|
static const char op[] = "appraise_data";
|
||||||
const char *cause = "unknown";
|
const char *cause = "unknown";
|
||||||
struct dentry *dentry = file_dentry(file);
|
struct dentry *dentry = file_dentry(file);
|
||||||
struct inode *inode = d_backing_inode(dentry);
|
struct inode *inode = d_backing_inode(dentry);
|
||||||
enum integrity_status status = INTEGRITY_UNKNOWN;
|
enum integrity_status status = INTEGRITY_UNKNOWN;
|
||||||
int rc = xattr_len, hash_start = 0;
|
int rc = xattr_len;
|
||||||
|
bool try_modsig = iint->flags & IMA_MODSIG_ALLOWED && modsig;
|
||||||
|
|
||||||
if (!(inode->i_opflags & IOP_XATTR))
|
/* If not appraising a modsig, we need an xattr. */
|
||||||
|
if (!(inode->i_opflags & IOP_XATTR) && !try_modsig)
|
||||||
return INTEGRITY_UNKNOWN;
|
return INTEGRITY_UNKNOWN;
|
||||||
|
|
||||||
if (rc <= 0) {
|
/* If reading the xattr failed and there's no modsig, error out. */
|
||||||
|
if (rc <= 0 && !try_modsig) {
|
||||||
if (rc && rc != -ENODATA)
|
if (rc && rc != -ENODATA)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -246,6 +353,10 @@ int ima_appraise_measurement(enum ima_hooks func,
|
||||||
case INTEGRITY_UNKNOWN:
|
case INTEGRITY_UNKNOWN:
|
||||||
break;
|
break;
|
||||||
case INTEGRITY_NOXATTRS: /* No EVM protected xattrs. */
|
case INTEGRITY_NOXATTRS: /* No EVM protected xattrs. */
|
||||||
|
/* It's fine not to have xattrs when using a modsig. */
|
||||||
|
if (try_modsig)
|
||||||
|
break;
|
||||||
|
/* fall through */
|
||||||
case INTEGRITY_NOLABEL: /* No security.evm xattr. */
|
case INTEGRITY_NOLABEL: /* No security.evm xattr. */
|
||||||
cause = "missing-HMAC";
|
cause = "missing-HMAC";
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -256,65 +367,18 @@ int ima_appraise_measurement(enum ima_hooks func,
|
||||||
WARN_ONCE(true, "Unexpected integrity status %d\n", status);
|
WARN_ONCE(true, "Unexpected integrity status %d\n", status);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (xattr_value->type) {
|
if (xattr_value)
|
||||||
case IMA_XATTR_DIGEST_NG:
|
rc = xattr_verify(func, iint, xattr_value, xattr_len, &status,
|
||||||
/* first byte contains algorithm id */
|
&cause);
|
||||||
hash_start = 1;
|
|
||||||
/* fall through */
|
/*
|
||||||
case IMA_XATTR_DIGEST:
|
* If we have a modsig and either no imasig or the imasig's key isn't
|
||||||
if (iint->flags & IMA_DIGSIG_REQUIRED) {
|
* known, then try verifying the modsig.
|
||||||
cause = "IMA-signature-required";
|
*/
|
||||||
status = INTEGRITY_FAIL;
|
if (try_modsig &&
|
||||||
break;
|
(!xattr_value || xattr_value->type == IMA_XATTR_DIGEST_NG ||
|
||||||
}
|
rc == -ENOKEY))
|
||||||
clear_bit(IMA_DIGSIG, &iint->atomic_flags);
|
rc = modsig_verify(func, modsig, &status, &cause);
|
||||||
if (xattr_len - sizeof(xattr_value->type) - hash_start >=
|
|
||||||
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);
|
|
||||||
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
|
|
||||||
(const char *)xattr_value,
|
|
||||||
xattr_len,
|
|
||||||
iint->ima_hash->digest,
|
|
||||||
iint->ima_hash->length);
|
|
||||||
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);
|
|
||||||
if (rc) {
|
|
||||||
cause = "invalid-signature";
|
|
||||||
status = INTEGRITY_FAIL;
|
|
||||||
} else {
|
|
||||||
status = INTEGRITY_PASS;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
status = INTEGRITY_UNKNOWN;
|
|
||||||
cause = "unknown-ima-data";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
/*
|
/*
|
||||||
|
@ -332,7 +396,7 @@ int ima_appraise_measurement(enum ima_hooks func,
|
||||||
op, cause, rc, 0);
|
op, cause, rc, 0);
|
||||||
} else if (status != INTEGRITY_PASS) {
|
} else if (status != INTEGRITY_PASS) {
|
||||||
/* Fix mode, but don't replace file signatures. */
|
/* Fix mode, but don't replace file signatures. */
|
||||||
if ((ima_appraise & IMA_APPRAISE_FIX) &&
|
if ((ima_appraise & IMA_APPRAISE_FIX) && !try_modsig &&
|
||||||
(!xattr_value ||
|
(!xattr_value ||
|
||||||
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
|
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
|
||||||
if (!ima_fix_xattr(dentry, iint))
|
if (!ima_fix_xattr(dentry, iint))
|
||||||
|
@ -371,7 +435,7 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
|
||||||
!(iint->flags & IMA_HASH))
|
!(iint->flags & IMA_HASH))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
rc = ima_collect_measurement(iint, file, NULL, 0, ima_hash_algo);
|
rc = ima_collect_measurement(iint, file, NULL, 0, ima_hash_algo, NULL);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -268,8 +268,16 @@ static int ima_calc_file_hash_atfm(struct file *file,
|
||||||
rbuf_len = min_t(loff_t, i_size - offset, rbuf_size[active]);
|
rbuf_len = min_t(loff_t, i_size - offset, rbuf_size[active]);
|
||||||
rc = integrity_kernel_read(file, offset, rbuf[active],
|
rc = integrity_kernel_read(file, offset, rbuf[active],
|
||||||
rbuf_len);
|
rbuf_len);
|
||||||
if (rc != rbuf_len)
|
if (rc != rbuf_len) {
|
||||||
|
if (rc >= 0)
|
||||||
|
rc = -EINVAL;
|
||||||
|
/*
|
||||||
|
* Forward current rc, do not overwrite with return value
|
||||||
|
* from ahash_wait()
|
||||||
|
*/
|
||||||
|
ahash_wait(ahash_rc, &wait);
|
||||||
goto out3;
|
goto out3;
|
||||||
|
}
|
||||||
|
|
||||||
if (rbuf[1] && offset) {
|
if (rbuf[1] && offset) {
|
||||||
/* Using two buffers, and it is not the first
|
/* Using two buffers, and it is not the first
|
||||||
|
|
|
@ -202,6 +202,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
|
||||||
int rc = 0, action, must_appraise = 0;
|
int rc = 0, action, must_appraise = 0;
|
||||||
int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
|
int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
|
||||||
struct evm_ima_xattr_data *xattr_value = NULL;
|
struct evm_ima_xattr_data *xattr_value = NULL;
|
||||||
|
struct modsig *modsig = NULL;
|
||||||
int xattr_len = 0;
|
int xattr_len = 0;
|
||||||
bool violation_check;
|
bool violation_check;
|
||||||
enum hash_algo hash_algo;
|
enum hash_algo hash_algo;
|
||||||
|
@ -302,13 +303,27 @@ static int process_measurement(struct file *file, const struct cred *cred,
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((action & IMA_APPRAISE_SUBMASK) ||
|
if ((action & IMA_APPRAISE_SUBMASK) ||
|
||||||
strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0)
|
strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0) {
|
||||||
/* read 'security.ima' */
|
/* read 'security.ima' */
|
||||||
xattr_len = ima_read_xattr(file_dentry(file), &xattr_value);
|
xattr_len = ima_read_xattr(file_dentry(file), &xattr_value);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the appended modsig if allowed by the policy, and allow
|
||||||
|
* an additional measurement list entry, if needed, based on the
|
||||||
|
* template format and whether the file was already measured.
|
||||||
|
*/
|
||||||
|
if (iint->flags & IMA_MODSIG_ALLOWED) {
|
||||||
|
rc = ima_read_modsig(func, buf, size, &modsig);
|
||||||
|
|
||||||
|
if (!rc && ima_template_has_modsig(template_desc) &&
|
||||||
|
iint->flags & IMA_MEASURED)
|
||||||
|
action |= IMA_MEASURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hash_algo = ima_get_hash_algo(xattr_value, xattr_len);
|
hash_algo = ima_get_hash_algo(xattr_value, xattr_len);
|
||||||
|
|
||||||
rc = ima_collect_measurement(iint, file, buf, size, hash_algo);
|
rc = ima_collect_measurement(iint, file, buf, size, hash_algo, modsig);
|
||||||
if (rc != 0 && rc != -EBADF && rc != -EINVAL)
|
if (rc != 0 && rc != -EBADF && rc != -EINVAL)
|
||||||
goto out_locked;
|
goto out_locked;
|
||||||
|
|
||||||
|
@ -317,12 +332,12 @@ static int process_measurement(struct file *file, const struct cred *cred,
|
||||||
|
|
||||||
if (action & IMA_MEASURE)
|
if (action & IMA_MEASURE)
|
||||||
ima_store_measurement(iint, file, pathname,
|
ima_store_measurement(iint, file, pathname,
|
||||||
xattr_value, xattr_len, pcr,
|
xattr_value, xattr_len, modsig, pcr,
|
||||||
template_desc);
|
template_desc);
|
||||||
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
|
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
rc = ima_appraise_measurement(func, iint, file, pathname,
|
rc = ima_appraise_measurement(func, iint, file, pathname,
|
||||||
xattr_value, xattr_len);
|
xattr_value, xattr_len, modsig);
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
rc = mmap_violation_check(func, file, &pathbuf,
|
rc = mmap_violation_check(func, file, &pathbuf,
|
||||||
|
@ -339,6 +354,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
|
||||||
rc = -EACCES;
|
rc = -EACCES;
|
||||||
mutex_unlock(&iint->mutex);
|
mutex_unlock(&iint->mutex);
|
||||||
kfree(xattr_value);
|
kfree(xattr_value);
|
||||||
|
ima_free_modsig(modsig);
|
||||||
out:
|
out:
|
||||||
if (pathbuf)
|
if (pathbuf)
|
||||||
__putname(pathbuf);
|
__putname(pathbuf);
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
// 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[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ima_hook_supports_modsig - can the policy allow modsig for this hook?
|
||||||
|
*
|
||||||
|
* modsig is only supported by hooks using ima_post_read_file(), because only
|
||||||
|
* they preload the contents of the file in a buffer. FILE_CHECK does that in
|
||||||
|
* some cases, but not when reached from vfs_open(). POLICY_CHECK can support
|
||||||
|
* it, but it's not useful in practice because it's a text file so deny.
|
||||||
|
*/
|
||||||
|
bool ima_hook_supports_modsig(enum ima_hooks func)
|
||||||
|
{
|
||||||
|
switch (func) {
|
||||||
|
case KEXEC_KERNEL_CHECK:
|
||||||
|
case KEXEC_INITRAMFS_CHECK:
|
||||||
|
case MODULE_CHECK:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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_SIG_STRING);
|
||||||
|
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_SIG_STRING, 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(sizeof(*hdr) + sig_len, GFP_KERNEL);
|
||||||
|
if (!hdr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
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);
|
||||||
|
hdr->raw_pkcs7_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.
|
||||||
|
*
|
||||||
|
* 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_SIG_STRING) +
|
||||||
|
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);
|
||||||
|
}
|
|
@ -6,6 +6,9 @@
|
||||||
* ima_policy.c
|
* ima_policy.c
|
||||||
* - initialize default measure policy rules
|
* - initialize default measure policy rules
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
@ -491,6 +494,9 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
|
||||||
struct ima_rule_entry *entry;
|
struct ima_rule_entry *entry;
|
||||||
int action = 0, actmask = flags | (flags << 1);
|
int action = 0, actmask = flags | (flags << 1);
|
||||||
|
|
||||||
|
if (template_desc)
|
||||||
|
*template_desc = ima_template_desc_current();
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
list_for_each_entry_rcu(entry, ima_rules, list) {
|
list_for_each_entry_rcu(entry, ima_rules, list) {
|
||||||
|
|
||||||
|
@ -510,6 +516,7 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
|
||||||
action |= IMA_FAIL_UNVERIFIABLE_SIGS;
|
action |= IMA_FAIL_UNVERIFIABLE_SIGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (entry->action & IMA_DO_MASK)
|
if (entry->action & IMA_DO_MASK)
|
||||||
actmask &= ~(entry->action | entry->action << 1);
|
actmask &= ~(entry->action | entry->action << 1);
|
||||||
else
|
else
|
||||||
|
@ -520,8 +527,6 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
|
||||||
|
|
||||||
if (template_desc && entry->template)
|
if (template_desc && entry->template)
|
||||||
*template_desc = entry->template;
|
*template_desc = entry->template;
|
||||||
else if (template_desc)
|
|
||||||
*template_desc = ima_template_desc_current();
|
|
||||||
|
|
||||||
if (!actmask)
|
if (!actmask)
|
||||||
break;
|
break;
|
||||||
|
@ -843,6 +848,38 @@ static void ima_log_string(struct audit_buffer *ab, char *key, char *value)
|
||||||
ima_log_string_op(ab, key, value, NULL);
|
ima_log_string_op(ab, key, value, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validating the appended signature included in the measurement list requires
|
||||||
|
* the file hash calculated without the appended signature (i.e., the 'd-modsig'
|
||||||
|
* field). Therefore, notify the user if they have the 'modsig' field but not
|
||||||
|
* the 'd-modsig' field in the template.
|
||||||
|
*/
|
||||||
|
static void check_template_modsig(const struct ima_template_desc *template)
|
||||||
|
{
|
||||||
|
#define MSG "template with 'modsig' field also needs 'd-modsig' field\n"
|
||||||
|
bool has_modsig, has_dmodsig;
|
||||||
|
static bool checked;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* We only need to notify the user once. */
|
||||||
|
if (checked)
|
||||||
|
return;
|
||||||
|
|
||||||
|
has_modsig = has_dmodsig = false;
|
||||||
|
for (i = 0; i < template->num_fields; i++) {
|
||||||
|
if (!strcmp(template->fields[i]->field_id, "modsig"))
|
||||||
|
has_modsig = true;
|
||||||
|
else if (!strcmp(template->fields[i]->field_id, "d-modsig"))
|
||||||
|
has_dmodsig = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_modsig && !has_dmodsig)
|
||||||
|
pr_notice(MSG);
|
||||||
|
|
||||||
|
checked = true;
|
||||||
|
#undef MSG
|
||||||
|
}
|
||||||
|
|
||||||
static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||||
{
|
{
|
||||||
struct audit_buffer *ab;
|
struct audit_buffer *ab;
|
||||||
|
@ -1128,6 +1165,10 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||||
ima_log_string(ab, "appraise_type", args[0].from);
|
ima_log_string(ab, "appraise_type", args[0].from);
|
||||||
if ((strcmp(args[0].from, "imasig")) == 0)
|
if ((strcmp(args[0].from, "imasig")) == 0)
|
||||||
entry->flags |= IMA_DIGSIG_REQUIRED;
|
entry->flags |= IMA_DIGSIG_REQUIRED;
|
||||||
|
else if (ima_hook_supports_modsig(entry->func) &&
|
||||||
|
strcmp(args[0].from, "imasig|modsig") == 0)
|
||||||
|
entry->flags |= IMA_DIGSIG_REQUIRED |
|
||||||
|
IMA_MODSIG_ALLOWED;
|
||||||
else
|
else
|
||||||
result = -EINVAL;
|
result = -EINVAL;
|
||||||
break;
|
break;
|
||||||
|
@ -1181,6 +1222,12 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||||
else if (entry->action == APPRAISE)
|
else if (entry->action == APPRAISE)
|
||||||
temp_ima_appraise |= ima_appraise_flag(entry->func);
|
temp_ima_appraise |= ima_appraise_flag(entry->func);
|
||||||
|
|
||||||
|
if (!result && entry->flags & IMA_MODSIG_ALLOWED) {
|
||||||
|
template_desc = entry->template ? entry->template :
|
||||||
|
ima_template_desc_current();
|
||||||
|
check_template_modsig(template_desc);
|
||||||
|
}
|
||||||
|
|
||||||
audit_log_format(ab, "res=%d", !result);
|
audit_log_format(ab, "res=%d", !result);
|
||||||
audit_log_end(ab);
|
audit_log_end(ab);
|
||||||
return result;
|
return result;
|
||||||
|
@ -1252,6 +1299,12 @@ void ima_delete_rules(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define __ima_hook_stringify(str) (#str),
|
||||||
|
|
||||||
|
const char *const func_tokens[] = {
|
||||||
|
__ima_hooks(__ima_hook_stringify)
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_IMA_READ_POLICY
|
#ifdef CONFIG_IMA_READ_POLICY
|
||||||
enum {
|
enum {
|
||||||
mask_exec = 0, mask_write, mask_read, mask_append
|
mask_exec = 0, mask_write, mask_read, mask_append
|
||||||
|
@ -1264,12 +1317,6 @@ static const char *const mask_tokens[] = {
|
||||||
"^MAY_APPEND"
|
"^MAY_APPEND"
|
||||||
};
|
};
|
||||||
|
|
||||||
#define __ima_hook_stringify(str) (#str),
|
|
||||||
|
|
||||||
static const char *const func_tokens[] = {
|
|
||||||
__ima_hooks(__ima_hook_stringify)
|
|
||||||
};
|
|
||||||
|
|
||||||
void *ima_policy_start(struct seq_file *m, loff_t *pos)
|
void *ima_policy_start(struct seq_file *m, loff_t *pos)
|
||||||
{
|
{
|
||||||
loff_t l = *pos;
|
loff_t l = *pos;
|
||||||
|
@ -1447,8 +1494,12 @@ int ima_policy_show(struct seq_file *m, void *v)
|
||||||
}
|
}
|
||||||
if (entry->template)
|
if (entry->template)
|
||||||
seq_printf(m, "template=%s ", entry->template->name);
|
seq_printf(m, "template=%s ", entry->template->name);
|
||||||
if (entry->flags & IMA_DIGSIG_REQUIRED)
|
if (entry->flags & IMA_DIGSIG_REQUIRED) {
|
||||||
seq_puts(m, "appraise_type=imasig ");
|
if (entry->flags & IMA_MODSIG_ALLOWED)
|
||||||
|
seq_puts(m, "appraise_type=imasig|modsig ");
|
||||||
|
else
|
||||||
|
seq_puts(m, "appraise_type=imasig ");
|
||||||
|
}
|
||||||
if (entry->flags & IMA_PERMIT_DIRECTIO)
|
if (entry->flags & IMA_PERMIT_DIRECTIO)
|
||||||
seq_puts(m, "permit_directio ");
|
seq_puts(m, "permit_directio ");
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
|
@ -23,6 +23,7 @@ static struct ima_template_desc builtin_templates[] = {
|
||||||
{.name = "ima-ng", .fmt = "d-ng|n-ng"},
|
{.name = "ima-ng", .fmt = "d-ng|n-ng"},
|
||||||
{.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
|
{.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
|
||||||
{.name = "ima-buf", .fmt = "d-ng|n-ng|buf"},
|
{.name = "ima-buf", .fmt = "d-ng|n-ng|buf"},
|
||||||
|
{.name = "ima-modsig", .fmt = "d-ng|n-ng|sig|d-modsig|modsig"},
|
||||||
{.name = "", .fmt = ""}, /* placeholder for a custom format */
|
{.name = "", .fmt = ""}, /* placeholder for a custom format */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,6 +43,10 @@ static const struct ima_template_field supported_fields[] = {
|
||||||
.field_show = ima_show_template_sig},
|
.field_show = ima_show_template_sig},
|
||||||
{.field_id = "buf", .field_init = ima_eventbuf_init,
|
{.field_id = "buf", .field_init = ima_eventbuf_init,
|
||||||
.field_show = ima_show_template_buf},
|
.field_show = ima_show_template_buf},
|
||||||
|
{.field_id = "d-modsig", .field_init = ima_eventdigest_modsig_init,
|
||||||
|
.field_show = ima_show_template_digest_ng},
|
||||||
|
{.field_id = "modsig", .field_init = ima_eventmodsig_init,
|
||||||
|
.field_show = ima_show_template_sig},
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -49,10 +54,29 @@ static const struct ima_template_field supported_fields[] = {
|
||||||
* need to be accounted for since they shouldn't be defined in the same template
|
* need to be accounted for since they shouldn't be defined in the same template
|
||||||
* description as 'd-ng' and 'n-ng' respectively.
|
* description as 'd-ng' and 'n-ng' respectively.
|
||||||
*/
|
*/
|
||||||
#define MAX_TEMPLATE_NAME_LEN sizeof("d-ng|n-ng|sig|buf")
|
#define MAX_TEMPLATE_NAME_LEN sizeof("d-ng|n-ng|sig|buf|d-modisg|modsig")
|
||||||
|
|
||||||
static struct ima_template_desc *ima_template;
|
static struct ima_template_desc *ima_template;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
static int __init ima_template_setup(char *str)
|
||||||
{
|
{
|
||||||
struct ima_template_desc *template_desc;
|
struct ima_template_desc *template_desc;
|
||||||
|
@ -282,9 +306,8 @@ static int ima_restore_template_data(struct ima_template_desc *template_desc,
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
*entry = kzalloc(sizeof(**entry) +
|
*entry = kzalloc(struct_size(*entry, template_data,
|
||||||
template_desc->num_fields * sizeof(struct ima_field_data),
|
template_desc->num_fields), GFP_NOFS);
|
||||||
GFP_NOFS);
|
|
||||||
if (!*entry)
|
if (!*entry)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
|
|
@ -225,7 +225,8 @@ int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo,
|
static int ima_eventdigest_init_common(const u8 *digest, u32 digestsize,
|
||||||
|
u8 hash_algo,
|
||||||
struct ima_field_data *field_data)
|
struct ima_field_data *field_data)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -328,6 +329,41 @@ int ima_eventdigest_ng_init(struct ima_event_data *event_data,
|
||||||
hash_algo, field_data);
|
hash_algo, field_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function writes the digest of the file which is expected to match the
|
||||||
|
* digest contained in the file's appended signature.
|
||||||
|
*/
|
||||||
|
int ima_eventdigest_modsig_init(struct ima_event_data *event_data,
|
||||||
|
struct ima_field_data *field_data)
|
||||||
|
{
|
||||||
|
enum hash_algo hash_algo;
|
||||||
|
const u8 *cur_digest;
|
||||||
|
u32 cur_digestsize;
|
||||||
|
|
||||||
|
if (!event_data->modsig)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (event_data->violation) {
|
||||||
|
/* Recording a violation. */
|
||||||
|
hash_algo = HASH_ALGO_SHA1;
|
||||||
|
cur_digest = NULL;
|
||||||
|
cur_digestsize = 0;
|
||||||
|
} else {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = ima_get_modsig_digest(event_data->modsig, &hash_algo,
|
||||||
|
&cur_digest, &cur_digestsize);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
else if (hash_algo == HASH_ALGO__LAST || cur_digestsize == 0)
|
||||||
|
/* There was some error collecting the digest. */
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ima_eventdigest_init_common(cur_digest, cur_digestsize,
|
||||||
|
hash_algo, field_data);
|
||||||
|
}
|
||||||
|
|
||||||
static int ima_eventname_init_common(struct ima_event_data *event_data,
|
static int ima_eventname_init_common(struct ima_event_data *event_data,
|
||||||
struct ima_field_data *field_data,
|
struct ima_field_data *field_data,
|
||||||
bool size_limit)
|
bool size_limit)
|
||||||
|
@ -406,3 +442,29 @@ int ima_eventbuf_init(struct ima_event_data *event_data,
|
||||||
event_data->buf_len, DATA_FMT_HEX,
|
event_data->buf_len, DATA_FMT_HEX,
|
||||||
field_data);
|
field_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ima_eventmodsig_init - include the appended file signature as part of the
|
||||||
|
* template data
|
||||||
|
*/
|
||||||
|
int ima_eventmodsig_init(struct ima_event_data *event_data,
|
||||||
|
struct ima_field_data *field_data)
|
||||||
|
{
|
||||||
|
const void *data;
|
||||||
|
u32 data_len;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (!event_data->modsig)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* modsig is a runtime structure containing pointers. Get its raw data
|
||||||
|
* instead.
|
||||||
|
*/
|
||||||
|
rc = ima_get_raw_modsig(event_data->modsig, &data, &data_len);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
return ima_write_template_field_data(data, data_len, DATA_FMT_HEX,
|
||||||
|
field_data);
|
||||||
|
}
|
||||||
|
|
|
@ -36,10 +36,14 @@ int ima_eventname_init(struct ima_event_data *event_data,
|
||||||
struct ima_field_data *field_data);
|
struct ima_field_data *field_data);
|
||||||
int ima_eventdigest_ng_init(struct ima_event_data *event_data,
|
int ima_eventdigest_ng_init(struct ima_event_data *event_data,
|
||||||
struct ima_field_data *field_data);
|
struct ima_field_data *field_data);
|
||||||
|
int ima_eventdigest_modsig_init(struct ima_event_data *event_data,
|
||||||
|
struct ima_field_data *field_data);
|
||||||
int ima_eventname_ng_init(struct ima_event_data *event_data,
|
int ima_eventname_ng_init(struct ima_event_data *event_data,
|
||||||
struct ima_field_data *field_data);
|
struct ima_field_data *field_data);
|
||||||
int ima_eventsig_init(struct ima_event_data *event_data,
|
int ima_eventsig_init(struct ima_event_data *event_data,
|
||||||
struct ima_field_data *field_data);
|
struct ima_field_data *field_data);
|
||||||
int ima_eventbuf_init(struct ima_event_data *event_data,
|
int ima_eventbuf_init(struct ima_event_data *event_data,
|
||||||
struct ima_field_data *field_data);
|
struct ima_field_data *field_data);
|
||||||
|
int ima_eventmodsig_init(struct ima_event_data *event_data,
|
||||||
|
struct ima_field_data *field_data);
|
||||||
#endif /* __LINUX_IMA_TEMPLATE_LIB_H */
|
#endif /* __LINUX_IMA_TEMPLATE_LIB_H */
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#define IMA_NEW_FILE 0x04000000
|
#define IMA_NEW_FILE 0x04000000
|
||||||
#define EVM_IMMUTABLE_DIGSIG 0x08000000
|
#define EVM_IMMUTABLE_DIGSIG 0x08000000
|
||||||
#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
|
#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
|
||||||
|
#define IMA_MODSIG_ALLOWED 0x20000000
|
||||||
|
|
||||||
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
|
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
|
||||||
IMA_HASH | IMA_APPRAISE_SUBMASK)
|
IMA_HASH | IMA_APPRAISE_SUBMASK)
|
||||||
|
@ -147,10 +148,13 @@ int integrity_kernel_read(struct file *file, loff_t offset,
|
||||||
|
|
||||||
extern struct dentry *integrity_dir;
|
extern struct dentry *integrity_dir;
|
||||||
|
|
||||||
|
struct modsig;
|
||||||
|
|
||||||
#ifdef CONFIG_INTEGRITY_SIGNATURE
|
#ifdef CONFIG_INTEGRITY_SIGNATURE
|
||||||
|
|
||||||
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||||
const char *digest, int digestlen);
|
const char *digest, int digestlen);
|
||||||
|
int integrity_modsig_verify(unsigned int id, const struct modsig *modsig);
|
||||||
|
|
||||||
int __init integrity_init_keyring(const unsigned int id);
|
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_x509(const unsigned int id, const char *path);
|
||||||
|
@ -165,6 +169,12 @@ static inline int integrity_digsig_verify(const unsigned int id,
|
||||||
return -EOPNOTSUPP;
|
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)
|
static inline int integrity_init_keyring(const unsigned int id)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -190,6 +200,16 @@ static inline int asymmetric_verify(struct key *keyring, const char *sig,
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
#ifdef CONFIG_IMA_LOAD_X509
|
||||||
void __init ima_load_x509(void);
|
void __init ima_load_x509(void);
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -37,11 +37,20 @@ is_ima_sig_required()
|
||||||
# sequentially. As a result, a policy rule may be defined, but
|
# sequentially. As a result, a policy rule may be defined, but
|
||||||
# might not necessarily be used. This test assumes if a policy
|
# might not necessarily be used. This test assumes if a policy
|
||||||
# rule is specified, that is the intent.
|
# rule is specified, that is the intent.
|
||||||
|
|
||||||
|
# First check for appended signature (modsig), then xattr
|
||||||
if [ $ima_read_policy -eq 1 ]; then
|
if [ $ima_read_policy -eq 1 ]; then
|
||||||
check_ima_policy "appraise" "func=KEXEC_KERNEL_CHECK" \
|
check_ima_policy "appraise" "func=KEXEC_KERNEL_CHECK" \
|
||||||
"appraise_type=imasig"
|
"appraise_type=imasig|modsig"
|
||||||
ret=$?
|
ret=$?
|
||||||
[ $ret -eq 1 ] && log_info "IMA signature required";
|
if [ $ret -eq 1 ]; then
|
||||||
|
log_info "IMA or appended(modsig) signature required"
|
||||||
|
else
|
||||||
|
check_ima_policy "appraise" "func=KEXEC_KERNEL_CHECK" \
|
||||||
|
"appraise_type=imasig"
|
||||||
|
ret=$?
|
||||||
|
[ $ret -eq 1 ] && log_info "IMA signature required";
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
return $ret
|
return $ret
|
||||||
}
|
}
|
||||||
|
@ -84,6 +93,22 @@ check_for_imasig()
|
||||||
return $ret
|
return $ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Return 1 for appended signature (modsig) found and 0 for not found.
|
||||||
|
check_for_modsig()
|
||||||
|
{
|
||||||
|
local module_sig_string="~Module signature appended~"
|
||||||
|
local sig="$(tail --bytes $((${#module_sig_string} + 1)) $KERNEL_IMAGE)"
|
||||||
|
local ret=0
|
||||||
|
|
||||||
|
if [ "$sig" == "$module_sig_string" ]; then
|
||||||
|
ret=1
|
||||||
|
log_info "kexec kernel image modsig signed"
|
||||||
|
else
|
||||||
|
log_info "kexec kernel image not modsig signed"
|
||||||
|
fi
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
|
||||||
kexec_file_load_test()
|
kexec_file_load_test()
|
||||||
{
|
{
|
||||||
local succeed_msg="kexec_file_load succeeded"
|
local succeed_msg="kexec_file_load succeeded"
|
||||||
|
@ -98,7 +123,8 @@ kexec_file_load_test()
|
||||||
# In secureboot mode with an architecture specific
|
# In secureboot mode with an architecture specific
|
||||||
# policy, make sure either an IMA or PE signature exists.
|
# policy, make sure either an IMA or PE signature exists.
|
||||||
if [ $secureboot -eq 1 ] && [ $arch_policy -eq 1 ] && \
|
if [ $secureboot -eq 1 ] && [ $arch_policy -eq 1 ] && \
|
||||||
[ $ima_signed -eq 0 ] && [ $pe_signed -eq 0 ]; then
|
[ $ima_signed -eq 0 ] && [ $pe_signed -eq 0 ] \
|
||||||
|
&& [ $ima_modsig -eq 0 ]; then
|
||||||
log_fail "$succeed_msg (missing sig)"
|
log_fail "$succeed_msg (missing sig)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -107,7 +133,8 @@ kexec_file_load_test()
|
||||||
log_fail "$succeed_msg (missing PE sig)"
|
log_fail "$succeed_msg (missing PE sig)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $ima_sig_required -eq 1 ] && [ $ima_signed -eq 0 ]; then
|
if [ $ima_sig_required -eq 1 ] && [ $ima_signed -eq 0 ] \
|
||||||
|
&& [ $ima_modsig -eq 0 ]; then
|
||||||
log_fail "$succeed_msg (missing IMA sig)"
|
log_fail "$succeed_msg (missing IMA sig)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -204,5 +231,8 @@ pe_signed=$?
|
||||||
check_for_imasig
|
check_for_imasig
|
||||||
ima_signed=$?
|
ima_signed=$?
|
||||||
|
|
||||||
|
check_for_modsig
|
||||||
|
ima_modsig=$?
|
||||||
|
|
||||||
# Test loading the kernel image via kexec_file_load syscall
|
# Test loading the kernel image via kexec_file_load syscall
|
||||||
kexec_file_load_test
|
kexec_file_load_test
|
||||||
|
|
Loading…
Reference in New Issue