Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

KEYS: Add invalidation support



Add support for invalidating a key - which renders it immediately invisible to
further searches and causes the garbage collector to immediately wake up,
remove it from keyrings and then destroy it when it's no longer referenced.

It's better not to do this with keyctl_revoke() as that marks the key to start
returning -EKEYREVOKED to searches when what is actually desired is to have the
key refetched.

To invalidate a key the caller must be granted SEARCH permission by the key.
This may be too strict.  It may be better to also permit invalidation if the
caller has any of READ, WRITE or SETATTR permission.

The primary use for this is to evict keys that are cached in special keyrings,
such as the DNS resolver or an ID mapper.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent 31d5a79d
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -805,6 +805,23 @@ The keyctl syscall functions are:
     kernel and resumes executing userspace.


 (*) Invalidate a key.

	long keyctl(KEYCTL_INVALIDATE, key_serial_t key);

     This function marks a key as being invalidated and then wakes up the
     garbage collector.  The garbage collector immediately removes invalidated
     keys from all keyrings and deletes the key when its reference count
     reaches zero.

     Keys that are marked invalidated become invisible to normal key operations
     immediately, though they are still visible in /proc/keys until deleted
     (they're marked with an 'i' flag).

     A process must have search permission on the key for this function to be
     successful.


===============
KERNEL SERVICES
===============
+3 −0
Original line number Diff line number Diff line
@@ -160,6 +160,7 @@ struct key {
#define KEY_FLAG_USER_CONSTRUCT	4	/* set if key is being constructed in userspace */
#define KEY_FLAG_NEGATIVE	5	/* set if key is negative */
#define KEY_FLAG_ROOT_CAN_CLEAR	6	/* set if key can be cleared by root without permission */
#define KEY_FLAG_INVALIDATED	7	/* set if key has been invalidated */

	/* the description string
	 * - this is used to match a key against search criteria
@@ -203,6 +204,7 @@ extern struct key *key_alloc(struct key_type *type,
#define KEY_ALLOC_NOT_IN_QUOTA	0x0002	/* not in quota */

extern void key_revoke(struct key *key);
extern void key_invalidate(struct key *key);
extern void key_put(struct key *key);

static inline struct key *key_get(struct key *key)
@@ -323,6 +325,7 @@ extern void key_init(void);
#define key_serial(k)			0
#define key_get(k) 			({ NULL; })
#define key_revoke(k)			do { } while(0)
#define key_invalidate(k)		do { } while(0)
#define key_put(k)			do { } while(0)
#define key_ref_put(k)			do { } while(0)
#define make_key_ref(k, p)		NULL
+1 −0
Original line number Diff line number Diff line
@@ -55,5 +55,6 @@
#define KEYCTL_SESSION_TO_PARENT	18	/* apply session keyring to parent process */
#define KEYCTL_REJECT			19	/* reject a partially constructed key */
#define KEYCTL_INSTANTIATE_IOV		20	/* instantiate a partially constructed key */
#define KEYCTL_INVALIDATE		21	/* invalidate a key */

#endif /*  _LINUX_KEYCTL_H */
+3 −0
Original line number Diff line number Diff line
@@ -135,6 +135,9 @@ asmlinkage long compat_sys_keyctl(u32 option,
		return compat_keyctl_instantiate_key_iov(
			arg2, compat_ptr(arg3), arg4, arg5);

	case KEYCTL_INVALIDATE:
		return keyctl_invalidate_key(arg2);

	default:
		return -EOPNOTSUPP;
	}
+14 −7
Original line number Diff line number Diff line
@@ -71,6 +71,15 @@ void key_schedule_gc(time_t gc_at)
	}
}

/*
 * Schedule a dead links collection run.
 */
void key_schedule_gc_links(void)
{
	set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags);
	queue_work(system_nrt_wq, &key_gc_work);
}

/*
 * Some key's cleanup time was met after it expired, so we need to get the
 * reaper to go through a cycle finding expired keys.
@@ -79,8 +88,7 @@ static void key_gc_timer_func(unsigned long data)
{
	kenter("");
	key_gc_next_run = LONG_MAX;
	set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags);
	queue_work(system_nrt_wq, &key_gc_work);
	key_schedule_gc_links();
}

/*
@@ -131,12 +139,12 @@ void key_gc_keytype(struct key_type *ktype)
static void key_gc_keyring(struct key *keyring, time_t limit)
{
	struct keyring_list *klist;
	struct key *key;
	int loop;

	kenter("%x", key_serial(keyring));

	if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
	if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) |
			      (1 << KEY_FLAG_REVOKED)))
		goto dont_gc;

	/* scan the keyring looking for dead keys */
@@ -148,9 +156,8 @@ static void key_gc_keyring(struct key *keyring, time_t limit)
	loop = klist->nkeys;
	smp_rmb();
	for (loop--; loop >= 0; loop--) {
		key = rcu_dereference(klist->keys[loop]);
		if (test_bit(KEY_FLAG_DEAD, &key->flags) ||
		    (key->expiry > 0 && key->expiry <= limit))
		struct key *key = rcu_dereference(klist->keys[loop]);
		if (key_is_dead(key, limit))
			goto do_gc;
	}

Loading