You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris:
"Highlights:
- A new LSM, "LoadPin", from Kees Cook is added, which allows forcing
of modules and firmware to be loaded from a specific device (this
is from ChromeOS, where the device as a whole is verified
cryptographically via dm-verity).
This is disabled by default but can be configured to be enabled by
default (don't do this if you don't know what you're doing).
- Keys: allow authentication data to be stored in an asymmetric key.
Lots of general fixes and updates.
- SELinux: add restrictions for loading of kernel modules via
finit_module(). Distinguish non-init user namespace capability
checks. Apply execstack check on thread stacks"
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (48 commits)
LSM: LoadPin: provide enablement CONFIG
Yama: use atomic allocations when reporting
seccomp: Fix comment typo
ima: add support for creating files using the mknodat syscall
ima: fix ima_inode_post_setattr
vfs: forbid write access when reading a file into memory
fs: fix over-zealous use of "const"
selinux: apply execstack check on thread stacks
selinux: distinguish non-init user namespace capability checks
LSM: LoadPin for kernel file loading restrictions
fs: define a string representation of the kernel_read_file_id enumeration
Yama: consolidate error reporting
string_helpers: add kstrdup_quotable_file
string_helpers: add kstrdup_quotable_cmdline
string_helpers: add kstrdup_quotable
selinux: check ss_initialized before revalidating an inode label
selinux: delay inode label lookup as long as possible
selinux: don't revalidate an inode's label when explicitly setting it
selinux: Change bool variable name to index.
KEYS: Add KEYCTL_DH_COMPUTE command
...
This commit is contained in:
17
Documentation/security/LoadPin.txt
Normal file
17
Documentation/security/LoadPin.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
LoadPin is a Linux Security Module that ensures all kernel-loaded files
|
||||
(modules, firmware, etc) all originate from the same filesystem, with
|
||||
the expectation that such a filesystem is backed by a read-only device
|
||||
such as dm-verity or CDROM. This allows systems that have a verified
|
||||
and/or unchangeable filesystem to enforce module and firmware loading
|
||||
restrictions without needing to sign the files individually.
|
||||
|
||||
The LSM is selectable at build-time with CONFIG_SECURITY_LOADPIN, and
|
||||
can be controlled at boot-time with the kernel command line option
|
||||
"loadpin.enabled". By default, it is enabled, but can be disabled at
|
||||
boot ("loadpin.enabled=0").
|
||||
|
||||
LoadPin starts pinning when it sees the first file loaded. If the
|
||||
block device backing the filesystem is not read-only, a sysctl is
|
||||
created to toggle pinning: /proc/sys/kernel/loadpin/enabled. (Having
|
||||
a mutable filesystem means pinning is mutable too, but having the
|
||||
sysctl allows for easy testing on systems with a mutable filesystem.)
|
||||
@@ -823,6 +823,36 @@ The keyctl syscall functions are:
|
||||
A process must have search permission on the key for this function to be
|
||||
successful.
|
||||
|
||||
(*) Compute a Diffie-Hellman shared secret or public key
|
||||
|
||||
long keyctl(KEYCTL_DH_COMPUTE, struct keyctl_dh_params *params,
|
||||
char *buffer, size_t buflen);
|
||||
|
||||
The params struct contains serial numbers for three keys:
|
||||
|
||||
- The prime, p, known to both parties
|
||||
- The local private key
|
||||
- The base integer, which is either a shared generator or the
|
||||
remote public key
|
||||
|
||||
The value computed is:
|
||||
|
||||
result = base ^ private (mod prime)
|
||||
|
||||
If the base is the shared generator, the result is the local
|
||||
public key. If the base is the remote public key, the result is
|
||||
the shared secret.
|
||||
|
||||
The buffer length must be at least the length of the prime, or zero.
|
||||
|
||||
If the buffer length is nonzero, the length of the result is
|
||||
returned when it is successfully calculated and copied in to the
|
||||
buffer. When the buffer length is zero, the minimum required
|
||||
buffer length is returned.
|
||||
|
||||
This function will return error EOPNOTSUPP if the key type is not
|
||||
supported, error ENOKEY if the key could not be found, or error
|
||||
EACCES if the key is not readable by the caller.
|
||||
|
||||
===============
|
||||
KERNEL SERVICES
|
||||
@@ -999,6 +1029,10 @@ payload contents" for more information.
|
||||
struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
|
||||
const struct cred *cred,
|
||||
key_perm_t perm,
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
unsigned long,
|
||||
const union key_payload *),
|
||||
unsigned long flags,
|
||||
struct key *dest);
|
||||
|
||||
@@ -1010,6 +1044,24 @@ payload contents" for more information.
|
||||
KEY_ALLOC_NOT_IN_QUOTA in flags if the keyring shouldn't be accounted
|
||||
towards the user's quota). Error ENOMEM can also be returned.
|
||||
|
||||
If restrict_link not NULL, it should point to a function that will be
|
||||
called each time an attempt is made to link a key into the new keyring.
|
||||
This function is called to check whether a key may be added into the keying
|
||||
or not. Callers of key_create_or_update() within the kernel can pass
|
||||
KEY_ALLOC_BYPASS_RESTRICTION to suppress the check. An example of using
|
||||
this is to manage rings of cryptographic keys that are set up when the
|
||||
kernel boots where userspace is also permitted to add keys - provided they
|
||||
can be verified by a key the kernel already has.
|
||||
|
||||
When called, the restriction function will be passed the keyring being
|
||||
added to, the key flags value and the type and payload of the key being
|
||||
added. Note that when a new key is being created, this is called between
|
||||
payload preparsing and actual key creation. The function should return 0
|
||||
to allow the link or an error to reject it.
|
||||
|
||||
A convenience function, restrict_link_reject, exists to always return
|
||||
-EPERM to in this case.
|
||||
|
||||
|
||||
(*) To check the validity of a key, this function can be called:
|
||||
|
||||
|
||||
@@ -10025,6 +10025,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git
|
||||
S: Supported
|
||||
F: security/apparmor/
|
||||
|
||||
LOADPIN SECURITY MODULE
|
||||
M: Kees Cook <keescook@chromium.org>
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git lsm/loadpin
|
||||
S: Supported
|
||||
F: security/loadpin/
|
||||
|
||||
YAMA SECURITY MODULE
|
||||
M: Kees Cook <keescook@chromium.org>
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git yama/tip
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/verify_pefile.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include <linux/verification.h>
|
||||
|
||||
#include <asm/bootparam.h>
|
||||
#include <asm/setup.h>
|
||||
@@ -529,18 +528,9 @@ static int bzImage64_cleanup(void *loader_data)
|
||||
#ifdef CONFIG_KEXEC_BZIMAGE_VERIFY_SIG
|
||||
static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len)
|
||||
{
|
||||
bool trusted;
|
||||
int ret;
|
||||
|
||||
ret = verify_pefile_signature(kernel, kernel_len,
|
||||
system_trusted_keyring,
|
||||
VERIFYING_KEXEC_PE_SIGNATURE,
|
||||
&trusted);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!trusted)
|
||||
return -EKEYREJECTED;
|
||||
return 0;
|
||||
return verify_pefile_signature(kernel, kernel_len,
|
||||
NULL,
|
||||
VERIFYING_KEXEC_PE_SIGNATURE);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ config MODULE_SIG_KEY
|
||||
config SYSTEM_TRUSTED_KEYRING
|
||||
bool "Provide system-wide ring of trusted keys"
|
||||
depends on KEYS
|
||||
depends on ASYMMETRIC_KEY_TYPE
|
||||
help
|
||||
Provide a system keyring to which trusted keys can be added. Keys in
|
||||
the keyring are considered to be trusted. Keys may be added at will
|
||||
@@ -55,4 +56,12 @@ config SYSTEM_EXTRA_CERTIFICATE_SIZE
|
||||
This is the number of bytes reserved in the kernel image for a
|
||||
certificate to be inserted.
|
||||
|
||||
config SECONDARY_TRUSTED_KEYRING
|
||||
bool "Provide a keyring to which extra trustable keys may be added"
|
||||
depends on SYSTEM_TRUSTED_KEYRING
|
||||
help
|
||||
If set, provide a keyring to which extra keys may be added, provided
|
||||
those keys are not blacklisted and are vouched for by a key built
|
||||
into the kernel or already in the secondary trusted keyring.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -18,29 +18,88 @@
|
||||
#include <keys/system_keyring.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
|
||||
struct key *system_trusted_keyring;
|
||||
EXPORT_SYMBOL_GPL(system_trusted_keyring);
|
||||
static struct key *builtin_trusted_keys;
|
||||
#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
|
||||
static struct key *secondary_trusted_keys;
|
||||
#endif
|
||||
|
||||
extern __initconst const u8 system_certificate_list[];
|
||||
extern __initconst const unsigned long system_certificate_list_size;
|
||||
|
||||
/**
|
||||
* restrict_link_to_builtin_trusted - Restrict keyring addition by built in CA
|
||||
*
|
||||
* Restrict the addition of keys into a keyring based on the key-to-be-added
|
||||
* being vouched for by a key in the built in system keyring.
|
||||
*/
|
||||
int restrict_link_by_builtin_trusted(struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload)
|
||||
{
|
||||
return restrict_link_by_signature(builtin_trusted_keys, type, payload);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
|
||||
/**
|
||||
* restrict_link_by_builtin_and_secondary_trusted - Restrict keyring
|
||||
* addition by both builtin and secondary keyrings
|
||||
*
|
||||
* Restrict the addition of keys into a keyring based on the key-to-be-added
|
||||
* being vouched for by a key in either the built-in or the secondary system
|
||||
* keyrings.
|
||||
*/
|
||||
int restrict_link_by_builtin_and_secondary_trusted(
|
||||
struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload)
|
||||
{
|
||||
/* If we have a secondary trusted keyring, then that contains a link
|
||||
* through to the builtin keyring and the search will follow that link.
|
||||
*/
|
||||
if (type == &key_type_keyring &&
|
||||
keyring == secondary_trusted_keys &&
|
||||
payload == &builtin_trusted_keys->payload)
|
||||
/* Allow the builtin keyring to be added to the secondary */
|
||||
return 0;
|
||||
|
||||
return restrict_link_by_signature(secondary_trusted_keys, type, payload);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Load the compiled-in keys
|
||||
* Create the trusted keyrings
|
||||
*/
|
||||
static __init int system_trusted_keyring_init(void)
|
||||
{
|
||||
pr_notice("Initialise system trusted keyring\n");
|
||||
pr_notice("Initialise system trusted keyrings\n");
|
||||
|
||||
system_trusted_keyring =
|
||||
keyring_alloc(".system_keyring",
|
||||
builtin_trusted_keys =
|
||||
keyring_alloc(".builtin_trusted_keys",
|
||||
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH),
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
if (IS_ERR(system_trusted_keyring))
|
||||
panic("Can't allocate system trusted keyring\n");
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
NULL, NULL);
|
||||
if (IS_ERR(builtin_trusted_keys))
|
||||
panic("Can't allocate builtin trusted keyring\n");
|
||||
|
||||
#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
|
||||
secondary_trusted_keys =
|
||||
keyring_alloc(".secondary_trusted_keys",
|
||||
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH |
|
||||
KEY_USR_WRITE),
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
restrict_link_by_builtin_and_secondary_trusted,
|
||||
NULL);
|
||||
if (IS_ERR(secondary_trusted_keys))
|
||||
panic("Can't allocate secondary trusted keyring\n");
|
||||
|
||||
if (key_link(secondary_trusted_keys, builtin_trusted_keys) < 0)
|
||||
panic("Can't link trusted keyrings\n");
|
||||
#endif
|
||||
|
||||
set_bit(KEY_FLAG_TRUSTED_ONLY, &system_trusted_keyring->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -76,7 +135,7 @@ static __init int load_system_certificate_list(void)
|
||||
if (plen > end - p)
|
||||
goto dodgy_cert;
|
||||
|
||||
key = key_create_or_update(make_key_ref(system_trusted_keyring, 1),
|
||||
key = key_create_or_update(make_key_ref(builtin_trusted_keys, 1),
|
||||
"asymmetric",
|
||||
NULL,
|
||||
p,
|
||||
@@ -84,8 +143,8 @@ static __init int load_system_certificate_list(void)
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ),
|
||||
KEY_ALLOC_NOT_IN_QUOTA |
|
||||
KEY_ALLOC_TRUSTED |
|
||||
KEY_ALLOC_BUILT_IN);
|
||||
KEY_ALLOC_BUILT_IN |
|
||||
KEY_ALLOC_BYPASS_RESTRICTION);
|
||||
if (IS_ERR(key)) {
|
||||
pr_err("Problem loading in-kernel X.509 certificate (%ld)\n",
|
||||
PTR_ERR(key));
|
||||
@@ -108,19 +167,27 @@ late_initcall(load_system_certificate_list);
|
||||
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
|
||||
|
||||
/**
|
||||
* Verify a PKCS#7-based signature on system data.
|
||||
* @data: The data to be verified.
|
||||
* 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 system_verify_data(const void *data, unsigned long len,
|
||||
const void *raw_pkcs7, size_t pkcs7_len,
|
||||
enum key_being_used_for usage)
|
||||
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;
|
||||
bool trusted;
|
||||
int ret;
|
||||
|
||||
pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len);
|
||||
@@ -128,7 +195,7 @@ int system_verify_data(const void *data, unsigned long len,
|
||||
return PTR_ERR(pkcs7);
|
||||
|
||||
/* The data should be detached - so we need to supply it. */
|
||||
if (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");
|
||||
ret = -EBADMSG;
|
||||
goto error;
|
||||
@@ -138,13 +205,33 @@ int system_verify_data(const void *data, unsigned long len,
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted);
|
||||
if (ret < 0)
|
||||
if (!trusted_keys) {
|
||||
trusted_keys = builtin_trusted_keys;
|
||||
} else if (trusted_keys == (void *)1UL) {
|
||||
#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
|
||||
trusted_keys = secondary_trusted_keys;
|
||||
#else
|
||||
trusted_keys = builtin_trusted_keys;
|
||||
#endif
|
||||
}
|
||||
ret = pkcs7_validate_trust(pkcs7, trusted_keys);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOKEY)
|
||||
pr_err("PKCS#7 signature not signed with a trusted key\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!trusted) {
|
||||
pr_err("PKCS#7 signature not signed with a trusted key\n");
|
||||
ret = -ENOKEY;
|
||||
if (view_content) {
|
||||
size_t asn1hdrlen;
|
||||
|
||||
ret = pkcs7_get_content_data(pkcs7, &data, &len, &asn1hdrlen);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENODATA)
|
||||
pr_devel("PKCS#7 message does not contain data\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = view_content(ctx, data, len, asn1hdrlen);
|
||||
}
|
||||
|
||||
error:
|
||||
@@ -152,6 +239,6 @@ error:
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(system_verify_data);
|
||||
EXPORT_SYMBOL_GPL(verify_pkcs7_signature);
|
||||
|
||||
#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
menuconfig ASYMMETRIC_KEY_TYPE
|
||||
tristate "Asymmetric (public-key cryptographic) key type"
|
||||
bool "Asymmetric (public-key cryptographic) key type"
|
||||
depends on KEYS
|
||||
help
|
||||
This option provides support for a key type that holds the data for
|
||||
@@ -40,8 +40,7 @@ config PKCS7_MESSAGE_PARSER
|
||||
|
||||
config PKCS7_TEST_KEY
|
||||
tristate "PKCS#7 testing key type"
|
||||
depends on PKCS7_MESSAGE_PARSER
|
||||
select SYSTEM_TRUSTED_KEYRING
|
||||
depends on SYSTEM_DATA_VERIFICATION
|
||||
help
|
||||
This option provides a type of key that can be loaded up from a
|
||||
PKCS#7 message - provided the message is signed by a trusted key. If
|
||||
@@ -54,6 +53,7 @@ config PKCS7_TEST_KEY
|
||||
config SIGNED_PE_FILE_VERIFICATION
|
||||
bool "Support for PE file signature verification"
|
||||
depends on PKCS7_MESSAGE_PARSER=y
|
||||
depends on SYSTEM_DATA_VERIFICATION
|
||||
select ASN1
|
||||
select OID_REGISTRY
|
||||
help
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
|
||||
obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
|
||||
|
||||
asymmetric_keys-y := asymmetric_type.o signature.o
|
||||
asymmetric_keys-y := \
|
||||
asymmetric_type.o \
|
||||
restrict.o \
|
||||
signature.o
|
||||
|
||||
obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <keys/asymmetric-type.h>
|
||||
|
||||
extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id);
|
||||
|
||||
extern int __asymmetric_key_hex_to_key_id(const char *id,
|
||||
|
||||
@@ -34,6 +34,95 @@ EXPORT_SYMBOL_GPL(key_being_used_for);
|
||||
static LIST_HEAD(asymmetric_key_parsers);
|
||||
static DECLARE_RWSEM(asymmetric_key_parsers_sem);
|
||||
|
||||
/**
|
||||
* find_asymmetric_key - Find a key by ID.
|
||||
* @keyring: The keys to search.
|
||||
* @id_0: The first ID to look for or NULL.
|
||||
* @id_1: The second ID to look for or NULL.
|
||||
* @partial: Use partial match if true, exact if false.
|
||||
*
|
||||
* Find a key in the given keyring by identifier. The preferred identifier is
|
||||
* the id_0 and the fallback identifier is the id_1. If both are given, the
|
||||
* lookup is by the former, but the latter must also match.
|
||||
*/
|
||||
struct key *find_asymmetric_key(struct key *keyring,
|
||||
const struct asymmetric_key_id *id_0,
|
||||
const struct asymmetric_key_id *id_1,
|
||||
bool partial)
|
||||
{
|
||||
struct key *key;
|
||||
key_ref_t ref;
|
||||
const char *lookup;
|
||||
char *req, *p;
|
||||
int len;
|
||||
|
||||
if (id_0) {
|
||||
lookup = id_0->data;
|
||||
len = id_0->len;
|
||||
} else {
|
||||
lookup = id_1->data;
|
||||
len = id_1->len;
|
||||
}
|
||||
|
||||
/* Construct an identifier "id:<keyid>". */
|
||||
p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
|
||||
if (!req)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (partial) {
|
||||
*p++ = 'i';
|
||||
*p++ = 'd';
|
||||
} else {
|
||||
*p++ = 'e';
|
||||
*p++ = 'x';
|
||||
}
|
||||
*p++ = ':';
|
||||
p = bin2hex(p, lookup, len);
|
||||
*p = 0;
|
||||
|
||||
pr_debug("Look up: \"%s\"\n", req);
|
||||
|
||||
ref = keyring_search(make_key_ref(keyring, 1),
|
||||
&key_type_asymmetric, req);
|
||||
if (IS_ERR(ref))
|
||||
pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref));
|
||||
kfree(req);
|
||||
|
||||
if (IS_ERR(ref)) {
|
||||
switch (PTR_ERR(ref)) {
|
||||
/* Hide some search errors */
|
||||
case -EACCES:
|
||||
case -ENOTDIR:
|
||||
case -EAGAIN:
|
||||
return ERR_PTR(-ENOKEY);
|
||||
default:
|
||||
return ERR_CAST(ref);
|
||||
}
|
||||
}
|
||||
|
||||
key = key_ref_to_ptr(ref);
|
||||
if (id_0 && id_1) {
|
||||
const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
|
||||
|
||||
if (!kids->id[0]) {
|
||||
pr_debug("First ID matches, but second is missing\n");
|
||||
goto reject;
|
||||
}
|
||||
if (!asymmetric_key_id_same(id_1, kids->id[1])) {
|
||||
pr_debug("First ID matches, but second does not\n");
|
||||
goto reject;
|
||||
}
|
||||
}
|
||||
|
||||
pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key));
|
||||
return key;
|
||||
|
||||
reject:
|
||||
key_put(key);
|
||||
return ERR_PTR(-EKEYREJECTED);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(find_asymmetric_key);
|
||||
|
||||
/**
|
||||
* asymmetric_key_generate_id: Construct an asymmetric key ID
|
||||
* @val_1: First binary blob
|
||||
@@ -331,7 +420,8 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
if (subtype) {
|
||||
subtype->destroy(prep->payload.data[asym_crypto]);
|
||||
subtype->destroy(prep->payload.data[asym_crypto],
|
||||
prep->payload.data[asym_auth]);
|
||||
module_put(subtype->owner);
|
||||
}
|
||||
asymmetric_key_free_kids(kids);
|
||||
@@ -346,13 +436,15 @@ static void asymmetric_key_destroy(struct key *key)
|
||||
struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
|
||||
struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids];
|
||||
void *data = key->payload.data[asym_crypto];
|
||||
void *auth = key->payload.data[asym_auth];
|
||||
|
||||
key->payload.data[asym_crypto] = NULL;
|
||||
key->payload.data[asym_subtype] = NULL;
|
||||
key->payload.data[asym_key_ids] = NULL;
|
||||
key->payload.data[asym_auth] = NULL;
|
||||
|
||||
if (subtype) {
|
||||
subtype->destroy(data);
|
||||
subtype->destroy(data, auth);
|
||||
module_put(subtype->owner);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,19 +21,13 @@
|
||||
/*
|
||||
* Parse a Microsoft Individual Code Signing blob
|
||||
*/
|
||||
int mscode_parse(struct pefile_context *ctx)
|
||||
int mscode_parse(void *_ctx, const void *content_data, size_t data_len,
|
||||
size_t asn1hdrlen)
|
||||
{
|
||||
const void *content_data;
|
||||
size_t data_len;
|
||||
int ret;
|
||||
|
||||
ret = pkcs7_get_content_data(ctx->pkcs7, &content_data, &data_len, 1);
|
||||
|
||||
if (ret) {
|
||||
pr_debug("PKCS#7 message does not contain data\n");
|
||||
return ret;
|
||||
}
|
||||
struct pefile_context *ctx = _ctx;
|
||||
|
||||
content_data -= asn1hdrlen;
|
||||
data_len += asn1hdrlen;
|
||||
pr_devel("Data: %zu [%*ph]\n", data_len, (unsigned)(data_len),
|
||||
content_data);
|
||||
|
||||
@@ -129,7 +123,6 @@ int mscode_note_digest(void *context, size_t hdrlen,
|
||||
{
|
||||
struct pefile_context *ctx = context;
|
||||
|
||||
ctx->digest = value;
|
||||
ctx->digest_len = vlen;
|
||||
return 0;
|
||||
ctx->digest = kmemdup(value, vlen, GFP_KERNEL);
|
||||
return ctx->digest ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -13,12 +13,9 @@
|
||||
#include <linux/key.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/verification.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include "pkcs7_parser.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("PKCS#7 testing key type");
|
||||
@@ -28,58 +25,45 @@ module_param_named(usage, pkcs7_usage, uint, S_IWUSR | S_IRUGO);
|
||||
MODULE_PARM_DESC(pkcs7_usage,
|
||||
"Usage to specify when verifying the PKCS#7 message");
|
||||
|
||||
/*
|
||||
* Retrieve the PKCS#7 message content.
|
||||
*/
|
||||
static int pkcs7_view_content(void *ctx, const void *data, size_t len,
|
||||
size_t asn1hdrlen)
|
||||
{
|
||||
struct key_preparsed_payload *prep = ctx;
|
||||
const void *saved_prep_data;
|
||||
size_t saved_prep_datalen;
|
||||
int ret;
|
||||
|
||||
saved_prep_data = prep->data;
|
||||
saved_prep_datalen = prep->datalen;
|
||||
prep->data = data;
|
||||
prep->datalen = len;
|
||||
|
||||
ret = user_preparse(prep);
|
||||
|
||||
prep->data = saved_prep_data;
|
||||
prep->datalen = saved_prep_datalen;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Preparse a PKCS#7 wrapped and validated data blob.
|
||||
*/
|
||||
static int pkcs7_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
enum key_being_used_for usage = pkcs7_usage;
|
||||
struct pkcs7_message *pkcs7;
|
||||
const void *data, *saved_prep_data;
|
||||
size_t datalen, saved_prep_datalen;
|
||||
bool trusted;
|
||||
int ret;
|
||||
|
||||
kenter("");
|
||||
|
||||
if (usage >= NR__KEY_BEING_USED_FOR) {
|
||||
pr_err("Invalid usage type %d\n", usage);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
saved_prep_data = prep->data;
|
||||
saved_prep_datalen = prep->datalen;
|
||||
pkcs7 = pkcs7_parse_message(saved_prep_data, saved_prep_datalen);
|
||||
if (IS_ERR(pkcs7)) {
|
||||
ret = PTR_ERR(pkcs7);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = pkcs7_verify(pkcs7, usage);
|
||||
if (ret < 0)
|
||||
goto error_free;
|
||||
|
||||
ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted);
|
||||
if (ret < 0)
|
||||
goto error_free;
|
||||
if (!trusted)
|
||||
pr_warn("PKCS#7 message doesn't chain back to a trusted key\n");
|
||||
|
||||
ret = pkcs7_get_content_data(pkcs7, &data, &datalen, false);
|
||||
if (ret < 0)
|
||||
goto error_free;
|
||||
|
||||
prep->data = data;
|
||||
prep->datalen = datalen;
|
||||
ret = user_preparse(prep);
|
||||
prep->data = saved_prep_data;
|
||||
prep->datalen = saved_prep_datalen;
|
||||
|
||||
error_free:
|
||||
pkcs7_free_message(pkcs7);
|
||||
error:
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
return verify_pkcs7_signature(NULL, 0,
|
||||
prep->data, prep->datalen,
|
||||
NULL, usage,
|
||||
pkcs7_view_content, prep);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -44,9 +44,7 @@ struct pkcs7_parse_context {
|
||||
static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)
|
||||
{
|
||||
if (sinfo) {
|
||||
kfree(sinfo->sig.s);
|
||||
kfree(sinfo->sig.digest);
|
||||
kfree(sinfo->signing_cert_id);
|
||||
public_key_signature_free(sinfo->sig);
|
||||
kfree(sinfo);
|
||||
}
|
||||
}
|
||||
@@ -125,6 +123,10 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
|
||||
ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
|
||||
if (!ctx->sinfo)
|
||||
goto out_no_sinfo;
|
||||
ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
|
||||
GFP_KERNEL);
|
||||
if (!ctx->sinfo->sig)
|
||||
goto out_no_sig;
|
||||
|
||||
ctx->data = (unsigned long)data;
|
||||
ctx->ppcerts = &ctx->certs;
|
||||
@@ -150,6 +152,7 @@ out:
|
||||
ctx->certs = cert->next;
|
||||
x509_free_certificate(cert);
|
||||
}
|
||||
out_no_sig:
|
||||
pkcs7_free_signed_info(ctx->sinfo);
|
||||
out_no_sinfo:
|
||||
pkcs7_free_message(ctx->msg);
|
||||
@@ -165,24 +168,25 @@ EXPORT_SYMBOL_GPL(pkcs7_parse_message);
|
||||
* @pkcs7: The preparsed PKCS#7 message to access
|
||||
* @_data: Place to return a pointer to the data
|
||||
* @_data_len: Place to return the data length
|
||||
* @want_wrapper: True if the ASN.1 object header should be included in the data
|
||||
* @_headerlen: Size of ASN.1 header not included in _data
|
||||
*
|
||||
* Get access to the data content of the PKCS#7 message, including, optionally,
|
||||
* the header of the ASN.1 object that contains it. Returns -ENODATA if the
|
||||
* data object was missing from the message.
|
||||
* Get access to the data content of the PKCS#7 message. The size of the
|
||||
* header of the ASN.1 object that contains it is also provided and can be used
|
||||
* to adjust *_data and *_data_len to get the entire object.
|
||||
*
|
||||
* Returns -ENODATA if the data object was missing from the message.
|
||||
*/
|
||||
int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
|
||||
const void **_data, size_t *_data_len,
|
||||
bool want_wrapper)
|
||||
size_t *_headerlen)
|
||||
{
|
||||
size_t wrapper;
|
||||
|
||||
if (!pkcs7->data)
|
||||
return -ENODATA;
|
||||
|
||||
wrapper = want_wrapper ? pkcs7->data_hdrlen : 0;
|
||||
*_data = pkcs7->data - wrapper;
|
||||
*_data_len = pkcs7->data_len + wrapper;
|
||||
*_data = pkcs7->data;
|
||||
*_data_len = pkcs7->data_len;
|
||||
if (_headerlen)
|
||||
*_headerlen = pkcs7->data_hdrlen;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_get_content_data);
|
||||
@@ -218,25 +222,25 @@ int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,
|
||||
|
||||
switch (ctx->last_oid) {
|
||||
case OID_md4:
|
||||
ctx->sinfo->sig.hash_algo = "md4";
|
||||
ctx->sinfo->sig->hash_algo = "md4";
|
||||
break;
|
||||
case OID_md5:
|
||||
ctx->sinfo->sig.hash_algo = "md5";
|
||||
ctx->sinfo->sig->hash_algo = "md5";
|
||||
break;
|
||||
case OID_sha1:
|
||||
ctx->sinfo->sig.hash_algo = "sha1";
|
||||
ctx->sinfo->sig->hash_algo = "sha1";
|
||||
break;
|
||||
case OID_sha256:
|
||||
ctx->sinfo->sig.hash_algo = "sha256";
|
||||
ctx->sinfo->sig->hash_algo = "sha256";
|
||||
break;
|
||||
case OID_sha384:
|
||||
ctx->sinfo->sig.hash_algo = "sha384";
|
||||
ctx->sinfo->sig->hash_algo = "sha384";
|
||||
break;
|
||||
case OID_sha512:
|
||||
ctx->sinfo->sig.hash_algo = "sha512";
|
||||
ctx->sinfo->sig->hash_algo = "sha512";
|
||||
break;
|
||||
case OID_sha224:
|
||||
ctx->sinfo->sig.hash_algo = "sha224";
|
||||
ctx->sinfo->sig->hash_algo = "sha224";
|
||||
break;
|
||||
default:
|
||||
printk("Unsupported digest algo: %u\n", ctx->last_oid);
|
||||
@@ -256,7 +260,7 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
|
||||
|
||||
switch (ctx->last_oid) {
|
||||
case OID_rsaEncryption:
|
||||
ctx->sinfo->sig.pkey_algo = "rsa";
|
||||
ctx->sinfo->sig->pkey_algo = "rsa";
|
||||
break;
|
||||
default:
|
||||
printk("Unsupported pkey algo: %u\n", ctx->last_oid);
|
||||
@@ -616,11 +620,11 @@ int pkcs7_sig_note_signature(void *context, size_t hdrlen,
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
ctx->sinfo->sig.s = kmemdup(value, vlen, GFP_KERNEL);
|
||||
if (!ctx->sinfo->sig.s)
|
||||
ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL);
|
||||
if (!ctx->sinfo->sig->s)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->sinfo->sig.s_size = vlen;
|
||||
ctx->sinfo->sig->s_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -656,12 +660,16 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen,
|
||||
|
||||
pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);
|
||||
|
||||
sinfo->signing_cert_id = kid;
|
||||
sinfo->sig->auth_ids[0] = kid;
|
||||
sinfo->index = ++ctx->sinfo_index;
|
||||
*ctx->ppsinfo = sinfo;
|
||||
ctx->ppsinfo = &sinfo->next;
|
||||
ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
|
||||
if (!ctx->sinfo)
|
||||
return -ENOMEM;
|
||||
ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
|
||||
GFP_KERNEL);
|
||||
if (!ctx->sinfo->sig)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ struct pkcs7_signed_info {
|
||||
struct pkcs7_signed_info *next;
|
||||
struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
|
||||
unsigned index;
|
||||
bool trusted;
|
||||
bool unsupported_crypto; /* T if not usable due to missing crypto */
|
||||
|
||||
/* Message digest - the digest of the Content Data (or NULL) */
|
||||
@@ -41,19 +40,17 @@ struct pkcs7_signed_info {
|
||||
#define sinfo_has_ms_statement_type 5
|
||||
time64_t signing_time;
|
||||
|
||||
/* Issuing cert serial number and issuer's name [PKCS#7 or CMS ver 1]
|
||||
* or issuing cert's SKID [CMS ver 3].
|
||||
*/
|
||||
struct asymmetric_key_id *signing_cert_id;
|
||||
|
||||
/* Message signature.
|
||||
*
|
||||
* This contains the generated digest of _either_ the Content Data or
|
||||
* the Authenticated Attributes [RFC2315 9.3]. If the latter, one of
|
||||
* the attributes contains the digest of the the Content Data within
|
||||
* it.
|
||||
*
|
||||
* THis also contains the issuing cert serial number and issuer's name
|
||||
* [PKCS#7 or CMS ver 1] or issuing cert's SKID [CMS ver 3].
|
||||
*/
|
||||
struct public_key_signature sig;
|
||||
struct public_key_signature *sig;
|
||||
};
|
||||
|
||||
struct pkcs7_message {
|
||||
|
||||
@@ -27,10 +27,9 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
|
||||
struct pkcs7_signed_info *sinfo,
|
||||
struct key *trust_keyring)
|
||||
{
|
||||
struct public_key_signature *sig = &sinfo->sig;
|
||||
struct public_key_signature *sig = sinfo->sig;
|
||||
struct x509_certificate *x509, *last = NULL, *p;
|
||||
struct key *key;
|
||||
bool trusted;
|
||||
int ret;
|
||||
|
||||
kenter(",%u,", sinfo->index);
|
||||
@@ -42,10 +41,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
|
||||
|
||||
for (x509 = sinfo->signer; x509; x509 = x509->signer) {
|
||||
if (x509->seen) {
|
||||
if (x509->verified) {
|
||||
trusted = x509->trusted;
|
||||
if (x509->verified)
|
||||
goto verified;
|
||||
}
|
||||
kleave(" = -ENOKEY [cached]");
|
||||
return -ENOKEY;
|
||||
}
|
||||
@@ -54,9 +51,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
|
||||
/* Look to see if this certificate is present in the trusted
|
||||
* keys.
|
||||
*/
|
||||
key = x509_request_asymmetric_key(trust_keyring,
|
||||
x509->id, x509->skid,
|
||||
false);
|
||||
key = find_asymmetric_key(trust_keyring,
|
||||
x509->id, x509->skid, false);
|
||||
if (!IS_ERR(key)) {
|
||||
/* One of the X.509 certificates in the PKCS#7 message
|
||||
* is apparently the same as one we already trust.
|
||||
@@ -80,17 +76,17 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
|
||||
|
||||
might_sleep();
|
||||
last = x509;
|
||||
sig = &last->sig;
|
||||
sig = last->sig;
|
||||
}
|
||||
|
||||
/* No match - see if the root certificate has a signer amongst the
|
||||
* trusted keys.
|
||||
*/
|
||||
if (last && (last->akid_id || last->akid_skid)) {
|
||||
key = x509_request_asymmetric_key(trust_keyring,
|
||||
last->akid_id,
|
||||
last->akid_skid,
|
||||
false);
|
||||
if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) {
|
||||
key = find_asymmetric_key(trust_keyring,
|
||||
last->sig->auth_ids[0],
|
||||
last->sig->auth_ids[1],
|
||||
false);
|
||||
if (!IS_ERR(key)) {
|
||||
x509 = last;
|
||||
pr_devel("sinfo %u: Root cert %u signer is key %x\n",
|
||||
@@ -104,10 +100,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
|
||||
/* As a last resort, see if we have a trusted public key that matches
|
||||
* the signed info directly.
|
||||
*/
|
||||
key = x509_request_asymmetric_key(trust_keyring,
|
||||
sinfo->signing_cert_id,
|
||||
NULL,
|
||||
false);
|
||||
key = find_asymmetric_key(trust_keyring,
|
||||
sinfo->sig->auth_ids[0], NULL, false);
|
||||
if (!IS_ERR(key)) {
|
||||
pr_devel("sinfo %u: Direct signer is key %x\n",
|
||||
sinfo->index, key_serial(key));
|
||||
@@ -122,7 +116,6 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
|
||||
|
||||
matched:
|
||||
ret = verify_signature(key, sig);
|
||||
trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags);
|
||||
key_put(key);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOMEM)
|
||||
@@ -134,12 +127,9 @@ matched:
|
||||
verified:
|
||||
if (x509) {
|
||||
x509->verified = true;
|
||||
for (p = sinfo->signer; p != x509; p = p->signer) {
|
||||
for (p = sinfo->signer; p != x509; p = p->signer)
|
||||
p->verified = true;
|
||||
p->trusted = trusted;
|
||||
}
|
||||
}
|
||||
sinfo->trusted = trusted;
|
||||
kleave(" = 0");
|
||||
return 0;
|
||||
}
|
||||
@@ -148,7 +138,6 @@ verified:
|
||||
* pkcs7_validate_trust - Validate PKCS#7 trust chain
|
||||
* @pkcs7: The PKCS#7 certificate to validate
|
||||
* @trust_keyring: Signing certificates to use as starting points
|
||||
* @_trusted: Set to true if trustworth, false otherwise
|
||||
*
|
||||
* Validate that the certificate chain inside the PKCS#7 message intersects
|
||||
* keys we already know and trust.
|
||||
@@ -170,16 +159,13 @@ verified:
|
||||
* May also return -ENOMEM.
|
||||
*/
|
||||
int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
|
||||
struct key *trust_keyring,
|
||||
bool *_trusted)
|
||||
struct key *trust_keyring)
|
||||
{
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
struct x509_certificate *p;
|
||||
int cached_ret = -ENOKEY;
|
||||
int ret;
|
||||
|
||||
*_trusted = false;
|
||||
|
||||
for (p = pkcs7->certs; p; p = p->next)
|
||||
p->seen = false;
|
||||
|
||||
@@ -193,7 +179,6 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
|
||||
cached_ret = -ENOPKG;
|
||||
continue;
|
||||
case 0:
|
||||
*_trusted |= sinfo->trusted;
|
||||
cached_ret = 0;
|
||||
continue;
|
||||
default:
|
||||
|
||||
@@ -25,34 +25,36 @@
|
||||
static int pkcs7_digest(struct pkcs7_message *pkcs7,
|
||||
struct pkcs7_signed_info *sinfo)
|
||||
{
|
||||
struct public_key_signature *sig = sinfo->sig;
|
||||
struct crypto_shash *tfm;
|
||||
struct shash_desc *desc;
|
||||
size_t digest_size, desc_size;
|
||||
void *digest;
|
||||
size_t desc_size;
|
||||
int ret;
|
||||
|
||||
kenter(",%u,%s", sinfo->index, sinfo->sig.hash_algo);
|
||||
kenter(",%u,%s", sinfo->index, sinfo->sig->hash_algo);
|
||||
|
||||
if (!sinfo->sig.hash_algo)
|
||||
if (!sinfo->sig->hash_algo)
|
||||
return -ENOPKG;
|
||||
|
||||
/* Allocate the hashing algorithm we're going to need and find out how
|
||||
* big the hash operational data will be.
|
||||
*/
|
||||
tfm = crypto_alloc_shash(sinfo->sig.hash_algo, 0, 0);
|
||||
tfm = crypto_alloc_shash(sinfo->sig->hash_algo, 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
|
||||
|
||||
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
|
||||
sinfo->sig.digest_size = digest_size = crypto_shash_digestsize(tfm);
|
||||
sig->digest_size = crypto_shash_digestsize(tfm);
|
||||
|
||||
ret = -ENOMEM;
|
||||
digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size,
|
||||
GFP_KERNEL);
|
||||
if (!digest)
|
||||
sig->digest = kmalloc(sig->digest_size, GFP_KERNEL);
|
||||
if (!sig->digest)
|
||||
goto error_no_desc;
|
||||
|
||||
desc = kzalloc(desc_size, GFP_KERNEL);
|
||||
if (!desc)
|
||||
goto error_no_desc;
|
||||
|
||||
desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc));
|
||||
desc->tfm = tfm;
|
||||
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
@@ -60,10 +62,11 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
|
||||
ret = crypto_shash_init(desc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest);
|
||||
ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len,
|
||||
sig->digest);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
pr_devel("MsgDigest = [%*ph]\n", 8, digest);
|
||||
pr_devel("MsgDigest = [%*ph]\n", 8, sig->digest);
|
||||
|
||||
/* However, if there are authenticated attributes, there must be a
|
||||
* message digest attribute amongst them which corresponds to the
|
||||
@@ -78,14 +81,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (sinfo->msgdigest_len != sinfo->sig.digest_size) {
|
||||
if (sinfo->msgdigest_len != sig->digest_size) {
|
||||
pr_debug("Sig %u: Invalid digest size (%u)\n",
|
||||
sinfo->index, sinfo->msgdigest_len);
|
||||
ret = -EBADMSG;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (memcmp(digest, sinfo->msgdigest, sinfo->msgdigest_len) != 0) {
|
||||
if (memcmp(sig->digest, sinfo->msgdigest,
|
||||
sinfo->msgdigest_len) != 0) {
|
||||
pr_debug("Sig %u: Message digest doesn't match\n",
|
||||
sinfo->index);
|
||||
ret = -EKEYREJECTED;
|
||||
@@ -97,7 +101,7 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
|
||||
* convert the attributes from a CONT.0 into a SET before we
|
||||
* hash it.
|
||||
*/
|
||||
memset(digest, 0, sinfo->sig.digest_size);
|
||||
memset(sig->digest, 0, sig->digest_size);
|
||||
|
||||
ret = crypto_shash_init(desc);
|
||||
if (ret < 0)
|
||||
@@ -107,17 +111,14 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
ret = crypto_shash_finup(desc, sinfo->authattrs,
|
||||
sinfo->authattrs_len, digest);
|
||||
sinfo->authattrs_len, sig->digest);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
pr_devel("AADigest = [%*ph]\n", 8, digest);
|
||||
pr_devel("AADigest = [%*ph]\n", 8, sig->digest);
|
||||
}
|
||||
|
||||
sinfo->sig.digest = digest;
|
||||
digest = NULL;
|
||||
|
||||
error:
|
||||
kfree(digest);
|
||||
kfree(desc);
|
||||
error_no_desc:
|
||||
crypto_free_shash(tfm);
|
||||
kleave(" = %d", ret);
|
||||
@@ -144,12 +145,12 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
|
||||
* PKCS#7 message - but I can't be 100% sure of that. It's
|
||||
* possible this will need element-by-element comparison.
|
||||
*/
|
||||
if (!asymmetric_key_id_same(x509->id, sinfo->signing_cert_id))
|
||||
if (!asymmetric_key_id_same(x509->id, sinfo->sig->auth_ids[0]))
|
||||
continue;
|
||||
pr_devel("Sig %u: Found cert serial match X.509[%u]\n",
|
||||
sinfo->index, certix);
|
||||
|
||||
if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) {
|
||||
if (x509->pub->pkey_algo != sinfo->sig->pkey_algo) {
|
||||
pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n",
|
||||
sinfo->index);
|
||||
continue;
|
||||
@@ -164,7 +165,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
|
||||
*/
|
||||
pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n",
|
||||
sinfo->index,
|
||||
sinfo->signing_cert_id->len, sinfo->signing_cert_id->data);
|
||||
sinfo->sig->auth_ids[0]->len, sinfo->sig->auth_ids[0]->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -174,6 +175,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
|
||||
static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
|
||||
struct pkcs7_signed_info *sinfo)
|
||||
{
|
||||
struct public_key_signature *sig;
|
||||
struct x509_certificate *x509 = sinfo->signer, *p;
|
||||
struct asymmetric_key_id *auth;
|
||||
int ret;
|
||||
@@ -188,34 +190,26 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
|
||||
x509->subject,
|
||||
x509->raw_serial_size, x509->raw_serial);
|
||||
x509->seen = true;
|
||||
ret = x509_get_sig_params(x509);
|
||||
if (ret < 0)
|
||||
goto maybe_missing_crypto_in_x509;
|
||||
if (x509->unsupported_key)
|
||||
goto unsupported_crypto_in_x509;
|
||||
|
||||
pr_debug("- issuer %s\n", x509->issuer);
|
||||
if (x509->akid_id)
|
||||
sig = x509->sig;
|
||||
if (sig->auth_ids[0])
|
||||
pr_debug("- authkeyid.id %*phN\n",
|
||||
x509->akid_id->len, x509->akid_id->data);
|
||||
if (x509->akid_skid)
|
||||
sig->auth_ids[0]->len, sig->auth_ids[0]->data);
|
||||
if (sig->auth_ids[1])
|
||||
pr_debug("- authkeyid.skid %*phN\n",
|
||||
x509->akid_skid->len, x509->akid_skid->data);
|
||||
sig->auth_ids[1]->len, sig->auth_ids[1]->data);
|
||||
|
||||
if ((!x509->akid_id && !x509->akid_skid) ||
|
||||
strcmp(x509->subject, x509->issuer) == 0) {
|
||||
if (x509->self_signed) {
|
||||
/* If there's no authority certificate specified, then
|
||||
* the certificate must be self-signed and is the root
|
||||
* of the chain. Likewise if the cert is its own
|
||||
* authority.
|
||||
*/
|
||||
pr_debug("- no auth?\n");
|
||||
if (x509->raw_subject_size != x509->raw_issuer_size ||
|
||||
memcmp(x509->raw_subject, x509->raw_issuer,
|
||||
x509->raw_issuer_size) != 0)
|
||||
return 0;
|
||||
|
||||
ret = x509_check_signature(x509->pub, x509);
|
||||
if (ret < 0)
|
||||
goto maybe_missing_crypto_in_x509;
|
||||
if (x509->unsupported_sig)
|
||||
goto unsupported_crypto_in_x509;
|
||||
x509->signer = x509;
|
||||
pr_debug("- self-signed\n");
|
||||
return 0;
|
||||
@@ -224,7 +218,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
|
||||
/* Look through the X.509 certificates in the PKCS#7 message's
|
||||
* list to see if the next one is there.
|
||||
*/
|
||||
auth = x509->akid_id;
|
||||
auth = sig->auth_ids[0];
|
||||
if (auth) {
|
||||
pr_debug("- want %*phN\n", auth->len, auth->data);
|
||||
for (p = pkcs7->certs; p; p = p->next) {
|
||||
@@ -234,7 +228,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
|
||||
goto found_issuer_check_skid;
|
||||
}
|
||||
} else {
|
||||
auth = x509->akid_skid;
|
||||
auth = sig->auth_ids[1];
|
||||
pr_debug("- want %*phN\n", auth->len, auth->data);
|
||||
for (p = pkcs7->certs; p; p = p->next) {
|
||||
if (!p->skid)
|
||||
@@ -254,8 +248,8 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
|
||||
/* We matched issuer + serialNumber, but if there's an
|
||||
* authKeyId.keyId, that must match the CA subjKeyId also.
|
||||
*/
|
||||
if (x509->akid_skid &&
|
||||
!asymmetric_key_id_same(p->skid, x509->akid_skid)) {
|
||||
if (sig->auth_ids[1] &&
|
||||
!asymmetric_key_id_same(p->skid, sig->auth_ids[1])) {
|
||||
pr_warn("Sig %u: X.509 chain contains auth-skid nonmatch (%u->%u)\n",
|
||||
sinfo->index, x509->index, p->index);
|
||||
return -EKEYREJECTED;
|
||||
@@ -267,7 +261,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
|
||||
sinfo->index);
|
||||
return 0;
|
||||
}
|
||||
ret = x509_check_signature(p->pub, x509);
|
||||
ret = public_key_verify_signature(p->pub, p->sig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
x509->signer = p;
|
||||
@@ -279,16 +273,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
|
||||
might_sleep();
|
||||
}
|
||||
|
||||
maybe_missing_crypto_in_x509:
|
||||
unsupported_crypto_in_x509:
|
||||
/* Just prune the certificate chain at this point if we lack some
|
||||
* crypto module to go further. Note, however, we don't want to set
|
||||
* sinfo->missing_crypto as the signed info block may still be
|
||||
* sinfo->unsupported_crypto as the signed info block may still be
|
||||
* validatable against an X.509 cert lower in the chain that we have a
|
||||
* trusted copy of.
|
||||
*/
|
||||
if (ret == -ENOPKG)
|
||||
return 0;
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -332,7 +324,7 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
|
||||
}
|
||||
|
||||
/* Verify the PKCS#7 binary against the key */
|
||||
ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig);
|
||||
ret = public_key_verify_signature(sinfo->signer->pub, sinfo->sig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -375,9 +367,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
|
||||
enum key_being_used_for usage)
|
||||
{
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
struct x509_certificate *x509;
|
||||
int enopkg = -ENOPKG;
|
||||
int ret, n;
|
||||
int ret;
|
||||
|
||||
kenter("");
|
||||
|
||||
@@ -419,12 +410,6 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (n = 0, x509 = pkcs7->certs; x509; x509 = x509->next, n++) {
|
||||
ret = x509_get_sig_params(x509);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
|
||||
ret = pkcs7_verify_one(pkcs7, sinfo);
|
||||
if (ret < 0) {
|
||||
|
||||
@@ -39,15 +39,23 @@ static void public_key_describe(const struct key *asymmetric_key,
|
||||
/*
|
||||
* Destroy a public key algorithm key.
|
||||
*/
|
||||
void public_key_destroy(void *payload)
|
||||
void public_key_free(struct public_key *key)
|
||||
{
|
||||
struct public_key *key = payload;
|
||||
|
||||
if (key)
|
||||
if (key) {
|
||||
kfree(key->key);
|
||||
kfree(key);
|
||||
kfree(key);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(public_key_free);
|
||||
|
||||
/*
|
||||
* Destroy a public key algorithm key.
|
||||
*/
|
||||
static void public_key_destroy(void *payload0, void *payload3)
|
||||
{
|
||||
public_key_free(payload0);
|
||||
public_key_signature_free(payload3);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(public_key_destroy);
|
||||
|
||||
struct public_key_completion {
|
||||
struct completion completion;
|
||||
|
||||
108
crypto/asymmetric_keys/restrict.c
Normal file
108
crypto/asymmetric_keys/restrict.c
Normal file
@@ -0,0 +1,108 @@
|
||||
/* Instantiate a public key crypto key from an X.509 Certificate
|
||||
*
|
||||
* Copyright (C) 2012, 2016 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "ASYM: "fmt
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <crypto/public_key.h>
|
||||
#include "asymmetric_keys.h"
|
||||
|
||||
static bool use_builtin_keys;
|
||||
static struct asymmetric_key_id *ca_keyid;
|
||||
|
||||
#ifndef MODULE
|
||||
static struct {
|
||||
struct asymmetric_key_id id;
|
||||
unsigned char data[10];
|
||||
} cakey;
|
||||
|
||||
static int __init ca_keys_setup(char *str)
|
||||
{
|
||||
if (!str) /* default system keyring */
|
||||
return 1;
|
||||
|
||||
if (strncmp(str, "id:", 3) == 0) {
|
||||
struct asymmetric_key_id *p = &cakey.id;
|
||||
size_t hexlen = (strlen(str) - 3) / 2;
|
||||
int ret;
|
||||
|
||||
if (hexlen == 0 || hexlen > sizeof(cakey.data)) {
|
||||
pr_err("Missing or invalid ca_keys id\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = __asymmetric_key_hex_to_key_id(str + 3, p, hexlen);
|
||||
if (ret < 0)
|
||||
pr_err("Unparsable ca_keys id hex string\n");
|
||||
else
|
||||
ca_keyid = p; /* owner key 'id:xxxxxx' */
|
||||
} else if (strcmp(str, "builtin") == 0) {
|
||||
use_builtin_keys = true;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
__setup("ca_keys=", ca_keys_setup);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* restrict_link_by_signature - Restrict additions to a ring of public keys
|
||||
* @trust_keyring: A ring of keys that can be used to vouch for the new cert.
|
||||
* @type: The type of key being added.
|
||||
* @payload: The payload of the new key.
|
||||
*
|
||||
* Check the new certificate against the ones in the trust keyring. If one of
|
||||
* those is the signing key and validates the new certificate, then mark the
|
||||
* new certificate as being trusted.
|
||||
*
|
||||
* Returns 0 if the new certificate was accepted, -ENOKEY if we couldn't find a
|
||||
* matching parent certificate in the trusted list, -EKEYREJECTED if the
|
||||
* signature check fails or the key is blacklisted and some other error if
|
||||
* there is a matching certificate but the signature check cannot be performed.
|
||||
*/
|
||||
int restrict_link_by_signature(struct key *trust_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload)
|
||||
{
|
||||
const struct public_key_signature *sig;
|
||||
struct key *key;
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
if (!trust_keyring)
|
||||
return -ENOKEY;
|
||||
|
||||
if (type != &key_type_asymmetric)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
sig = payload->data[asym_auth];
|
||||
if (!sig->auth_ids[0] && !sig->auth_ids[1])
|
||||
return 0;
|
||||
|
||||
if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid))
|
||||
return -EPERM;
|
||||
|
||||
/* See if we have a key that signed this one. */
|
||||
key = find_asymmetric_key(trust_keyring,
|
||||
sig->auth_ids[0], sig->auth_ids[1],
|
||||
false);
|
||||
if (IS_ERR(key))
|
||||
return -ENOKEY;
|
||||
|
||||
if (use_builtin_keys && !test_bit(KEY_FLAG_BUILTIN, &key->flags))
|
||||
ret = -ENOKEY;
|
||||
else
|
||||
ret = verify_signature(key, sig);
|
||||
key_put(key);
|
||||
return ret;
|
||||
}
|
||||
@@ -15,9 +15,27 @@
|
||||
#include <keys/asymmetric-subtype.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <crypto/public_key.h>
|
||||
#include "asymmetric_keys.h"
|
||||
|
||||
/*
|
||||
* Destroy a public key signature.
|
||||
*/
|
||||
void public_key_signature_free(struct public_key_signature *sig)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (sig) {
|
||||
for (i = 0; i < ARRAY_SIZE(sig->auth_ids); i++)
|
||||
kfree(sig->auth_ids[i]);
|
||||
kfree(sig->s);
|
||||
kfree(sig->digest);
|
||||
kfree(sig);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(public_key_signature_free);
|
||||
|
||||
/**
|
||||
* verify_signature - Initiate the use of an asymmetric key to verify a signature
|
||||
* @key: The asymmetric key to verify against
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/pe.h>
|
||||
#include <linux/asn1.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include <linux/verification.h>
|
||||
#include <crypto/hash.h>
|
||||
#include "verify_pefile.h"
|
||||
|
||||
@@ -392,9 +392,8 @@ error_no_desc:
|
||||
* verify_pefile_signature - Verify the signature on a PE binary image
|
||||
* @pebuf: Buffer containing the PE binary image
|
||||
* @pelen: Length of the binary image
|
||||
* @trust_keyring: Signing certificates to use as starting points
|
||||
* @trust_keys: Signing certificate(s) to use as starting points
|
||||
* @usage: The use to which the key is being put.
|
||||
* @_trusted: Set to true if trustworth, false otherwise
|
||||
*
|
||||
* Validate that the certificate chain inside the PKCS#7 message inside the PE
|
||||
* binary image intersects keys we already know and trust.
|
||||
@@ -418,14 +417,10 @@ error_no_desc:
|
||||
* May also return -ENOMEM.
|
||||
*/
|
||||
int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
||||
struct key *trusted_keyring,
|
||||
enum key_being_used_for usage,
|
||||
bool *_trusted)
|
||||
struct key *trusted_keys,
|
||||
enum key_being_used_for usage)
|
||||
{
|
||||
struct pkcs7_message *pkcs7;
|
||||
struct pefile_context ctx;
|
||||
const void *data;
|
||||
size_t datalen;
|
||||
int ret;
|
||||
|
||||
kenter("");
|
||||
@@ -439,19 +434,10 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pkcs7 = pkcs7_parse_message(pebuf + ctx.sig_offset, ctx.sig_len);
|
||||
if (IS_ERR(pkcs7))
|
||||
return PTR_ERR(pkcs7);
|
||||
ctx.pkcs7 = pkcs7;
|
||||
|
||||
ret = pkcs7_get_content_data(ctx.pkcs7, &data, &datalen, false);
|
||||
if (ret < 0 || datalen == 0) {
|
||||
pr_devel("PKCS#7 message does not contain data\n");
|
||||
ret = -EBADMSG;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = mscode_parse(&ctx);
|
||||
ret = verify_pkcs7_signature(NULL, 0,
|
||||
pebuf + ctx.sig_offset, ctx.sig_len,
|
||||
trusted_keys, usage,
|
||||
mscode_parse, &ctx);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
@@ -462,16 +448,8 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
||||
* contents.
|
||||
*/
|
||||
ret = pefile_digest_pe(pebuf, pelen, &ctx);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = pkcs7_verify(pkcs7, usage);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = pkcs7_validate_trust(pkcs7, trusted_keyring, _trusted);
|
||||
|
||||
error:
|
||||
pkcs7_free_message(ctx.pkcs7);
|
||||
kfree(ctx.digest);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user