[PATCH 2/2] KEYS: Add keyagent request_key

From: Benjamin Coddington
Date: Tue Jul 12 2022 - 08:35:36 EST


During key construction, search the calling process' session keyring for a
keyagent key with a description that matches the requested key_type. If
found, link the authkey into the keyagent's process_keyring, and signal the
keyagent task with a realtime signal containing the serial number of the
key that needs to be constructed.

Signed-off-by: Benjamin Coddington <bcodding@xxxxxxxxxx>
---
include/uapi/asm-generic/siginfo.h | 1 +
security/keys/internal.h | 4 ++
security/keys/keyagent.c | 85 ++++++++++++++++++++++++++++++
security/keys/request_key.c | 9 ++++
4 files changed, 99 insertions(+)

diff --git a/include/uapi/asm-generic/siginfo.h b/include/uapi/asm-generic/siginfo.h
index ffbe4cec9f32..542e297f4466 100644
--- a/include/uapi/asm-generic/siginfo.h
+++ b/include/uapi/asm-generic/siginfo.h
@@ -185,6 +185,7 @@ typedef struct siginfo {
#define SI_SIGIO -5 /* sent by queued SIGIO */
#define SI_TKILL -6 /* sent by tkill system call */
#define SI_DETHREAD -7 /* sent by execve() killing subsidiary threads */
+#define SI_KEYAGENT -8 /* sent by request-key */
#define SI_ASYNCNL -60 /* sent by glibc async name lookup completion */

#define SI_FROMUSER(siptr) ((siptr)->si_code <= 0)
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 9b9cf3b6fcbb..a6db6eecfff5 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -372,5 +372,9 @@ static inline void key_check(const struct key *key)

#define key_check(key) do {} while(0)

+#endif
+
+#ifdef CONFIG_KEYAGENT
+extern int keyagent_request_key(struct key *authkey, void *aux);
#endif
#endif /* _INTERNAL_H */
diff --git a/security/keys/keyagent.c b/security/keys/keyagent.c
index 87ebfe00c710..cf70146925f0 100644
--- a/security/keys/keyagent.c
+++ b/security/keys/keyagent.c
@@ -9,8 +9,11 @@
#include <linux/slab.h>
#include <linux/key.h>
#include <linux/key-type.h>
+#include <linux/sched/signal.h>
+#include <linux/sched/task.h>

#include <keys/user-type.h>
+#include <keys/request_key_auth-type.h>

/*
* Keyagent key payload.
@@ -20,6 +23,88 @@ struct keyagent {
int sig;
};

+struct key_type key_type_keyagent;
+
+/*
+ * Given a key representing a keyagent and a target_key to construct, link
+ * the the authkey into the keyagent's process_keyring and signal the
+ * keyagent to construct the target_key.
+ */
+static int keyagent_signal(struct key *ka_key, struct key *target_key,
+ struct key *authkey)
+{
+ struct keyagent *ka = ka_key->payload.data[0];
+ struct task_struct *task;
+ const struct cred *cred;
+ kernel_siginfo_t info = {
+ .si_code = SI_KEYAGENT,
+ .si_signo = ka->sig,
+ .si_int = target_key->serial,
+ };
+ int ret = -ENOKEY;
+
+ task = get_pid_task(ka->pid, PIDTYPE_PID);
+ /* If the task is gone, should we revoke the keyagent key? */
+ if (!task) {
+ key_revoke(ka_key);
+ goto out;
+ }
+
+ /* We're expecting valid keyagents to have a process keyring,
+ * if not, should we warn? */
+ cred = get_cred(task->cred);
+ if (!cred->process_keyring)
+ goto out_nolink;
+
+ /* Link the autkey to the keyagent's process_keyring */
+ ret = key_link(cred->process_keyring, authkey);
+ if (ret < 0)
+ goto out_nolink;
+
+ ret = send_sig_info(ka->sig, &info, task);
+
+out_nolink:
+ put_cred(cred);
+ put_task_struct(task);
+out:
+ return ret;
+}
+
+/*
+ * Search the calling process' keyrings for a keyagent that
+ * matches the requested key type. If found, signal the keyagent
+ * to construct and link the key, else return -ENOKEY.
+ */
+int keyagent_request_key(struct key *authkey, void *aux)
+{
+ struct key *ka_key, *target_key;
+ struct request_key_auth *rka;
+ key_ref_t ka_ref;
+ const struct cred *cred = current_cred();
+ int ret;
+
+ /* We must be careful not to touch authkey and aux if
+ * returning -ENOKEY, since it will be reused. */
+ rka = get_request_key_auth(authkey);
+ target_key = rka->target_key;
+
+ /* Does the calling process have a keyagent in its session keyring? */
+ ka_ref = keyring_search(
+ make_key_ref(cred->session_keyring, 1),
+ &key_type_keyagent,
+ target_key->type->name, false);
+
+ if (IS_ERR(ka_ref))
+ return -ENOKEY;
+
+ /* We found a keyagent, let's call out to it. */
+ ka_key = key_ref_to_ptr(ka_ref);
+ ret = keyagent_signal(ka_key, target_key, authkey);
+ key_put(key_ref_to_ptr(ka_ref));
+
+ return ret;
+}
+
/*
* Instantiate takes a reference to the current task's struct pid
* and the requested realtime signal number.
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 2da4404276f0..4c1f5ef55856 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -240,9 +240,18 @@ static int construct_key(struct key *key, const void *callout_info,
actor = call_sbin_request_key;
if (key->type->request_key)
actor = key->type->request_key;
+#ifdef CONFIG_KEYAGENT
+ else {
+ ret = keyagent_request_key(authkey, aux);

+ /* ENOKEY: no keyagents match on calling process' keyrings */
+ if (ret != -ENOKEY)
+ goto done;
+ }
+#endif
ret = actor(authkey, aux);

+done:
/* check that the actor called complete_request_key() prior to
* returning an error */
WARN_ON(ret < 0 &&
--
2.31.1