Commit f0df90cd authored by David Howells's avatar David Howells
Browse files

Merge branch 'keyctl-restrict' of...

Merge branch 'keyctl-restrict' of git://git.kernel.org/pub/scm/linux/kernel/git/martineau/linux into keys-next

To quote Mat Martineau:

"""
Keyrings recently acquired the ability to validate keys before they are
linked using kernel internal APIs. This patch set enables configuration
of restricted keyrings from userspace.

These patches apply to linux-fs/keys-misc and are also available here:

    https://git.kernel.org/cgit/linux/kernel/git/martineau/linux.git/log/?h=keyctl-restrict



v13: Detect and avoid cycles in restriction references, and change
restrictions to store a single key pointer rather than arbitrary data.

v12: Rework the KEYCTL_RESTRICT_KEYRING command to take an additional
parameter, renamed some functions based on feedback, and dropped an
unnecessary locking change (patch 1 in previous set).

v11: Configure restrictions using KEYCTL_RESTRICT_KEYRING instead of
using a keyring payload at creation time. Make the garbage collector
aware of restrictions.

v10: Fixups from maintainer feedback. Added some missing documentation.

v9: Rebased on linux-fs/keys-misc (v4.9-rc5)

v8: Add option to look for signing keys within the destination keyring.
Fix a consistency issue with keyring locking and restriction checks.

v7: Rework key restriction payload syntax. Move key-type-specific payload
parsing to the key-type. Attach more restriction information to keyrings
(restriction function, data, and data free) so future restrictions are not
limited to storing a key ID to use for key validation. Validate key before
using it to verify another key. Modify key type locking model to allow key
type lookup during keyring creation.

v6: Return error if only restrict_key is supplied, address misc. review
comments.

v5: Fixed signature bypass problem in patch 3/6

v4: Added userspace restriction options based on builtin keyrings.
restrict_link_by_signature implementation is no longer modified. Split
up v3's patch 2/5 to isolate the change to key.h.

v3: Updated commit message for patch 2/5 (restrict_link_by_signature_indirect)

v2: Payload is now preparsed
"""

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parents 73cdd290 8e323a02
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
@@ -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.
+50 −16
Original line number Diff line number Diff line
@@ -857,6 +857,31 @@ The keyctl syscall functions are:
     supported, error ENOKEY if the key could not be found, or error
     EACCES if the key is not readable by the caller.

 (*) 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 +1057,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 +1069,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 +1470,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
============================
+31 −8
Original line number Diff line number Diff line
@@ -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");
+94 −8
Original line number Diff line number Diff line
@@ -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,6 +452,90 @@ 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,
@@ -460,6 +545,7 @@ struct key_type key_type_asymmetric = {
	.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);

+158 −3
Original line number Diff line number Diff line
@@ -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);
}
Loading