Commit 30a83251 authored by James Morris's avatar James Morris

Merge tag 'keys-next-20170412' of...

Merge tag 'keys-next-20170412' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs into next
parents 622f6e32 4cd4ca7c
...@@ -311,3 +311,54 @@ Functions are provided to register and unregister parsers: ...@@ -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 Parsers may not have the same name. The names are otherwise only used for
displaying in debugging messages. 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: ...@@ -827,7 +827,7 @@ The keyctl syscall functions are:
long keyctl(KEYCTL_DH_COMPUTE, struct keyctl_dh_params *params, long keyctl(KEYCTL_DH_COMPUTE, struct keyctl_dh_params *params,
char *buffer, size_t buflen, char *buffer, size_t buflen,
void *reserved); struct keyctl_kdf_params *kdf);
The params struct contains serial numbers for three keys: The params struct contains serial numbers for three keys:
...@@ -844,18 +844,61 @@ The keyctl syscall functions are: ...@@ -844,18 +844,61 @@ The keyctl syscall functions are:
public key. If the base is the remote public key, the result is public key. If the base is the remote public key, the result is
the shared secret. 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 - If the buffer length is nonzero, the length of the result is
returned when it is successfully calculated and copied in to the returned when it is successfully calculated and copied in to the
buffer. When the buffer length is zero, the minimum required buffer. When the buffer length is zero, the minimum required
buffer length is returned. 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 This function will return error EOPNOTSUPP if the key type is not
supported, error ENOKEY if the key could not be found, or error 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 KERNEL SERVICES
...@@ -1032,10 +1075,7 @@ payload contents" for more information. ...@@ -1032,10 +1075,7 @@ payload contents" for more information.
struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
const struct cred *cred, const struct cred *cred,
key_perm_t perm, key_perm_t perm,
int (*restrict_link)(struct key *, struct key_restriction *restrict_link,
const struct key_type *,
unsigned long,
const union key_payload *),
unsigned long flags, unsigned long flags,
struct key *dest); struct key *dest);
...@@ -1047,20 +1087,23 @@ payload contents" for more information. ...@@ -1047,20 +1087,23 @@ payload contents" for more information.
KEY_ALLOC_NOT_IN_QUOTA in flags if the keyring shouldn't be accounted 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. 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 If restrict_link is not NULL, it should point to a structure that contains
called each time an attempt is made to link a key into the new keyring. the function that will be called each time an attempt is made to link a
This function is called to check whether a key may be added into the keying key into the new keyring. The structure may also contain a key pointer
or not. Callers of key_create_or_update() within the kernel can pass and an associated key type. The function is called to check whether a key
KEY_ALLOC_BYPASS_RESTRICTION to suppress the check. An example of using may be added into the keyring or not. The key type is used by the garbage
this is to manage rings of cryptographic keys that are set up when the collector to clean up function or data pointers in this structure if the
kernel boots where userspace is also permitted to add keys - provided they given key type is unregistered. Callers of key_create_or_update() within
can be verified by a key the kernel already has. 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 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 to, the key type, the payload of the key being added, and data to be
added. Note that when a new key is being created, this is called between used in the restriction check. Note that when a new key is being created,
payload preparsing and actual key creation. The function should return 0 this is called between payload preparsing and actual key creation. The
to allow the link or an error to reject it. function should return 0 to allow the link or an error to reject it.
A convenience function, restrict_link_reject, exists to always return A convenience function, restrict_link_reject, exists to always return
-EPERM to in this case. -EPERM to in this case.
...@@ -1445,6 +1488,15 @@ The structure has a number of fields, some of which are mandatory: ...@@ -1445,6 +1488,15 @@ The structure has a number of fields, some of which are mandatory:
The authorisation key. 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 REQUEST-KEY CALLBACK SERVICE
============================ ============================
......
...@@ -64,4 +64,22 @@ config SECONDARY_TRUSTED_KEYRING ...@@ -64,4 +64,22 @@ config SECONDARY_TRUSTED_KEYRING
those keys are not blacklisted and are vouched for by a key built those keys are not blacklisted and are vouched for by a key built
into the kernel or already in the secondary trusted keyring. 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 endmenu
...@@ -3,6 +3,12 @@ ...@@ -3,6 +3,12 @@
# #
obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o 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) ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y)
......
/* 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);
#include <linux/kernel.h>
extern const char __initdata *const blacklist_hashes[];
#include "blacklist.h"
const char __initdata *const blacklist_hashes[] = {
#include CONFIG_SYSTEM_BLACKLIST_HASH_LIST
, NULL
};
#include "blacklist.h"
const char __initdata *const blacklist_hashes[] = {
NULL
};
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/slab.h>
#include <keys/asymmetric-type.h> #include <keys/asymmetric-type.h>
#include <keys/system_keyring.h> #include <keys/system_keyring.h>
#include <crypto/pkcs7.h> #include <crypto/pkcs7.h>
...@@ -32,11 +33,13 @@ extern __initconst const unsigned long system_certificate_list_size; ...@@ -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 * 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. * 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 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 #ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
...@@ -49,20 +52,40 @@ int restrict_link_by_builtin_trusted(struct key *keyring, ...@@ -49,20 +52,40 @@ int restrict_link_by_builtin_trusted(struct key *keyring,
* keyrings. * keyrings.
*/ */
int restrict_link_by_builtin_and_secondary_trusted( int restrict_link_by_builtin_and_secondary_trusted(
struct key *keyring, struct key *dest_keyring,
const struct key_type *type, 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 /* If we have a secondary trusted keyring, then that contains a link
* through to the builtin keyring and the search will follow that link. * through to the builtin keyring and the search will follow that link.
*/ */
if (type == &key_type_keyring && if (type == &key_type_keyring &&
keyring == secondary_trusted_keys && dest_keyring == secondary_trusted_keys &&
payload == &builtin_trusted_keys->payload) payload == &builtin_trusted_keys->payload)
/* Allow the builtin keyring to be added to the secondary */ /* Allow the builtin keyring to be added to the secondary */
return 0; 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 #endif
...@@ -91,7 +114,7 @@ static __init int system_trusted_keyring_init(void) ...@@ -91,7 +114,7 @@ static __init int system_trusted_keyring_init(void)
KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH |
KEY_USR_WRITE), KEY_USR_WRITE),
KEY_ALLOC_NOT_IN_QUOTA, KEY_ALLOC_NOT_IN_QUOTA,
restrict_link_by_builtin_and_secondary_trusted, get_builtin_and_secondary_restriction(),
NULL); NULL);
if (IS_ERR(secondary_trusted_keys)) if (IS_ERR(secondary_trusted_keys))
panic("Can't allocate secondary trusted keyring\n"); panic("Can't allocate secondary trusted keyring\n");
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <keys/system_keyring.h>
#include "asymmetric_keys.h" #include "asymmetric_keys.h"
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -451,15 +452,100 @@ static void asymmetric_key_destroy(struct key *key) ...@@ -451,15 +452,100 @@ static void asymmetric_key_destroy(struct key *key)
asymmetric_key_free_kids(kids); 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 = { struct key_type key_type_asymmetric = {
.name = "asymmetric", .name = "asymmetric",
.preparse = asymmetric_key_preparse, .preparse = asymmetric_key_preparse,
.free_preparse = asymmetric_key_free_preparse, .free_preparse = asymmetric_key_free_preparse,
.instantiate = generic_key_instantiate, .instantiate = generic_key_instantiate,
.match_preparse = asymmetric_key_match_preparse, .match_preparse = asymmetric_key_match_preparse,
.match_free = asymmetric_key_match_free, .match_free = asymmetric_key_match_free,
.destroy = asymmetric_key_destroy, .destroy = asymmetric_key_destroy,
.describe = asymmetric_key_describe, .describe = asymmetric_key_describe,
.lookup_restriction = asymmetric_lookup_restriction,
}; };
EXPORT_SYMBOL_GPL(key_type_asymmetric); EXPORT_SYMBOL_GPL(key_type_asymmetric);
......
...@@ -23,6 +23,7 @@ struct pkcs7_signed_info { ...@@ -23,6 +23,7 @@ struct pkcs7_signed_info {
struct x509_certificate *signer; /* Signing certificate (in msg->certs) */ struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
unsigned index; unsigned index;
bool unsupported_crypto; /* T if not usable due to missing crypto */ bool unsupported_crypto; /* T if not usable due to missing crypto */
bool blacklisted;
/* Message digest - the digest of the Content Data (or NULL) */ /* Message digest - the digest of the Content Data (or NULL) */
const void *msgdigest; const void *msgdigest;
......
...@@ -190,6 +190,18 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, ...@@ -190,6 +190,18 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
x509->subject, x509->subject,
x509->raw_serial_size, x509->raw_serial); x509->raw_serial_size, x509->raw_serial);
x509->seen = true; 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) if (x509->unsupported_key)
goto unsupported_crypto_in_x509; goto unsupported_crypto_in_x509;
...@@ -357,17 +369,19 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, ...@@ -357,17 +369,19 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
* *
* (*) -EBADMSG if some part of the message was invalid, or: * (*) -EBADMSG if some part of the message was invalid, or:
* *
* (*) -ENOPKG if none of the signature chains are verifiable because suitable * (*) 0 if no signature chains were found to be blacklisted or to contain
* crypto modules couldn't be found, or: * unsupported crypto, or:
* *
* (*) 0 if all the signature chains that don't incur -ENOPKG can be verified * (*) -EKEYREJECTED if a blacklisted key was encountered, or:
* (note that a signature chain may be of zero length), 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, int pkcs7_verify(struct pkcs7_message *pkcs7,
enum key_being_used_for usage) enum key_being_used_for usage)
{ {
struct pkcs7_signed_info *sinfo; struct pkcs7_signed_info *sinfo;
int enopkg = -ENOPKG; int actual_ret = -ENOPKG;
int ret; int ret;
kenter(""); kenter("");
...@@ -412,6 +426,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, ...@@ -412,6 +426,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
ret = pkcs7_verify_one(pkcs7, sinfo); ret = pkcs7_verify_one(pkcs7, sinfo);
if (sinfo->blacklisted && actual_ret == -ENOPKG)
actual_ret = -EKEYREJECTED;
if (ret < 0) { if (ret < 0) {
if (ret == -ENOPKG) { if (ret == -ENOPKG) {
sinfo->unsupported_crypto = true; sinfo->unsupported_crypto = true;
...@@ -420,11 +436,11 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, ...@@ -420,11 +436,11 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
kleave(" = %d", ret); kleave(" = %d", ret);
return ret; return ret;
} }
enopkg = 0; actual_ret = 0;
} }
kleave(" = %d", enopkg); kleave(" = %d", actual_ret);
return enopkg; return actual_ret;
} }
EXPORT_SYMBOL_GPL(pkcs7_verify); EXPORT_SYMBOL_GPL(pkcs7_verify);
......
...@@ -56,9 +56,10 @@ __setup("ca_keys=", ca_keys_setup); ...@@ -56,9 +56,10 @@ __setup("ca_keys=", ca_keys_setup);
/** /**
* restrict_link_by_signature - Restrict additions to a ring of public keys * 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. * @type: The type of key being added.
* @payload: The payload of the new key. * @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 * 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 * those is the signing key and validates the new certificate, then mark the
...@@ -69,9 +70,10 @@ __setup("ca_keys=", ca_keys_setup); ...@@ -69,9 +70,10 @@ __setup("ca_keys=", ca_keys_setup);
* signature check fails or the key is blacklisted and some other error if * 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. * 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 struct key_type *type,
const union key_payload *payload) const union key_payload *payload,
struct key *trust_keyring)
{ {
const struct public_key_signature *sig; const struct public_key_signature *sig;
struct key *key; struct key *key;
...@@ -106,3 +108,156 @@ int restrict_link_by_signature(struct key *trust_keyring, ...@@ -106,3 +108,156 @@ int restrict_link_by_signature(struct key *trust_keyring,
key_put(key); key_put(key);
return ret; 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 { ...@@ -42,6 +42,7 @@ struct x509_certificate {
bool self_signed; /* T if self-signed (check unsupported_sig too) */ bool self_signed; /* T if self-signed (check unsupported_sig too) */
bool unsupported_key; /* T if key uses unsupported crypto */ bool unsupported_key; /* T if key uses unsupported crypto */
bool unsupported_sig; /* T if signature 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) ...@@ -84,6 +84,16 @@ int x509_get_sig_params(struct x509_certificate *cert)
goto error_2; goto error_2;
might_sleep(); might_sleep();
ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest); 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: error_2:
kfree(desc); kfree(desc);
...@@ -186,6 +196,11 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) ...@@ -186,6 +196,11 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
cert->sig->pkey_algo, cert->sig->hash_algo); 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 */ /* Propose a description */
sulen = strlen(cert->subject); sulen = strlen(cert->subject);
if (cert->raw_skid) { if (cert->raw_skid) {
......
...@@ -50,9 +50,20 @@ struct key; ...@@ -50,9 +50,20 @@ struct key;
struct key_type; struct key_type;
union key_payload; 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 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, extern int verify_signature(const struct key *key,
const struct public_key_signature *sig); const struct public_key_signature *sig);
......
...@@ -18,7 +18,8 @@ ...@@ -18,7 +18,8 @@
extern int restrict_link_by_builtin_trusted(struct key *keyring, extern int restrict_link_by_builtin_trusted(struct key *keyring,
const struct key_type *type, const struct key_type *type,
const union key_payload *payload); const union key_payload *payload,
struct key *restriction_key);
#else #else
#define restrict_link_by_builtin_trusted restrict_link_reject #define restrict_link_by_builtin_trusted restrict_link_reject
...@@ -28,11 +29,24 @@ extern int restrict_link_by_builtin_trusted(struct key *keyring, ...@@ -28,11 +29,24 @@ extern int restrict_link_by_builtin_trusted(struct key *keyring,
extern int restrict_link_by_builtin_and_secondary_trusted( extern int restrict_link_by_builtin_and_secondary_trusted(
struct key *keyring, struct key *keyring,
const struct key_type *type, const struct key_type *type,
const union key_payload *payload); const union key_payload *payload,
struct key *restriction_key);
#else #else
#define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted #define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted
#endif #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 #ifdef CONFIG_IMA_BLACKLIST_KEYRING
extern struct key *ima_blacklist_keyring; extern struct key *ima_blacklist_keyring;
......
...@@ -295,6 +295,13 @@ struct compat_old_sigaction { ...@@ -295,6 +295,13 @@ struct compat_old_sigaction {
}; };
#endif #endif
struct compat_keyctl_kdf_params {
compat_uptr_t hashname;
compat_uptr_t otherinfo;
__u32 otherinfolen;
__u32 __spare[8];
};
struct compat_statfs; struct compat_statfs;
struct compat_statfs64; struct compat_statfs64;
struct compat_old_linux_dirent; struct compat_old_linux_dirent;
......
...@@ -147,6 +147,14 @@ struct key_type { ...@@ -147,6 +147,14 @@ struct key_type {
*/ */
request_key_actor_t request_key; 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 */ /* internal fields */
struct list_head link; /* link in types list */ struct list_head link; /* link in types list */
struct lock_class_key lock_class; /* key->sem lock class */ 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) ...@@ -127,6 +127,17 @@ static inline bool is_key_possessed(const key_ref_t key_ref)
return (unsigned long) key_ref & 1UL; 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 * authentication token / access credential / keyring
...@@ -206,18 +217,17 @@ struct key { ...@@ -206,18 +217,17 @@ struct key {
}; };
/* This is set on a keyring to restrict the addition of a link to a 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 * 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 * This is intended for use with rings of trusted keys whereby addition
* to the keyring needs to be controlled. KEY_ALLOC_BYPASS_RESTRICTION * to the keyring needs to be controlled. KEY_ALLOC_BYPASS_RESTRICTION
* overrides this, allowing the kernel to add extra keys without * overrides this, allowing the kernel to add extra keys without
* restriction. * restriction.
*/ */
int (*restrict_link)(struct key *keyring, struct key_restriction *restrict_link;
const struct key_type *type,
const union key_payload *payload);
}; };
extern struct key *key_alloc(struct key_type *type, extern struct key *key_alloc(struct key_type *type,
...@@ -226,9 +236,7 @@ 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, const struct cred *cred,
key_perm_t perm, key_perm_t perm,
unsigned long flags, unsigned long flags,
int (*restrict_link)(struct key *, struct key_restriction *restrict_link);
const struct key_type *,
const union key_payload *));
#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ #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 ...@@ -304,14 +312,13 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid
const struct cred *cred, const struct cred *cred,
key_perm_t perm, key_perm_t perm,
unsigned long flags, unsigned long flags,
int (*restrict_link)(struct key *, struct key_restriction *restrict_link,
const struct key_type *,
const union key_payload *),
struct key *dest); struct key *dest);
extern int restrict_link_reject(struct key *keyring, extern int restrict_link_reject(struct key *keyring,
const struct key_type *type, 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); extern int keyring_clear(struct key *keyring);
...@@ -322,6 +329,9 @@ extern key_ref_t keyring_search(key_ref_t keyring, ...@@ -322,6 +329,9 @@ extern key_ref_t keyring_search(key_ref_t keyring,
extern int keyring_add_key(struct key *keyring, extern int keyring_add_key(struct key *keyring,
struct key *key); 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); extern struct key *key_lookup(key_serial_t id);
static inline key_serial_t key_serial(const struct key *key) static inline key_serial_t key_serial(const struct key *key)
......
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
#define KEYCTL_INVALIDATE 21 /* invalidate a key */ #define KEYCTL_INVALIDATE 21 /* invalidate a key */
#define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */ #define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */
#define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */ #define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */
#define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */
/* keyctl structures */ /* keyctl structures */
struct keyctl_dh_params { struct keyctl_dh_params {
...@@ -68,4 +69,11 @@ struct keyctl_dh_params { ...@@ -68,4 +69,11 @@ struct keyctl_dh_params {
__s32 base; __s32 base;
}; };
struct keyctl_kdf_params {
char *hashname;
char *otherinfo;
__u32 otherinfolen;
__u32 __spare[8];
};
#endif /* _LINUX_KEYCTL_H */ #endif /* _LINUX_KEYCTL_H */
...@@ -81,18 +81,25 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, ...@@ -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) int __init integrity_init_keyring(const unsigned int id)
{ {
const struct cred *cred = current_cred(); const struct cred *cred = current_cred();
struct key_restriction *restriction;
int err = 0; int err = 0;
if (!init_keyring) if (!init_keyring)
return 0; 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), keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
KGIDT_INIT(0), cred, KGIDT_INIT(0), cred,
((KEY_POS_ALL & ~KEY_POS_SETATTR) | ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ | KEY_USR_VIEW | KEY_USR_READ |
KEY_USR_WRITE | KEY_USR_SEARCH), KEY_USR_WRITE | KEY_USR_SEARCH),
KEY_ALLOC_NOT_IN_QUOTA, KEY_ALLOC_NOT_IN_QUOTA,
restrict_link_to_ima, NULL); restriction, NULL);
if (IS_ERR(keyring[id])) { if (IS_ERR(keyring[id])) {
err = PTR_ERR(keyring[id]); err = PTR_ERR(keyring[id]);
pr_info("Can't allocate %s keyring (%d)\n", pr_info("Can't allocate %s keyring (%d)\n",
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h>
#include <keys/system_keyring.h> #include <keys/system_keyring.h>
...@@ -27,15 +28,23 @@ struct key *ima_blacklist_keyring; ...@@ -27,15 +28,23 @@ struct key *ima_blacklist_keyring;
*/ */
__init int ima_mok_init(void) __init int ima_mok_init(void)
{ {
struct key_restriction *restriction;
pr_notice("Allocating IMA blacklist keyring.\n"); 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", ima_blacklist_keyring = keyring_alloc(".ima_blacklist",
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
(KEY_POS_ALL & ~KEY_POS_SETATTR) | (KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ | KEY_USR_VIEW | KEY_USR_READ |
KEY_USR_WRITE | KEY_USR_SEARCH, KEY_USR_WRITE | KEY_USR_SEARCH,
KEY_ALLOC_NOT_IN_QUOTA, KEY_ALLOC_NOT_IN_QUOTA,
restrict_link_by_builtin_trusted, NULL); restriction, NULL);
if (IS_ERR(ima_blacklist_keyring)) if (IS_ERR(ima_blacklist_keyring))
panic("Can't allocate IMA blacklist keyring."); panic("Can't allocate IMA blacklist keyring.");
......
...@@ -90,6 +90,8 @@ config KEY_DH_OPERATIONS ...@@ -90,6 +90,8 @@ config KEY_DH_OPERATIONS
bool "Diffie-Hellman operations on retained keys" bool "Diffie-Hellman operations on retained keys"
depends on KEYS depends on KEYS
select MPILIB select MPILIB
select CRYPTO
select CRYPTO_HASH
help help
This option provides support for calculating Diffie-Hellman This option provides support for calculating Diffie-Hellman
public keys and shared secrets using values stored as keys public keys and shared secrets using values stored as keys
......
...@@ -15,7 +15,8 @@ obj-y := \ ...@@ -15,7 +15,8 @@ obj-y := \
request_key.o \ request_key.o \
request_key_auth.o \ request_key_auth.o \
user_defined.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_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o obj-$(CONFIG_SYSCTL) += sysctl.o
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
......
...@@ -133,8 +133,13 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, ...@@ -133,8 +133,13 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
return keyctl_get_persistent(arg2, arg3); return keyctl_get_persistent(arg2, arg3);
case KEYCTL_DH_COMPUTE: case KEYCTL_DH_COMPUTE:
return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3), return compat_keyctl_dh_compute(compat_ptr(arg2),
arg4, compat_ptr(arg5)); compat_ptr(arg3),
arg4, compat_ptr(arg5));
case KEYCTL_RESTRICT_KEYRING:
return keyctl_restrict_keyring(arg2, compat_ptr(arg3),
compat_ptr(arg4));
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
......
/* 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 @@ ...@@ -11,6 +11,8 @@
#include <linux/mpi.h> #include <linux/mpi.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/crypto.h>
#include <crypto/hash.h>
#include <keys/user-type.h> #include <keys/user-type.h>
#include "internal.h" #include "internal.h"
...@@ -77,9 +79,146 @@ static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi) ...@@ -77,9 +79,146 @@ static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi)
return ret; return ret;
} }
long keyctl_dh_compute(struct keyctl_dh_params __user *params, struct kdf_sdesc {
char __user *buffer, size_t buflen, struct shash_desc shash;
void __user *reserved) 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; long ret;
MPI base, private, prime, result; MPI base, private, prime, result;
...@@ -88,6 +227,7 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params, ...@@ -88,6 +227,7 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
uint8_t *kbuf; uint8_t *kbuf;
ssize_t keylen; ssize_t keylen;
size_t resultlen; size_t resultlen;
struct kdf_sdesc *sdesc = NULL;
if (!params || (!buffer && buflen)) { if (!params || (!buffer && buflen)) {
ret = -EINVAL; ret = -EINVAL;
...@@ -98,12 +238,34 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params, ...@@ -98,12 +238,34 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
goto out; goto out;
} }
if (reserved) { if (kdfcopy) {
ret = -EINVAL; char *hashname;
goto out;
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) { if (keylen < 0 || !prime) {
/* buflen == 0 may be used to query the required buffer size, /* buflen == 0 may be used to query the required buffer size,
* which is the prime key length. * which is the prime key length.
...@@ -133,12 +295,25 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params, ...@@ -133,12 +295,25 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
goto error3; 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) { if (!kbuf) {
ret = -ENOMEM; ret = -ENOMEM;
goto error4; 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); ret = do_dh(result, base, private, prime);
if (ret) if (ret)
goto error5; goto error5;
...@@ -147,12 +322,17 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params, ...@@ -147,12 +322,17 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
if (ret != 0) if (ret != 0)
goto error5; goto error5;
ret = nbytes; if (kdfcopy) {
if (copy_to_user(buffer, kbuf, nbytes) != 0) ret = keyctl_dh_compute_kdf(sdesc, buffer, buflen, kbuf,
ret = -EFAULT; resultlen + kdfcopy->otherinfolen);
} else {
ret = nbytes;
if (copy_to_user(buffer, kbuf, nbytes) != 0)
ret = -EFAULT;
}
error5: error5:
kfree(kbuf); kzfree(kbuf);
error4: error4:
mpi_free(result); mpi_free(result);
error3: error3:
...@@ -162,5 +342,21 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params, ...@@ -162,5 +342,21 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
error1: error1:
mpi_free(prime); mpi_free(prime);
out: out:
kdf_dealloc(sdesc);
return ret; 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 @@ static void key_garbage_collector(struct work_struct *work) ...@@ -229,6 +229,9 @@ static void key_garbage_collector(struct work_struct *work)
set_bit(KEY_FLAG_DEAD, &key->flags); set_bit(KEY_FLAG_DEAD, &key->flags);
key->perm = 0; key->perm = 0;
goto skip_dead_key; goto skip_dead_key;
} else if (key->type == &key_type_keyring &&
key->restrict_link) {
goto found_restricted_keyring;
} }
} }
...@@ -334,6 +337,14 @@ static void key_garbage_collector(struct work_struct *work) ...@@ -334,6 +337,14 @@ static void key_garbage_collector(struct work_struct *work)
gc_state |= KEY_GC_REAP_AGAIN; gc_state |= KEY_GC_REAP_AGAIN;
goto maybe_resched; 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 /* 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 * 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 * have to wait for the old payload to be destroyed by RCU before we
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/task_work.h> #include <linux/task_work.h>
#include <linux/keyctl.h> #include <linux/keyctl.h>
#include <linux/refcount.h> #include <linux/refcount.h>
#include <linux/compat.h>
struct iovec; struct iovec;
...@@ -168,6 +169,8 @@ extern void key_change_session_keyring(struct callback_head *twork); ...@@ -168,6 +169,8 @@ extern void key_change_session_keyring(struct callback_head *twork);
extern struct work_struct key_gc_work; extern struct work_struct key_gc_work;
extern unsigned key_gc_delay; extern unsigned key_gc_delay;
extern void keyring_gc(struct key *keyring, time_t limit); 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(time_t gc_at);
extern void key_schedule_gc_links(void); extern void key_schedule_gc_links(void);
extern void key_gc_keytype(struct key_type *ktype); extern void key_gc_keytype(struct key_type *ktype);
...@@ -250,6 +253,9 @@ struct iov_iter; ...@@ -250,6 +253,9 @@ struct iov_iter;
extern long keyctl_instantiate_key_common(key_serial_t, extern long keyctl_instantiate_key_common(key_serial_t,
struct iov_iter *, struct iov_iter *,
key_serial_t); key_serial_t);
extern long keyctl_restrict_keyring(key_serial_t id,
const char __user *_type,
const char __user *_restriction);
#ifdef CONFIG_PERSISTENT_KEYRINGS #ifdef CONFIG_PERSISTENT_KEYRINGS
extern long keyctl_get_persistent(uid_t, key_serial_t); extern long keyctl_get_persistent(uid_t, key_serial_t);
extern unsigned persistent_keyring_expiry; extern unsigned persistent_keyring_expiry;
...@@ -262,15 +268,34 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring) ...@@ -262,15 +268,34 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
#ifdef CONFIG_KEY_DH_OPERATIONS #ifdef CONFIG_KEY_DH_OPERATIONS
extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *, 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 #else
static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params, static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params,
char __user *buffer, size_t buflen, 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; return -EOPNOTSUPP;
} }
#endif #endif
#endif
/* /*
* Debugging key validation * Debugging key validation
......
...@@ -201,12 +201,15 @@ static inline void key_alloc_serial(struct key *key) ...@@ -201,12 +201,15 @@ static inline void key_alloc_serial(struct key *key)
* @cred: The credentials specifying UID namespace. * @cred: The credentials specifying UID namespace.
* @perm: The permissions mask of the new key. * @perm: The permissions mask of the new key.
* @flags: Flags specifying quota properties. * @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 * 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 * returned in an uninstantiated state and the caller needs to instantiate the
* key before returning. * 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 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 * the user's key data quota has the default for the key type reserved. The
* instantiation function should amend this as necessary. If insufficient * instantiation function should amend this as necessary. If insufficient
...@@ -225,9 +228,7 @@ static inline void key_alloc_serial(struct key *key) ...@@ -225,9 +228,7 @@ static inline void key_alloc_serial(struct key *key)
struct key *key_alloc(struct key_type *type, const char *desc, struct key *key_alloc(struct key_type *type, const char *desc,
kuid_t uid, kgid_t gid, const struct cred *cred, 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 *, struct key_restriction *restrict_link)
const struct key_type *,
const union key_payload *))
{ {
struct key_user *user = NULL; struct key_user *user = NULL;
struct key *key; struct key *key;
...@@ -499,19 +500,23 @@ int key_instantiate_and_link(struct key *key, ...@@ -499,19 +500,23 @@ int key_instantiate_and_link(struct key *key,
} }
if (keyring) { 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); ret = __key_link_begin(keyring, &key->index_key, &edit);
if (ret < 0) if (ret < 0)
goto error; 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); ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);
error_link_end:
if (keyring) if (keyring)
__key_link_end(keyring, &key->index_key, edit); __key_link_end(keyring, &key->index_key, edit);
...@@ -806,9 +811,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ...@@ -806,9 +811,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
struct key *keyring, *key = NULL; struct key *keyring, *key = NULL;
key_ref_t key_ref; key_ref_t key_ref;
int ret; int ret;
int (*restrict_link)(struct key *, struct key_restriction *restrict_link = NULL;
const struct key_type *,
const union key_payload *) = NULL;
/* look up the key type to see if it's one of the registered kernel /* look up the key type to see if it's one of the registered kernel
* types */ * types */
...@@ -854,20 +857,21 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ...@@ -854,20 +857,21 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
} }
index_key.desc_len = strlen(index_key.description); 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); ret = __key_link_begin(keyring, &index_key, &edit);
if (ret < 0) { if (ret < 0) {
key_ref = ERR_PTR(ret); key_ref = ERR_PTR(ret);
goto error_free_prep; 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 /* if we're going to allocate a new key, we're going to have
* to modify the keyring */ * to modify the keyring */
ret = key_permission(keyring_ref, KEY_NEED_WRITE); ret = key_permission(keyring_ref, KEY_NEED_WRITE);
......
...@@ -1582,6 +1582,59 @@ long keyctl_session_to_parent(void) ...@@ -1582,6 +1582,59 @@ long keyctl_session_to_parent(void)
return ret; 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 * The key control system call
*/ */
...@@ -1691,7 +1744,12 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, ...@@ -1691,7 +1744,12 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
case KEYCTL_DH_COMPUTE: case KEYCTL_DH_COMPUTE:
return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2, return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2,
(char __user *) arg3, (size_t) arg4, (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: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
......
...@@ -394,6 +394,13 @@ static void keyring_destroy(struct key *keyring) ...@@ -394,6 +394,13 @@ static void keyring_destroy(struct key *keyring)
write_unlock(&keyring_name_lock); 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); assoc_array_destroy(&keyring->keys, &keyring_assoc_array_ops);
} }
...@@ -492,9 +499,7 @@ static long keyring_read(const struct key *keyring, ...@@ -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, struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
const struct cred *cred, key_perm_t perm, const struct cred *cred, key_perm_t perm,
unsigned long flags, unsigned long flags,
int (*restrict_link)(struct key *, struct key_restriction *restrict_link,
const struct key_type *,
const union key_payload *),
struct key *dest) struct key *dest)
{ {
struct key *keyring; struct key *keyring;
...@@ -519,17 +524,19 @@ EXPORT_SYMBOL(keyring_alloc); ...@@ -519,17 +524,19 @@ EXPORT_SYMBOL(keyring_alloc);
* @keyring: The keyring being added to. * @keyring: The keyring being added to.
* @type: The type of key being added. * @type: The type of key being added.
* @payload: The payload of the key intended to be 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 * 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 * passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when
* adding a key to a keyring. * adding a key to a keyring.
* *
* This is meant to be passed as the restrict_link parameter to * This is meant to be stored in a key_restriction structure which is passed
* keyring_alloc(). * in the restrict_link parameter to keyring_alloc().
*/ */
int restrict_link_reject(struct key *keyring, int restrict_link_reject(struct key *keyring,
const struct key_type *type, const struct key_type *type,
const union key_payload *payload) const union key_payload *payload,
struct key *restriction_key)
{ {
return -EPERM; return -EPERM;
} }
...@@ -940,6 +947,111 @@ key_ref_t keyring_search(key_ref_t keyring, ...@@ -940,6 +947,111 @@ key_ref_t keyring_search(key_ref_t keyring,
} }
EXPORT_SYMBOL(keyring_search); 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. * Search the given keyring for a key that might be updated.
* *
...@@ -1220,9 +1332,10 @@ void __key_link_end(struct key *keyring, ...@@ -1220,9 +1332,10 @@ void __key_link_end(struct key *keyring,
*/ */
static int __key_link_check_restriction(struct key *keyring, struct key *key) 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 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 @@ void keyring_gc(struct key *keyring, time_t limit) ...@@ -1426,3 +1539,53 @@ void keyring_gc(struct key *keyring, time_t limit)
up_write(&keyring->sem); up_write(&keyring->sem);
kleave(" [gc]"); 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]");
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment