root/security/keys/request_key_auth.c
// SPDX-License-Identifier: GPL-2.0-or-later
/* Request key authorisation token key definition.
 *
 * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * See Documentation/security/keys/request-key.rst
 */

#include <linux/sched.h>
#include <linux/err.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "internal.h"
#include <keys/request_key_auth-type.h>

static int request_key_auth_preparse(struct key_preparsed_payload *);
static void request_key_auth_free_preparse(struct key_preparsed_payload *);
static int request_key_auth_instantiate(struct key *,
                                        struct key_preparsed_payload *);
static void request_key_auth_describe(const struct key *, struct seq_file *);
static void request_key_auth_revoke(struct key *);
static void request_key_auth_destroy(struct key *);
static long request_key_auth_read(const struct key *, char *, size_t);

/*
 * The request-key authorisation key type definition.
 */
struct key_type key_type_request_key_auth = {
        .name           = ".request_key_auth",
        .def_datalen    = sizeof(struct request_key_auth),
        .preparse       = request_key_auth_preparse,
        .free_preparse  = request_key_auth_free_preparse,
        .instantiate    = request_key_auth_instantiate,
        .describe       = request_key_auth_describe,
        .revoke         = request_key_auth_revoke,
        .destroy        = request_key_auth_destroy,
        .read           = request_key_auth_read,
};

static int request_key_auth_preparse(struct key_preparsed_payload *prep)
{
        return 0;
}

static void request_key_auth_free_preparse(struct key_preparsed_payload *prep)
{
}

/*
 * Instantiate a request-key authorisation key.
 */
static int request_key_auth_instantiate(struct key *key,
                                        struct key_preparsed_payload *prep)
{
        rcu_assign_keypointer(key, (struct request_key_auth *)prep->data);
        return 0;
}

/*
 * Describe an authorisation token.
 */
static void request_key_auth_describe(const struct key *key,
                                      struct seq_file *m)
{
        struct request_key_auth *rka = dereference_key_rcu(key);

        if (!rka)
                return;

        seq_puts(m, "key:");
        seq_puts(m, key->description);
        if (key_is_positive(key))
                seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len);
}

/*
 * Read the callout_info data (retrieves the callout information).
 * - the key's semaphore is read-locked
 */
static long request_key_auth_read(const struct key *key,
                                  char *buffer, size_t buflen)
{
        struct request_key_auth *rka = dereference_key_locked(key);
        size_t datalen;
        long ret;

        if (!rka)
                return -EKEYREVOKED;

        datalen = rka->callout_len;
        ret = datalen;

        /* we can return the data as is */
        if (buffer && buflen > 0) {
                if (buflen > datalen)
                        buflen = datalen;

                memcpy(buffer, rka->callout_info, buflen);
        }

        return ret;
}

static void free_request_key_auth(struct request_key_auth *rka)
{
        if (!rka)
                return;
        key_put(rka->target_key);
        key_put(rka->dest_keyring);
        if (rka->cred)
                put_cred(rka->cred);
        kfree(rka->callout_info);
        kfree(rka);
}

/*
 * Dispose of the request_key_auth record under RCU conditions
 */
static void request_key_auth_rcu_disposal(struct rcu_head *rcu)
{
        struct request_key_auth *rka =
                container_of(rcu, struct request_key_auth, rcu);

        free_request_key_auth(rka);
}

/*
 * Handle revocation of an authorisation token key.
 *
 * Called with the key sem write-locked.
 */
static void request_key_auth_revoke(struct key *key)
{
        struct request_key_auth *rka = dereference_key_locked(key);

        kenter("{%d}", key->serial);
        rcu_assign_keypointer(key, NULL);
        call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
}

/*
 * Destroy an instantiation authorisation token key.
 */
static void request_key_auth_destroy(struct key *key)
{
        struct request_key_auth *rka = rcu_access_pointer(key->payload.rcu_data0);

        kenter("{%d}", key->serial);
        if (rka) {
                rcu_assign_keypointer(key, NULL);
                call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
        }
}

/*
 * Create an authorisation token for /sbin/request-key or whoever to gain
 * access to the caller's security data.
 */
struct key *request_key_auth_new(struct key *target, const char *op,
                                 const void *callout_info, size_t callout_len,
                                 struct key *dest_keyring)
{
        struct request_key_auth *rka, *irka;
        const struct cred *cred = current_cred();
        struct key *authkey = NULL;
        char desc[20];
        int ret = -ENOMEM;

        kenter("%d,", target->serial);

        /* allocate a auth record */
        rka = kzalloc_obj(*rka);
        if (!rka)
                goto error;
        rka->callout_info = kmemdup(callout_info, callout_len, GFP_KERNEL);
        if (!rka->callout_info)
                goto error_free_rka;
        rka->callout_len = callout_len;
        strscpy(rka->op, op, sizeof(rka->op));

        /* see if the calling process is already servicing the key request of
         * another process */
        if (cred->request_key_auth) {
                /* it is - use that instantiation context here too */
                down_read(&cred->request_key_auth->sem);

                /* if the auth key has been revoked, then the key we're
                 * servicing is already instantiated */
                if (test_bit(KEY_FLAG_REVOKED,
                             &cred->request_key_auth->flags)) {
                        up_read(&cred->request_key_auth->sem);
                        ret = -EKEYREVOKED;
                        goto error_free_rka;
                }

                irka = cred->request_key_auth->payload.data[0];
                rka->cred = get_cred(irka->cred);
                rka->pid = irka->pid;

                up_read(&cred->request_key_auth->sem);
        }
        else {
                /* it isn't - use this process as the context */
                rka->cred = get_cred(cred);
                rka->pid = current->pid;
        }

        rka->target_key = key_get(target);
        rka->dest_keyring = key_get(dest_keyring);

        /* allocate the auth key */
        sprintf(desc, "%x", target->serial);

        authkey = key_alloc(&key_type_request_key_auth, desc,
                            cred->fsuid, cred->fsgid, cred,
                            KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | KEY_POS_LINK |
                            KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL);
        if (IS_ERR(authkey)) {
                ret = PTR_ERR(authkey);
                goto error_free_rka;
        }

        /* construct the auth key */
        ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL);
        if (ret < 0)
                goto error_put_authkey;

        kleave(" = {%d,%d}", authkey->serial, refcount_read(&authkey->usage));
        return authkey;

error_put_authkey:
        key_put(authkey);
error_free_rka:
        free_request_key_auth(rka);
error:
        kleave("= %d", ret);
        return ERR_PTR(ret);
}

/*
 * Search the current process's keyrings for the authorisation key for
 * instantiation of a key.
 */
struct key *key_get_instantiation_authkey(key_serial_t target_id)
{
        char description[16];
        struct keyring_search_context ctx = {
                .index_key.type         = &key_type_request_key_auth,
                .index_key.description  = description,
                .cred                   = current_cred(),
                .match_data.cmp         = key_default_cmp,
                .match_data.raw_data    = description,
                .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
                .flags                  = (KEYRING_SEARCH_DO_STATE_CHECK |
                                           KEYRING_SEARCH_RECURSE),
        };
        struct key *authkey;
        key_ref_t authkey_ref;

        ctx.index_key.desc_len = sprintf(description, "%x", target_id);

        rcu_read_lock();
        authkey_ref = search_process_keyrings_rcu(&ctx);
        rcu_read_unlock();

        if (IS_ERR(authkey_ref)) {
                authkey = ERR_CAST(authkey_ref);
                if (authkey == ERR_PTR(-EAGAIN))
                        authkey = ERR_PTR(-ENOKEY);
                goto error;
        }

        authkey = key_ref_to_ptr(authkey_ref);
        if (test_bit(KEY_FLAG_REVOKED, &authkey->flags)) {
                key_put(authkey);
                authkey = ERR_PTR(-EKEYREVOKED);
        }

error:
        return authkey;
}