forked from Minki/linux
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:
commit
f4f27d0028
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;
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/verify_pefile.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include <crypto/hash_info.h>
|
||||
|
||||
@ -23,7 +22,6 @@ struct pefile_context {
|
||||
unsigned sig_offset;
|
||||
unsigned sig_len;
|
||||
const struct section_header *secs;
|
||||
struct pkcs7_message *pkcs7;
|
||||
|
||||
/* PKCS#7 MS Individual Code Signing content */
|
||||
const void *digest; /* Digest */
|
||||
@ -39,4 +37,5 @@ struct pefile_context {
|
||||
/*
|
||||
* mscode_parser.c
|
||||
*/
|
||||
extern int mscode_parse(struct pefile_context *ctx);
|
||||
extern int mscode_parse(void *_ctx, const void *content_data, size_t data_len,
|
||||
size_t asn1hdrlen);
|
||||
|
@ -47,15 +47,12 @@ struct x509_parse_context {
|
||||
void x509_free_certificate(struct x509_certificate *cert)
|
||||
{
|
||||
if (cert) {
|
||||
public_key_destroy(cert->pub);
|
||||
public_key_free(cert->pub);
|
||||
public_key_signature_free(cert->sig);
|
||||
kfree(cert->issuer);
|
||||
kfree(cert->subject);
|
||||
kfree(cert->id);
|
||||
kfree(cert->skid);
|
||||
kfree(cert->akid_id);
|
||||
kfree(cert->akid_skid);
|
||||
kfree(cert->sig.digest);
|
||||
kfree(cert->sig.s);
|
||||
kfree(cert);
|
||||
}
|
||||
}
|
||||
@ -78,6 +75,9 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
|
||||
cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
|
||||
if (!cert->pub)
|
||||
goto error_no_ctx;
|
||||
cert->sig = kzalloc(sizeof(struct public_key_signature), GFP_KERNEL);
|
||||
if (!cert->sig)
|
||||
goto error_no_ctx;
|
||||
ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
goto error_no_ctx;
|
||||
@ -108,6 +108,11 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
|
||||
|
||||
cert->pub->keylen = ctx->key_size;
|
||||
|
||||
/* Grab the signature bits */
|
||||
ret = x509_get_sig_params(cert);
|
||||
if (ret < 0)
|
||||
goto error_decode;
|
||||
|
||||
/* Generate cert issuer + serial number key ID */
|
||||
kid = asymmetric_key_generate_id(cert->raw_serial,
|
||||
cert->raw_serial_size,
|
||||
@ -119,6 +124,11 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
|
||||
}
|
||||
cert->id = kid;
|
||||
|
||||
/* Detect self-signed certificates */
|
||||
ret = x509_check_for_self_signed(cert);
|
||||
if (ret < 0)
|
||||
goto error_decode;
|
||||
|
||||
kfree(ctx);
|
||||
return cert;
|
||||
|
||||
@ -188,33 +198,33 @@ int x509_note_pkey_algo(void *context, size_t hdrlen,
|
||||
return -ENOPKG; /* Unsupported combination */
|
||||
|
||||
case OID_md4WithRSAEncryption:
|
||||
ctx->cert->sig.hash_algo = "md4";
|
||||
ctx->cert->sig.pkey_algo = "rsa";
|
||||
ctx->cert->sig->hash_algo = "md4";
|
||||
ctx->cert->sig->pkey_algo = "rsa";
|
||||
break;
|
||||
|
||||
case OID_sha1WithRSAEncryption:
|
||||
ctx->cert->sig.hash_algo = "sha1";
|
||||
ctx->cert->sig.pkey_algo = "rsa";
|
||||
ctx->cert->sig->hash_algo = "sha1";
|
||||
ctx->cert->sig->pkey_algo = "rsa";
|
||||
break;
|
||||
|
||||
case OID_sha256WithRSAEncryption:
|
||||
ctx->cert->sig.hash_algo = "sha256";
|
||||
ctx->cert->sig.pkey_algo = "rsa";
|
||||
ctx->cert->sig->hash_algo = "sha256";
|
||||
ctx->cert->sig->pkey_algo = "rsa";
|
||||
break;
|
||||
|
||||
case OID_sha384WithRSAEncryption:
|
||||
ctx->cert->sig.hash_algo = "sha384";
|
||||
ctx->cert->sig.pkey_algo = "rsa";
|
||||
ctx->cert->sig->hash_algo = "sha384";
|
||||
ctx->cert->sig->pkey_algo = "rsa";
|
||||
break;
|
||||
|
||||
case OID_sha512WithRSAEncryption:
|
||||
ctx->cert->sig.hash_algo = "sha512";
|
||||
ctx->cert->sig.pkey_algo = "rsa";
|
||||
ctx->cert->sig->hash_algo = "sha512";
|
||||
ctx->cert->sig->pkey_algo = "rsa";
|
||||
break;
|
||||
|
||||
case OID_sha224WithRSAEncryption:
|
||||
ctx->cert->sig.hash_algo = "sha224";
|
||||
ctx->cert->sig.pkey_algo = "rsa";
|
||||
ctx->cert->sig->hash_algo = "sha224";
|
||||
ctx->cert->sig->pkey_algo = "rsa";
|
||||
break;
|
||||
}
|
||||
|
||||
@ -572,14 +582,14 @@ int x509_akid_note_kid(void *context, size_t hdrlen,
|
||||
|
||||
pr_debug("AKID: keyid: %*phN\n", (int)vlen, value);
|
||||
|
||||
if (ctx->cert->akid_skid)
|
||||
if (ctx->cert->sig->auth_ids[1])
|
||||
return 0;
|
||||
|
||||
kid = asymmetric_key_generate_id(value, vlen, "", 0);
|
||||
if (IS_ERR(kid))
|
||||
return PTR_ERR(kid);
|
||||
pr_debug("authkeyid %*phN\n", kid->len, kid->data);
|
||||
ctx->cert->akid_skid = kid;
|
||||
ctx->cert->sig->auth_ids[1] = kid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -611,7 +621,7 @@ int x509_akid_note_serial(void *context, size_t hdrlen,
|
||||
|
||||
pr_debug("AKID: serial: %*phN\n", (int)vlen, value);
|
||||
|
||||
if (!ctx->akid_raw_issuer || ctx->cert->akid_id)
|
||||
if (!ctx->akid_raw_issuer || ctx->cert->sig->auth_ids[0])
|
||||
return 0;
|
||||
|
||||
kid = asymmetric_key_generate_id(value,
|
||||
@ -622,6 +632,6 @@ int x509_akid_note_serial(void *context, size_t hdrlen,
|
||||
return PTR_ERR(kid);
|
||||
|
||||
pr_debug("authkeyid %*phN\n", kid->len, kid->data);
|
||||
ctx->cert->akid_id = kid;
|
||||
ctx->cert->sig->auth_ids[0] = kid;
|
||||
return 0;
|
||||
}
|
||||
|
@ -17,13 +17,11 @@ struct x509_certificate {
|
||||
struct x509_certificate *next;
|
||||
struct x509_certificate *signer; /* Certificate that signed this one */
|
||||
struct public_key *pub; /* Public key details */
|
||||
struct public_key_signature sig; /* Signature parameters */
|
||||
struct public_key_signature *sig; /* Signature parameters */
|
||||
char *issuer; /* Name of certificate issuer */
|
||||
char *subject; /* Name of certificate subject */
|
||||
struct asymmetric_key_id *id; /* Issuer + Serial number */
|
||||
struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */
|
||||
struct asymmetric_key_id *akid_id; /* CA AuthKeyId matching ->id (optional) */
|
||||
struct asymmetric_key_id *akid_skid; /* CA AuthKeyId matching ->skid (optional) */
|
||||
time64_t valid_from;
|
||||
time64_t valid_to;
|
||||
const void *tbs; /* Signed data */
|
||||
@ -41,8 +39,9 @@ struct x509_certificate {
|
||||
unsigned index;
|
||||
bool seen; /* Infinite recursion prevention */
|
||||
bool verified;
|
||||
bool trusted;
|
||||
bool unsupported_crypto; /* T if can't be verified due to missing crypto */
|
||||
bool self_signed; /* T if self-signed (check unsupported_sig too) */
|
||||
bool unsupported_key; /* T if key uses unsupported crypto */
|
||||
bool unsupported_sig; /* T if signature uses unsupported crypto */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -58,5 +57,4 @@ extern int x509_decode_time(time64_t *_t, size_t hdrlen,
|
||||
* x509_public_key.c
|
||||
*/
|
||||
extern int x509_get_sig_params(struct x509_certificate *cert);
|
||||
extern int x509_check_signature(const struct public_key *pub,
|
||||
struct x509_certificate *cert);
|
||||
extern int x509_check_for_self_signed(struct x509_certificate *cert);
|
||||
|
@ -20,256 +20,133 @@
|
||||
#include "asymmetric_keys.h"
|
||||
#include "x509_parser.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
|
||||
|
||||
/**
|
||||
* x509_request_asymmetric_key - Request a key by X.509 certificate params.
|
||||
* @keyring: The keys to search.
|
||||
* @id: The issuer & serialNumber to look for or NULL.
|
||||
* @skid: The subjectKeyIdentifier 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 issuer + serialNumber and the fallback identifier is the
|
||||
* subjectKeyIdentifier. If both are given, the lookup is by the former, but
|
||||
* the latter must also match.
|
||||
*/
|
||||
struct key *x509_request_asymmetric_key(struct key *keyring,
|
||||
const struct asymmetric_key_id *id,
|
||||
const struct asymmetric_key_id *skid,
|
||||
bool partial)
|
||||
{
|
||||
struct key *key;
|
||||
key_ref_t ref;
|
||||
const char *lookup;
|
||||
char *req, *p;
|
||||
int len;
|
||||
|
||||
if (id) {
|
||||
lookup = id->data;
|
||||
len = id->len;
|
||||
} else {
|
||||
lookup = skid->data;
|
||||
len = skid->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 && skid) {
|
||||
const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
|
||||
if (!kids->id[1]) {
|
||||
pr_debug("issuer+serial match, but expected SKID missing\n");
|
||||
goto reject;
|
||||
}
|
||||
if (!asymmetric_key_id_same(skid, kids->id[1])) {
|
||||
pr_debug("issuer+serial match, but SKID 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(x509_request_asymmetric_key);
|
||||
|
||||
/*
|
||||
* Set up the signature parameters in an X.509 certificate. This involves
|
||||
* digesting the signed data and extracting the signature.
|
||||
*/
|
||||
int x509_get_sig_params(struct x509_certificate *cert)
|
||||
{
|
||||
struct public_key_signature *sig = cert->sig;
|
||||
struct crypto_shash *tfm;
|
||||
struct shash_desc *desc;
|
||||
size_t digest_size, desc_size;
|
||||
void *digest;
|
||||
size_t desc_size;
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
if (cert->unsupported_crypto)
|
||||
return -ENOPKG;
|
||||
if (cert->sig.s)
|
||||
return 0;
|
||||
if (!cert->pub->pkey_algo)
|
||||
cert->unsupported_key = true;
|
||||
|
||||
cert->sig.s = kmemdup(cert->raw_sig, cert->raw_sig_size,
|
||||
GFP_KERNEL);
|
||||
if (!cert->sig.s)
|
||||
if (!sig->pkey_algo)
|
||||
cert->unsupported_sig = true;
|
||||
|
||||
/* We check the hash if we can - even if we can't then verify it */
|
||||
if (!sig->hash_algo) {
|
||||
cert->unsupported_sig = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sig->s = kmemdup(cert->raw_sig, cert->raw_sig_size, GFP_KERNEL);
|
||||
if (!sig->s)
|
||||
return -ENOMEM;
|
||||
|
||||
cert->sig.s_size = cert->raw_sig_size;
|
||||
sig->s_size = cert->raw_sig_size;
|
||||
|
||||
/* Allocate the hashing algorithm we're going to need and find out how
|
||||
* big the hash operational data will be.
|
||||
*/
|
||||
tfm = crypto_alloc_shash(cert->sig.hash_algo, 0, 0);
|
||||
tfm = crypto_alloc_shash(sig->hash_algo, 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
if (PTR_ERR(tfm) == -ENOENT) {
|
||||
cert->unsupported_crypto = true;
|
||||
return -ENOPKG;
|
||||
cert->unsupported_sig = true;
|
||||
return 0;
|
||||
}
|
||||
return PTR_ERR(tfm);
|
||||
}
|
||||
|
||||
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
|
||||
digest_size = crypto_shash_digestsize(tfm);
|
||||
sig->digest_size = crypto_shash_digestsize(tfm);
|
||||
|
||||
/* We allocate the hash operational data storage on the end of the
|
||||
* digest storage space.
|
||||
*/
|
||||
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;
|
||||
|
||||
cert->sig.digest = digest;
|
||||
cert->sig.digest_size = digest_size;
|
||||
desc = kzalloc(desc_size, GFP_KERNEL);
|
||||
if (!desc)
|
||||
goto error;
|
||||
|
||||
desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc));
|
||||
desc->tfm = tfm;
|
||||
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
ret = crypto_shash_init(desc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
goto error_2;
|
||||
might_sleep();
|
||||
ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, digest);
|
||||
ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest);
|
||||
|
||||
error_2:
|
||||
kfree(desc);
|
||||
error:
|
||||
crypto_free_shash(tfm);
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(x509_get_sig_params);
|
||||
|
||||
/*
|
||||
* Check the signature on a certificate using the provided public key
|
||||
* Check for self-signedness in an X.509 cert and if found, check the signature
|
||||
* immediately if we can.
|
||||
*/
|
||||
int x509_check_signature(const struct public_key *pub,
|
||||
struct x509_certificate *cert)
|
||||
int x509_check_for_self_signed(struct x509_certificate *cert)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
ret = x509_get_sig_params(cert);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (cert->raw_subject_size != cert->raw_issuer_size ||
|
||||
memcmp(cert->raw_subject, cert->raw_issuer,
|
||||
cert->raw_issuer_size) != 0)
|
||||
goto not_self_signed;
|
||||
|
||||
ret = public_key_verify_signature(pub, &cert->sig);
|
||||
if (ret == -ENOPKG)
|
||||
cert->unsupported_crypto = true;
|
||||
pr_debug("Cert Verification: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(x509_check_signature);
|
||||
if (cert->sig->auth_ids[0] || cert->sig->auth_ids[1]) {
|
||||
/* If the AKID is present it may have one or two parts. If
|
||||
* both are supplied, both must match.
|
||||
*/
|
||||
bool a = asymmetric_key_id_same(cert->skid, cert->sig->auth_ids[1]);
|
||||
bool b = asymmetric_key_id_same(cert->id, cert->sig->auth_ids[0]);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Return 0 if the new certificate was successfully validated, 1 if we couldn't
|
||||
* find a matching parent certificate in the trusted list and an error if there
|
||||
* is a matching certificate but the signature check fails.
|
||||
*/
|
||||
static int x509_validate_trust(struct x509_certificate *cert,
|
||||
struct key *trust_keyring)
|
||||
{
|
||||
struct key *key;
|
||||
int ret = 1;
|
||||
if (!a && !b)
|
||||
goto not_self_signed;
|
||||
|
||||
if (!trust_keyring)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (ca_keyid && !asymmetric_key_id_partial(cert->akid_skid, ca_keyid))
|
||||
return -EPERM;
|
||||
|
||||
key = x509_request_asymmetric_key(trust_keyring,
|
||||
cert->akid_id, cert->akid_skid,
|
||||
false);
|
||||
if (!IS_ERR(key)) {
|
||||
if (!use_builtin_keys
|
||||
|| test_bit(KEY_FLAG_BUILTIN, &key->flags))
|
||||
ret = x509_check_signature(key->payload.data[asym_crypto],
|
||||
cert);
|
||||
key_put(key);
|
||||
ret = -EKEYREJECTED;
|
||||
if (((a && !b) || (b && !a)) &&
|
||||
cert->sig->auth_ids[0] && cert->sig->auth_ids[1])
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = -EKEYREJECTED;
|
||||
if (cert->pub->pkey_algo != cert->sig->pkey_algo)
|
||||
goto out;
|
||||
|
||||
ret = public_key_verify_signature(cert->pub, cert->sig);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOPKG) {
|
||||
cert->unsupported_sig = true;
|
||||
ret = 0;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
pr_devel("Cert Self-signature verified");
|
||||
cert->self_signed = true;
|
||||
|
||||
out:
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
|
||||
not_self_signed:
|
||||
pr_devel("<==%s() = 0 [not]\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -291,34 +168,22 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
|
||||
pr_devel("Cert Issuer: %s\n", cert->issuer);
|
||||
pr_devel("Cert Subject: %s\n", cert->subject);
|
||||
|
||||
if (!cert->pub->pkey_algo ||
|
||||
!cert->sig.pkey_algo ||
|
||||
!cert->sig.hash_algo) {
|
||||
if (cert->unsupported_key) {
|
||||
ret = -ENOPKG;
|
||||
goto error_free_cert;
|
||||
}
|
||||
|
||||
pr_devel("Cert Key Algo: %s\n", cert->pub->pkey_algo);
|
||||
pr_devel("Cert Valid period: %lld-%lld\n", cert->valid_from, cert->valid_to);
|
||||
pr_devel("Cert Signature: %s + %s\n",
|
||||
cert->sig.pkey_algo,
|
||||
cert->sig.hash_algo);
|
||||
|
||||
cert->pub->id_type = "X509";
|
||||
|
||||
/* Check the signature on the key if it appears to be self-signed */
|
||||
if ((!cert->akid_skid && !cert->akid_id) ||
|
||||
asymmetric_key_id_same(cert->skid, cert->akid_skid) ||
|
||||
asymmetric_key_id_same(cert->id, cert->akid_id)) {
|
||||
ret = x509_check_signature(cert->pub, cert); /* self-signed */
|
||||
if (ret < 0)
|
||||
goto error_free_cert;
|
||||
} else if (!prep->trusted) {
|
||||
ret = x509_validate_trust(cert, get_system_trusted_keyring());
|
||||
if (ret)
|
||||
ret = x509_validate_trust(cert, get_ima_mok_keyring());
|
||||
if (!ret)
|
||||
prep->trusted = 1;
|
||||
if (cert->unsupported_sig) {
|
||||
public_key_signature_free(cert->sig);
|
||||
cert->sig = NULL;
|
||||
} else {
|
||||
pr_devel("Cert Signature: %s + %s\n",
|
||||
cert->sig->pkey_algo, cert->sig->hash_algo);
|
||||
}
|
||||
|
||||
/* Propose a description */
|
||||
@ -353,6 +218,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
|
||||
prep->payload.data[asym_subtype] = &public_key_subtype;
|
||||
prep->payload.data[asym_key_ids] = kids;
|
||||
prep->payload.data[asym_crypto] = cert->pub;
|
||||
prep->payload.data[asym_auth] = cert->sig;
|
||||
prep->description = desc;
|
||||
prep->quotalen = 100;
|
||||
|
||||
@ -360,6 +226,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
|
||||
cert->pub = NULL;
|
||||
cert->id = NULL;
|
||||
cert->skid = NULL;
|
||||
cert->sig = NULL;
|
||||
desc = NULL;
|
||||
ret = 0;
|
||||
|
||||
|
@ -360,7 +360,7 @@ init_cifs_idmap(void)
|
||||
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
|
||||
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ,
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
|
||||
if (IS_ERR(keyring)) {
|
||||
ret = PTR_ERR(keyring);
|
||||
goto failed_put_cred;
|
||||
|
29
fs/exec.c
29
fs/exec.c
@ -850,15 +850,25 @@ int kernel_read_file(struct file *file, void **buf, loff_t *size,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = deny_write_access(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i_size = i_size_read(file_inode(file));
|
||||
if (max_size > 0 && i_size > max_size)
|
||||
return -EFBIG;
|
||||
if (i_size <= 0)
|
||||
return -EINVAL;
|
||||
if (max_size > 0 && i_size > max_size) {
|
||||
ret = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
if (i_size <= 0) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*buf = vmalloc(i_size);
|
||||
if (!*buf)
|
||||
return -ENOMEM;
|
||||
if (!*buf) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
while (pos < i_size) {
|
||||
@ -876,18 +886,21 @@ int kernel_read_file(struct file *file, void **buf, loff_t *size,
|
||||
|
||||
if (pos != i_size) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
ret = security_kernel_post_read_file(file, *buf, i_size, id);
|
||||
if (!ret)
|
||||
*size = pos;
|
||||
|
||||
out:
|
||||
out_free:
|
||||
if (ret < 0) {
|
||||
vfree(*buf);
|
||||
*buf = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
allow_write_access(file);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kernel_read_file);
|
||||
|
@ -3627,6 +3627,8 @@ retry:
|
||||
switch (mode & S_IFMT) {
|
||||
case 0: case S_IFREG:
|
||||
error = vfs_create(path.dentry->d_inode,dentry,mode,true);
|
||||
if (!error)
|
||||
ima_post_path_mknod(dentry);
|
||||
break;
|
||||
case S_IFCHR: case S_IFBLK:
|
||||
error = vfs_mknod(path.dentry->d_inode,dentry,mode,
|
||||
|
@ -201,7 +201,7 @@ int nfs_idmap_init(void)
|
||||
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
|
||||
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ,
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
|
||||
if (IS_ERR(keyring)) {
|
||||
ret = PTR_ERR(keyring);
|
||||
goto failed_put_cred;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#ifndef _CRYPTO_PKCS7_H
|
||||
#define _CRYPTO_PKCS7_H
|
||||
|
||||
#include <linux/verification.h>
|
||||
#include <crypto/public_key.h>
|
||||
|
||||
struct key;
|
||||
@ -26,14 +27,13 @@ extern void pkcs7_free_message(struct pkcs7_message *pkcs7);
|
||||
|
||||
extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
|
||||
const void **_data, size_t *_datalen,
|
||||
bool want_wrapper);
|
||||
size_t *_headerlen);
|
||||
|
||||
/*
|
||||
* pkcs7_trust.c
|
||||
*/
|
||||
extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
|
||||
struct key *trust_keyring,
|
||||
bool *_trusted);
|
||||
struct key *trust_keyring);
|
||||
|
||||
/*
|
||||
* pkcs7_verify.c
|
||||
|
@ -14,20 +14,6 @@
|
||||
#ifndef _LINUX_PUBLIC_KEY_H
|
||||
#define _LINUX_PUBLIC_KEY_H
|
||||
|
||||
/*
|
||||
* The use to which an asymmetric key is being put.
|
||||
*/
|
||||
enum key_being_used_for {
|
||||
VERIFYING_MODULE_SIGNATURE,
|
||||
VERIFYING_FIRMWARE_SIGNATURE,
|
||||
VERIFYING_KEXEC_PE_SIGNATURE,
|
||||
VERIFYING_KEY_SIGNATURE,
|
||||
VERIFYING_KEY_SELF_SIGNATURE,
|
||||
VERIFYING_UNSPECIFIED_SIGNATURE,
|
||||
NR__KEY_BEING_USED_FOR
|
||||
};
|
||||
extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR];
|
||||
|
||||
/*
|
||||
* Cryptographic data for the public-key subtype of the asymmetric key type.
|
||||
*
|
||||
@ -41,12 +27,13 @@ struct public_key {
|
||||
const char *pkey_algo;
|
||||
};
|
||||
|
||||
extern void public_key_destroy(void *payload);
|
||||
extern void public_key_free(struct public_key *key);
|
||||
|
||||
/*
|
||||
* Public key cryptography signature data
|
||||
*/
|
||||
struct public_key_signature {
|
||||
struct asymmetric_key_id *auth_ids[2];
|
||||
u8 *s; /* Signature */
|
||||
u32 s_size; /* Number of bytes in signature */
|
||||
u8 *digest;
|
||||
@ -55,17 +42,21 @@ struct public_key_signature {
|
||||
const char *hash_algo;
|
||||
};
|
||||
|
||||
extern void public_key_signature_free(struct public_key_signature *sig);
|
||||
|
||||
extern struct asymmetric_key_subtype public_key_subtype;
|
||||
|
||||
struct key;
|
||||
struct key_type;
|
||||
union key_payload;
|
||||
|
||||
extern int restrict_link_by_signature(struct key *trust_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload);
|
||||
|
||||
extern int verify_signature(const struct key *key,
|
||||
const struct public_key_signature *sig);
|
||||
|
||||
struct asymmetric_key_id;
|
||||
extern struct key *x509_request_asymmetric_key(struct key *keyring,
|
||||
const struct asymmetric_key_id *id,
|
||||
const struct asymmetric_key_id *skid,
|
||||
bool partial);
|
||||
|
||||
int public_key_verify_signature(const struct public_key *pkey,
|
||||
const struct public_key_signature *sig);
|
||||
|
||||
|
@ -32,7 +32,7 @@ struct asymmetric_key_subtype {
|
||||
void (*describe)(const struct key *key, struct seq_file *m);
|
||||
|
||||
/* Destroy a key of this subtype */
|
||||
void (*destroy)(void *payload);
|
||||
void (*destroy)(void *payload_crypto, void *payload_auth);
|
||||
|
||||
/* Verify the signature on a key of this subtype (optional) */
|
||||
int (*verify_signature)(const struct key *key,
|
||||
|
@ -15,6 +15,7 @@
|
||||
#define _KEYS_ASYMMETRIC_TYPE_H
|
||||
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/verification.h>
|
||||
|
||||
extern struct key_type key_type_asymmetric;
|
||||
|
||||
@ -23,9 +24,10 @@ extern struct key_type key_type_asymmetric;
|
||||
* follows:
|
||||
*/
|
||||
enum asymmetric_payload_bits {
|
||||
asym_crypto,
|
||||
asym_subtype,
|
||||
asym_key_ids,
|
||||
asym_crypto, /* The data representing the key */
|
||||
asym_subtype, /* Pointer to an asymmetric_key_subtype struct */
|
||||
asym_key_ids, /* Pointer to an asymmetric_key_ids struct */
|
||||
asym_auth /* The key's authorisation (signature, parent key ID) */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -74,6 +76,11 @@ const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key)
|
||||
return key->payload.data[asym_key_ids];
|
||||
}
|
||||
|
||||
extern struct key *find_asymmetric_key(struct key *keyring,
|
||||
const struct asymmetric_key_id *id_0,
|
||||
const struct asymmetric_key_id *id_1,
|
||||
bool partial);
|
||||
|
||||
/*
|
||||
* The payload is at the discretion of the subtype.
|
||||
*/
|
||||
|
@ -12,51 +12,40 @@
|
||||
#ifndef _KEYS_SYSTEM_KEYRING_H
|
||||
#define _KEYS_SYSTEM_KEYRING_H
|
||||
|
||||
#include <linux/key.h>
|
||||
|
||||
#ifdef CONFIG_SYSTEM_TRUSTED_KEYRING
|
||||
|
||||
#include <linux/key.h>
|
||||
#include <crypto/public_key.h>
|
||||
extern int restrict_link_by_builtin_trusted(struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload);
|
||||
|
||||
extern struct key *system_trusted_keyring;
|
||||
static inline struct key *get_system_trusted_keyring(void)
|
||||
{
|
||||
return system_trusted_keyring;
|
||||
}
|
||||
#else
|
||||
static inline struct key *get_system_trusted_keyring(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#define restrict_link_by_builtin_trusted restrict_link_reject
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
|
||||
extern int system_verify_data(const void *data, unsigned long len,
|
||||
const void *raw_pkcs7, size_t pkcs7_len,
|
||||
enum key_being_used_for usage);
|
||||
#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
|
||||
extern int restrict_link_by_builtin_and_secondary_trusted(
|
||||
struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload);
|
||||
#else
|
||||
#define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IMA_MOK_KEYRING
|
||||
extern struct key *ima_mok_keyring;
|
||||
#ifdef CONFIG_IMA_BLACKLIST_KEYRING
|
||||
extern struct key *ima_blacklist_keyring;
|
||||
|
||||
static inline struct key *get_ima_mok_keyring(void)
|
||||
{
|
||||
return ima_mok_keyring;
|
||||
}
|
||||
static inline struct key *get_ima_blacklist_keyring(void)
|
||||
{
|
||||
return ima_blacklist_keyring;
|
||||
}
|
||||
#else
|
||||
static inline struct key *get_ima_mok_keyring(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline struct key *get_ima_blacklist_keyring(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_IMA_MOK_KEYRING */
|
||||
#endif /* CONFIG_IMA_BLACKLIST_KEYRING */
|
||||
|
||||
|
||||
#endif /* _KEYS_SYSTEM_KEYRING_H */
|
||||
|
@ -2634,15 +2634,34 @@ static inline void i_readcount_inc(struct inode *inode)
|
||||
#endif
|
||||
extern int do_pipe_flags(int *, int);
|
||||
|
||||
#define __kernel_read_file_id(id) \
|
||||
id(UNKNOWN, unknown) \
|
||||
id(FIRMWARE, firmware) \
|
||||
id(MODULE, kernel-module) \
|
||||
id(KEXEC_IMAGE, kexec-image) \
|
||||
id(KEXEC_INITRAMFS, kexec-initramfs) \
|
||||
id(POLICY, security-policy) \
|
||||
id(MAX_ID, )
|
||||
|
||||
#define __fid_enumify(ENUM, dummy) READING_ ## ENUM,
|
||||
#define __fid_stringify(dummy, str) #str,
|
||||
|
||||
enum kernel_read_file_id {
|
||||
READING_FIRMWARE = 1,
|
||||
READING_MODULE,
|
||||
READING_KEXEC_IMAGE,
|
||||
READING_KEXEC_INITRAMFS,
|
||||
READING_POLICY,
|
||||
READING_MAX_ID
|
||||
__kernel_read_file_id(__fid_enumify)
|
||||
};
|
||||
|
||||
static const char * const kernel_read_file_str[] = {
|
||||
__kernel_read_file_id(__fid_stringify)
|
||||
};
|
||||
|
||||
static inline const char *kernel_read_file_id_str(enum kernel_read_file_id id)
|
||||
{
|
||||
if (id < 0 || id >= READING_MAX_ID)
|
||||
return kernel_read_file_str[READING_UNKNOWN];
|
||||
|
||||
return kernel_read_file_str[id];
|
||||
}
|
||||
|
||||
extern int kernel_read(struct file *, loff_t, char *, unsigned long);
|
||||
extern int kernel_read_file(struct file *, void **, loff_t *, loff_t,
|
||||
enum kernel_read_file_id);
|
||||
|
@ -21,6 +21,7 @@ extern int ima_file_mmap(struct file *file, unsigned long prot);
|
||||
extern int ima_read_file(struct file *file, enum kernel_read_file_id id);
|
||||
extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
|
||||
enum kernel_read_file_id id);
|
||||
extern void ima_post_path_mknod(struct dentry *dentry);
|
||||
|
||||
#else
|
||||
static inline int ima_bprm_check(struct linux_binprm *bprm)
|
||||
@ -54,6 +55,11 @@ static inline int ima_post_read_file(struct file *file, void *buf, loff_t size,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ima_post_path_mknod(struct dentry *dentry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IMA */
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
|
@ -45,7 +45,6 @@ struct key_preparsed_payload {
|
||||
size_t datalen; /* Raw datalen */
|
||||
size_t quotalen; /* Quota length for proposed payload */
|
||||
time_t expiry; /* Expiry time of key */
|
||||
bool trusted; /* True if key is trusted */
|
||||
};
|
||||
|
||||
typedef int (*request_key_actor_t)(struct key_construction *key,
|
||||
|
@ -173,11 +173,9 @@ struct key {
|
||||
#define KEY_FLAG_NEGATIVE 5 /* set if key is negative */
|
||||
#define KEY_FLAG_ROOT_CAN_CLEAR 6 /* set if key can be cleared by root without permission */
|
||||
#define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */
|
||||
#define KEY_FLAG_TRUSTED 8 /* set if key is trusted */
|
||||
#define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */
|
||||
#define KEY_FLAG_BUILTIN 10 /* set if key is builtin */
|
||||
#define KEY_FLAG_ROOT_CAN_INVAL 11 /* set if key can be invalidated by root without permission */
|
||||
#define KEY_FLAG_KEEP 12 /* set if key should not be removed */
|
||||
#define KEY_FLAG_BUILTIN 8 /* set if key is built in to the kernel */
|
||||
#define KEY_FLAG_ROOT_CAN_INVAL 9 /* set if key can be invalidated by root without permission */
|
||||
#define KEY_FLAG_KEEP 10 /* set if key should not be removed */
|
||||
|
||||
/* the key type and key description string
|
||||
* - the desc is used to match a key against search criteria
|
||||
@ -205,6 +203,20 @@ struct key {
|
||||
};
|
||||
int reject_error;
|
||||
};
|
||||
|
||||
/* This is set on a keyring to restrict the addition of a link to a key
|
||||
* to it. If this method isn't provided then it is assumed that the
|
||||
* keyring is open to any addition. It is ignored for non-keyring
|
||||
* keys.
|
||||
*
|
||||
* This is intended for use with rings of trusted keys whereby addition
|
||||
* to the keyring needs to be controlled. KEY_ALLOC_BYPASS_RESTRICTION
|
||||
* overrides this, allowing the kernel to add extra keys without
|
||||
* restriction.
|
||||
*/
|
||||
int (*restrict_link)(struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload);
|
||||
};
|
||||
|
||||
extern struct key *key_alloc(struct key_type *type,
|
||||
@ -212,14 +224,17 @@ extern struct key *key_alloc(struct key_type *type,
|
||||
kuid_t uid, kgid_t gid,
|
||||
const struct cred *cred,
|
||||
key_perm_t perm,
|
||||
unsigned long flags);
|
||||
unsigned long flags,
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
const union key_payload *));
|
||||
|
||||
|
||||
#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */
|
||||
#define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */
|
||||
#define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */
|
||||
#define KEY_ALLOC_TRUSTED 0x0004 /* Key should be flagged as trusted */
|
||||
#define KEY_ALLOC_BUILT_IN 0x0008 /* Key is built into kernel */
|
||||
#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */
|
||||
#define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */
|
||||
#define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */
|
||||
#define KEY_ALLOC_BUILT_IN 0x0004 /* Key is built into kernel */
|
||||
#define KEY_ALLOC_BYPASS_RESTRICTION 0x0008 /* Override the check on restricted keyrings */
|
||||
|
||||
extern void key_revoke(struct key *key);
|
||||
extern void key_invalidate(struct key *key);
|
||||
@ -288,8 +303,15 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid
|
||||
const struct cred *cred,
|
||||
key_perm_t perm,
|
||||
unsigned long flags,
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
const union key_payload *),
|
||||
struct key *dest);
|
||||
|
||||
extern int restrict_link_reject(struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload);
|
||||
|
||||
extern int keyring_clear(struct key *keyring);
|
||||
|
||||
extern key_ref_t keyring_search(key_ref_t keyring,
|
||||
|
@ -1805,7 +1805,6 @@ struct security_hook_heads {
|
||||
struct list_head tun_dev_attach_queue;
|
||||
struct list_head tun_dev_attach;
|
||||
struct list_head tun_dev_open;
|
||||
struct list_head skb_owned_by;
|
||||
#endif /* CONFIG_SECURITY_NETWORK */
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
struct list_head xfrm_policy_alloc_security;
|
||||
@ -1894,5 +1893,10 @@ extern void __init yama_add_hooks(void);
|
||||
#else
|
||||
static inline void __init yama_add_hooks(void) { }
|
||||
#endif
|
||||
#ifdef CONFIG_SECURITY_LOADPIN
|
||||
void __init loadpin_add_hooks(void);
|
||||
#else
|
||||
static inline void loadpin_add_hooks(void) { };
|
||||
#endif
|
||||
|
||||
#endif /* ! __LINUX_LSM_HOOKS_H */
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct file;
|
||||
|
||||
/* Descriptions of the types of units to
|
||||
* print in */
|
||||
enum string_size_units {
|
||||
@ -68,4 +70,8 @@ static inline int string_escape_str_any_np(const char *src, char *dst,
|
||||
return string_escape_str(src, dst, sz, ESCAPE_ANY_NP, only);
|
||||
}
|
||||
|
||||
char *kstrdup_quotable(const char *src, gfp_t gfp);
|
||||
char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp);
|
||||
char *kstrdup_quotable_file(struct file *file, gfp_t gfp);
|
||||
|
||||
#endif
|
||||
|
49
include/linux/verification.h
Normal file
49
include/linux/verification.h
Normal file
@ -0,0 +1,49 @@
|
||||
/* Signature verification
|
||||
*
|
||||
* Copyright (C) 2014 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_VERIFICATION_H
|
||||
#define _LINUX_VERIFICATION_H
|
||||
|
||||
/*
|
||||
* The use to which an asymmetric key is being put.
|
||||
*/
|
||||
enum key_being_used_for {
|
||||
VERIFYING_MODULE_SIGNATURE,
|
||||
VERIFYING_FIRMWARE_SIGNATURE,
|
||||
VERIFYING_KEXEC_PE_SIGNATURE,
|
||||
VERIFYING_KEY_SIGNATURE,
|
||||
VERIFYING_KEY_SELF_SIGNATURE,
|
||||
VERIFYING_UNSPECIFIED_SIGNATURE,
|
||||
NR__KEY_BEING_USED_FOR
|
||||
};
|
||||
extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR];
|
||||
|
||||
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
|
||||
|
||||
struct key;
|
||||
|
||||
extern 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);
|
||||
|
||||
#ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION
|
||||
extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
||||
struct key *trusted_keys,
|
||||
enum key_being_used_for usage);
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
|
||||
#endif /* _LINUX_VERIFY_PEFILE_H */
|
@ -1,22 +0,0 @@
|
||||
/* Signed PE file verification
|
||||
*
|
||||
* Copyright (C) 2014 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_VERIFY_PEFILE_H
|
||||
#define _LINUX_VERIFY_PEFILE_H
|
||||
|
||||
#include <crypto/public_key.h>
|
||||
|
||||
extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
||||
struct key *trusted_keyring,
|
||||
enum key_being_used_for usage,
|
||||
bool *_trusted);
|
||||
|
||||
#endif /* _LINUX_VERIFY_PEFILE_H */
|
@ -12,6 +12,8 @@
|
||||
#ifndef _LINUX_KEYCTL_H
|
||||
#define _LINUX_KEYCTL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* special process keyring shortcut IDs */
|
||||
#define KEY_SPEC_THREAD_KEYRING -1 /* - key ID for thread-specific keyring */
|
||||
#define KEY_SPEC_PROCESS_KEYRING -2 /* - key ID for process-specific keyring */
|
||||
@ -57,5 +59,13 @@
|
||||
#define KEYCTL_INSTANTIATE_IOV 20 /* instantiate a partially constructed key */
|
||||
#define KEYCTL_INVALIDATE 21 /* invalidate a key */
|
||||
#define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */
|
||||
#define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */
|
||||
|
||||
/* keyctl structures */
|
||||
struct keyctl_dh_params {
|
||||
__s32 private;
|
||||
__s32 prime;
|
||||
__s32 base;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_KEYCTL_H */
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include <linux/verification.h>
|
||||
#include <crypto/public_key.h>
|
||||
#include "module-internal.h"
|
||||
|
||||
@ -80,6 +80,7 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
return system_verify_data(mod, modlen, mod + modlen, sig_len,
|
||||
VERIFYING_MODULE_SIGNATURE);
|
||||
return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
|
||||
NULL, VERIFYING_MODULE_SIGNATURE,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
@ -915,7 +915,7 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
|
||||
|
||||
fprog = filter->prog->orig_prog;
|
||||
if (!fprog) {
|
||||
/* This must be a new non-cBPF filter, since we save every
|
||||
/* This must be a new non-cBPF filter, since we save
|
||||
* every cBPF filter's orig_prog above when
|
||||
* CONFIG_CHECKPOINT_RESTORE is enabled.
|
||||
*/
|
||||
|
@ -10,6 +10,10 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/string_helpers.h>
|
||||
|
||||
@ -534,3 +538,91 @@ int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
|
||||
return p - dst;
|
||||
}
|
||||
EXPORT_SYMBOL(string_escape_mem);
|
||||
|
||||
/*
|
||||
* Return an allocated string that has been escaped of special characters
|
||||
* and double quotes, making it safe to log in quotes.
|
||||
*/
|
||||
char *kstrdup_quotable(const char *src, gfp_t gfp)
|
||||
{
|
||||
size_t slen, dlen;
|
||||
char *dst;
|
||||
const int flags = ESCAPE_HEX;
|
||||
const char esc[] = "\f\n\r\t\v\a\e\\\"";
|
||||
|
||||
if (!src)
|
||||
return NULL;
|
||||
slen = strlen(src);
|
||||
|
||||
dlen = string_escape_mem(src, slen, NULL, 0, flags, esc);
|
||||
dst = kmalloc(dlen + 1, gfp);
|
||||
if (!dst)
|
||||
return NULL;
|
||||
|
||||
WARN_ON(string_escape_mem(src, slen, dst, dlen, flags, esc) != dlen);
|
||||
dst[dlen] = '\0';
|
||||
|
||||
return dst;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kstrdup_quotable);
|
||||
|
||||
/*
|
||||
* Returns allocated NULL-terminated string containing process
|
||||
* command line, with inter-argument NULLs replaced with spaces,
|
||||
* and other special characters escaped.
|
||||
*/
|
||||
char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp)
|
||||
{
|
||||
char *buffer, *quoted;
|
||||
int i, res;
|
||||
|
||||
buffer = kmalloc(PAGE_SIZE, GFP_TEMPORARY);
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
|
||||
res = get_cmdline(task, buffer, PAGE_SIZE - 1);
|
||||
buffer[res] = '\0';
|
||||
|
||||
/* Collapse trailing NULLs, leave res pointing to last non-NULL. */
|
||||
while (--res >= 0 && buffer[res] == '\0')
|
||||
;
|
||||
|
||||
/* Replace inter-argument NULLs. */
|
||||
for (i = 0; i <= res; i++)
|
||||
if (buffer[i] == '\0')
|
||||
buffer[i] = ' ';
|
||||
|
||||
/* Make sure result is printable. */
|
||||
quoted = kstrdup_quotable(buffer, gfp);
|
||||
kfree(buffer);
|
||||
return quoted;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kstrdup_quotable_cmdline);
|
||||
|
||||
/*
|
||||
* Returns allocated NULL-terminated string containing pathname,
|
||||
* with special characters escaped, able to be safely logged. If
|
||||
* there is an error, the leading character will be "<".
|
||||
*/
|
||||
char *kstrdup_quotable_file(struct file *file, gfp_t gfp)
|
||||
{
|
||||
char *temp, *pathname;
|
||||
|
||||
if (!file)
|
||||
return kstrdup("<unknown>", gfp);
|
||||
|
||||
/* We add 11 spaces for ' (deleted)' to be appended */
|
||||
temp = kmalloc(PATH_MAX + 11, GFP_TEMPORARY);
|
||||
if (!temp)
|
||||
return kstrdup("<no_memory>", gfp);
|
||||
|
||||
pathname = file_path(file, temp, PATH_MAX + 11);
|
||||
if (IS_ERR(pathname))
|
||||
pathname = kstrdup("<too_long>", gfp);
|
||||
else
|
||||
pathname = kstrdup_quotable(pathname, gfp);
|
||||
|
||||
kfree(temp);
|
||||
return pathname;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kstrdup_quotable_file);
|
||||
|
@ -281,7 +281,7 @@ static int __init init_dns_resolver(void)
|
||||
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
|
||||
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ,
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
|
||||
if (IS_ERR(keyring)) {
|
||||
ret = PTR_ERR(keyring);
|
||||
goto failed_put_cred;
|
||||
|
@ -677,7 +677,7 @@ int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap,
|
||||
u32 spot = start;
|
||||
|
||||
while (rc == 0 && spot <= end) {
|
||||
if (((spot & (BITS_PER_LONG - 1)) != 0) &&
|
||||
if (((spot & (BITS_PER_LONG - 1)) == 0) &&
|
||||
((end - spot) > BITS_PER_LONG)) {
|
||||
rc = netlbl_catmap_setlong(catmap,
|
||||
spot,
|
||||
|
@ -965,7 +965,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn,
|
||||
|
||||
key = key_alloc(&key_type_rxrpc, "x",
|
||||
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0,
|
||||
KEY_ALLOC_NOT_IN_QUOTA);
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
if (IS_ERR(key)) {
|
||||
_leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
|
||||
return -ENOMEM;
|
||||
@ -1012,7 +1012,7 @@ struct key *rxrpc_get_null_key(const char *keyname)
|
||||
|
||||
key = key_alloc(&key_type_rxrpc, keyname,
|
||||
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
|
||||
KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA);
|
||||
KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
if (IS_ERR(key))
|
||||
return key;
|
||||
|
||||
|
@ -122,6 +122,7 @@ source security/selinux/Kconfig
|
||||
source security/smack/Kconfig
|
||||
source security/tomoyo/Kconfig
|
||||
source security/apparmor/Kconfig
|
||||
source security/loadpin/Kconfig
|
||||
source security/yama/Kconfig
|
||||
|
||||
source security/integrity/Kconfig
|
||||
|
@ -8,6 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack
|
||||
subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo
|
||||
subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
|
||||
subdir-$(CONFIG_SECURITY_YAMA) += yama
|
||||
subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
|
||||
|
||||
# always enable default capabilities
|
||||
obj-y += commoncap.o
|
||||
@ -22,6 +23,7 @@ obj-$(CONFIG_AUDIT) += lsm_audit.o
|
||||
obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/
|
||||
obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/
|
||||
obj-$(CONFIG_SECURITY_YAMA) += yama/
|
||||
obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
|
||||
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
|
||||
|
||||
# Object integrity file lists
|
||||
|
@ -35,7 +35,6 @@ config INTEGRITY_ASYMMETRIC_KEYS
|
||||
default n
|
||||
select ASYMMETRIC_KEY_TYPE
|
||||
select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
||||
select PUBLIC_KEY_ALGO_RSA
|
||||
select CRYPTO_RSA
|
||||
select X509_CERTIFICATE_PARSER
|
||||
help
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include <linux/cred.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/digsig.h>
|
||||
#include <crypto/public_key.h>
|
||||
#include <keys/system_keyring.h>
|
||||
|
||||
#include "integrity.h"
|
||||
|
||||
@ -40,6 +42,12 @@ static bool init_keyring __initdata = true;
|
||||
static bool init_keyring __initdata;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
|
||||
#define restrict_link_to_ima restrict_link_by_builtin_and_secondary_trusted
|
||||
#else
|
||||
#define restrict_link_to_ima restrict_link_by_builtin_trusted
|
||||
#endif
|
||||
|
||||
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
const char *digest, int digestlen)
|
||||
{
|
||||
@ -83,10 +91,9 @@ int __init integrity_init_keyring(const unsigned int id)
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ |
|
||||
KEY_USR_WRITE | KEY_USR_SEARCH),
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
if (!IS_ERR(keyring[id]))
|
||||
set_bit(KEY_FLAG_TRUSTED_ONLY, &keyring[id]->flags);
|
||||
else {
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
restrict_link_to_ima, NULL);
|
||||
if (IS_ERR(keyring[id])) {
|
||||
err = PTR_ERR(keyring[id]);
|
||||
pr_info("Can't allocate %s keyring (%d)\n",
|
||||
keyring_name[id], err);
|
||||
|
@ -155,23 +155,33 @@ config IMA_TRUSTED_KEYRING
|
||||
|
||||
This option is deprecated in favor of INTEGRITY_TRUSTED_KEYRING
|
||||
|
||||
config IMA_MOK_KEYRING
|
||||
bool "Create IMA machine owner keys (MOK) and blacklist keyrings"
|
||||
config IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
|
||||
bool "Permit keys validly signed by a built-in or secondary CA cert (EXPERIMENTAL)"
|
||||
depends on SYSTEM_TRUSTED_KEYRING
|
||||
depends on SECONDARY_TRUSTED_KEYRING
|
||||
depends on INTEGRITY_ASYMMETRIC_KEYS
|
||||
select INTEGRITY_TRUSTED_KEYRING
|
||||
default n
|
||||
help
|
||||
Keys may be added to the IMA or IMA blacklist keyrings, if the
|
||||
key is validly signed by a CA cert in the system built-in or
|
||||
secondary trusted keyrings.
|
||||
|
||||
Intermediate keys between those the kernel has compiled in and the
|
||||
IMA keys to be added may be added to the system secondary keyring,
|
||||
provided they are validly signed by a key already resident in the
|
||||
built-in or secondary trusted keyrings.
|
||||
|
||||
config IMA_BLACKLIST_KEYRING
|
||||
bool "Create IMA machine owner blacklist keyrings (EXPERIMENTAL)"
|
||||
depends on SYSTEM_TRUSTED_KEYRING
|
||||
depends on IMA_TRUSTED_KEYRING
|
||||
default n
|
||||
help
|
||||
This option creates IMA MOK and blacklist keyrings. IMA MOK is an
|
||||
intermediate keyring that sits between .system and .ima keyrings,
|
||||
effectively forming a simple CA hierarchy. To successfully import a
|
||||
key into .ima_mok it must be signed by a key which CA is in .system
|
||||
keyring. On turn any key that needs to go in .ima keyring must be
|
||||
signed by CA in either .system or .ima_mok keyrings. IMA MOK is empty
|
||||
at kernel boot.
|
||||
|
||||
IMA blacklist keyring contains all revoked IMA keys. It is consulted
|
||||
before any other keyring. If the search is successful the requested
|
||||
operation is rejected and error is returned to the caller.
|
||||
This option creates an IMA blacklist keyring, which contains all
|
||||
revoked IMA keys. It is consulted before any other keyring. If
|
||||
the search is successful the requested operation is rejected and
|
||||
an error is returned to the caller.
|
||||
|
||||
config IMA_LOAD_X509
|
||||
bool "Load X509 certificate onto the '.ima' trusted keyring"
|
||||
|
@ -8,4 +8,4 @@ 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_policy.o ima_template.o ima_template_lib.o
|
||||
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
|
||||
obj-$(CONFIG_IMA_MOK_KEYRING) += ima_mok.o
|
||||
obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
|
||||
|
@ -275,6 +275,11 @@ out:
|
||||
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
|
||||
if (!ima_fix_xattr(dentry, iint))
|
||||
status = INTEGRITY_PASS;
|
||||
} else if ((inode->i_size == 0) &&
|
||||
(iint->flags & IMA_NEW_FILE) &&
|
||||
(xattr_value &&
|
||||
xattr_value->type == EVM_IMA_XATTR_DIGSIG)) {
|
||||
status = INTEGRITY_PASS;
|
||||
}
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
|
||||
op, cause, rc, 0);
|
||||
@ -328,7 +333,7 @@ void ima_inode_post_setattr(struct dentry *dentry)
|
||||
if (iint) {
|
||||
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
|
||||
IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
|
||||
IMA_ACTION_FLAGS);
|
||||
IMA_ACTION_RULE_FLAGS);
|
||||
if (must_appraise)
|
||||
iint->flags |= IMA_APPRAISE;
|
||||
}
|
||||
|
@ -246,7 +246,8 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
|
||||
ima_audit_measurement(iint, pathname);
|
||||
|
||||
out_digsig:
|
||||
if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG))
|
||||
if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG) &&
|
||||
!(iint->flags & IMA_NEW_FILE))
|
||||
rc = -EACCES;
|
||||
kfree(xattr_value);
|
||||
out_free:
|
||||
@ -315,6 +316,28 @@ int ima_file_check(struct file *file, int mask, int opened)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ima_file_check);
|
||||
|
||||
/**
|
||||
* ima_post_path_mknod - mark as a new inode
|
||||
* @dentry: newly created dentry
|
||||
*
|
||||
* Mark files created via the mknodat syscall as new, so that the
|
||||
* file data can be written later.
|
||||
*/
|
||||
void ima_post_path_mknod(struct dentry *dentry)
|
||||
{
|
||||
struct integrity_iint_cache *iint;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
int must_appraise;
|
||||
|
||||
must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK);
|
||||
if (!must_appraise)
|
||||
return;
|
||||
|
||||
iint = integrity_inode_get(inode);
|
||||
if (iint)
|
||||
iint->flags |= IMA_NEW_FILE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ima_read_file - pre-measure/appraise hook decision based on policy
|
||||
* @file: pointer to the file to be measured/appraised/audit
|
||||
|
@ -17,38 +17,29 @@
|
||||
#include <linux/cred.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include <keys/system_keyring.h>
|
||||
|
||||
|
||||
struct key *ima_mok_keyring;
|
||||
struct key *ima_blacklist_keyring;
|
||||
|
||||
/*
|
||||
* Allocate the IMA MOK and blacklist keyrings
|
||||
* Allocate the IMA blacklist keyring
|
||||
*/
|
||||
__init int ima_mok_init(void)
|
||||
{
|
||||
pr_notice("Allocating IMA MOK and blacklist keyrings.\n");
|
||||
|
||||
ima_mok_keyring = keyring_alloc(".ima_mok",
|
||||
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
|
||||
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ |
|
||||
KEY_USR_WRITE | KEY_USR_SEARCH,
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
pr_notice("Allocating IMA blacklist keyring.\n");
|
||||
|
||||
ima_blacklist_keyring = keyring_alloc(".ima_blacklist",
|
||||
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
|
||||
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ |
|
||||
KEY_USR_WRITE | KEY_USR_SEARCH,
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
restrict_link_by_builtin_trusted, NULL);
|
||||
|
||||
if (IS_ERR(ima_mok_keyring) || IS_ERR(ima_blacklist_keyring))
|
||||
panic("Can't allocate IMA MOK or blacklist keyrings.");
|
||||
set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_mok_keyring->flags);
|
||||
if (IS_ERR(ima_blacklist_keyring))
|
||||
panic("Can't allocate IMA blacklist keyring.");
|
||||
|
||||
set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_blacklist_keyring->flags);
|
||||
set_bit(KEY_FLAG_KEEP, &ima_blacklist_keyring->flags);
|
||||
return 0;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
|
||||
/* iint cache flags */
|
||||
#define IMA_ACTION_FLAGS 0xff000000
|
||||
#define IMA_ACTION_RULE_FLAGS 0x06000000
|
||||
#define IMA_DIGSIG 0x01000000
|
||||
#define IMA_DIGSIG_REQUIRED 0x02000000
|
||||
#define IMA_PERMIT_DIRECTIO 0x04000000
|
||||
|
@ -41,6 +41,10 @@ config BIG_KEYS
|
||||
bool "Large payload keys"
|
||||
depends on KEYS
|
||||
depends on TMPFS
|
||||
select CRYPTO
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_RNG
|
||||
help
|
||||
This option provides support for holding large keys within the kernel
|
||||
(for example Kerberos ticket caches). The data may be stored out to
|
||||
@ -81,3 +85,14 @@ config ENCRYPTED_KEYS
|
||||
Userspace only ever sees/stores encrypted blobs.
|
||||
|
||||
If you are unsure as to whether this is required, answer N.
|
||||
|
||||
config KEY_DH_OPERATIONS
|
||||
bool "Diffie-Hellman operations on retained keys"
|
||||
depends on KEYS
|
||||
select MPILIB
|
||||
help
|
||||
This option provides support for calculating Diffie-Hellman
|
||||
public keys and shared secrets using values stored as keys
|
||||
in the kernel.
|
||||
|
||||
If you are unsure as to whether this is required, answer N.
|
||||
|
@ -19,6 +19,7 @@ obj-$(CONFIG_KEYS_COMPAT) += compat.o
|
||||
obj-$(CONFIG_PROC_FS) += proc.o
|
||||
obj-$(CONFIG_SYSCTL) += sysctl.o
|
||||
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
|
||||
obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
|
||||
|
||||
#
|
||||
# Key types
|
||||
|
@ -14,8 +14,10 @@
|
||||
#include <linux/file.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/big_key-type.h>
|
||||
#include <crypto/rng.h>
|
||||
|
||||
/*
|
||||
* Layout of key payload words.
|
||||
@ -27,6 +29,14 @@ enum {
|
||||
big_key_len,
|
||||
};
|
||||
|
||||
/*
|
||||
* Crypto operation with big_key data
|
||||
*/
|
||||
enum big_key_op {
|
||||
BIG_KEY_ENC,
|
||||
BIG_KEY_DEC,
|
||||
};
|
||||
|
||||
/*
|
||||
* If the data is under this limit, there's no point creating a shm file to
|
||||
* hold it as the permanently resident metadata for the shmem fs will be at
|
||||
@ -34,6 +44,11 @@ enum {
|
||||
*/
|
||||
#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
|
||||
|
||||
/*
|
||||
* Key size for big_key data encryption
|
||||
*/
|
||||
#define ENC_KEY_SIZE 16
|
||||
|
||||
/*
|
||||
* big_key defined keys take an arbitrary string as the description and an
|
||||
* arbitrary blob of data as the payload
|
||||
@ -49,6 +64,54 @@ struct key_type key_type_big_key = {
|
||||
.read = big_key_read,
|
||||
};
|
||||
|
||||
/*
|
||||
* Crypto names for big_key data encryption
|
||||
*/
|
||||
static const char big_key_rng_name[] = "stdrng";
|
||||
static const char big_key_alg_name[] = "ecb(aes)";
|
||||
|
||||
/*
|
||||
* Crypto algorithms for big_key data encryption
|
||||
*/
|
||||
static struct crypto_rng *big_key_rng;
|
||||
static struct crypto_blkcipher *big_key_blkcipher;
|
||||
|
||||
/*
|
||||
* Generate random key to encrypt big_key data
|
||||
*/
|
||||
static inline int big_key_gen_enckey(u8 *key)
|
||||
{
|
||||
return crypto_rng_get_bytes(big_key_rng, key, ENC_KEY_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encrypt/decrypt big_key data
|
||||
*/
|
||||
static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
struct scatterlist sgio;
|
||||
struct blkcipher_desc desc;
|
||||
|
||||
if (crypto_blkcipher_setkey(big_key_blkcipher, key, ENC_KEY_SIZE)) {
|
||||
ret = -EAGAIN;
|
||||
goto error;
|
||||
}
|
||||
|
||||
desc.flags = 0;
|
||||
desc.tfm = big_key_blkcipher;
|
||||
|
||||
sg_init_one(&sgio, data, datalen);
|
||||
|
||||
if (op == BIG_KEY_ENC)
|
||||
ret = crypto_blkcipher_encrypt(&desc, &sgio, &sgio, datalen);
|
||||
else
|
||||
ret = crypto_blkcipher_decrypt(&desc, &sgio, &sgio, datalen);
|
||||
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Preparse a big key
|
||||
*/
|
||||
@ -56,6 +119,8 @@ int big_key_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct path *path = (struct path *)&prep->payload.data[big_key_path];
|
||||
struct file *file;
|
||||
u8 *enckey;
|
||||
u8 *data = NULL;
|
||||
ssize_t written;
|
||||
size_t datalen = prep->datalen;
|
||||
int ret;
|
||||
@ -73,16 +138,43 @@ int big_key_preparse(struct key_preparsed_payload *prep)
|
||||
/* Create a shmem file to store the data in. This will permit the data
|
||||
* to be swapped out if needed.
|
||||
*
|
||||
* TODO: Encrypt the stored data with a temporary key.
|
||||
* File content is stored encrypted with randomly generated key.
|
||||
*/
|
||||
file = shmem_kernel_file_setup("", datalen, 0);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher));
|
||||
|
||||
/* prepare aligned data to encrypt */
|
||||
data = kmalloc(enclen, GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(data, prep->data, datalen);
|
||||
memset(data + datalen, 0x00, enclen - datalen);
|
||||
|
||||
/* generate random key */
|
||||
enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL);
|
||||
if (!enckey) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
written = kernel_write(file, prep->data, prep->datalen, 0);
|
||||
if (written != datalen) {
|
||||
ret = big_key_gen_enckey(enckey);
|
||||
if (ret)
|
||||
goto err_enckey;
|
||||
|
||||
/* encrypt aligned data */
|
||||
ret = big_key_crypt(BIG_KEY_ENC, data, enclen, enckey);
|
||||
if (ret)
|
||||
goto err_enckey;
|
||||
|
||||
/* save aligned data to file */
|
||||
file = shmem_kernel_file_setup("", enclen, 0);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
goto err_enckey;
|
||||
}
|
||||
|
||||
written = kernel_write(file, data, enclen, 0);
|
||||
if (written != enclen) {
|
||||
ret = written;
|
||||
if (written >= 0)
|
||||
ret = -ENOMEM;
|
||||
@ -92,12 +184,15 @@ int big_key_preparse(struct key_preparsed_payload *prep)
|
||||
/* Pin the mount and dentry to the key so that we can open it again
|
||||
* later
|
||||
*/
|
||||
prep->payload.data[big_key_data] = enckey;
|
||||
*path = file->f_path;
|
||||
path_get(path);
|
||||
fput(file);
|
||||
kfree(data);
|
||||
} else {
|
||||
/* Just store the data in a buffer */
|
||||
void *data = kmalloc(datalen, GFP_KERNEL);
|
||||
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -108,7 +203,10 @@ int big_key_preparse(struct key_preparsed_payload *prep)
|
||||
|
||||
err_fput:
|
||||
fput(file);
|
||||
err_enckey:
|
||||
kfree(enckey);
|
||||
error:
|
||||
kfree(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -119,10 +217,10 @@ void big_key_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
if (prep->datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
struct path *path = (struct path *)&prep->payload.data[big_key_path];
|
||||
|
||||
path_put(path);
|
||||
} else {
|
||||
kfree(prep->payload.data[big_key_data]);
|
||||
}
|
||||
kfree(prep->payload.data[big_key_data]);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -147,15 +245,15 @@ void big_key_destroy(struct key *key)
|
||||
{
|
||||
size_t datalen = (size_t)key->payload.data[big_key_len];
|
||||
|
||||
if (datalen) {
|
||||
if (datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
struct path *path = (struct path *)&key->payload.data[big_key_path];
|
||||
|
||||
path_put(path);
|
||||
path->mnt = NULL;
|
||||
path->dentry = NULL;
|
||||
} else {
|
||||
kfree(key->payload.data[big_key_data]);
|
||||
key->payload.data[big_key_data] = NULL;
|
||||
}
|
||||
kfree(key->payload.data[big_key_data]);
|
||||
key->payload.data[big_key_data] = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -188,17 +286,41 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
|
||||
if (datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
struct path *path = (struct path *)&key->payload.data[big_key_path];
|
||||
struct file *file;
|
||||
loff_t pos;
|
||||
u8 *data;
|
||||
u8 *enckey = (u8 *)key->payload.data[big_key_data];
|
||||
size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher));
|
||||
|
||||
data = kmalloc(enclen, GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
file = dentry_open(path, O_RDONLY, current_cred());
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
goto error;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
ret = vfs_read(file, buffer, datalen, &pos);
|
||||
fput(file);
|
||||
if (ret >= 0 && ret != datalen)
|
||||
/* read file to kernel and decrypt */
|
||||
ret = kernel_read(file, 0, data, enclen);
|
||||
if (ret >= 0 && ret != enclen) {
|
||||
ret = -EIO;
|
||||
goto err_fput;
|
||||
}
|
||||
|
||||
ret = big_key_crypt(BIG_KEY_DEC, data, enclen, enckey);
|
||||
if (ret)
|
||||
goto err_fput;
|
||||
|
||||
ret = datalen;
|
||||
|
||||
/* copy decrypted data to user */
|
||||
if (copy_to_user(buffer, data, datalen) != 0)
|
||||
ret = -EFAULT;
|
||||
|
||||
err_fput:
|
||||
fput(file);
|
||||
error:
|
||||
kfree(data);
|
||||
} else {
|
||||
ret = datalen;
|
||||
if (copy_to_user(buffer, key->payload.data[big_key_data],
|
||||
@ -209,8 +331,48 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register key type
|
||||
*/
|
||||
static int __init big_key_init(void)
|
||||
{
|
||||
return register_key_type(&key_type_big_key);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize big_key crypto and RNG algorithms
|
||||
*/
|
||||
static int __init big_key_crypto_init(void)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
||||
/* init RNG */
|
||||
big_key_rng = crypto_alloc_rng(big_key_rng_name, 0, 0);
|
||||
if (IS_ERR(big_key_rng)) {
|
||||
big_key_rng = NULL;
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* seed RNG */
|
||||
ret = crypto_rng_reset(big_key_rng, NULL, crypto_rng_seedsize(big_key_rng));
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
/* init block cipher */
|
||||
big_key_blkcipher = crypto_alloc_blkcipher(big_key_alg_name, 0, 0);
|
||||
if (IS_ERR(big_key_blkcipher)) {
|
||||
big_key_blkcipher = NULL;
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
crypto_free_rng(big_key_rng);
|
||||
big_key_rng = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
device_initcall(big_key_init);
|
||||
late_initcall(big_key_crypto_init);
|
||||
|
@ -132,6 +132,10 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
|
||||
case KEYCTL_GET_PERSISTENT:
|
||||
return keyctl_get_persistent(arg2, arg3);
|
||||
|
||||
case KEYCTL_DH_COMPUTE:
|
||||
return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3),
|
||||
arg4);
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
160
security/keys/dh.c
Normal file
160
security/keys/dh.c
Normal file
@ -0,0 +1,160 @@
|
||||
/* Crypto operations using stored keys
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/mpi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <keys/user-type.h>
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* Public key or shared secret generation function [RFC2631 sec 2.1.1]
|
||||
*
|
||||
* ya = g^xa mod p;
|
||||
* or
|
||||
* ZZ = yb^xa mod p;
|
||||
*
|
||||
* where xa is the local private key, ya is the local public key, g is
|
||||
* the generator, p is the prime, yb is the remote public key, and ZZ
|
||||
* is the shared secret.
|
||||
*
|
||||
* Both are the same calculation, so g or yb are the "base" and ya or
|
||||
* ZZ are the "result".
|
||||
*/
|
||||
static int do_dh(MPI result, MPI base, MPI xa, MPI p)
|
||||
{
|
||||
return mpi_powm(result, base, xa, p);
|
||||
}
|
||||
|
||||
static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi)
|
||||
{
|
||||
struct key *key;
|
||||
key_ref_t key_ref;
|
||||
long status;
|
||||
ssize_t ret;
|
||||
|
||||
key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ);
|
||||
if (IS_ERR(key_ref)) {
|
||||
ret = -ENOKEY;
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = key_ref_to_ptr(key_ref);
|
||||
|
||||
ret = -EOPNOTSUPP;
|
||||
if (key->type == &key_type_user) {
|
||||
down_read(&key->sem);
|
||||
status = key_validate(key);
|
||||
if (status == 0) {
|
||||
const struct user_key_payload *payload;
|
||||
|
||||
payload = user_key_payload(key);
|
||||
|
||||
if (maxlen == 0) {
|
||||
*mpi = NULL;
|
||||
ret = payload->datalen;
|
||||
} else if (payload->datalen <= maxlen) {
|
||||
*mpi = mpi_read_raw_data(payload->data,
|
||||
payload->datalen);
|
||||
if (*mpi)
|
||||
ret = payload->datalen;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
up_read(&key->sem);
|
||||
}
|
||||
|
||||
key_put(key);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen)
|
||||
{
|
||||
long ret;
|
||||
MPI base, private, prime, result;
|
||||
unsigned nbytes;
|
||||
struct keyctl_dh_params pcopy;
|
||||
uint8_t *kbuf;
|
||||
ssize_t keylen;
|
||||
size_t resultlen;
|
||||
|
||||
if (!params || (!buffer && buflen)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
keylen = mpi_from_key(pcopy.prime, buflen, &prime);
|
||||
if (keylen < 0 || !prime) {
|
||||
/* buflen == 0 may be used to query the required buffer size,
|
||||
* which is the prime key length.
|
||||
*/
|
||||
ret = keylen;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The result is never longer than the prime */
|
||||
resultlen = keylen;
|
||||
|
||||
keylen = mpi_from_key(pcopy.base, SIZE_MAX, &base);
|
||||
if (keylen < 0 || !base) {
|
||||
ret = keylen;
|
||||
goto error1;
|
||||
}
|
||||
|
||||
keylen = mpi_from_key(pcopy.private, SIZE_MAX, &private);
|
||||
if (keylen < 0 || !private) {
|
||||
ret = keylen;
|
||||
goto error2;
|
||||
}
|
||||
|
||||
result = mpi_alloc(0);
|
||||
if (!result) {
|
||||
ret = -ENOMEM;
|
||||
goto error3;
|
||||
}
|
||||
|
||||
kbuf = kmalloc(resultlen, GFP_KERNEL);
|
||||
if (!kbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto error4;
|
||||
}
|
||||
|
||||
ret = do_dh(result, base, private, prime);
|
||||
if (ret)
|
||||
goto error5;
|
||||
|
||||
ret = mpi_read_buffer(result, kbuf, resultlen, &nbytes, NULL);
|
||||
if (ret != 0)
|
||||
goto error5;
|
||||
|
||||
ret = nbytes;
|
||||
if (copy_to_user(buffer, kbuf, nbytes) != 0)
|
||||
ret = -EFAULT;
|
||||
|
||||
error5:
|
||||
kfree(kbuf);
|
||||
error4:
|
||||
mpi_free(result);
|
||||
error3:
|
||||
mpi_free(private);
|
||||
error2:
|
||||
mpi_free(base);
|
||||
error1:
|
||||
mpi_free(prime);
|
||||
out:
|
||||
return ret;
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/task_work.h>
|
||||
#include <linux/keyctl.h>
|
||||
|
||||
struct iovec;
|
||||
|
||||
@ -257,6 +258,17 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KEY_DH_OPERATIONS
|
||||
extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *,
|
||||
size_t);
|
||||
#else
|
||||
static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Debugging key validation
|
||||
*/
|
||||
|
@ -201,6 +201,7 @@ serial_exists:
|
||||
* @cred: The credentials specifying UID namespace.
|
||||
* @perm: The permissions mask of the new key.
|
||||
* @flags: Flags specifying quota properties.
|
||||
* @restrict_link: Optional link restriction method for new keyrings.
|
||||
*
|
||||
* Allocate a key of the specified type with the attributes given. The key is
|
||||
* returned in an uninstantiated state and the caller needs to instantiate the
|
||||
@ -223,7 +224,10 @@ serial_exists:
|
||||
*/
|
||||
struct key *key_alloc(struct key_type *type, const char *desc,
|
||||
kuid_t uid, kgid_t gid, const struct cred *cred,
|
||||
key_perm_t perm, unsigned long flags)
|
||||
key_perm_t perm, unsigned long flags,
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
const union key_payload *))
|
||||
{
|
||||
struct key_user *user = NULL;
|
||||
struct key *key;
|
||||
@ -291,11 +295,10 @@ struct key *key_alloc(struct key_type *type, const char *desc,
|
||||
key->uid = uid;
|
||||
key->gid = gid;
|
||||
key->perm = perm;
|
||||
key->restrict_link = restrict_link;
|
||||
|
||||
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
|
||||
key->flags |= 1 << KEY_FLAG_IN_QUOTA;
|
||||
if (flags & KEY_ALLOC_TRUSTED)
|
||||
key->flags |= 1 << KEY_FLAG_TRUSTED;
|
||||
if (flags & KEY_ALLOC_BUILT_IN)
|
||||
key->flags |= 1 << KEY_FLAG_BUILTIN;
|
||||
|
||||
@ -496,6 +499,12 @@ int key_instantiate_and_link(struct key *key,
|
||||
}
|
||||
|
||||
if (keyring) {
|
||||
if (keyring->restrict_link) {
|
||||
ret = keyring->restrict_link(keyring, key->type,
|
||||
&prep.payload);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
ret = __key_link_begin(keyring, &key->index_key, &edit);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
@ -551,8 +560,12 @@ int key_reject_and_link(struct key *key,
|
||||
awaken = 0;
|
||||
ret = -EBUSY;
|
||||
|
||||
if (keyring)
|
||||
if (keyring) {
|
||||
if (keyring->restrict_link)
|
||||
return -EPERM;
|
||||
|
||||
link_ret = __key_link_begin(keyring, &key->index_key, &edit);
|
||||
}
|
||||
|
||||
mutex_lock(&key_construction_mutex);
|
||||
|
||||
@ -793,6 +806,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||
struct key *keyring, *key = NULL;
|
||||
key_ref_t key_ref;
|
||||
int ret;
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
const union key_payload *) = NULL;
|
||||
|
||||
/* look up the key type to see if it's one of the registered kernel
|
||||
* types */
|
||||
@ -811,6 +827,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||
|
||||
key_check(keyring);
|
||||
|
||||
key_ref = ERR_PTR(-EPERM);
|
||||
if (!(flags & KEY_ALLOC_BYPASS_RESTRICTION))
|
||||
restrict_link = keyring->restrict_link;
|
||||
|
||||
key_ref = ERR_PTR(-ENOTDIR);
|
||||
if (keyring->type != &key_type_keyring)
|
||||
goto error_put_type;
|
||||
@ -819,7 +839,6 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||
prep.data = payload;
|
||||
prep.datalen = plen;
|
||||
prep.quotalen = index_key.type->def_datalen;
|
||||
prep.trusted = flags & KEY_ALLOC_TRUSTED;
|
||||
prep.expiry = TIME_T_MAX;
|
||||
if (index_key.type->preparse) {
|
||||
ret = index_key.type->preparse(&prep);
|
||||
@ -835,10 +854,13 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||
}
|
||||
index_key.desc_len = strlen(index_key.description);
|
||||
|
||||
key_ref = ERR_PTR(-EPERM);
|
||||
if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags))
|
||||
goto error_free_prep;
|
||||
flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0;
|
||||
if (restrict_link) {
|
||||
ret = restrict_link(keyring, index_key.type, &prep.payload);
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_free_prep;
|
||||
}
|
||||
}
|
||||
|
||||
ret = __key_link_begin(keyring, &index_key, &edit);
|
||||
if (ret < 0) {
|
||||
@ -879,7 +901,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||
|
||||
/* allocate a new key */
|
||||
key = key_alloc(index_key.type, index_key.description,
|
||||
cred->fsuid, cred->fsgid, cred, perm, flags);
|
||||
cred->fsuid, cred->fsgid, cred, perm, flags, NULL);
|
||||
if (IS_ERR(key)) {
|
||||
key_ref = ERR_CAST(key);
|
||||
goto error_link_end;
|
||||
|
@ -1686,6 +1686,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
||||
case KEYCTL_GET_PERSISTENT:
|
||||
return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3);
|
||||
|
||||
case KEYCTL_DH_COMPUTE:
|
||||
return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2,
|
||||
(char __user *) arg3,
|
||||
(size_t) arg4);
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -491,13 +491,17 @@ static long keyring_read(const struct key *keyring,
|
||||
*/
|
||||
struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
|
||||
const struct cred *cred, key_perm_t perm,
|
||||
unsigned long flags, struct key *dest)
|
||||
unsigned long flags,
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
const union key_payload *),
|
||||
struct key *dest)
|
||||
{
|
||||
struct key *keyring;
|
||||
int ret;
|
||||
|
||||
keyring = key_alloc(&key_type_keyring, description,
|
||||
uid, gid, cred, perm, flags);
|
||||
uid, gid, cred, perm, flags, restrict_link);
|
||||
if (!IS_ERR(keyring)) {
|
||||
ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);
|
||||
if (ret < 0) {
|
||||
@ -510,6 +514,26 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
|
||||
}
|
||||
EXPORT_SYMBOL(keyring_alloc);
|
||||
|
||||
/**
|
||||
* restrict_link_reject - Give -EPERM to restrict link
|
||||
* @keyring: The keyring being added to.
|
||||
* @type: The type of key being added.
|
||||
* @payload: The payload of the key intended to be added.
|
||||
*
|
||||
* Reject the addition of any links to a keyring. It can be overridden by
|
||||
* passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when
|
||||
* adding a key to a keyring.
|
||||
*
|
||||
* This is meant to be passed as the restrict_link parameter to
|
||||
* keyring_alloc().
|
||||
*/
|
||||
int restrict_link_reject(struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload)
|
||||
{
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/*
|
||||
* By default, we keys found by getting an exact match on their descriptions.
|
||||
*/
|
||||
@ -1191,6 +1215,16 @@ void __key_link_end(struct key *keyring,
|
||||
up_write(&keyring->sem);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check addition of keys to restricted keyrings.
|
||||
*/
|
||||
static int __key_link_check_restriction(struct key *keyring, struct key *key)
|
||||
{
|
||||
if (!keyring->restrict_link)
|
||||
return 0;
|
||||
return keyring->restrict_link(keyring, key->type, &key->payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* key_link - Link a key to a keyring
|
||||
* @keyring: The keyring to make the link in.
|
||||
@ -1221,14 +1255,12 @@ int key_link(struct key *keyring, struct key *key)
|
||||
key_check(keyring);
|
||||
key_check(key);
|
||||
|
||||
if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) &&
|
||||
!test_bit(KEY_FLAG_TRUSTED, &key->flags))
|
||||
return -EPERM;
|
||||
|
||||
ret = __key_link_begin(keyring, &key->index_key, &edit);
|
||||
if (ret == 0) {
|
||||
kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage));
|
||||
ret = __key_link_check_live_key(keyring, key);
|
||||
ret = __key_link_check_restriction(keyring, key);
|
||||
if (ret == 0)
|
||||
ret = __key_link_check_live_key(keyring, key);
|
||||
if (ret == 0)
|
||||
__key_link(key, &edit);
|
||||
__key_link_end(keyring, &key->index_key, edit);
|
||||
|
@ -26,7 +26,7 @@ static int key_create_persistent_register(struct user_namespace *ns)
|
||||
current_cred(),
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ),
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
|
||||
if (IS_ERR(reg))
|
||||
return PTR_ERR(reg);
|
||||
|
||||
@ -60,7 +60,7 @@ static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid,
|
||||
uid, INVALID_GID, current_cred(),
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ),
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL,
|
||||
ns->persistent_keyring_register);
|
||||
if (IS_ERR(persistent))
|
||||
return ERR_CAST(persistent);
|
||||
|
@ -76,7 +76,8 @@ int install_user_keyrings(void)
|
||||
if (IS_ERR(uid_keyring)) {
|
||||
uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID,
|
||||
cred, user_keyring_perm,
|
||||
KEY_ALLOC_IN_QUOTA, NULL);
|
||||
KEY_ALLOC_IN_QUOTA,
|
||||
NULL, NULL);
|
||||
if (IS_ERR(uid_keyring)) {
|
||||
ret = PTR_ERR(uid_keyring);
|
||||
goto error;
|
||||
@ -92,7 +93,8 @@ int install_user_keyrings(void)
|
||||
session_keyring =
|
||||
keyring_alloc(buf, user->uid, INVALID_GID,
|
||||
cred, user_keyring_perm,
|
||||
KEY_ALLOC_IN_QUOTA, NULL);
|
||||
KEY_ALLOC_IN_QUOTA,
|
||||
NULL, NULL);
|
||||
if (IS_ERR(session_keyring)) {
|
||||
ret = PTR_ERR(session_keyring);
|
||||
goto error_release;
|
||||
@ -134,7 +136,8 @@ int install_thread_keyring_to_cred(struct cred *new)
|
||||
|
||||
keyring = keyring_alloc("_tid", new->uid, new->gid, new,
|
||||
KEY_POS_ALL | KEY_USR_VIEW,
|
||||
KEY_ALLOC_QUOTA_OVERRUN, NULL);
|
||||
KEY_ALLOC_QUOTA_OVERRUN,
|
||||
NULL, NULL);
|
||||
if (IS_ERR(keyring))
|
||||
return PTR_ERR(keyring);
|
||||
|
||||
@ -180,7 +183,8 @@ int install_process_keyring_to_cred(struct cred *new)
|
||||
|
||||
keyring = keyring_alloc("_pid", new->uid, new->gid, new,
|
||||
KEY_POS_ALL | KEY_USR_VIEW,
|
||||
KEY_ALLOC_QUOTA_OVERRUN, NULL);
|
||||
KEY_ALLOC_QUOTA_OVERRUN,
|
||||
NULL, NULL);
|
||||
if (IS_ERR(keyring))
|
||||
return PTR_ERR(keyring);
|
||||
|
||||
@ -231,7 +235,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
|
||||
|
||||
keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred,
|
||||
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
|
||||
flags, NULL);
|
||||
flags, NULL, NULL);
|
||||
if (IS_ERR(keyring))
|
||||
return PTR_ERR(keyring);
|
||||
} else {
|
||||
@ -785,7 +789,7 @@ long join_session_keyring(const char *name)
|
||||
keyring = keyring_alloc(
|
||||
name, old->uid, old->gid, old,
|
||||
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK,
|
||||
KEY_ALLOC_IN_QUOTA, NULL);
|
||||
KEY_ALLOC_IN_QUOTA, NULL, NULL);
|
||||
if (IS_ERR(keyring)) {
|
||||
ret = PTR_ERR(keyring);
|
||||
goto error2;
|
||||
|
@ -116,7 +116,7 @@ static int call_sbin_request_key(struct key_construction *cons,
|
||||
cred = get_current_cred();
|
||||
keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred,
|
||||
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
|
||||
KEY_ALLOC_QUOTA_OVERRUN, NULL);
|
||||
KEY_ALLOC_QUOTA_OVERRUN, NULL, NULL);
|
||||
put_cred(cred);
|
||||
if (IS_ERR(keyring)) {
|
||||
ret = PTR_ERR(keyring);
|
||||
@ -355,7 +355,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
|
||||
|
||||
key = key_alloc(ctx->index_key.type, ctx->index_key.description,
|
||||
ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred,
|
||||
perm, flags);
|
||||
perm, flags, NULL);
|
||||
if (IS_ERR(key))
|
||||
goto alloc_failed;
|
||||
|
||||
|
@ -202,7 +202,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
|
||||
authkey = key_alloc(&key_type_request_key_auth, desc,
|
||||
cred->fsuid, cred->fsgid, cred,
|
||||
KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
|
||||
KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA);
|
||||
KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
if (IS_ERR(authkey)) {
|
||||
ret = PTR_ERR(authkey);
|
||||
goto error_alloc;
|
||||
|
@ -96,45 +96,25 @@ EXPORT_SYMBOL_GPL(user_free_preparse);
|
||||
*/
|
||||
int user_update(struct key *key, struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct user_key_payload *upayload, *zap;
|
||||
size_t datalen = prep->datalen;
|
||||
struct user_key_payload *zap = NULL;
|
||||
int ret;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
goto error;
|
||||
|
||||
/* construct a replacement payload */
|
||||
ret = -ENOMEM;
|
||||
upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
|
||||
if (!upayload)
|
||||
goto error;
|
||||
|
||||
upayload->datalen = datalen;
|
||||
memcpy(upayload->data, prep->data, datalen);
|
||||
|
||||
/* check the quota and attach the new data */
|
||||
zap = upayload;
|
||||
ret = key_payload_reserve(key, prep->datalen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = key_payload_reserve(key, datalen);
|
||||
|
||||
if (ret == 0) {
|
||||
/* attach the new data, displacing the old */
|
||||
if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
|
||||
zap = key->payload.data[0];
|
||||
else
|
||||
zap = NULL;
|
||||
rcu_assign_keypointer(key, upayload);
|
||||
key->expiry = 0;
|
||||
}
|
||||
/* attach the new data, displacing the old */
|
||||
key->expiry = prep->expiry;
|
||||
if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
|
||||
zap = rcu_dereference_key(key);
|
||||
rcu_assign_keypointer(key, prep->payload.data[0]);
|
||||
prep->payload.data[0] = NULL;
|
||||
|
||||
if (zap)
|
||||
kfree_rcu(zap, rcu);
|
||||
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(user_update);
|
||||
|
||||
/*
|
||||
|
19
security/loadpin/Kconfig
Normal file
19
security/loadpin/Kconfig
Normal file
@ -0,0 +1,19 @@
|
||||
config SECURITY_LOADPIN
|
||||
bool "Pin load of kernel files (modules, fw, etc) to one filesystem"
|
||||
depends on SECURITY && BLOCK
|
||||
help
|
||||
Any files read through the kernel file reading interface
|
||||
(kernel modules, firmware, kexec images, security policy)
|
||||
can be pinned to the first filesystem used for loading. When
|
||||
enabled, any files that come from other filesystems will be
|
||||
rejected. This is best used on systems without an initrd that
|
||||
have a root filesystem backed by a read-only device such as
|
||||
dm-verity or a CDROM.
|
||||
|
||||
config SECURITY_LOADPIN_ENABLED
|
||||
bool "Enforce LoadPin at boot"
|
||||
depends on SECURITY_LOADPIN
|
||||
help
|
||||
If selected, LoadPin will enforce pinning at boot. If not
|
||||
selected, it can be enabled at boot with the kernel parameter
|
||||
"loadpin.enabled=1".
|
1
security/loadpin/Makefile
Normal file
1
security/loadpin/Makefile
Normal file
@ -0,0 +1 @@
|
||||
obj-$(CONFIG_SECURITY_LOADPIN) += loadpin.o
|
190
security/loadpin/loadpin.c
Normal file
190
security/loadpin/loadpin.c
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Module and Firmware Pinning Security Module
|
||||
*
|
||||
* Copyright 2011-2016 Google Inc.
|
||||
*
|
||||
* Author: Kees Cook <keescook@chromium.org>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "LoadPin: " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fs_struct.h>
|
||||
#include <linux/lsm_hooks.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/path.h>
|
||||
#include <linux/sched.h> /* current */
|
||||
#include <linux/string_helpers.h>
|
||||
|
||||
static void report_load(const char *origin, struct file *file, char *operation)
|
||||
{
|
||||
char *cmdline, *pathname;
|
||||
|
||||
pathname = kstrdup_quotable_file(file, GFP_KERNEL);
|
||||
cmdline = kstrdup_quotable_cmdline(current, GFP_KERNEL);
|
||||
|
||||
pr_notice("%s %s obj=%s%s%s pid=%d cmdline=%s%s%s\n",
|
||||
origin, operation,
|
||||
(pathname && pathname[0] != '<') ? "\"" : "",
|
||||
pathname,
|
||||
(pathname && pathname[0] != '<') ? "\"" : "",
|
||||
task_pid_nr(current),
|
||||
cmdline ? "\"" : "", cmdline, cmdline ? "\"" : "");
|
||||
|
||||
kfree(cmdline);
|
||||
kfree(pathname);
|
||||
}
|
||||
|
||||
static int enabled = IS_ENABLED(CONFIG_SECURITY_LOADPIN_ENABLED);
|
||||
static struct super_block *pinned_root;
|
||||
static DEFINE_SPINLOCK(pinned_root_spinlock);
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static int zero;
|
||||
static int one = 1;
|
||||
|
||||
static struct ctl_path loadpin_sysctl_path[] = {
|
||||
{ .procname = "kernel", },
|
||||
{ .procname = "loadpin", },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct ctl_table loadpin_sysctl_table[] = {
|
||||
{
|
||||
.procname = "enabled",
|
||||
.data = &enabled,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &one,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
/*
|
||||
* This must be called after early kernel init, since then the rootdev
|
||||
* is available.
|
||||
*/
|
||||
static void check_pinning_enforcement(struct super_block *mnt_sb)
|
||||
{
|
||||
bool ro = false;
|
||||
|
||||
/*
|
||||
* If load pinning is not enforced via a read-only block
|
||||
* device, allow sysctl to change modes for testing.
|
||||
*/
|
||||
if (mnt_sb->s_bdev) {
|
||||
ro = bdev_read_only(mnt_sb->s_bdev);
|
||||
pr_info("dev(%u,%u): %s\n",
|
||||
MAJOR(mnt_sb->s_bdev->bd_dev),
|
||||
MINOR(mnt_sb->s_bdev->bd_dev),
|
||||
ro ? "read-only" : "writable");
|
||||
} else
|
||||
pr_info("mnt_sb lacks block device, treating as: writable\n");
|
||||
|
||||
if (!ro) {
|
||||
if (!register_sysctl_paths(loadpin_sysctl_path,
|
||||
loadpin_sysctl_table))
|
||||
pr_notice("sysctl registration failed!\n");
|
||||
else
|
||||
pr_info("load pinning can be disabled.\n");
|
||||
} else
|
||||
pr_info("load pinning engaged.\n");
|
||||
}
|
||||
#else
|
||||
static void check_pinning_enforcement(struct super_block *mnt_sb)
|
||||
{
|
||||
pr_info("load pinning engaged.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
static void loadpin_sb_free_security(struct super_block *mnt_sb)
|
||||
{
|
||||
/*
|
||||
* When unmounting the filesystem we were using for load
|
||||
* pinning, we acknowledge the superblock release, but make sure
|
||||
* no other modules or firmware can be loaded.
|
||||
*/
|
||||
if (!IS_ERR_OR_NULL(pinned_root) && mnt_sb == pinned_root) {
|
||||
pinned_root = ERR_PTR(-EIO);
|
||||
pr_info("umount pinned fs: refusing further loads\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int loadpin_read_file(struct file *file, enum kernel_read_file_id id)
|
||||
{
|
||||
struct super_block *load_root;
|
||||
const char *origin = kernel_read_file_id_str(id);
|
||||
|
||||
/* This handles the older init_module API that has a NULL file. */
|
||||
if (!file) {
|
||||
if (!enabled) {
|
||||
report_load(origin, NULL, "old-api-pinning-ignored");
|
||||
return 0;
|
||||
}
|
||||
|
||||
report_load(origin, NULL, "old-api-denied");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
load_root = file->f_path.mnt->mnt_sb;
|
||||
|
||||
/* First loaded module/firmware defines the root for all others. */
|
||||
spin_lock(&pinned_root_spinlock);
|
||||
/*
|
||||
* pinned_root is only NULL at startup. Otherwise, it is either
|
||||
* a valid reference, or an ERR_PTR.
|
||||
*/
|
||||
if (!pinned_root) {
|
||||
pinned_root = load_root;
|
||||
/*
|
||||
* Unlock now since it's only pinned_root we care about.
|
||||
* In the worst case, we will (correctly) report pinning
|
||||
* failures before we have announced that pinning is
|
||||
* enabled. This would be purely cosmetic.
|
||||
*/
|
||||
spin_unlock(&pinned_root_spinlock);
|
||||
check_pinning_enforcement(pinned_root);
|
||||
report_load(origin, file, "pinned");
|
||||
} else {
|
||||
spin_unlock(&pinned_root_spinlock);
|
||||
}
|
||||
|
||||
if (IS_ERR_OR_NULL(pinned_root) || load_root != pinned_root) {
|
||||
if (unlikely(!enabled)) {
|
||||
report_load(origin, file, "pinning-ignored");
|
||||
return 0;
|
||||
}
|
||||
|
||||
report_load(origin, file, "denied");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct security_hook_list loadpin_hooks[] = {
|
||||
LSM_HOOK_INIT(sb_free_security, loadpin_sb_free_security),
|
||||
LSM_HOOK_INIT(kernel_read_file, loadpin_read_file),
|
||||
};
|
||||
|
||||
void __init loadpin_add_hooks(void)
|
||||
{
|
||||
pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
|
||||
security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks));
|
||||
}
|
||||
|
||||
/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
|
||||
module_param(enabled, int, 0);
|
||||
MODULE_PARM_DESC(enabled, "Pin module/firmware loading (default: true)");
|
@ -60,6 +60,7 @@ int __init security_init(void)
|
||||
*/
|
||||
capability_add_hooks();
|
||||
yama_add_hooks();
|
||||
loadpin_add_hooks();
|
||||
|
||||
/*
|
||||
* Load all the remaining security modules.
|
||||
@ -1848,7 +1849,6 @@ struct security_hook_heads security_hook_heads = {
|
||||
.tun_dev_attach =
|
||||
LIST_HEAD_INIT(security_hook_heads.tun_dev_attach),
|
||||
.tun_dev_open = LIST_HEAD_INIT(security_hook_heads.tun_dev_open),
|
||||
.skb_owned_by = LIST_HEAD_INIT(security_hook_heads.skb_owned_by),
|
||||
#endif /* CONFIG_SECURITY_NETWORK */
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
.xfrm_policy_alloc_security =
|
||||
|
@ -259,7 +259,7 @@ static int __inode_security_revalidate(struct inode *inode,
|
||||
|
||||
might_sleep_if(may_sleep);
|
||||
|
||||
if (isec->initialized == LABEL_INVALID) {
|
||||
if (ss_initialized && isec->initialized != LABEL_INITIALIZED) {
|
||||
if (!may_sleep)
|
||||
return -ECHILD;
|
||||
|
||||
@ -297,6 +297,13 @@ static struct inode_security_struct *inode_security(struct inode *inode)
|
||||
return inode->i_security;
|
||||
}
|
||||
|
||||
static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = d_backing_inode(dentry);
|
||||
|
||||
return inode->i_security;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the security label of a dentry's backing inode.
|
||||
*/
|
||||
@ -687,7 +694,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
|
||||
struct superblock_security_struct *sbsec = sb->s_security;
|
||||
const char *name = sb->s_type->name;
|
||||
struct dentry *root = sbsec->sb->s_root;
|
||||
struct inode_security_struct *root_isec = backing_inode_security(root);
|
||||
struct inode_security_struct *root_isec;
|
||||
u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
|
||||
u32 defcontext_sid = 0;
|
||||
char **mount_options = opts->mnt_opts;
|
||||
@ -730,6 +737,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
|
||||
&& (num_opts == 0))
|
||||
goto out;
|
||||
|
||||
root_isec = backing_inode_security_novalidate(root);
|
||||
|
||||
/*
|
||||
* parse the mount options, check if they are valid sids.
|
||||
* also check if someone is trying to mount the same sb more
|
||||
@ -1623,7 +1632,7 @@ static int current_has_perm(const struct task_struct *tsk,
|
||||
|
||||
/* Check whether a task is allowed to use a capability. */
|
||||
static int cred_has_capability(const struct cred *cred,
|
||||
int cap, int audit)
|
||||
int cap, int audit, bool initns)
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
struct av_decision avd;
|
||||
@ -1637,10 +1646,10 @@ static int cred_has_capability(const struct cred *cred,
|
||||
|
||||
switch (CAP_TO_INDEX(cap)) {
|
||||
case 0:
|
||||
sclass = SECCLASS_CAPABILITY;
|
||||
sclass = initns ? SECCLASS_CAPABILITY : SECCLASS_CAP_USERNS;
|
||||
break;
|
||||
case 1:
|
||||
sclass = SECCLASS_CAPABILITY2;
|
||||
sclass = initns ? SECCLASS_CAPABILITY2 : SECCLASS_CAP2_USERNS;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR
|
||||
@ -1782,7 +1791,6 @@ static int selinux_determine_inode_label(struct inode *dir,
|
||||
u32 *_new_isid)
|
||||
{
|
||||
const struct superblock_security_struct *sbsec = dir->i_sb->s_security;
|
||||
const struct inode_security_struct *dsec = inode_security(dir);
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
|
||||
if ((sbsec->flags & SE_SBINITIALIZED) &&
|
||||
@ -1792,6 +1800,7 @@ static int selinux_determine_inode_label(struct inode *dir,
|
||||
tsec->create_sid) {
|
||||
*_new_isid = tsec->create_sid;
|
||||
} else {
|
||||
const struct inode_security_struct *dsec = inode_security(dir);
|
||||
return security_transition_sid(tsec->sid, dsec->sid, tclass,
|
||||
name, _new_isid);
|
||||
}
|
||||
@ -2076,7 +2085,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,
|
||||
u32 sid = task_sid(to);
|
||||
struct file_security_struct *fsec = file->f_security;
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct inode_security_struct *isec = backing_inode_security(dentry);
|
||||
struct inode_security_struct *isec;
|
||||
struct common_audit_data ad;
|
||||
int rc;
|
||||
|
||||
@ -2095,6 +2104,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,
|
||||
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
|
||||
return 0;
|
||||
|
||||
isec = backing_inode_security(dentry);
|
||||
return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file),
|
||||
&ad);
|
||||
}
|
||||
@ -2143,7 +2153,7 @@ static int selinux_capset(struct cred *new, const struct cred *old,
|
||||
static int selinux_capable(const struct cred *cred, struct user_namespace *ns,
|
||||
int cap, int audit)
|
||||
{
|
||||
return cred_has_capability(cred, cap, audit);
|
||||
return cred_has_capability(cred, cap, audit, ns == &init_user_ns);
|
||||
}
|
||||
|
||||
static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
|
||||
@ -2221,7 +2231,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
|
||||
int rc, cap_sys_admin = 0;
|
||||
|
||||
rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN,
|
||||
SECURITY_CAP_NOAUDIT);
|
||||
SECURITY_CAP_NOAUDIT, true);
|
||||
if (rc == 0)
|
||||
cap_sys_admin = 1;
|
||||
|
||||
@ -2230,6 +2240,20 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
|
||||
|
||||
/* binprm security operations */
|
||||
|
||||
static u32 ptrace_parent_sid(struct task_struct *task)
|
||||
{
|
||||
u32 sid = 0;
|
||||
struct task_struct *tracer;
|
||||
|
||||
rcu_read_lock();
|
||||
tracer = ptrace_parent(task);
|
||||
if (tracer)
|
||||
sid = task_sid(tracer);
|
||||
rcu_read_unlock();
|
||||
|
||||
return sid;
|
||||
}
|
||||
|
||||
static int check_nnp_nosuid(const struct linux_binprm *bprm,
|
||||
const struct task_security_struct *old_tsec,
|
||||
const struct task_security_struct *new_tsec)
|
||||
@ -2351,18 +2375,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
|
||||
* changes its SID has the appropriate permit */
|
||||
if (bprm->unsafe &
|
||||
(LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
|
||||
struct task_struct *tracer;
|
||||
struct task_security_struct *sec;
|
||||
u32 ptsid = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
tracer = ptrace_parent(current);
|
||||
if (likely(tracer != NULL)) {
|
||||
sec = __task_cred(tracer)->security;
|
||||
ptsid = sec->sid;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
u32 ptsid = ptrace_parent_sid(current);
|
||||
if (ptsid != 0) {
|
||||
rc = avc_has_perm(ptsid, new_tsec->sid,
|
||||
SECCLASS_PROCESS,
|
||||
@ -3046,7 +3059,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags)
|
||||
{
|
||||
struct inode *inode = d_backing_inode(dentry);
|
||||
struct inode_security_struct *isec = backing_inode_security(dentry);
|
||||
struct inode_security_struct *isec;
|
||||
struct superblock_security_struct *sbsec;
|
||||
struct common_audit_data ad;
|
||||
u32 newsid, sid = current_sid();
|
||||
@ -3065,6 +3078,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
|
||||
ad.type = LSM_AUDIT_DATA_DENTRY;
|
||||
ad.u.dentry = dentry;
|
||||
|
||||
isec = backing_inode_security(dentry);
|
||||
rc = avc_has_perm(sid, isec->sid, isec->sclass,
|
||||
FILE__RELABELFROM, &ad);
|
||||
if (rc)
|
||||
@ -3123,7 +3137,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
|
||||
int flags)
|
||||
{
|
||||
struct inode *inode = d_backing_inode(dentry);
|
||||
struct inode_security_struct *isec = backing_inode_security(dentry);
|
||||
struct inode_security_struct *isec;
|
||||
u32 newsid;
|
||||
int rc;
|
||||
|
||||
@ -3140,6 +3154,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
isec = backing_inode_security(dentry);
|
||||
isec->sclass = inode_mode_to_security_class(inode->i_mode);
|
||||
isec->sid = newsid;
|
||||
isec->initialized = LABEL_INITIALIZED;
|
||||
@ -3181,7 +3196,7 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void
|
||||
u32 size;
|
||||
int error;
|
||||
char *context = NULL;
|
||||
struct inode_security_struct *isec = inode_security(inode);
|
||||
struct inode_security_struct *isec;
|
||||
|
||||
if (strcmp(name, XATTR_SELINUX_SUFFIX))
|
||||
return -EOPNOTSUPP;
|
||||
@ -3199,7 +3214,8 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void
|
||||
SECURITY_CAP_NOAUDIT);
|
||||
if (!error)
|
||||
error = cred_has_capability(current_cred(), CAP_MAC_ADMIN,
|
||||
SECURITY_CAP_NOAUDIT);
|
||||
SECURITY_CAP_NOAUDIT, true);
|
||||
isec = inode_security(inode);
|
||||
if (!error)
|
||||
error = security_sid_to_context_force(isec->sid, &context,
|
||||
&size);
|
||||
@ -3220,7 +3236,7 @@ out_nofree:
|
||||
static int selinux_inode_setsecurity(struct inode *inode, const char *name,
|
||||
const void *value, size_t size, int flags)
|
||||
{
|
||||
struct inode_security_struct *isec = inode_security(inode);
|
||||
struct inode_security_struct *isec = inode_security_novalidate(inode);
|
||||
u32 newsid;
|
||||
int rc;
|
||||
|
||||
@ -3309,7 +3325,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
|
||||
struct common_audit_data ad;
|
||||
struct file_security_struct *fsec = file->f_security;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct inode_security_struct *isec = inode_security(inode);
|
||||
struct inode_security_struct *isec;
|
||||
struct lsm_ioctlop_audit ioctl;
|
||||
u32 ssid = cred_sid(cred);
|
||||
int rc;
|
||||
@ -3333,6 +3349,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
|
||||
if (unlikely(IS_PRIVATE(inode)))
|
||||
return 0;
|
||||
|
||||
isec = inode_security(inode);
|
||||
rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass,
|
||||
requested, driver, xperm, &ad);
|
||||
out:
|
||||
@ -3374,7 +3391,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
|
||||
case KDSKBENT:
|
||||
case KDSKBSENT:
|
||||
error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG,
|
||||
SECURITY_CAP_AUDIT);
|
||||
SECURITY_CAP_AUDIT, true);
|
||||
break;
|
||||
|
||||
/* default case assumes that the command will go
|
||||
@ -3463,8 +3480,9 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
|
||||
vma->vm_end <= vma->vm_mm->brk) {
|
||||
rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);
|
||||
} else if (!vma->vm_file &&
|
||||
vma->vm_start <= vma->vm_mm->start_stack &&
|
||||
vma->vm_end >= vma->vm_mm->start_stack) {
|
||||
((vma->vm_start <= vma->vm_mm->start_stack &&
|
||||
vma->vm_end >= vma->vm_mm->start_stack) ||
|
||||
vma_is_stack_for_task(vma, current))) {
|
||||
rc = current_has_perm(current, PROCESS__EXECSTACK);
|
||||
} else if (vma->vm_file && vma->anon_vma) {
|
||||
/*
|
||||
@ -3720,6 +3738,52 @@ static int selinux_kernel_module_request(char *kmod_name)
|
||||
SYSTEM__MODULE_REQUEST, &ad);
|
||||
}
|
||||
|
||||
static int selinux_kernel_module_from_file(struct file *file)
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
struct inode_security_struct *isec;
|
||||
struct file_security_struct *fsec;
|
||||
u32 sid = current_sid();
|
||||
int rc;
|
||||
|
||||
/* init_module */
|
||||
if (file == NULL)
|
||||
return avc_has_perm(sid, sid, SECCLASS_SYSTEM,
|
||||
SYSTEM__MODULE_LOAD, NULL);
|
||||
|
||||
/* finit_module */
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_PATH;
|
||||
ad.u.path = file->f_path;
|
||||
|
||||
fsec = file->f_security;
|
||||
if (sid != fsec->sid) {
|
||||
rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
isec = inode_security(file_inode(file));
|
||||
return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM,
|
||||
SYSTEM__MODULE_LOAD, &ad);
|
||||
}
|
||||
|
||||
static int selinux_kernel_read_file(struct file *file,
|
||||
enum kernel_read_file_id id)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (id) {
|
||||
case READING_MODULE:
|
||||
rc = selinux_kernel_module_from_file(file);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
|
||||
{
|
||||
return current_has_perm(p, PROCESS__SETPGID);
|
||||
@ -4599,6 +4663,7 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
|
||||
{
|
||||
u32 peer_secid = SECSID_NULL;
|
||||
u16 family;
|
||||
struct inode_security_struct *isec;
|
||||
|
||||
if (skb && skb->protocol == htons(ETH_P_IP))
|
||||
family = PF_INET;
|
||||
@ -4609,9 +4674,10 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
|
||||
else
|
||||
goto out;
|
||||
|
||||
if (sock && family == PF_UNIX)
|
||||
selinux_inode_getsecid(SOCK_INODE(sock), &peer_secid);
|
||||
else if (skb)
|
||||
if (sock && family == PF_UNIX) {
|
||||
isec = inode_security_novalidate(SOCK_INODE(sock));
|
||||
peer_secid = isec->sid;
|
||||
} else if (skb)
|
||||
selinux_skb_peerlbl_sid(skb, family, &peer_secid);
|
||||
|
||||
out:
|
||||
@ -5676,7 +5742,6 @@ static int selinux_setprocattr(struct task_struct *p,
|
||||
char *name, void *value, size_t size)
|
||||
{
|
||||
struct task_security_struct *tsec;
|
||||
struct task_struct *tracer;
|
||||
struct cred *new;
|
||||
u32 sid = 0, ptsid;
|
||||
int error;
|
||||
@ -5783,14 +5848,8 @@ static int selinux_setprocattr(struct task_struct *p,
|
||||
|
||||
/* Check for ptracing, and update the task SID if ok.
|
||||
Otherwise, leave SID unchanged and fail. */
|
||||
ptsid = 0;
|
||||
rcu_read_lock();
|
||||
tracer = ptrace_parent(p);
|
||||
if (tracer)
|
||||
ptsid = task_sid(tracer);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (tracer) {
|
||||
ptsid = ptrace_parent_sid(p);
|
||||
if (ptsid != 0) {
|
||||
error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
|
||||
PROCESS__PTRACE, NULL);
|
||||
if (error)
|
||||
@ -6021,6 +6080,7 @@ static struct security_hook_list selinux_hooks[] = {
|
||||
LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as),
|
||||
LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as),
|
||||
LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request),
|
||||
LSM_HOOK_INIT(kernel_read_file, selinux_kernel_read_file),
|
||||
LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid),
|
||||
LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid),
|
||||
LSM_HOOK_INIT(task_getsid, selinux_task_getsid),
|
||||
|
@ -12,6 +12,18 @@
|
||||
#define COMMON_IPC_PERMS "create", "destroy", "getattr", "setattr", "read", \
|
||||
"write", "associate", "unix_read", "unix_write"
|
||||
|
||||
#define COMMON_CAP_PERMS "chown", "dac_override", "dac_read_search", \
|
||||
"fowner", "fsetid", "kill", "setgid", "setuid", "setpcap", \
|
||||
"linux_immutable", "net_bind_service", "net_broadcast", \
|
||||
"net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module", \
|
||||
"sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin", \
|
||||
"sys_boot", "sys_nice", "sys_resource", "sys_time", \
|
||||
"sys_tty_config", "mknod", "lease", "audit_write", \
|
||||
"audit_control", "setfcap"
|
||||
|
||||
#define COMMON_CAP2_PERMS "mac_override", "mac_admin", "syslog", \
|
||||
"wake_alarm", "block_suspend", "audit_read"
|
||||
|
||||
/*
|
||||
* Note: The name for any socket class should be suffixed by "socket",
|
||||
* and doesn't contain more than one substr of "socket".
|
||||
@ -32,16 +44,9 @@ struct security_class_mapping secclass_map[] = {
|
||||
"setsockcreate", NULL } },
|
||||
{ "system",
|
||||
{ "ipc_info", "syslog_read", "syslog_mod",
|
||||
"syslog_console", "module_request", NULL } },
|
||||
"syslog_console", "module_request", "module_load", NULL } },
|
||||
{ "capability",
|
||||
{ "chown", "dac_override", "dac_read_search",
|
||||
"fowner", "fsetid", "kill", "setgid", "setuid", "setpcap",
|
||||
"linux_immutable", "net_bind_service", "net_broadcast",
|
||||
"net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module",
|
||||
"sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin",
|
||||
"sys_boot", "sys_nice", "sys_resource", "sys_time",
|
||||
"sys_tty_config", "mknod", "lease", "audit_write",
|
||||
"audit_control", "setfcap", NULL } },
|
||||
{ COMMON_CAP_PERMS, NULL } },
|
||||
{ "filesystem",
|
||||
{ "mount", "remount", "unmount", "getattr",
|
||||
"relabelfrom", "relabelto", "associate", "quotamod",
|
||||
@ -150,12 +155,15 @@ struct security_class_mapping secclass_map[] = {
|
||||
{ "memprotect", { "mmap_zero", NULL } },
|
||||
{ "peer", { "recv", NULL } },
|
||||
{ "capability2",
|
||||
{ "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend",
|
||||
"audit_read", NULL } },
|
||||
{ COMMON_CAP2_PERMS, NULL } },
|
||||
{ "kernel_service", { "use_as_override", "create_files_as", NULL } },
|
||||
{ "tun_socket",
|
||||
{ COMMON_SOCK_PERMS, "attach_queue", NULL } },
|
||||
{ "binder", { "impersonate", "call", "set_context_mgr", "transfer",
|
||||
NULL } },
|
||||
{ "cap_userns",
|
||||
{ COMMON_CAP_PERMS, NULL } },
|
||||
{ "cap2_userns",
|
||||
{ COMMON_CAP2_PERMS, NULL } },
|
||||
{ NULL }
|
||||
};
|
||||
|
@ -17,6 +17,6 @@ int security_get_bools(int *len, char ***names, int **values);
|
||||
|
||||
int security_set_bools(int len, int *values);
|
||||
|
||||
int security_get_bool_value(int bool);
|
||||
int security_get_bool_value(int index);
|
||||
|
||||
#endif
|
||||
|
@ -38,9 +38,8 @@ struct task_security_struct {
|
||||
};
|
||||
|
||||
enum label_initialized {
|
||||
LABEL_MISSING, /* not initialized */
|
||||
LABEL_INITIALIZED, /* inizialized */
|
||||
LABEL_INVALID /* invalid */
|
||||
LABEL_INVALID, /* invalid or not initialized */
|
||||
LABEL_INITIALIZED /* initialized */
|
||||
};
|
||||
|
||||
struct inode_security_struct {
|
||||
|
@ -2696,7 +2696,7 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int security_get_bool_value(int bool)
|
||||
int security_get_bool_value(int index)
|
||||
{
|
||||
int rc;
|
||||
int len;
|
||||
@ -2705,10 +2705,10 @@ int security_get_bool_value(int bool)
|
||||
|
||||
rc = -EFAULT;
|
||||
len = policydb.p_bools.nprim;
|
||||
if (bool >= len)
|
||||
if (index >= len)
|
||||
goto out;
|
||||
|
||||
rc = policydb.bool_val_to_struct[bool]->state;
|
||||
rc = policydb.bool_val_to_struct[index]->state;
|
||||
out:
|
||||
read_unlock(&policy_rwlock);
|
||||
return rc;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/prctl.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/string_helpers.h>
|
||||
|
||||
#define YAMA_SCOPE_DISABLED 0
|
||||
#define YAMA_SCOPE_RELATIONAL 1
|
||||
@ -41,6 +42,22 @@ static DEFINE_SPINLOCK(ptracer_relations_lock);
|
||||
static void yama_relation_cleanup(struct work_struct *work);
|
||||
static DECLARE_WORK(yama_relation_work, yama_relation_cleanup);
|
||||
|
||||
static void report_access(const char *access, struct task_struct *target,
|
||||
struct task_struct *agent)
|
||||
{
|
||||
char *target_cmd, *agent_cmd;
|
||||
|
||||
target_cmd = kstrdup_quotable_cmdline(target, GFP_ATOMIC);
|
||||
agent_cmd = kstrdup_quotable_cmdline(agent, GFP_ATOMIC);
|
||||
|
||||
pr_notice_ratelimited(
|
||||
"ptrace %s of \"%s\"[%d] was attempted by \"%s\"[%d]\n",
|
||||
access, target_cmd, target->pid, agent_cmd, agent->pid);
|
||||
|
||||
kfree(agent_cmd);
|
||||
kfree(target_cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* yama_relation_cleanup - remove invalid entries from the relation list
|
||||
*
|
||||
@ -307,11 +324,8 @@ static int yama_ptrace_access_check(struct task_struct *child,
|
||||
}
|
||||
}
|
||||
|
||||
if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0) {
|
||||
printk_ratelimited(KERN_NOTICE
|
||||
"ptrace of pid %d was attempted by: %s (pid %d)\n",
|
||||
child->pid, current->comm, current->pid);
|
||||
}
|
||||
if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0)
|
||||
report_access("attach", child, current);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -337,11 +351,8 @@ int yama_ptrace_traceme(struct task_struct *parent)
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
printk_ratelimited(KERN_NOTICE
|
||||
"ptraceme of pid %d was attempted by: %s (pid %d)\n",
|
||||
current->pid, parent->comm, parent->pid);
|
||||
}
|
||||
if (rc)
|
||||
report_access("traceme", current, parent);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user