700 lines
16 KiB
C
700 lines
16 KiB
C
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
/* RxRPC key management
|
||
|
*
|
||
|
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
|
||
|
* Written by David Howells (dhowells@redhat.com)
|
||
|
*
|
||
|
* RxRPC keys should have a description of describing their purpose:
|
||
|
* "afs@example.com"
|
||
|
*/
|
||
|
|
||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||
|
|
||
|
#include <crypto/skcipher.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/net.h>
|
||
|
#include <linux/skbuff.h>
|
||
|
#include <linux/key-type.h>
|
||
|
#include <linux/ctype.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <net/sock.h>
|
||
|
#include <net/af_rxrpc.h>
|
||
|
#include <keys/rxrpc-type.h>
|
||
|
#include <keys/user-type.h>
|
||
|
#include "ar-internal.h"
|
||
|
|
||
|
static int rxrpc_preparse(struct key_preparsed_payload *);
|
||
|
static void rxrpc_free_preparse(struct key_preparsed_payload *);
|
||
|
static void rxrpc_destroy(struct key *);
|
||
|
static void rxrpc_describe(const struct key *, struct seq_file *);
|
||
|
static long rxrpc_read(const struct key *, char *, size_t);
|
||
|
|
||
|
/*
|
||
|
* rxrpc defined keys take an arbitrary string as the description and an
|
||
|
* arbitrary blob of data as the payload
|
||
|
*/
|
||
|
struct key_type key_type_rxrpc = {
|
||
|
.name = "rxrpc",
|
||
|
.flags = KEY_TYPE_NET_DOMAIN,
|
||
|
.preparse = rxrpc_preparse,
|
||
|
.free_preparse = rxrpc_free_preparse,
|
||
|
.instantiate = generic_key_instantiate,
|
||
|
.destroy = rxrpc_destroy,
|
||
|
.describe = rxrpc_describe,
|
||
|
.read = rxrpc_read,
|
||
|
};
|
||
|
EXPORT_SYMBOL(key_type_rxrpc);
|
||
|
|
||
|
/*
|
||
|
* parse an RxKAD type XDR format token
|
||
|
* - the caller guarantees we have at least 4 words
|
||
|
*/
|
||
|
static int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep,
|
||
|
size_t datalen,
|
||
|
const __be32 *xdr, unsigned int toklen)
|
||
|
{
|
||
|
struct rxrpc_key_token *token, **pptoken;
|
||
|
time64_t expiry;
|
||
|
size_t plen;
|
||
|
u32 tktlen;
|
||
|
|
||
|
_enter(",{%x,%x,%x,%x},%u",
|
||
|
ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]),
|
||
|
toklen);
|
||
|
|
||
|
if (toklen <= 8 * 4)
|
||
|
return -EKEYREJECTED;
|
||
|
tktlen = ntohl(xdr[7]);
|
||
|
_debug("tktlen: %x", tktlen);
|
||
|
if (tktlen > AFSTOKEN_RK_TIX_MAX)
|
||
|
return -EKEYREJECTED;
|
||
|
if (toklen < 8 * 4 + tktlen)
|
||
|
return -EKEYREJECTED;
|
||
|
|
||
|
plen = sizeof(*token) + sizeof(*token->kad) + tktlen;
|
||
|
prep->quotalen = datalen + plen;
|
||
|
|
||
|
plen -= sizeof(*token);
|
||
|
token = kzalloc(sizeof(*token), GFP_KERNEL);
|
||
|
if (!token)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
token->kad = kzalloc(plen, GFP_KERNEL);
|
||
|
if (!token->kad) {
|
||
|
kfree(token);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
token->security_index = RXRPC_SECURITY_RXKAD;
|
||
|
token->kad->ticket_len = tktlen;
|
||
|
token->kad->vice_id = ntohl(xdr[0]);
|
||
|
token->kad->kvno = ntohl(xdr[1]);
|
||
|
token->kad->start = ntohl(xdr[4]);
|
||
|
token->kad->expiry = ntohl(xdr[5]);
|
||
|
token->kad->primary_flag = ntohl(xdr[6]);
|
||
|
memcpy(&token->kad->session_key, &xdr[2], 8);
|
||
|
memcpy(&token->kad->ticket, &xdr[8], tktlen);
|
||
|
|
||
|
_debug("SCIX: %u", token->security_index);
|
||
|
_debug("TLEN: %u", token->kad->ticket_len);
|
||
|
_debug("EXPY: %x", token->kad->expiry);
|
||
|
_debug("KVNO: %u", token->kad->kvno);
|
||
|
_debug("PRIM: %u", token->kad->primary_flag);
|
||
|
_debug("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x",
|
||
|
token->kad->session_key[0], token->kad->session_key[1],
|
||
|
token->kad->session_key[2], token->kad->session_key[3],
|
||
|
token->kad->session_key[4], token->kad->session_key[5],
|
||
|
token->kad->session_key[6], token->kad->session_key[7]);
|
||
|
if (token->kad->ticket_len >= 8)
|
||
|
_debug("TCKT: %02x%02x%02x%02x%02x%02x%02x%02x",
|
||
|
token->kad->ticket[0], token->kad->ticket[1],
|
||
|
token->kad->ticket[2], token->kad->ticket[3],
|
||
|
token->kad->ticket[4], token->kad->ticket[5],
|
||
|
token->kad->ticket[6], token->kad->ticket[7]);
|
||
|
|
||
|
/* count the number of tokens attached */
|
||
|
prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1);
|
||
|
|
||
|
/* attach the data */
|
||
|
for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0];
|
||
|
*pptoken;
|
||
|
pptoken = &(*pptoken)->next)
|
||
|
continue;
|
||
|
*pptoken = token;
|
||
|
expiry = rxrpc_u32_to_time64(token->kad->expiry);
|
||
|
if (expiry < prep->expiry)
|
||
|
prep->expiry = expiry;
|
||
|
|
||
|
_leave(" = 0");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* attempt to parse the data as the XDR format
|
||
|
* - the caller guarantees we have more than 7 words
|
||
|
*/
|
||
|
static int rxrpc_preparse_xdr(struct key_preparsed_payload *prep)
|
||
|
{
|
||
|
const __be32 *xdr = prep->data, *token, *p;
|
||
|
const char *cp;
|
||
|
unsigned int len, paddedlen, loop, ntoken, toklen, sec_ix;
|
||
|
size_t datalen = prep->datalen;
|
||
|
int ret, ret2;
|
||
|
|
||
|
_enter(",{%x,%x,%x,%x},%zu",
|
||
|
ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]),
|
||
|
prep->datalen);
|
||
|
|
||
|
if (datalen > AFSTOKEN_LENGTH_MAX)
|
||
|
goto not_xdr;
|
||
|
|
||
|
/* XDR is an array of __be32's */
|
||
|
if (datalen & 3)
|
||
|
goto not_xdr;
|
||
|
|
||
|
/* the flags should be 0 (the setpag bit must be handled by
|
||
|
* userspace) */
|
||
|
if (ntohl(*xdr++) != 0)
|
||
|
goto not_xdr;
|
||
|
datalen -= 4;
|
||
|
|
||
|
/* check the cell name */
|
||
|
len = ntohl(*xdr++);
|
||
|
if (len < 1 || len > AFSTOKEN_CELL_MAX)
|
||
|
goto not_xdr;
|
||
|
datalen -= 4;
|
||
|
paddedlen = (len + 3) & ~3;
|
||
|
if (paddedlen > datalen)
|
||
|
goto not_xdr;
|
||
|
|
||
|
cp = (const char *) xdr;
|
||
|
for (loop = 0; loop < len; loop++)
|
||
|
if (!isprint(cp[loop]))
|
||
|
goto not_xdr;
|
||
|
for (; loop < paddedlen; loop++)
|
||
|
if (cp[loop])
|
||
|
goto not_xdr;
|
||
|
_debug("cellname: [%u/%u] '%*.*s'",
|
||
|
len, paddedlen, len, len, (const char *) xdr);
|
||
|
datalen -= paddedlen;
|
||
|
xdr += paddedlen >> 2;
|
||
|
|
||
|
/* get the token count */
|
||
|
if (datalen < 12)
|
||
|
goto not_xdr;
|
||
|
ntoken = ntohl(*xdr++);
|
||
|
datalen -= 4;
|
||
|
_debug("ntoken: %x", ntoken);
|
||
|
if (ntoken < 1 || ntoken > AFSTOKEN_MAX)
|
||
|
goto not_xdr;
|
||
|
|
||
|
/* check each token wrapper */
|
||
|
p = xdr;
|
||
|
loop = ntoken;
|
||
|
do {
|
||
|
if (datalen < 8)
|
||
|
goto not_xdr;
|
||
|
toklen = ntohl(*p++);
|
||
|
sec_ix = ntohl(*p);
|
||
|
datalen -= 4;
|
||
|
_debug("token: [%x/%zx] %x", toklen, datalen, sec_ix);
|
||
|
paddedlen = (toklen + 3) & ~3;
|
||
|
if (toklen < 20 || toklen > datalen || paddedlen > datalen)
|
||
|
goto not_xdr;
|
||
|
datalen -= paddedlen;
|
||
|
p += paddedlen >> 2;
|
||
|
|
||
|
} while (--loop > 0);
|
||
|
|
||
|
_debug("remainder: %zu", datalen);
|
||
|
if (datalen != 0)
|
||
|
goto not_xdr;
|
||
|
|
||
|
/* okay: we're going to assume it's valid XDR format
|
||
|
* - we ignore the cellname, relying on the key to be correctly named
|
||
|
*/
|
||
|
ret = -EPROTONOSUPPORT;
|
||
|
do {
|
||
|
toklen = ntohl(*xdr++);
|
||
|
token = xdr;
|
||
|
xdr += (toklen + 3) / 4;
|
||
|
|
||
|
sec_ix = ntohl(*token++);
|
||
|
toklen -= 4;
|
||
|
|
||
|
_debug("TOKEN type=%x len=%x", sec_ix, toklen);
|
||
|
|
||
|
switch (sec_ix) {
|
||
|
case RXRPC_SECURITY_RXKAD:
|
||
|
ret2 = rxrpc_preparse_xdr_rxkad(prep, datalen, token, toklen);
|
||
|
break;
|
||
|
default:
|
||
|
ret2 = -EPROTONOSUPPORT;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch (ret2) {
|
||
|
case 0:
|
||
|
ret = 0;
|
||
|
break;
|
||
|
case -EPROTONOSUPPORT:
|
||
|
break;
|
||
|
case -ENOPKG:
|
||
|
if (ret != 0)
|
||
|
ret = -ENOPKG;
|
||
|
break;
|
||
|
default:
|
||
|
ret = ret2;
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
} while (--ntoken > 0);
|
||
|
|
||
|
error:
|
||
|
_leave(" = %d", ret);
|
||
|
return ret;
|
||
|
|
||
|
not_xdr:
|
||
|
_leave(" = -EPROTO");
|
||
|
return -EPROTO;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Preparse an rxrpc defined key.
|
||
|
*
|
||
|
* Data should be of the form:
|
||
|
* OFFSET LEN CONTENT
|
||
|
* 0 4 key interface version number
|
||
|
* 4 2 security index (type)
|
||
|
* 6 2 ticket length
|
||
|
* 8 4 key expiry time (time_t)
|
||
|
* 12 4 kvno
|
||
|
* 16 8 session key
|
||
|
* 24 [len] ticket
|
||
|
*
|
||
|
* if no data is provided, then a no-security key is made
|
||
|
*/
|
||
|
static int rxrpc_preparse(struct key_preparsed_payload *prep)
|
||
|
{
|
||
|
const struct rxrpc_key_data_v1 *v1;
|
||
|
struct rxrpc_key_token *token, **pp;
|
||
|
time64_t expiry;
|
||
|
size_t plen;
|
||
|
u32 kver;
|
||
|
int ret;
|
||
|
|
||
|
_enter("%zu", prep->datalen);
|
||
|
|
||
|
/* handle a no-security key */
|
||
|
if (!prep->data && prep->datalen == 0)
|
||
|
return 0;
|
||
|
|
||
|
/* determine if the XDR payload format is being used */
|
||
|
if (prep->datalen > 7 * 4) {
|
||
|
ret = rxrpc_preparse_xdr(prep);
|
||
|
if (ret != -EPROTO)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* get the key interface version number */
|
||
|
ret = -EINVAL;
|
||
|
if (prep->datalen <= 4 || !prep->data)
|
||
|
goto error;
|
||
|
memcpy(&kver, prep->data, sizeof(kver));
|
||
|
prep->data += sizeof(kver);
|
||
|
prep->datalen -= sizeof(kver);
|
||
|
|
||
|
_debug("KEY I/F VERSION: %u", kver);
|
||
|
|
||
|
ret = -EKEYREJECTED;
|
||
|
if (kver != 1)
|
||
|
goto error;
|
||
|
|
||
|
/* deal with a version 1 key */
|
||
|
ret = -EINVAL;
|
||
|
if (prep->datalen < sizeof(*v1))
|
||
|
goto error;
|
||
|
|
||
|
v1 = prep->data;
|
||
|
if (prep->datalen != sizeof(*v1) + v1->ticket_length)
|
||
|
goto error;
|
||
|
|
||
|
_debug("SCIX: %u", v1->security_index);
|
||
|
_debug("TLEN: %u", v1->ticket_length);
|
||
|
_debug("EXPY: %x", v1->expiry);
|
||
|
_debug("KVNO: %u", v1->kvno);
|
||
|
_debug("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x",
|
||
|
v1->session_key[0], v1->session_key[1],
|
||
|
v1->session_key[2], v1->session_key[3],
|
||
|
v1->session_key[4], v1->session_key[5],
|
||
|
v1->session_key[6], v1->session_key[7]);
|
||
|
if (v1->ticket_length >= 8)
|
||
|
_debug("TCKT: %02x%02x%02x%02x%02x%02x%02x%02x",
|
||
|
v1->ticket[0], v1->ticket[1],
|
||
|
v1->ticket[2], v1->ticket[3],
|
||
|
v1->ticket[4], v1->ticket[5],
|
||
|
v1->ticket[6], v1->ticket[7]);
|
||
|
|
||
|
ret = -EPROTONOSUPPORT;
|
||
|
if (v1->security_index != RXRPC_SECURITY_RXKAD)
|
||
|
goto error;
|
||
|
|
||
|
plen = sizeof(*token->kad) + v1->ticket_length;
|
||
|
prep->quotalen = plen + sizeof(*token);
|
||
|
|
||
|
ret = -ENOMEM;
|
||
|
token = kzalloc(sizeof(*token), GFP_KERNEL);
|
||
|
if (!token)
|
||
|
goto error;
|
||
|
token->kad = kzalloc(plen, GFP_KERNEL);
|
||
|
if (!token->kad)
|
||
|
goto error_free;
|
||
|
|
||
|
token->security_index = RXRPC_SECURITY_RXKAD;
|
||
|
token->kad->ticket_len = v1->ticket_length;
|
||
|
token->kad->expiry = v1->expiry;
|
||
|
token->kad->kvno = v1->kvno;
|
||
|
memcpy(&token->kad->session_key, &v1->session_key, 8);
|
||
|
memcpy(&token->kad->ticket, v1->ticket, v1->ticket_length);
|
||
|
|
||
|
/* count the number of tokens attached */
|
||
|
prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1);
|
||
|
|
||
|
/* attach the data */
|
||
|
pp = (struct rxrpc_key_token **)&prep->payload.data[0];
|
||
|
while (*pp)
|
||
|
pp = &(*pp)->next;
|
||
|
*pp = token;
|
||
|
expiry = rxrpc_u32_to_time64(token->kad->expiry);
|
||
|
if (expiry < prep->expiry)
|
||
|
prep->expiry = expiry;
|
||
|
token = NULL;
|
||
|
ret = 0;
|
||
|
|
||
|
error_free:
|
||
|
kfree(token);
|
||
|
error:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Free token list.
|
||
|
*/
|
||
|
static void rxrpc_free_token_list(struct rxrpc_key_token *token)
|
||
|
{
|
||
|
struct rxrpc_key_token *next;
|
||
|
|
||
|
for (; token; token = next) {
|
||
|
next = token->next;
|
||
|
switch (token->security_index) {
|
||
|
case RXRPC_SECURITY_RXKAD:
|
||
|
kfree(token->kad);
|
||
|
break;
|
||
|
default:
|
||
|
pr_err("Unknown token type %x on rxrpc key\n",
|
||
|
token->security_index);
|
||
|
BUG();
|
||
|
}
|
||
|
|
||
|
kfree(token);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Clean up preparse data.
|
||
|
*/
|
||
|
static void rxrpc_free_preparse(struct key_preparsed_payload *prep)
|
||
|
{
|
||
|
rxrpc_free_token_list(prep->payload.data[0]);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* dispose of the data dangling from the corpse of a rxrpc key
|
||
|
*/
|
||
|
static void rxrpc_destroy(struct key *key)
|
||
|
{
|
||
|
rxrpc_free_token_list(key->payload.data[0]);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* describe the rxrpc key
|
||
|
*/
|
||
|
static void rxrpc_describe(const struct key *key, struct seq_file *m)
|
||
|
{
|
||
|
const struct rxrpc_key_token *token;
|
||
|
const char *sep = ": ";
|
||
|
|
||
|
seq_puts(m, key->description);
|
||
|
|
||
|
for (token = key->payload.data[0]; token; token = token->next) {
|
||
|
seq_puts(m, sep);
|
||
|
|
||
|
switch (token->security_index) {
|
||
|
case RXRPC_SECURITY_RXKAD:
|
||
|
seq_puts(m, "ka");
|
||
|
break;
|
||
|
default: /* we have a ticket we can't encode */
|
||
|
seq_printf(m, "%u", token->security_index);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
sep = " ";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* grab the security key for a socket
|
||
|
*/
|
||
|
int rxrpc_request_key(struct rxrpc_sock *rx, sockptr_t optval, int optlen)
|
||
|
{
|
||
|
struct key *key;
|
||
|
char *description;
|
||
|
|
||
|
_enter("");
|
||
|
|
||
|
if (optlen <= 0 || optlen > PAGE_SIZE - 1 || rx->securities)
|
||
|
return -EINVAL;
|
||
|
|
||
|
description = memdup_sockptr_nul(optval, optlen);
|
||
|
if (IS_ERR(description))
|
||
|
return PTR_ERR(description);
|
||
|
|
||
|
key = request_key_net(&key_type_rxrpc, description, sock_net(&rx->sk), NULL);
|
||
|
if (IS_ERR(key)) {
|
||
|
kfree(description);
|
||
|
_leave(" = %ld", PTR_ERR(key));
|
||
|
return PTR_ERR(key);
|
||
|
}
|
||
|
|
||
|
rx->key = key;
|
||
|
kfree(description);
|
||
|
_leave(" = 0 [key %x]", key->serial);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* generate a server data key
|
||
|
*/
|
||
|
int rxrpc_get_server_data_key(struct rxrpc_connection *conn,
|
||
|
const void *session_key,
|
||
|
time64_t expiry,
|
||
|
u32 kvno)
|
||
|
{
|
||
|
const struct cred *cred = current_cred();
|
||
|
struct key *key;
|
||
|
int ret;
|
||
|
|
||
|
struct {
|
||
|
u32 kver;
|
||
|
struct rxrpc_key_data_v1 v1;
|
||
|
} data;
|
||
|
|
||
|
_enter("");
|
||
|
|
||
|
key = key_alloc(&key_type_rxrpc, "x",
|
||
|
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0,
|
||
|
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||
|
if (IS_ERR(key)) {
|
||
|
_leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
_debug("key %d", key_serial(key));
|
||
|
|
||
|
data.kver = 1;
|
||
|
data.v1.security_index = RXRPC_SECURITY_RXKAD;
|
||
|
data.v1.ticket_length = 0;
|
||
|
data.v1.expiry = rxrpc_time64_to_u32(expiry);
|
||
|
data.v1.kvno = 0;
|
||
|
|
||
|
memcpy(&data.v1.session_key, session_key, sizeof(data.v1.session_key));
|
||
|
|
||
|
ret = key_instantiate_and_link(key, &data, sizeof(data), NULL, NULL);
|
||
|
if (ret < 0)
|
||
|
goto error;
|
||
|
|
||
|
conn->key = key;
|
||
|
_leave(" = 0 [%d]", key_serial(key));
|
||
|
return 0;
|
||
|
|
||
|
error:
|
||
|
key_revoke(key);
|
||
|
key_put(key);
|
||
|
_leave(" = -ENOMEM [ins %d]", ret);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
EXPORT_SYMBOL(rxrpc_get_server_data_key);
|
||
|
|
||
|
/**
|
||
|
* rxrpc_get_null_key - Generate a null RxRPC key
|
||
|
* @keyname: The name to give the key.
|
||
|
*
|
||
|
* Generate a null RxRPC key that can be used to indicate anonymous security is
|
||
|
* required for a particular domain.
|
||
|
*/
|
||
|
struct key *rxrpc_get_null_key(const char *keyname)
|
||
|
{
|
||
|
const struct cred *cred = current_cred();
|
||
|
struct key *key;
|
||
|
int ret;
|
||
|
|
||
|
key = key_alloc(&key_type_rxrpc, keyname,
|
||
|
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
|
||
|
KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||
|
if (IS_ERR(key))
|
||
|
return key;
|
||
|
|
||
|
ret = key_instantiate_and_link(key, NULL, 0, NULL, NULL);
|
||
|
if (ret < 0) {
|
||
|
key_revoke(key);
|
||
|
key_put(key);
|
||
|
return ERR_PTR(ret);
|
||
|
}
|
||
|
|
||
|
return key;
|
||
|
}
|
||
|
EXPORT_SYMBOL(rxrpc_get_null_key);
|
||
|
|
||
|
/*
|
||
|
* read the contents of an rxrpc key
|
||
|
* - this returns the result in XDR form
|
||
|
*/
|
||
|
static long rxrpc_read(const struct key *key,
|
||
|
char *buffer, size_t buflen)
|
||
|
{
|
||
|
const struct rxrpc_key_token *token;
|
||
|
size_t size;
|
||
|
__be32 *xdr, *oldxdr;
|
||
|
u32 cnlen, toksize, ntoks, tok, zero;
|
||
|
u16 toksizes[AFSTOKEN_MAX];
|
||
|
|
||
|
_enter("");
|
||
|
|
||
|
/* we don't know what form we should return non-AFS keys in */
|
||
|
if (memcmp(key->description, "afs@", 4) != 0)
|
||
|
return -EOPNOTSUPP;
|
||
|
cnlen = strlen(key->description + 4);
|
||
|
|
||
|
#define RND(X) (((X) + 3) & ~3)
|
||
|
|
||
|
/* AFS keys we return in XDR form, so we need to work out the size of
|
||
|
* the XDR */
|
||
|
size = 2 * 4; /* flags, cellname len */
|
||
|
size += RND(cnlen); /* cellname */
|
||
|
size += 1 * 4; /* token count */
|
||
|
|
||
|
ntoks = 0;
|
||
|
for (token = key->payload.data[0]; token; token = token->next) {
|
||
|
toksize = 4; /* sec index */
|
||
|
|
||
|
switch (token->security_index) {
|
||
|
case RXRPC_SECURITY_RXKAD:
|
||
|
toksize += 8 * 4; /* viceid, kvno, key*2, begin,
|
||
|
* end, primary, tktlen */
|
||
|
if (!token->no_leak_key)
|
||
|
toksize += RND(token->kad->ticket_len);
|
||
|
break;
|
||
|
|
||
|
default: /* we have a ticket we can't encode */
|
||
|
pr_err("Unsupported key token type (%u)\n",
|
||
|
token->security_index);
|
||
|
return -ENOPKG;
|
||
|
}
|
||
|
|
||
|
_debug("token[%u]: toksize=%u", ntoks, toksize);
|
||
|
if (WARN_ON(toksize > AFSTOKEN_LENGTH_MAX))
|
||
|
return -EIO;
|
||
|
|
||
|
toksizes[ntoks++] = toksize;
|
||
|
size += toksize + 4; /* each token has a length word */
|
||
|
}
|
||
|
|
||
|
#undef RND
|
||
|
|
||
|
if (!buffer || buflen < size)
|
||
|
return size;
|
||
|
|
||
|
xdr = (__be32 *)buffer;
|
||
|
zero = 0;
|
||
|
#define ENCODE(x) \
|
||
|
do { \
|
||
|
*xdr++ = htonl(x); \
|
||
|
} while(0)
|
||
|
#define ENCODE_DATA(l, s) \
|
||
|
do { \
|
||
|
u32 _l = (l); \
|
||
|
ENCODE(l); \
|
||
|
memcpy(xdr, (s), _l); \
|
||
|
if (_l & 3) \
|
||
|
memcpy((u8 *)xdr + _l, &zero, 4 - (_l & 3)); \
|
||
|
xdr += (_l + 3) >> 2; \
|
||
|
} while(0)
|
||
|
#define ENCODE_BYTES(l, s) \
|
||
|
do { \
|
||
|
u32 _l = (l); \
|
||
|
memcpy(xdr, (s), _l); \
|
||
|
if (_l & 3) \
|
||
|
memcpy((u8 *)xdr + _l, &zero, 4 - (_l & 3)); \
|
||
|
xdr += (_l + 3) >> 2; \
|
||
|
} while(0)
|
||
|
#define ENCODE64(x) \
|
||
|
do { \
|
||
|
__be64 y = cpu_to_be64(x); \
|
||
|
memcpy(xdr, &y, 8); \
|
||
|
xdr += 8 >> 2; \
|
||
|
} while(0)
|
||
|
#define ENCODE_STR(s) \
|
||
|
do { \
|
||
|
const char *_s = (s); \
|
||
|
ENCODE_DATA(strlen(_s), _s); \
|
||
|
} while(0)
|
||
|
|
||
|
ENCODE(0); /* flags */
|
||
|
ENCODE_DATA(cnlen, key->description + 4); /* cellname */
|
||
|
ENCODE(ntoks);
|
||
|
|
||
|
tok = 0;
|
||
|
for (token = key->payload.data[0]; token; token = token->next) {
|
||
|
toksize = toksizes[tok++];
|
||
|
ENCODE(toksize);
|
||
|
oldxdr = xdr;
|
||
|
ENCODE(token->security_index);
|
||
|
|
||
|
switch (token->security_index) {
|
||
|
case RXRPC_SECURITY_RXKAD:
|
||
|
ENCODE(token->kad->vice_id);
|
||
|
ENCODE(token->kad->kvno);
|
||
|
ENCODE_BYTES(8, token->kad->session_key);
|
||
|
ENCODE(token->kad->start);
|
||
|
ENCODE(token->kad->expiry);
|
||
|
ENCODE(token->kad->primary_flag);
|
||
|
if (token->no_leak_key)
|
||
|
ENCODE(0);
|
||
|
else
|
||
|
ENCODE_DATA(token->kad->ticket_len, token->kad->ticket);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
pr_err("Unsupported key token type (%u)\n",
|
||
|
token->security_index);
|
||
|
return -ENOPKG;
|
||
|
}
|
||
|
|
||
|
if (WARN_ON((unsigned long)xdr - (unsigned long)oldxdr !=
|
||
|
toksize))
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
#undef ENCODE_STR
|
||
|
#undef ENCODE_DATA
|
||
|
#undef ENCODE64
|
||
|
#undef ENCODE
|
||
|
|
||
|
if (WARN_ON(tok != ntoks))
|
||
|
return -EIO;
|
||
|
if (WARN_ON((unsigned long)xdr - (unsigned long)buffer != size))
|
||
|
return -EIO;
|
||
|
_leave(" = %zu", size);
|
||
|
return size;
|
||
|
}
|