linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: David Howells <dhowells@redhat.com>
To: keyrings@vger.kernel.org, trond.myklebust@hammerspace.com,
	sfrench@samba.org
Cc: linux-security-module@vger.kernel.org, linux-nfs@vger.kernel.org,
	linux-cifs@vger.kernel.org, linux-fsdevel@vger.kernel.org,
	rgb@redhat.com, dhowells@redhat.com,
	linux-kernel@vger.kernel.org
Subject: [RFC PATCH 18/27] keys: Find the least-recently used unseen key in a keyring.
Date: Fri, 15 Feb 2019 16:10:13 +0000	[thread overview]
Message-ID: <155024701313.21651.15123621736164077230.stgit@warthog.procyon.org.uk> (raw)
In-Reply-To: <155024683432.21651.14153938339749694146.stgit@warthog.procyon.org.uk>

Provide a keyctl by which the oldest "unseen" key in a keyring can be
found.  The "unseenness" is controlled by a flag on the key, so is shared
across all keyrings that might link to a key.  The flag is only set by this
keyctl.  The keyctl looks like:

	key = keyctl_find_lru(key_serial_t keyring,
			      const char *type_name)

It searches the nominated keyring subtree for a valid key of the specified
type and returns its serial number or -ENOKEY if no valid, unseen keys are
found.

This is primarily intended for use with ".request_key_auth"-type keys in
container upcall management.  Ordinarily, it should be possible to just
pick the serial numbers out of the notification records from when an auth
key gets added to the upcall keyring, but if the buffer gets overrun, then
some other means must be employed.

[!] I'm not sure I need to do the "unseen" check at all.  This call is only
    really needed if there's a notification buffer overrun.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 include/linux/key.h         |    1 
 include/uapi/linux/keyctl.h |    1 
 security/keys/compat.c      |    2 +
 security/keys/container.c   |  106 +++++++++++++++++++++++++++++++++++++++++++
 security/keys/internal.h    |    1 
 security/keys/keyctl.c      |    3 +
 6 files changed, 114 insertions(+)

diff --git a/include/linux/key.h b/include/linux/key.h
index 165f842ec042..de190036512b 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -219,6 +219,7 @@ struct key {
 #define KEY_FLAG_KEEP		8	/* set if key should not be removed */
 #define KEY_FLAG_UID_KEYRING	9	/* set if key is a user or user session keyring */
 #define KEY_FLAG_SET_WATCH_PROXY 10	/* Set if watch_proxy should be set on added keys */
+#define KEY_FLAG_SEEN		11	/* Set if returned by keyctl_find_oldest_key() */
 
 	/* the key type and key description string
 	 * - the desc is used to match a key against search criteria
diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h
index 425bbd9612c4..5b792303a05b 100644
--- a/include/uapi/linux/keyctl.h
+++ b/include/uapi/linux/keyctl.h
@@ -71,6 +71,7 @@
 #define KEYCTL_CONTAINER_INTERCEPT	31	/* Intercept upcalls inside a container */
 #define KEYCTL_QUERY_REQUEST_KEY_AUTH	32	/* Query a request_key_auth key */
 #define KEYCTL_MOVE			33	/* Move keys between keyrings */
+#define KEYCTL_FIND_LRU			34	/* Find the least-recently used key in a keyring */
 
 /* keyctl structures */
 struct keyctl_dh_params {
diff --git a/security/keys/compat.c b/security/keys/compat.c
index ed36efa13c48..160fb7b37352 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -166,6 +166,8 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
 		return keyctl_container_intercept(arg2, compat_ptr(arg3), arg4, arg5);
 	case KEYCTL_QUERY_REQUEST_KEY_AUTH:
 		return keyctl_query_request_key_auth(arg2, compat_ptr(arg3));
+	case KEYCTL_FIND_LRU:
+		return keyctl_find_lru(arg2, compat_ptr(arg3));
 #endif
 
 	case KEYCTL_MOVE:
diff --git a/security/keys/container.c b/security/keys/container.c
index 115998e867cd..8e6b3c8710e2 100644
--- a/security/keys/container.c
+++ b/security/keys/container.c
@@ -267,3 +267,109 @@ long keyctl_query_request_key_auth(key_serial_t auth_id,
 
 	return 0;
 }
+
+struct key_lru_search_state {
+	struct key	*candidate;
+	time64_t	oldest;
+};
+
+/*
+ * Iterate over all the keys in the keyring looking for the one with the oldest
+ * timestamp.
+ */
+static bool cmp_lru(const struct key *key,
+			   const struct key_match_data *match_data)
+{
+	struct key_lru_search_state *state = (void *)match_data->raw_data;
+	time64_t t;
+
+	t = READ_ONCE(key->last_used_at);
+	if (state->oldest > t && !test_bit(KEY_FLAG_SEEN, &key->flags)) {
+		state->oldest = t;
+		state->candidate = (struct key *)key;
+	}
+
+	return false;
+}
+
+/*
+ * Find the oldest key in a keyring of a particular type.
+ */
+long keyctl_find_lru(key_serial_t _keyring, const char __user *type_name)
+{
+	struct key_lru_search_state state;
+	struct keyring_search_context ctx = {
+		.index_key.description	= NULL,
+		.cred			= current_cred(),
+		.match_data.cmp		= cmp_lru,
+		.match_data.raw_data	= &state,
+		.match_data.lookup_type	= KEYRING_SEARCH_LOOKUP_ITERATE,
+		.flags			= KEYRING_SEARCH_DO_STATE_CHECK,
+	};
+	struct key_type *ktype;
+	struct key *key;
+	key_ref_t keyring_ref, ref;
+	char type[32];
+	int ret, max_iter = 10;
+
+	if (!_keyring || !type_name)
+		return -EINVAL;
+
+	/* We want to allow special types, such as ".request_key_auth" */
+	ret = strncpy_from_user(type, type_name, sizeof(type));
+	if (ret < 0)
+		return ret;
+	if (ret == 0 || ret >= sizeof(type))
+		return -EINVAL;
+	type[ret] = '\0';
+
+	keyring_ref = lookup_user_key(_keyring, 0, KEY_NEED_SEARCH);
+	if (IS_ERR(keyring_ref))
+		return PTR_ERR(keyring_ref);
+
+	if (strcmp(type, key_type_request_key_auth.name) == 0) {
+		ktype = &key_type_request_key_auth;
+	} else {
+		ktype = key_type_lookup(type);
+		if (IS_ERR(ktype)) {
+			ret = PTR_ERR(ktype);
+			goto error_ring;
+		}
+	}
+
+	ctx.index_key.type = ktype;
+
+	do {
+		state.oldest = TIME64_MAX;
+		state.candidate = NULL;
+
+		rcu_read_lock();
+
+		/* Scan the keyring.  We expect this to end in -EAGAIN as we
+		 * can't generate a result until the entire scan is completed.
+		 */
+		ret = -EAGAIN;
+		ref = keyring_search_aux(keyring_ref, &ctx);
+
+		key = state.candidate;
+		if (key &&
+		    !test_and_set_bit(KEY_FLAG_SEEN, &key->flags) &&
+		    key_validate(key) == 0) {
+			ret = key->serial;
+			goto error_unlock;
+		}
+
+
+		rcu_read_unlock();
+	} while (--max_iter > 0);
+	goto error_type;
+
+error_unlock:
+	rcu_read_unlock();
+error_type:
+	if (ktype != &key_type_request_key_auth)
+		key_type_put(ktype);
+error_ring:
+	key_ref_put(keyring_ref);
+	return ret;
+}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index bad4a8038a99..fe4a4da1ff17 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -365,6 +365,7 @@ static inline long keyctl_watch_key(key_serial_t key_id, int watch_fd, int watch
 extern long keyctl_container_intercept(int, const char __user *, unsigned int, key_serial_t);
 extern long keyctl_query_request_key_auth(key_serial_t,
 					  struct keyctl_query_request_key_auth __user *);
+extern long keyctl_find_lru(key_serial_t, const char __user *);
 #endif
 
 /*
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 6057b810c611..1446bc52e369 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1916,6 +1916,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
 		return keyctl_query_request_key_auth(
 			(key_serial_t)arg2,
 			(struct keyctl_query_request_key_auth __user *)arg3);
+	case KEYCTL_FIND_LRU:
+		return keyctl_find_lru((key_serial_t)arg2,
+				       (const char __user *)arg3);
 #endif
 
 	case KEYCTL_MOVE:


  parent reply	other threads:[~2019-02-15 16:10 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-02-15 16:07 [RFC PATCH 00/27] Containers and using authenticated filesystems David Howells
2019-02-15 16:07 ` [RFC PATCH 01/27] containers: Rename linux/container.h to linux/container_dev.h David Howells
2019-02-15 16:07 ` [RFC PATCH 02/27] containers: Implement containers as kernel objects David Howells
2019-02-17 18:57   ` Trond Myklebust
2019-02-17 19:39   ` James Bottomley
2019-02-19 16:56   ` Eric W. Biederman
2019-02-19 23:03   ` David Howells
2019-02-20 14:23     ` Trond Myklebust
2019-02-19 23:06   ` David Howells
2019-02-20  2:20     ` James Bottomley
2019-02-20  3:04       ` Ian Kent
2019-02-20  3:46         ` James Bottomley
2019-02-20  4:42           ` Ian Kent
2019-02-20  6:57           ` Paul Moore
2019-02-19 23:13   ` David Howells
2019-02-19 23:55   ` Tycho Andersen
2019-02-20  2:46   ` Ian Kent
2019-02-20 13:26     ` Christian Brauner
2019-02-21 10:39       ` Ian Kent
2019-02-15 16:07 ` [RFC PATCH 03/27] containers: Provide /proc/containers David Howells
2019-02-15 16:07 ` [RFC PATCH 04/27] containers: Allow a process to be forked into a container David Howells
2019-02-15 17:39   ` Stephen Smalley
2019-02-19 16:39   ` Eric W. Biederman
2019-02-19 23:16   ` David Howells
2019-02-15 16:07 ` [RFC PATCH 05/27] containers: Open a socket inside " David Howells
2019-02-19 16:41   ` Eric W. Biederman
2019-02-15 16:08 ` [RFC PATCH 06/27] containers, vfs: Allow syscall dirfd arguments to take a container fd David Howells
2019-02-19 16:45   ` Eric W. Biederman
2019-02-19 23:24   ` David Howells
2019-02-15 16:08 ` [RFC PATCH 07/27] containers: Make fsopen() able to create a superblock in a container David Howells
2019-02-15 16:08 ` [RFC PATCH 08/27] containers, vfs: Honour CONTAINER_NEW_EMPTY_FS_NS David Howells
2019-02-17  0:11   ` Al Viro
2019-02-15 16:08 ` [RFC PATCH 09/27] vfs: Allow mounting to other namespaces David Howells
2019-02-17  0:14   ` Al Viro
2019-02-15 16:08 ` [RFC PATCH 10/27] containers: Provide fs_context op for container setting David Howells
2019-02-15 16:09 ` [RFC PATCH 11/27] containers: Sample program for driving container objects David Howells
2019-02-15 16:09 ` [RFC PATCH 12/27] containers: Allow a daemon to intercept request_key upcalls in a container David Howells
2019-02-15 16:09 ` [RFC PATCH 13/27] keys: Provide a keyctl to query a request_key authentication key David Howells
2019-02-15 16:09 ` [RFC PATCH 14/27] keys: Break bits out of key_unlink() David Howells
2019-02-15 16:09 ` [RFC PATCH 15/27] keys: Make __key_link_begin() handle lockdep nesting David Howells
2019-02-15 16:09 ` [RFC PATCH 16/27] keys: Grant Link permission to possessers of request_key auth keys David Howells
2019-02-15 16:10 ` [RFC PATCH 17/27] keys: Add a keyctl to move a key between keyrings David Howells
2019-02-15 16:10 ` David Howells [this message]
2019-02-15 16:10 ` [RFC PATCH 19/27] containers: Sample: request_key upcall handling David Howells
2019-02-15 16:10 ` [RFC PATCH 20/27] container, keys: Add a container keyring David Howells
2019-02-15 21:46   ` Eric Biggers
2019-02-15 16:11 ` [RFC PATCH 21/27] keys: Fix request_key() lack of Link perm check on found key David Howells
2019-02-15 16:11 ` [RFC PATCH 22/27] KEYS: Replace uid/gid/perm permissions checking with an ACL David Howells
2019-02-15 17:32   ` Stephen Smalley
2019-02-15 17:39   ` David Howells
2019-02-15 16:11 ` [RFC PATCH 23/27] KEYS: Provide KEYCTL_GRANT_PERMISSION David Howells
2019-02-15 16:11 ` [RFC PATCH 24/27] keys: Allow a container to be specified as a subject in a key's ACL David Howells
2019-02-15 16:11 ` [RFC PATCH 25/27] keys: Provide a way to ask for the container keyring David Howells
2019-02-15 16:12 ` [RFC PATCH 26/27] keys: Allow containers to be included in key ACLs by name David Howells
2019-02-15 16:12 ` [RFC PATCH 27/27] containers: Sample to grant access to a key in a container David Howells
2019-02-15 22:36 ` [RFC PATCH 00/27] Containers and using authenticated filesystems James Morris
2019-02-19 16:35 ` Eric W. Biederman
2019-02-20 14:18   ` Christian Brauner
2019-02-19 23:42 ` David Howells
2019-02-20  7:00   ` Paul Moore
2019-02-20 18:54   ` Steve French

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=155024701313.21651.15123621736164077230.stgit@warthog.procyon.org.uk \
    --to=dhowells@redhat.com \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-cifs@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-nfs@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=rgb@redhat.com \
    --cc=sfrench@samba.org \
    --cc=trond.myklebust@hammerspace.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).