Merge tag 'keys-next-20170412' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs into next
This commit is contained in:
commit
30a83251dd
@ -311,3 +311,54 @@ Functions are provided to register and unregister parsers:
|
||||
|
||||
Parsers may not have the same name. The names are otherwise only used for
|
||||
displaying in debugging messages.
|
||||
|
||||
|
||||
=========================
|
||||
KEYRING LINK RESTRICTIONS
|
||||
=========================
|
||||
|
||||
Keyrings created from userspace using add_key can be configured to check the
|
||||
signature of the key being linked.
|
||||
|
||||
Several restriction methods are available:
|
||||
|
||||
(1) Restrict using the kernel builtin trusted keyring
|
||||
|
||||
- Option string used with KEYCTL_RESTRICT_KEYRING:
|
||||
- "builtin_trusted"
|
||||
|
||||
The kernel builtin trusted keyring will be searched for the signing
|
||||
key. The ca_keys kernel parameter also affects which keys are used for
|
||||
signature verification.
|
||||
|
||||
(2) Restrict using the kernel builtin and secondary trusted keyrings
|
||||
|
||||
- Option string used with KEYCTL_RESTRICT_KEYRING:
|
||||
- "builtin_and_secondary_trusted"
|
||||
|
||||
The kernel builtin and secondary trusted keyrings will be searched for the
|
||||
signing key. The ca_keys kernel parameter also affects which keys are used
|
||||
for signature verification.
|
||||
|
||||
(3) Restrict using a separate key or keyring
|
||||
|
||||
- Option string used with KEYCTL_RESTRICT_KEYRING:
|
||||
- "key_or_keyring:<key or keyring serial number>[:chain]"
|
||||
|
||||
Whenever a key link is requested, the link will only succeed if the key
|
||||
being linked is signed by one of the designated keys. This key may be
|
||||
specified directly by providing a serial number for one asymmetric key, or
|
||||
a group of keys may be searched for the signing key by providing the
|
||||
serial number for a keyring.
|
||||
|
||||
When the "chain" option is provided at the end of the string, the keys
|
||||
within the destination keyring will also be searched for signing keys.
|
||||
This allows for verification of certificate chains by adding each
|
||||
cert in order (starting closest to the root) to one keyring.
|
||||
|
||||
In all of these cases, if the signing key is found the signature of the key to
|
||||
be linked will be verified using the signing key. The requested key is added
|
||||
to the keyring only if the signature is successfully verified. -ENOKEY is
|
||||
returned if the parent certificate could not be found, or -EKEYREJECTED is
|
||||
returned if the signature check fails or the key is blacklisted. Other errors
|
||||
may be returned if the signature check could not be performed.
|
||||
|
@ -827,7 +827,7 @@ The keyctl syscall functions are:
|
||||
|
||||
long keyctl(KEYCTL_DH_COMPUTE, struct keyctl_dh_params *params,
|
||||
char *buffer, size_t buflen,
|
||||
void *reserved);
|
||||
struct keyctl_kdf_params *kdf);
|
||||
|
||||
The params struct contains serial numbers for three keys:
|
||||
|
||||
@ -844,18 +844,61 @@ The keyctl syscall functions are:
|
||||
public key. If the base is the remote public key, the result is
|
||||
the shared secret.
|
||||
|
||||
The reserved argument must be set to NULL.
|
||||
If the parameter kdf is NULL, the following applies:
|
||||
|
||||
The buffer length must be at least the length of the prime, or zero.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
The kdf parameter allows the caller to apply a key derivation function
|
||||
(KDF) on the Diffie-Hellman computation where only the result
|
||||
of the KDF is returned to the caller. The KDF is characterized with
|
||||
struct keyctl_kdf_params as follows:
|
||||
|
||||
- char *hashname specifies the NUL terminated string identifying
|
||||
the hash used from the kernel crypto API and applied for the KDF
|
||||
operation. The KDF implemenation complies with SP800-56A as well
|
||||
as with SP800-108 (the counter KDF).
|
||||
|
||||
- char *otherinfo specifies the OtherInfo data as documented in
|
||||
SP800-56A section 5.8.1.2. The length of the buffer is given with
|
||||
otherinfolen. The format of OtherInfo is defined by the caller.
|
||||
The otherinfo pointer may be NULL if no OtherInfo shall be used.
|
||||
|
||||
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.
|
||||
EACCES if the key is not readable by the caller. In addition, the
|
||||
function will return EMSGSIZE when the parameter kdf is non-NULL
|
||||
and either the buffer length or the OtherInfo length exceeds the
|
||||
allowed length.
|
||||
|
||||
(*) Restrict keyring linkage
|
||||
|
||||
long keyctl(KEYCTL_RESTRICT_KEYRING, key_serial_t keyring,
|
||||
const char *type, const char *restriction);
|
||||
|
||||
An existing keyring can restrict linkage of additional keys by evaluating
|
||||
the contents of the key according to a restriction scheme.
|
||||
|
||||
"keyring" is the key ID for an existing keyring to apply a restriction
|
||||
to. It may be empty or may already have keys linked. Existing linked keys
|
||||
will remain in the keyring even if the new restriction would reject them.
|
||||
|
||||
"type" is a registered key type.
|
||||
|
||||
"restriction" is a string describing how key linkage is to be restricted.
|
||||
The format varies depending on the key type, and the string is passed to
|
||||
the lookup_restriction() function for the requested type. It may specify
|
||||
a method and relevant data for the restriction such as signature
|
||||
verification or constraints on key payload. If the requested key type is
|
||||
later unregistered, no keys may be added to the keyring after the key type
|
||||
is removed.
|
||||
|
||||
To apply a keyring restriction the process must have Set Attribute
|
||||
permission and the keyring must not be previously restricted.
|
||||
|
||||
===============
|
||||
KERNEL SERVICES
|
||||
@ -1032,10 +1075,7 @@ 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 *),
|
||||
struct key_restriction *restrict_link,
|
||||
unsigned long flags,
|
||||
struct key *dest);
|
||||
|
||||
@ -1047,20 +1087,23 @@ 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.
|
||||
If restrict_link is not NULL, it should point to a structure that contains
|
||||
the function that will be called each time an attempt is made to link a
|
||||
key into the new keyring. The structure may also contain a key pointer
|
||||
and an associated key type. The function is called to check whether a key
|
||||
may be added into the keyring or not. The key type is used by the garbage
|
||||
collector to clean up function or data pointers in this structure if the
|
||||
given key type is unregistered. 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.
|
||||
added to, the key type, the payload of the key being added, and data to be
|
||||
used in the restriction check. 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.
|
||||
@ -1445,6 +1488,15 @@ The structure has a number of fields, some of which are mandatory:
|
||||
The authorisation key.
|
||||
|
||||
|
||||
(*) struct key_restriction *(*lookup_restriction)(const char *params);
|
||||
|
||||
This optional method is used to enable userspace configuration of keyring
|
||||
restrictions. The restriction parameter string (not including the key type
|
||||
name) is passed in, and this method returns a pointer to a key_restriction
|
||||
structure containing the relevant functions and data to evaluate each
|
||||
attempted key link operation. If there is no match, -EINVAL is returned.
|
||||
|
||||
|
||||
============================
|
||||
REQUEST-KEY CALLBACK SERVICE
|
||||
============================
|
||||
|
@ -64,4 +64,22 @@ config SECONDARY_TRUSTED_KEYRING
|
||||
those keys are not blacklisted and are vouched for by a key built
|
||||
into the kernel or already in the secondary trusted keyring.
|
||||
|
||||
config SYSTEM_BLACKLIST_KEYRING
|
||||
bool "Provide system-wide ring of blacklisted keys"
|
||||
depends on KEYS
|
||||
help
|
||||
Provide a system keyring to which blacklisted keys can be added.
|
||||
Keys in the keyring are considered entirely untrusted. Keys in this
|
||||
keyring are used by the module signature checking to reject loading
|
||||
of modules signed with a blacklisted key.
|
||||
|
||||
config SYSTEM_BLACKLIST_HASH_LIST
|
||||
string "Hashes to be preloaded into the system blacklist keyring"
|
||||
depends on SYSTEM_BLACKLIST_KEYRING
|
||||
help
|
||||
If set, this option should be the filename of a list of hashes in the
|
||||
form "<hash>", "<hash>", ... . This will be included into a C
|
||||
wrapper to incorporate the list into the kernel. Each <hash> should
|
||||
be a string of hex digits.
|
||||
|
||||
endmenu
|
||||
|
@ -3,6 +3,12 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o
|
||||
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist.o
|
||||
ifneq ($(CONFIG_SYSTEM_BLACKLIST_HASH_LIST),"")
|
||||
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_hashes.o
|
||||
else
|
||||
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_nohashes.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y)
|
||||
|
||||
|
174
certs/blacklist.c
Normal file
174
certs/blacklist.c
Normal file
@ -0,0 +1,174 @@
|
||||
/* System hash blacklist.
|
||||
*
|
||||
* Copyright (C) 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) "blacklist: "fmt
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include "blacklist.h"
|
||||
|
||||
static struct key *blacklist_keyring;
|
||||
|
||||
/*
|
||||
* The description must be a type prefix, a colon and then an even number of
|
||||
* hex digits. The hash is kept in the description.
|
||||
*/
|
||||
static int blacklist_vet_description(const char *desc)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
if (*desc == ':')
|
||||
return -EINVAL;
|
||||
for (; *desc; desc++)
|
||||
if (*desc == ':')
|
||||
goto found_colon;
|
||||
return -EINVAL;
|
||||
|
||||
found_colon:
|
||||
desc++;
|
||||
for (; *desc; desc++) {
|
||||
if (!isxdigit(*desc))
|
||||
return -EINVAL;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n == 0 || n & 1)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The hash to be blacklisted is expected to be in the description. There will
|
||||
* be no payload.
|
||||
*/
|
||||
static int blacklist_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
if (prep->datalen > 0)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void blacklist_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
}
|
||||
|
||||
static void blacklist_describe(const struct key *key, struct seq_file *m)
|
||||
{
|
||||
seq_puts(m, key->description);
|
||||
}
|
||||
|
||||
static struct key_type key_type_blacklist = {
|
||||
.name = "blacklist",
|
||||
.vet_description = blacklist_vet_description,
|
||||
.preparse = blacklist_preparse,
|
||||
.free_preparse = blacklist_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.describe = blacklist_describe,
|
||||
};
|
||||
|
||||
/**
|
||||
* mark_hash_blacklisted - Add a hash to the system blacklist
|
||||
* @hash - The hash as a hex string with a type prefix (eg. "tbs:23aa429783")
|
||||
*/
|
||||
int mark_hash_blacklisted(const char *hash)
|
||||
{
|
||||
key_ref_t key;
|
||||
|
||||
key = key_create_or_update(make_key_ref(blacklist_keyring, true),
|
||||
"blacklist",
|
||||
hash,
|
||||
NULL,
|
||||
0,
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW),
|
||||
KEY_ALLOC_NOT_IN_QUOTA |
|
||||
KEY_ALLOC_BUILT_IN);
|
||||
if (IS_ERR(key)) {
|
||||
pr_err("Problem blacklisting hash (%ld)\n", PTR_ERR(key));
|
||||
return PTR_ERR(key);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* is_hash_blacklisted - Determine if a hash is blacklisted
|
||||
* @hash: The hash to be checked as a binary blob
|
||||
* @hash_len: The length of the binary hash
|
||||
* @type: Type of hash
|
||||
*/
|
||||
int is_hash_blacklisted(const u8 *hash, size_t hash_len, const char *type)
|
||||
{
|
||||
key_ref_t kref;
|
||||
size_t type_len = strlen(type);
|
||||
char *buffer, *p;
|
||||
int ret = 0;
|
||||
|
||||
buffer = kmalloc(type_len + 1 + hash_len * 2 + 1, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
p = memcpy(buffer, type, type_len);
|
||||
p += type_len;
|
||||
*p++ = ':';
|
||||
bin2hex(p, hash, hash_len);
|
||||
p += hash_len * 2;
|
||||
*p = 0;
|
||||
|
||||
kref = keyring_search(make_key_ref(blacklist_keyring, true),
|
||||
&key_type_blacklist, buffer);
|
||||
if (!IS_ERR(kref)) {
|
||||
key_ref_put(kref);
|
||||
ret = -EKEYREJECTED;
|
||||
}
|
||||
|
||||
kfree(buffer);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(is_hash_blacklisted);
|
||||
|
||||
/*
|
||||
* Intialise the blacklist
|
||||
*/
|
||||
static int __init blacklist_init(void)
|
||||
{
|
||||
const char *const *bl;
|
||||
|
||||
if (register_key_type(&key_type_blacklist) < 0)
|
||||
panic("Can't allocate system blacklist key type\n");
|
||||
|
||||
blacklist_keyring =
|
||||
keyring_alloc(".blacklist",
|
||||
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 |
|
||||
KEY_FLAG_KEEP,
|
||||
NULL, NULL);
|
||||
if (IS_ERR(blacklist_keyring))
|
||||
panic("Can't allocate system blacklist keyring\n");
|
||||
|
||||
for (bl = blacklist_hashes; *bl; bl++)
|
||||
if (mark_hash_blacklisted(*bl) < 0)
|
||||
pr_err("- blacklisting failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be initialised before we try and load the keys into the keyring.
|
||||
*/
|
||||
device_initcall(blacklist_init);
|
3
certs/blacklist.h
Normal file
3
certs/blacklist.h
Normal file
@ -0,0 +1,3 @@
|
||||
#include <linux/kernel.h>
|
||||
|
||||
extern const char __initdata *const blacklist_hashes[];
|
6
certs/blacklist_hashes.c
Normal file
6
certs/blacklist_hashes.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include "blacklist.h"
|
||||
|
||||
const char __initdata *const blacklist_hashes[] = {
|
||||
#include CONFIG_SYSTEM_BLACKLIST_HASH_LIST
|
||||
, NULL
|
||||
};
|
5
certs/blacklist_nohashes.c
Normal file
5
certs/blacklist_nohashes.c
Normal file
@ -0,0 +1,5 @@
|
||||
#include "blacklist.h"
|
||||
|
||||
const char __initdata *const blacklist_hashes[] = {
|
||||
NULL
|
||||
};
|
@ -14,6 +14,7 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
@ -32,11 +33,13 @@ extern __initconst const unsigned long system_certificate_list_size;
|
||||
* 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,
|
||||
int restrict_link_by_builtin_trusted(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload)
|
||||
const union key_payload *payload,
|
||||
struct key *restriction_key)
|
||||
{
|
||||
return restrict_link_by_signature(builtin_trusted_keys, type, payload);
|
||||
return restrict_link_by_signature(dest_keyring, type, payload,
|
||||
builtin_trusted_keys);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
|
||||
@ -49,20 +52,40 @@ int restrict_link_by_builtin_trusted(struct key *keyring,
|
||||
* keyrings.
|
||||
*/
|
||||
int restrict_link_by_builtin_and_secondary_trusted(
|
||||
struct key *keyring,
|
||||
struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload)
|
||||
const union key_payload *payload,
|
||||
struct key *restrict_key)
|
||||
{
|
||||
/* 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 &&
|
||||
dest_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);
|
||||
return restrict_link_by_signature(dest_keyring, type, payload,
|
||||
secondary_trusted_keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a struct key_restriction for the "builtin and secondary trust"
|
||||
* keyring. Only for use in system_trusted_keyring_init().
|
||||
*/
|
||||
static __init struct key_restriction *get_builtin_and_secondary_restriction(void)
|
||||
{
|
||||
struct key_restriction *restriction;
|
||||
|
||||
restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
|
||||
|
||||
if (!restriction)
|
||||
panic("Can't allocate secondary trusted keyring restriction\n");
|
||||
|
||||
restriction->check = restrict_link_by_builtin_and_secondary_trusted;
|
||||
|
||||
return restriction;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -91,7 +114,7 @@ static __init int system_trusted_keyring_init(void)
|
||||
KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH |
|
||||
KEY_USR_WRITE),
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
restrict_link_by_builtin_and_secondary_trusted,
|
||||
get_builtin_and_secondary_restriction(),
|
||||
NULL);
|
||||
if (IS_ERR(secondary_trusted_keys))
|
||||
panic("Can't allocate secondary trusted keyring\n");
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include "asymmetric_keys.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
@ -451,15 +452,100 @@ static void asymmetric_key_destroy(struct key *key)
|
||||
asymmetric_key_free_kids(kids);
|
||||
}
|
||||
|
||||
static struct key_restriction *asymmetric_restriction_alloc(
|
||||
key_restrict_link_func_t check,
|
||||
struct key *key)
|
||||
{
|
||||
struct key_restriction *keyres =
|
||||
kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
|
||||
|
||||
if (!keyres)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
keyres->check = check;
|
||||
keyres->key = key;
|
||||
keyres->keytype = &key_type_asymmetric;
|
||||
|
||||
return keyres;
|
||||
}
|
||||
|
||||
/*
|
||||
* look up keyring restrict functions for asymmetric keys
|
||||
*/
|
||||
static struct key_restriction *asymmetric_lookup_restriction(
|
||||
const char *restriction)
|
||||
{
|
||||
char *restrict_method;
|
||||
char *parse_buf;
|
||||
char *next;
|
||||
struct key_restriction *ret = ERR_PTR(-EINVAL);
|
||||
|
||||
if (strcmp("builtin_trusted", restriction) == 0)
|
||||
return asymmetric_restriction_alloc(
|
||||
restrict_link_by_builtin_trusted, NULL);
|
||||
|
||||
if (strcmp("builtin_and_secondary_trusted", restriction) == 0)
|
||||
return asymmetric_restriction_alloc(
|
||||
restrict_link_by_builtin_and_secondary_trusted, NULL);
|
||||
|
||||
parse_buf = kstrndup(restriction, PAGE_SIZE, GFP_KERNEL);
|
||||
if (!parse_buf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
next = parse_buf;
|
||||
restrict_method = strsep(&next, ":");
|
||||
|
||||
if ((strcmp(restrict_method, "key_or_keyring") == 0) && next) {
|
||||
char *key_text;
|
||||
key_serial_t serial;
|
||||
struct key *key;
|
||||
key_restrict_link_func_t link_fn =
|
||||
restrict_link_by_key_or_keyring;
|
||||
bool allow_null_key = false;
|
||||
|
||||
key_text = strsep(&next, ":");
|
||||
|
||||
if (next) {
|
||||
if (strcmp(next, "chain") != 0)
|
||||
goto out;
|
||||
|
||||
link_fn = restrict_link_by_key_or_keyring_chain;
|
||||
allow_null_key = true;
|
||||
}
|
||||
|
||||
if (kstrtos32(key_text, 0, &serial) < 0)
|
||||
goto out;
|
||||
|
||||
if ((serial == 0) && allow_null_key) {
|
||||
key = NULL;
|
||||
} else {
|
||||
key = key_lookup(serial);
|
||||
if (IS_ERR(key)) {
|
||||
ret = ERR_CAST(key);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = asymmetric_restriction_alloc(link_fn, key);
|
||||
if (IS_ERR(ret))
|
||||
key_put(key);
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(parse_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct key_type key_type_asymmetric = {
|
||||
.name = "asymmetric",
|
||||
.preparse = asymmetric_key_preparse,
|
||||
.free_preparse = asymmetric_key_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match_preparse = asymmetric_key_match_preparse,
|
||||
.match_free = asymmetric_key_match_free,
|
||||
.destroy = asymmetric_key_destroy,
|
||||
.describe = asymmetric_key_describe,
|
||||
.name = "asymmetric",
|
||||
.preparse = asymmetric_key_preparse,
|
||||
.free_preparse = asymmetric_key_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match_preparse = asymmetric_key_match_preparse,
|
||||
.match_free = asymmetric_key_match_free,
|
||||
.destroy = asymmetric_key_destroy,
|
||||
.describe = asymmetric_key_describe,
|
||||
.lookup_restriction = asymmetric_lookup_restriction,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(key_type_asymmetric);
|
||||
|
||||
|
@ -23,6 +23,7 @@ struct pkcs7_signed_info {
|
||||
struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
|
||||
unsigned index;
|
||||
bool unsupported_crypto; /* T if not usable due to missing crypto */
|
||||
bool blacklisted;
|
||||
|
||||
/* Message digest - the digest of the Content Data (or NULL) */
|
||||
const void *msgdigest;
|
||||
|
@ -190,6 +190,18 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
|
||||
x509->subject,
|
||||
x509->raw_serial_size, x509->raw_serial);
|
||||
x509->seen = true;
|
||||
|
||||
if (x509->blacklisted) {
|
||||
/* If this cert is blacklisted, then mark everything
|
||||
* that depends on this as blacklisted too.
|
||||
*/
|
||||
sinfo->blacklisted = true;
|
||||
for (p = sinfo->signer; p != x509; p = p->signer)
|
||||
p->blacklisted = true;
|
||||
pr_debug("- blacklisted\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (x509->unsupported_key)
|
||||
goto unsupported_crypto_in_x509;
|
||||
|
||||
@ -357,17 +369,19 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
|
||||
*
|
||||
* (*) -EBADMSG if some part of the message was invalid, or:
|
||||
*
|
||||
* (*) -ENOPKG if none of the signature chains are verifiable because suitable
|
||||
* crypto modules couldn't be found, or:
|
||||
* (*) 0 if no signature chains were found to be blacklisted or to contain
|
||||
* unsupported crypto, or:
|
||||
*
|
||||
* (*) 0 if all the signature chains that don't incur -ENOPKG can be verified
|
||||
* (note that a signature chain may be of zero length), or:
|
||||
* (*) -EKEYREJECTED if a blacklisted key was encountered, or:
|
||||
*
|
||||
* (*) -ENOPKG if none of the signature chains are verifiable because suitable
|
||||
* crypto modules couldn't be found.
|
||||
*/
|
||||
int pkcs7_verify(struct pkcs7_message *pkcs7,
|
||||
enum key_being_used_for usage)
|
||||
{
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
int enopkg = -ENOPKG;
|
||||
int actual_ret = -ENOPKG;
|
||||
int ret;
|
||||
|
||||
kenter("");
|
||||
@ -412,6 +426,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
|
||||
|
||||
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
|
||||
ret = pkcs7_verify_one(pkcs7, sinfo);
|
||||
if (sinfo->blacklisted && actual_ret == -ENOPKG)
|
||||
actual_ret = -EKEYREJECTED;
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOPKG) {
|
||||
sinfo->unsupported_crypto = true;
|
||||
@ -420,11 +436,11 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
enopkg = 0;
|
||||
actual_ret = 0;
|
||||
}
|
||||
|
||||
kleave(" = %d", enopkg);
|
||||
return enopkg;
|
||||
kleave(" = %d", actual_ret);
|
||||
return actual_ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_verify);
|
||||
|
||||
|
@ -56,9 +56,10 @@ __setup("ca_keys=", ca_keys_setup);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @dest_keyring: Keyring being linked to.
|
||||
* @type: The type of key being added.
|
||||
* @payload: The payload of the new key.
|
||||
* @trust_keyring: A ring of keys that can be used to vouch for the new cert.
|
||||
*
|
||||
* 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
|
||||
@ -69,9 +70,10 @@ __setup("ca_keys=", ca_keys_setup);
|
||||
* 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,
|
||||
int restrict_link_by_signature(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload)
|
||||
const union key_payload *payload,
|
||||
struct key *trust_keyring)
|
||||
{
|
||||
const struct public_key_signature *sig;
|
||||
struct key *key;
|
||||
@ -106,3 +108,156 @@ int restrict_link_by_signature(struct key *trust_keyring,
|
||||
key_put(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool match_either_id(const struct asymmetric_key_ids *pair,
|
||||
const struct asymmetric_key_id *single)
|
||||
{
|
||||
return (asymmetric_key_id_same(pair->id[0], single) ||
|
||||
asymmetric_key_id_same(pair->id[1], single));
|
||||
}
|
||||
|
||||
static int key_or_keyring_common(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload,
|
||||
struct key *trusted, bool check_dest)
|
||||
{
|
||||
const struct public_key_signature *sig;
|
||||
struct key *key = NULL;
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
if (!dest_keyring)
|
||||
return -ENOKEY;
|
||||
else if (dest_keyring->type != &key_type_keyring)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!trusted && !check_dest)
|
||||
return -ENOKEY;
|
||||
|
||||
if (type != &key_type_asymmetric)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
sig = payload->data[asym_auth];
|
||||
if (!sig->auth_ids[0] && !sig->auth_ids[1])
|
||||
return -ENOKEY;
|
||||
|
||||
if (trusted) {
|
||||
if (trusted->type == &key_type_keyring) {
|
||||
/* See if we have a key that signed this one. */
|
||||
key = find_asymmetric_key(trusted, sig->auth_ids[0],
|
||||
sig->auth_ids[1], false);
|
||||
if (IS_ERR(key))
|
||||
key = NULL;
|
||||
} else if (trusted->type == &key_type_asymmetric) {
|
||||
const struct asymmetric_key_ids *signer_ids;
|
||||
|
||||
signer_ids = asymmetric_key_ids(trusted);
|
||||
|
||||
/*
|
||||
* The auth_ids come from the candidate key (the
|
||||
* one that is being considered for addition to
|
||||
* dest_keyring) and identify the key that was
|
||||
* used to sign.
|
||||
*
|
||||
* The signer_ids are identifiers for the
|
||||
* signing key specified for dest_keyring.
|
||||
*
|
||||
* The first auth_id is the preferred id, and
|
||||
* the second is the fallback. If only one
|
||||
* auth_id is present, it may match against
|
||||
* either signer_id. If two auth_ids are
|
||||
* present, the first auth_id must match one
|
||||
* signer_id and the second auth_id must match
|
||||
* the second signer_id.
|
||||
*/
|
||||
if (!sig->auth_ids[0] || !sig->auth_ids[1]) {
|
||||
const struct asymmetric_key_id *auth_id;
|
||||
|
||||
auth_id = sig->auth_ids[0] ?: sig->auth_ids[1];
|
||||
if (match_either_id(signer_ids, auth_id))
|
||||
key = __key_get(trusted);
|
||||
|
||||
} else if (asymmetric_key_id_same(signer_ids->id[1],
|
||||
sig->auth_ids[1]) &&
|
||||
match_either_id(signer_ids,
|
||||
sig->auth_ids[0])) {
|
||||
key = __key_get(trusted);
|
||||
}
|
||||
} else {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
if (check_dest && !key) {
|
||||
/* See if the destination has a key that signed this one. */
|
||||
key = find_asymmetric_key(dest_keyring, sig->auth_ids[0],
|
||||
sig->auth_ids[1], false);
|
||||
if (IS_ERR(key))
|
||||
key = NULL;
|
||||
}
|
||||
|
||||
if (!key)
|
||||
return -ENOKEY;
|
||||
|
||||
ret = key_validate(key);
|
||||
if (ret == 0)
|
||||
ret = verify_signature(key, sig);
|
||||
|
||||
key_put(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* restrict_link_by_key_or_keyring - Restrict additions to a ring of public
|
||||
* keys using the restrict_key information stored in the ring.
|
||||
* @dest_keyring: Keyring being linked to.
|
||||
* @type: The type of key being added.
|
||||
* @payload: The payload of the new key.
|
||||
* @trusted: A key or ring of keys that can be used to vouch for the new cert.
|
||||
*
|
||||
* Check the new certificate only against the key or keys passed in the data
|
||||
* parameter. If one of those is the signing key and validates the new
|
||||
* certificate, then mark the new certificate as being ok to link.
|
||||
*
|
||||
* 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, and some other error if
|
||||
* there is a matching certificate but the signature check cannot be
|
||||
* performed.
|
||||
*/
|
||||
int restrict_link_by_key_or_keyring(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload,
|
||||
struct key *trusted)
|
||||
{
|
||||
return key_or_keyring_common(dest_keyring, type, payload, trusted,
|
||||
false);
|
||||
}
|
||||
|
||||
/**
|
||||
* restrict_link_by_key_or_keyring_chain - Restrict additions to a ring of
|
||||
* public keys using the restrict_key information stored in the ring.
|
||||
* @dest_keyring: Keyring being linked to.
|
||||
* @type: The type of key being added.
|
||||
* @payload: The payload of the new key.
|
||||
* @trusted: A key or ring of keys that can be used to vouch for the new cert.
|
||||
*
|
||||
* Check the new certificate only against the key or keys passed in the data
|
||||
* parameter. If one of those is the signing key and validates the new
|
||||
* certificate, then mark the new certificate as being ok to link.
|
||||
*
|
||||
* 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, and some other error if
|
||||
* there is a matching certificate but the signature check cannot be
|
||||
* performed.
|
||||
*/
|
||||
int restrict_link_by_key_or_keyring_chain(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload,
|
||||
struct key *trusted)
|
||||
{
|
||||
return key_or_keyring_common(dest_keyring, type, payload, trusted,
|
||||
true);
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ struct x509_certificate {
|
||||
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 */
|
||||
bool blacklisted;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -84,6 +84,16 @@ int x509_get_sig_params(struct x509_certificate *cert)
|
||||
goto error_2;
|
||||
might_sleep();
|
||||
ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest);
|
||||
if (ret < 0)
|
||||
goto error_2;
|
||||
|
||||
ret = is_hash_blacklisted(sig->digest, sig->digest_size, "tbs");
|
||||
if (ret == -EKEYREJECTED) {
|
||||
pr_err("Cert %*phN is blacklisted\n",
|
||||
sig->digest_size, sig->digest);
|
||||
cert->blacklisted = true;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
error_2:
|
||||
kfree(desc);
|
||||
@ -186,6 +196,11 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
|
||||
cert->sig->pkey_algo, cert->sig->hash_algo);
|
||||
}
|
||||
|
||||
/* Don't permit addition of blacklisted keys */
|
||||
ret = -EKEYREJECTED;
|
||||
if (cert->blacklisted)
|
||||
goto error_free_cert;
|
||||
|
||||
/* Propose a description */
|
||||
sulen = strlen(cert->subject);
|
||||
if (cert->raw_skid) {
|
||||
|
@ -50,9 +50,20 @@ struct key;
|
||||
struct key_type;
|
||||
union key_payload;
|
||||
|
||||
extern int restrict_link_by_signature(struct key *trust_keyring,
|
||||
extern int restrict_link_by_signature(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload);
|
||||
const union key_payload *payload,
|
||||
struct key *trust_keyring);
|
||||
|
||||
extern int restrict_link_by_key_or_keyring(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload,
|
||||
struct key *trusted);
|
||||
|
||||
extern int restrict_link_by_key_or_keyring_chain(struct key *trust_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload,
|
||||
struct key *trusted);
|
||||
|
||||
extern int verify_signature(const struct key *key,
|
||||
const struct public_key_signature *sig);
|
||||
|
@ -18,7 +18,8 @@
|
||||
|
||||
extern int restrict_link_by_builtin_trusted(struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload);
|
||||
const union key_payload *payload,
|
||||
struct key *restriction_key);
|
||||
|
||||
#else
|
||||
#define restrict_link_by_builtin_trusted restrict_link_reject
|
||||
@ -28,11 +29,24 @@ extern int restrict_link_by_builtin_trusted(struct key *keyring,
|
||||
extern int restrict_link_by_builtin_and_secondary_trusted(
|
||||
struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload);
|
||||
const union key_payload *payload,
|
||||
struct key *restriction_key);
|
||||
#else
|
||||
#define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING
|
||||
extern int mark_hash_blacklisted(const char *hash);
|
||||
extern int is_hash_blacklisted(const u8 *hash, size_t hash_len,
|
||||
const char *type);
|
||||
#else
|
||||
static inline int is_hash_blacklisted(const u8 *hash, size_t hash_len,
|
||||
const char *type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IMA_BLACKLIST_KEYRING
|
||||
extern struct key *ima_blacklist_keyring;
|
||||
|
||||
|
@ -295,6 +295,13 @@ struct compat_old_sigaction {
|
||||
};
|
||||
#endif
|
||||
|
||||
struct compat_keyctl_kdf_params {
|
||||
compat_uptr_t hashname;
|
||||
compat_uptr_t otherinfo;
|
||||
__u32 otherinfolen;
|
||||
__u32 __spare[8];
|
||||
};
|
||||
|
||||
struct compat_statfs;
|
||||
struct compat_statfs64;
|
||||
struct compat_old_linux_dirent;
|
||||
|
@ -147,6 +147,14 @@ struct key_type {
|
||||
*/
|
||||
request_key_actor_t request_key;
|
||||
|
||||
/* Look up a keyring access restriction (optional)
|
||||
*
|
||||
* - NULL is a valid return value (meaning the requested restriction
|
||||
* is known but will never block addition of a key)
|
||||
* - should return -EINVAL if the restriction is unknown
|
||||
*/
|
||||
struct key_restriction *(*lookup_restriction)(const char *params);
|
||||
|
||||
/* internal fields */
|
||||
struct list_head link; /* link in types list */
|
||||
struct lock_class_key lock_class; /* key->sem lock class */
|
||||
|
@ -127,6 +127,17 @@ static inline bool is_key_possessed(const key_ref_t key_ref)
|
||||
return (unsigned long) key_ref & 1UL;
|
||||
}
|
||||
|
||||
typedef int (*key_restrict_link_func_t)(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload,
|
||||
struct key *restriction_key);
|
||||
|
||||
struct key_restriction {
|
||||
key_restrict_link_func_t check;
|
||||
struct key *key;
|
||||
struct key_type *keytype;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* authentication token / access credential / keyring
|
||||
@ -206,18 +217,17 @@ struct key {
|
||||
};
|
||||
|
||||
/* 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
|
||||
* to it. If this structure isn't provided then it is assumed that the
|
||||
* keyring is open to any addition. It is ignored for non-keyring
|
||||
* keys.
|
||||
* keys. Only set this value using keyring_restrict(), keyring_alloc(),
|
||||
* or key_alloc().
|
||||
*
|
||||
* 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);
|
||||
struct key_restriction *restrict_link;
|
||||
};
|
||||
|
||||
extern struct key *key_alloc(struct key_type *type,
|
||||
@ -226,9 +236,7 @@ extern struct key *key_alloc(struct key_type *type,
|
||||
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_restriction *restrict_link);
|
||||
|
||||
|
||||
#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */
|
||||
@ -304,14 +312,13 @@ 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_restriction *restrict_link,
|
||||
struct key *dest);
|
||||
|
||||
extern int restrict_link_reject(struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload);
|
||||
const union key_payload *payload,
|
||||
struct key *restriction_key);
|
||||
|
||||
extern int keyring_clear(struct key *keyring);
|
||||
|
||||
@ -322,6 +329,9 @@ extern key_ref_t keyring_search(key_ref_t keyring,
|
||||
extern int keyring_add_key(struct key *keyring,
|
||||
struct key *key);
|
||||
|
||||
extern int keyring_restrict(key_ref_t keyring, const char *type,
|
||||
const char *restriction);
|
||||
|
||||
extern struct key *key_lookup(key_serial_t id);
|
||||
|
||||
static inline key_serial_t key_serial(const struct key *key)
|
||||
|
@ -60,6 +60,7 @@
|
||||
#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 */
|
||||
#define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */
|
||||
|
||||
/* keyctl structures */
|
||||
struct keyctl_dh_params {
|
||||
@ -68,4 +69,11 @@ struct keyctl_dh_params {
|
||||
__s32 base;
|
||||
};
|
||||
|
||||
struct keyctl_kdf_params {
|
||||
char *hashname;
|
||||
char *otherinfo;
|
||||
__u32 otherinfolen;
|
||||
__u32 __spare[8];
|
||||
};
|
||||
|
||||
#endif /* _LINUX_KEYCTL_H */
|
||||
|
@ -81,18 +81,25 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
int __init integrity_init_keyring(const unsigned int id)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
struct key_restriction *restriction;
|
||||
int err = 0;
|
||||
|
||||
if (!init_keyring)
|
||||
return 0;
|
||||
|
||||
restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
|
||||
if (!restriction)
|
||||
return -ENOMEM;
|
||||
|
||||
restriction->check = restrict_link_to_ima;
|
||||
|
||||
keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
|
||||
KGIDT_INIT(0), cred,
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ |
|
||||
KEY_USR_WRITE | KEY_USR_SEARCH),
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
restrict_link_to_ima, NULL);
|
||||
restriction, NULL);
|
||||
if (IS_ERR(keyring[id])) {
|
||||
err = PTR_ERR(keyring[id]);
|
||||
pr_info("Can't allocate %s keyring (%d)\n",
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/cred.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <keys/system_keyring.h>
|
||||
|
||||
|
||||
@ -27,15 +28,23 @@ struct key *ima_blacklist_keyring;
|
||||
*/
|
||||
__init int ima_mok_init(void)
|
||||
{
|
||||
struct key_restriction *restriction;
|
||||
|
||||
pr_notice("Allocating IMA blacklist keyring.\n");
|
||||
|
||||
restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
|
||||
if (!restriction)
|
||||
panic("Can't allocate IMA blacklist restriction.");
|
||||
|
||||
restriction->check = restrict_link_by_builtin_trusted;
|
||||
|
||||
ima_blacklist_keyring = keyring_alloc(".ima_blacklist",
|
||||
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
|
||||
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ |
|
||||
KEY_USR_WRITE | KEY_USR_SEARCH,
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
restrict_link_by_builtin_trusted, NULL);
|
||||
restriction, NULL);
|
||||
|
||||
if (IS_ERR(ima_blacklist_keyring))
|
||||
panic("Can't allocate IMA blacklist keyring.");
|
||||
|
@ -90,6 +90,8 @@ config KEY_DH_OPERATIONS
|
||||
bool "Diffie-Hellman operations on retained keys"
|
||||
depends on KEYS
|
||||
select MPILIB
|
||||
select CRYPTO
|
||||
select CRYPTO_HASH
|
||||
help
|
||||
This option provides support for calculating Diffie-Hellman
|
||||
public keys and shared secrets using values stored as keys
|
||||
|
@ -15,7 +15,8 @@ obj-y := \
|
||||
request_key.o \
|
||||
request_key_auth.o \
|
||||
user_defined.o
|
||||
obj-$(CONFIG_KEYS_COMPAT) += compat.o
|
||||
compat-obj-$(CONFIG_KEY_DH_OPERATIONS) += compat_dh.o
|
||||
obj-$(CONFIG_KEYS_COMPAT) += compat.o $(compat-obj-y)
|
||||
obj-$(CONFIG_PROC_FS) += proc.o
|
||||
obj-$(CONFIG_SYSCTL) += sysctl.o
|
||||
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
|
||||
|
@ -133,8 +133,13 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
|
||||
return keyctl_get_persistent(arg2, arg3);
|
||||
|
||||
case KEYCTL_DH_COMPUTE:
|
||||
return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3),
|
||||
arg4, compat_ptr(arg5));
|
||||
return compat_keyctl_dh_compute(compat_ptr(arg2),
|
||||
compat_ptr(arg3),
|
||||
arg4, compat_ptr(arg5));
|
||||
|
||||
case KEYCTL_RESTRICT_KEYRING:
|
||||
return keyctl_restrict_keyring(arg2, compat_ptr(arg3),
|
||||
compat_ptr(arg4));
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
|
38
security/keys/compat_dh.c
Normal file
38
security/keys/compat_dh.c
Normal file
@ -0,0 +1,38 @@
|
||||
/* 32-bit compatibility syscall for 64-bit systems for DH operations
|
||||
*
|
||||
* Copyright (C) 2016 Stephan Mueller <smueller@chronox.de>
|
||||
*
|
||||
* 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/uaccess.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* Perform the DH computation or DH based key derivation.
|
||||
*
|
||||
* If successful, 0 will be returned.
|
||||
*/
|
||||
long compat_keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct compat_keyctl_kdf_params __user *kdf)
|
||||
{
|
||||
struct keyctl_kdf_params kdfcopy;
|
||||
struct compat_keyctl_kdf_params compat_kdfcopy;
|
||||
|
||||
if (!kdf)
|
||||
return __keyctl_dh_compute(params, buffer, buflen, NULL);
|
||||
|
||||
if (copy_from_user(&compat_kdfcopy, kdf, sizeof(compat_kdfcopy)) != 0)
|
||||
return -EFAULT;
|
||||
|
||||
kdfcopy.hashname = compat_ptr(compat_kdfcopy.hashname);
|
||||
kdfcopy.otherinfo = compat_ptr(compat_kdfcopy.otherinfo);
|
||||
kdfcopy.otherinfolen = compat_kdfcopy.otherinfolen;
|
||||
|
||||
return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy);
|
||||
}
|
@ -11,6 +11,8 @@
|
||||
#include <linux/mpi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <keys/user-type.h>
|
||||
#include "internal.h"
|
||||
|
||||
@ -77,9 +79,146 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
void __user *reserved)
|
||||
struct kdf_sdesc {
|
||||
struct shash_desc shash;
|
||||
char ctx[];
|
||||
};
|
||||
|
||||
static int kdf_alloc(struct kdf_sdesc **sdesc_ret, char *hashname)
|
||||
{
|
||||
struct crypto_shash *tfm;
|
||||
struct kdf_sdesc *sdesc;
|
||||
int size;
|
||||
|
||||
/* allocate synchronous hash */
|
||||
tfm = crypto_alloc_shash(hashname, 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
pr_info("could not allocate digest TFM handle %s\n", hashname);
|
||||
return PTR_ERR(tfm);
|
||||
}
|
||||
|
||||
size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm);
|
||||
sdesc = kmalloc(size, GFP_KERNEL);
|
||||
if (!sdesc)
|
||||
return -ENOMEM;
|
||||
sdesc->shash.tfm = tfm;
|
||||
sdesc->shash.flags = 0x0;
|
||||
|
||||
*sdesc_ret = sdesc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kdf_dealloc(struct kdf_sdesc *sdesc)
|
||||
{
|
||||
if (!sdesc)
|
||||
return;
|
||||
|
||||
if (sdesc->shash.tfm)
|
||||
crypto_free_shash(sdesc->shash.tfm);
|
||||
|
||||
kzfree(sdesc);
|
||||
}
|
||||
|
||||
/* convert 32 bit integer into its string representation */
|
||||
static inline void crypto_kw_cpu_to_be32(u32 val, u8 *buf)
|
||||
{
|
||||
__be32 *a = (__be32 *)buf;
|
||||
|
||||
*a = cpu_to_be32(val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of the KDF in counter mode according to SP800-108 section 5.1
|
||||
* as well as SP800-56A section 5.8.1 (Single-step KDF).
|
||||
*
|
||||
* SP800-56A:
|
||||
* The src pointer is defined as Z || other info where Z is the shared secret
|
||||
* from DH and other info is an arbitrary string (see SP800-56A section
|
||||
* 5.8.1.2).
|
||||
*/
|
||||
static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen,
|
||||
u8 *dst, unsigned int dlen)
|
||||
{
|
||||
struct shash_desc *desc = &sdesc->shash;
|
||||
unsigned int h = crypto_shash_digestsize(desc->tfm);
|
||||
int err = 0;
|
||||
u8 *dst_orig = dst;
|
||||
u32 i = 1;
|
||||
u8 iteration[sizeof(u32)];
|
||||
|
||||
while (dlen) {
|
||||
err = crypto_shash_init(desc);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
crypto_kw_cpu_to_be32(i, iteration);
|
||||
err = crypto_shash_update(desc, iteration, sizeof(u32));
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
if (src && slen) {
|
||||
err = crypto_shash_update(desc, src, slen);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (dlen < h) {
|
||||
u8 tmpbuffer[h];
|
||||
|
||||
err = crypto_shash_final(desc, tmpbuffer);
|
||||
if (err)
|
||||
goto err;
|
||||
memcpy(dst, tmpbuffer, dlen);
|
||||
memzero_explicit(tmpbuffer, h);
|
||||
return 0;
|
||||
} else {
|
||||
err = crypto_shash_final(desc, dst);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
dlen -= h;
|
||||
dst += h;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
memzero_explicit(dst_orig, dlen);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int keyctl_dh_compute_kdf(struct kdf_sdesc *sdesc,
|
||||
char __user *buffer, size_t buflen,
|
||||
uint8_t *kbuf, size_t kbuflen)
|
||||
{
|
||||
uint8_t *outbuf = NULL;
|
||||
int ret;
|
||||
|
||||
outbuf = kmalloc(buflen, GFP_KERNEL);
|
||||
if (!outbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = kdf_ctr(sdesc, kbuf, kbuflen, outbuf, buflen);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = buflen;
|
||||
if (copy_to_user(buffer, outbuf, buflen) != 0)
|
||||
ret = -EFAULT;
|
||||
|
||||
err:
|
||||
kzfree(outbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
long __keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct keyctl_kdf_params *kdfcopy)
|
||||
{
|
||||
long ret;
|
||||
MPI base, private, prime, result;
|
||||
@ -88,6 +227,7 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
uint8_t *kbuf;
|
||||
ssize_t keylen;
|
||||
size_t resultlen;
|
||||
struct kdf_sdesc *sdesc = NULL;
|
||||
|
||||
if (!params || (!buffer && buflen)) {
|
||||
ret = -EINVAL;
|
||||
@ -98,12 +238,34 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (reserved) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
if (kdfcopy) {
|
||||
char *hashname;
|
||||
|
||||
if (buflen > KEYCTL_KDF_MAX_OUTPUT_LEN ||
|
||||
kdfcopy->otherinfolen > KEYCTL_KDF_MAX_OI_LEN) {
|
||||
ret = -EMSGSIZE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* get KDF name string */
|
||||
hashname = strndup_user(kdfcopy->hashname, CRYPTO_MAX_ALG_NAME);
|
||||
if (IS_ERR(hashname)) {
|
||||
ret = PTR_ERR(hashname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* allocate KDF from the kernel crypto API */
|
||||
ret = kdf_alloc(&sdesc, hashname);
|
||||
kfree(hashname);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
keylen = mpi_from_key(pcopy.prime, buflen, &prime);
|
||||
/*
|
||||
* If the caller requests postprocessing with a KDF, allow an
|
||||
* arbitrary output buffer size since the KDF ensures proper truncation.
|
||||
*/
|
||||
keylen = mpi_from_key(pcopy.prime, kdfcopy ? SIZE_MAX : buflen, &prime);
|
||||
if (keylen < 0 || !prime) {
|
||||
/* buflen == 0 may be used to query the required buffer size,
|
||||
* which is the prime key length.
|
||||
@ -133,12 +295,25 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
goto error3;
|
||||
}
|
||||
|
||||
kbuf = kmalloc(resultlen, GFP_KERNEL);
|
||||
/* allocate space for DH shared secret and SP800-56A otherinfo */
|
||||
kbuf = kmalloc(kdfcopy ? (resultlen + kdfcopy->otherinfolen) : resultlen,
|
||||
GFP_KERNEL);
|
||||
if (!kbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto error4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Concatenate SP800-56A otherinfo past DH shared secret -- the
|
||||
* input to the KDF is (DH shared secret || otherinfo)
|
||||
*/
|
||||
if (kdfcopy && kdfcopy->otherinfo &&
|
||||
copy_from_user(kbuf + resultlen, kdfcopy->otherinfo,
|
||||
kdfcopy->otherinfolen) != 0) {
|
||||
ret = -EFAULT;
|
||||
goto error5;
|
||||
}
|
||||
|
||||
ret = do_dh(result, base, private, prime);
|
||||
if (ret)
|
||||
goto error5;
|
||||
@ -147,12 +322,17 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
if (ret != 0)
|
||||
goto error5;
|
||||
|
||||
ret = nbytes;
|
||||
if (copy_to_user(buffer, kbuf, nbytes) != 0)
|
||||
ret = -EFAULT;
|
||||
if (kdfcopy) {
|
||||
ret = keyctl_dh_compute_kdf(sdesc, buffer, buflen, kbuf,
|
||||
resultlen + kdfcopy->otherinfolen);
|
||||
} else {
|
||||
ret = nbytes;
|
||||
if (copy_to_user(buffer, kbuf, nbytes) != 0)
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
||||
error5:
|
||||
kfree(kbuf);
|
||||
kzfree(kbuf);
|
||||
error4:
|
||||
mpi_free(result);
|
||||
error3:
|
||||
@ -162,5 +342,21 @@ error2:
|
||||
error1:
|
||||
mpi_free(prime);
|
||||
out:
|
||||
kdf_dealloc(sdesc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct keyctl_kdf_params __user *kdf)
|
||||
{
|
||||
struct keyctl_kdf_params kdfcopy;
|
||||
|
||||
if (!kdf)
|
||||
return __keyctl_dh_compute(params, buffer, buflen, NULL);
|
||||
|
||||
if (copy_from_user(&kdfcopy, kdf, sizeof(kdfcopy)) != 0)
|
||||
return -EFAULT;
|
||||
|
||||
return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy);
|
||||
}
|
||||
|
@ -229,6 +229,9 @@ continue_scanning:
|
||||
set_bit(KEY_FLAG_DEAD, &key->flags);
|
||||
key->perm = 0;
|
||||
goto skip_dead_key;
|
||||
} else if (key->type == &key_type_keyring &&
|
||||
key->restrict_link) {
|
||||
goto found_restricted_keyring;
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,6 +337,14 @@ found_unreferenced_key:
|
||||
gc_state |= KEY_GC_REAP_AGAIN;
|
||||
goto maybe_resched;
|
||||
|
||||
/* We found a restricted keyring and need to update the restriction if
|
||||
* it is associated with the dead key type.
|
||||
*/
|
||||
found_restricted_keyring:
|
||||
spin_unlock(&key_serial_lock);
|
||||
keyring_restriction_gc(key, key_gc_dead_keytype);
|
||||
goto maybe_resched;
|
||||
|
||||
/* We found a keyring and we need to check the payload for links to
|
||||
* dead or expired keys. We don't flag another reap immediately as we
|
||||
* have to wait for the old payload to be destroyed by RCU before we
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/task_work.h>
|
||||
#include <linux/keyctl.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
struct iovec;
|
||||
|
||||
@ -168,6 +169,8 @@ extern void key_change_session_keyring(struct callback_head *twork);
|
||||
extern struct work_struct key_gc_work;
|
||||
extern unsigned key_gc_delay;
|
||||
extern void keyring_gc(struct key *keyring, time_t limit);
|
||||
extern void keyring_restriction_gc(struct key *keyring,
|
||||
struct key_type *dead_type);
|
||||
extern void key_schedule_gc(time_t gc_at);
|
||||
extern void key_schedule_gc_links(void);
|
||||
extern void key_gc_keytype(struct key_type *ktype);
|
||||
@ -250,6 +253,9 @@ struct iov_iter;
|
||||
extern long keyctl_instantiate_key_common(key_serial_t,
|
||||
struct iov_iter *,
|
||||
key_serial_t);
|
||||
extern long keyctl_restrict_keyring(key_serial_t id,
|
||||
const char __user *_type,
|
||||
const char __user *_restriction);
|
||||
#ifdef CONFIG_PERSISTENT_KEYRINGS
|
||||
extern long keyctl_get_persistent(uid_t, key_serial_t);
|
||||
extern unsigned persistent_keyring_expiry;
|
||||
@ -262,14 +268,33 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
|
||||
|
||||
#ifdef CONFIG_KEY_DH_OPERATIONS
|
||||
extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *,
|
||||
size_t, void __user *);
|
||||
size_t, struct keyctl_kdf_params __user *);
|
||||
extern long __keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *,
|
||||
size_t, struct keyctl_kdf_params *);
|
||||
#ifdef CONFIG_KEYS_COMPAT
|
||||
extern long compat_keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct compat_keyctl_kdf_params __user *kdf);
|
||||
#endif
|
||||
#define KEYCTL_KDF_MAX_OUTPUT_LEN 1024 /* max length of KDF output */
|
||||
#define KEYCTL_KDF_MAX_OI_LEN 64 /* max length of otherinfo */
|
||||
#else
|
||||
static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
void __user *reserved)
|
||||
struct keyctl_kdf_params __user *kdf)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KEYS_COMPAT
|
||||
static inline long compat_keyctl_dh_compute(
|
||||
struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct keyctl_kdf_params __user *kdf)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -201,12 +201,15 @@ 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.
|
||||
* @restrict_link: Optional link restriction 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
|
||||
* key before returning.
|
||||
*
|
||||
* The restrict_link structure (if not NULL) will be freed when the
|
||||
* keyring is destroyed, so it must be dynamically allocated.
|
||||
*
|
||||
* The user's key count quota is updated to reflect the creation of the key and
|
||||
* the user's key data quota has the default for the key type reserved. The
|
||||
* instantiation function should amend this as necessary. If insufficient
|
||||
@ -225,9 +228,7 @@ 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,
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
const union key_payload *))
|
||||
struct key_restriction *restrict_link)
|
||||
{
|
||||
struct key_user *user = NULL;
|
||||
struct key *key;
|
||||
@ -499,19 +500,23 @@ 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;
|
||||
|
||||
if (keyring->restrict_link && keyring->restrict_link->check) {
|
||||
struct key_restriction *keyres = keyring->restrict_link;
|
||||
|
||||
ret = keyres->check(keyring, key->type, &prep.payload,
|
||||
keyres->key);
|
||||
if (ret < 0)
|
||||
goto error_link_end;
|
||||
}
|
||||
}
|
||||
|
||||
ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);
|
||||
|
||||
error_link_end:
|
||||
if (keyring)
|
||||
__key_link_end(keyring, &key->index_key, edit);
|
||||
|
||||
@ -806,9 +811,7 @@ 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;
|
||||
struct key_restriction *restrict_link = NULL;
|
||||
|
||||
/* look up the key type to see if it's one of the registered kernel
|
||||
* types */
|
||||
@ -854,20 +857,21 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||
}
|
||||
index_key.desc_len = strlen(index_key.description);
|
||||
|
||||
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) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_free_prep;
|
||||
}
|
||||
|
||||
if (restrict_link && restrict_link->check) {
|
||||
ret = restrict_link->check(keyring, index_key.type,
|
||||
&prep.payload, restrict_link->key);
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_link_end;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we're going to allocate a new key, we're going to have
|
||||
* to modify the keyring */
|
||||
ret = key_permission(keyring_ref, KEY_NEED_WRITE);
|
||||
|
@ -1582,6 +1582,59 @@ error_keyring:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply a restriction to a given keyring.
|
||||
*
|
||||
* The caller must have Setattr permission to change keyring restrictions.
|
||||
*
|
||||
* The requested type name may be a NULL pointer to reject all attempts
|
||||
* to link to the keyring. If _type is non-NULL, _restriction can be
|
||||
* NULL or a pointer to a string describing the restriction. If _type is
|
||||
* NULL, _restriction must also be NULL.
|
||||
*
|
||||
* Returns 0 if successful.
|
||||
*/
|
||||
long keyctl_restrict_keyring(key_serial_t id, const char __user *_type,
|
||||
const char __user *_restriction)
|
||||
{
|
||||
key_ref_t key_ref;
|
||||
bool link_reject = !_type;
|
||||
char type[32];
|
||||
char *restriction = NULL;
|
||||
long ret;
|
||||
|
||||
key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR);
|
||||
if (IS_ERR(key_ref))
|
||||
return PTR_ERR(key_ref);
|
||||
|
||||
if (_type) {
|
||||
ret = key_get_type_from_user(type, _type, sizeof(type));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (_restriction) {
|
||||
if (!_type) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
restriction = strndup_user(_restriction, PAGE_SIZE);
|
||||
if (IS_ERR(restriction)) {
|
||||
ret = PTR_ERR(restriction);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
ret = keyring_restrict(key_ref, link_reject ? NULL : type, restriction);
|
||||
kfree(restriction);
|
||||
|
||||
error:
|
||||
key_ref_put(key_ref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The key control system call
|
||||
*/
|
||||
@ -1691,7 +1744,12 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
||||
case KEYCTL_DH_COMPUTE:
|
||||
return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2,
|
||||
(char __user *) arg3, (size_t) arg4,
|
||||
(void __user *) arg5);
|
||||
(struct keyctl_kdf_params __user *) arg5);
|
||||
|
||||
case KEYCTL_RESTRICT_KEYRING:
|
||||
return keyctl_restrict_keyring((key_serial_t) arg2,
|
||||
(const char __user *) arg3,
|
||||
(const char __user *) arg4);
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
|
@ -394,6 +394,13 @@ static void keyring_destroy(struct key *keyring)
|
||||
write_unlock(&keyring_name_lock);
|
||||
}
|
||||
|
||||
if (keyring->restrict_link) {
|
||||
struct key_restriction *keyres = keyring->restrict_link;
|
||||
|
||||
key_put(keyres->key);
|
||||
kfree(keyres);
|
||||
}
|
||||
|
||||
assoc_array_destroy(&keyring->keys, &keyring_assoc_array_ops);
|
||||
}
|
||||
|
||||
@ -492,9 +499,7 @@ 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,
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
const union key_payload *),
|
||||
struct key_restriction *restrict_link,
|
||||
struct key *dest)
|
||||
{
|
||||
struct key *keyring;
|
||||
@ -519,17 +524,19 @@ EXPORT_SYMBOL(keyring_alloc);
|
||||
* @keyring: The keyring being added to.
|
||||
* @type: The type of key being added.
|
||||
* @payload: The payload of the key intended to be added.
|
||||
* @data: Additional data for evaluating restriction.
|
||||
*
|
||||
* 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().
|
||||
* This is meant to be stored in a key_restriction structure which is passed
|
||||
* in the restrict_link parameter to keyring_alloc().
|
||||
*/
|
||||
int restrict_link_reject(struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload)
|
||||
const union key_payload *payload,
|
||||
struct key *restriction_key)
|
||||
{
|
||||
return -EPERM;
|
||||
}
|
||||
@ -940,6 +947,111 @@ key_ref_t keyring_search(key_ref_t keyring,
|
||||
}
|
||||
EXPORT_SYMBOL(keyring_search);
|
||||
|
||||
static struct key_restriction *keyring_restriction_alloc(
|
||||
key_restrict_link_func_t check)
|
||||
{
|
||||
struct key_restriction *keyres =
|
||||
kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
|
||||
|
||||
if (!keyres)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
keyres->check = check;
|
||||
|
||||
return keyres;
|
||||
}
|
||||
|
||||
/*
|
||||
* Semaphore to serialise restriction setup to prevent reference count
|
||||
* cycles through restriction key pointers.
|
||||
*/
|
||||
static DECLARE_RWSEM(keyring_serialise_restrict_sem);
|
||||
|
||||
/*
|
||||
* Check for restriction cycles that would prevent keyring garbage collection.
|
||||
* keyring_serialise_restrict_sem must be held.
|
||||
*/
|
||||
static bool keyring_detect_restriction_cycle(const struct key *dest_keyring,
|
||||
struct key_restriction *keyres)
|
||||
{
|
||||
while (keyres && keyres->key &&
|
||||
keyres->key->type == &key_type_keyring) {
|
||||
if (keyres->key == dest_keyring)
|
||||
return true;
|
||||
|
||||
keyres = keyres->key->restrict_link;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* keyring_restrict - Look up and apply a restriction to a keyring
|
||||
*
|
||||
* @keyring: The keyring to be restricted
|
||||
* @restriction: The restriction options to apply to the keyring
|
||||
*/
|
||||
int keyring_restrict(key_ref_t keyring_ref, const char *type,
|
||||
const char *restriction)
|
||||
{
|
||||
struct key *keyring;
|
||||
struct key_type *restrict_type = NULL;
|
||||
struct key_restriction *restrict_link;
|
||||
int ret = 0;
|
||||
|
||||
keyring = key_ref_to_ptr(keyring_ref);
|
||||
key_check(keyring);
|
||||
|
||||
if (keyring->type != &key_type_keyring)
|
||||
return -ENOTDIR;
|
||||
|
||||
if (!type) {
|
||||
restrict_link = keyring_restriction_alloc(restrict_link_reject);
|
||||
} else {
|
||||
restrict_type = key_type_lookup(type);
|
||||
|
||||
if (IS_ERR(restrict_type))
|
||||
return PTR_ERR(restrict_type);
|
||||
|
||||
if (!restrict_type->lookup_restriction) {
|
||||
ret = -ENOENT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
restrict_link = restrict_type->lookup_restriction(restriction);
|
||||
}
|
||||
|
||||
if (IS_ERR(restrict_link)) {
|
||||
ret = PTR_ERR(restrict_link);
|
||||
goto error;
|
||||
}
|
||||
|
||||
down_write(&keyring->sem);
|
||||
down_write(&keyring_serialise_restrict_sem);
|
||||
|
||||
if (keyring->restrict_link)
|
||||
ret = -EEXIST;
|
||||
else if (keyring_detect_restriction_cycle(keyring, restrict_link))
|
||||
ret = -EDEADLK;
|
||||
else
|
||||
keyring->restrict_link = restrict_link;
|
||||
|
||||
up_write(&keyring_serialise_restrict_sem);
|
||||
up_write(&keyring->sem);
|
||||
|
||||
if (ret < 0) {
|
||||
key_put(restrict_link->key);
|
||||
kfree(restrict_link);
|
||||
}
|
||||
|
||||
error:
|
||||
if (restrict_type)
|
||||
key_type_put(restrict_type);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(keyring_restrict);
|
||||
|
||||
/*
|
||||
* Search the given keyring for a key that might be updated.
|
||||
*
|
||||
@ -1220,9 +1332,10 @@ void __key_link_end(struct key *keyring,
|
||||
*/
|
||||
static int __key_link_check_restriction(struct key *keyring, struct key *key)
|
||||
{
|
||||
if (!keyring->restrict_link)
|
||||
if (!keyring->restrict_link || !keyring->restrict_link->check)
|
||||
return 0;
|
||||
return keyring->restrict_link(keyring, key->type, &key->payload);
|
||||
return keyring->restrict_link->check(keyring, key->type, &key->payload,
|
||||
keyring->restrict_link->key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1426,3 +1539,53 @@ do_gc:
|
||||
up_write(&keyring->sem);
|
||||
kleave(" [gc]");
|
||||
}
|
||||
|
||||
/*
|
||||
* Garbage collect restriction pointers from a keyring.
|
||||
*
|
||||
* Keyring restrictions are associated with a key type, and must be cleaned
|
||||
* up if the key type is unregistered. The restriction is altered to always
|
||||
* reject additional keys so a keyring cannot be opened up by unregistering
|
||||
* a key type.
|
||||
*
|
||||
* Not called with any keyring locks held. The keyring's key struct will not
|
||||
* be deallocated under us as only our caller may deallocate it.
|
||||
*
|
||||
* The caller is required to hold key_types_sem and dead_type->sem. This is
|
||||
* fulfilled by key_gc_keytype() holding the locks on behalf of
|
||||
* key_garbage_collector(), which it invokes on a workqueue.
|
||||
*/
|
||||
void keyring_restriction_gc(struct key *keyring, struct key_type *dead_type)
|
||||
{
|
||||
struct key_restriction *keyres;
|
||||
|
||||
kenter("%x{%s}", keyring->serial, keyring->description ?: "");
|
||||
|
||||
/*
|
||||
* keyring->restrict_link is only assigned at key allocation time
|
||||
* or with the key type locked, so the only values that could be
|
||||
* concurrently assigned to keyring->restrict_link are for key
|
||||
* types other than dead_type. Given this, it's ok to check
|
||||
* the key type before acquiring keyring->sem.
|
||||
*/
|
||||
if (!dead_type || !keyring->restrict_link ||
|
||||
keyring->restrict_link->keytype != dead_type) {
|
||||
kleave(" [no restriction gc]");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Lock the keyring to ensure that a link is not in progress */
|
||||
down_write(&keyring->sem);
|
||||
|
||||
keyres = keyring->restrict_link;
|
||||
|
||||
keyres->check = restrict_link_reject;
|
||||
|
||||
key_put(keyres->key);
|
||||
keyres->key = NULL;
|
||||
keyres->keytype = NULL;
|
||||
|
||||
up_write(&keyring->sem);
|
||||
|
||||
kleave(" [restriction gc]");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user