xref: /openbmc/linux/net/rxrpc/key.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28c3e34a4SDavid Howells /* RxRPC key management
38c3e34a4SDavid Howells  *
48c3e34a4SDavid Howells  * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
58c3e34a4SDavid Howells  * Written by David Howells (dhowells@redhat.com)
68c3e34a4SDavid Howells  *
78c3e34a4SDavid Howells  * RxRPC keys should have a description of describing their purpose:
8177b8989SDavid Howells  *	"afs@example.com"
98c3e34a4SDavid Howells  */
108c3e34a4SDavid Howells 
118c3e34a4SDavid Howells #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
128c3e34a4SDavid Howells 
138c3e34a4SDavid Howells #include <crypto/skcipher.h>
148c3e34a4SDavid Howells #include <linux/module.h>
158c3e34a4SDavid Howells #include <linux/net.h>
168c3e34a4SDavid Howells #include <linux/skbuff.h>
178c3e34a4SDavid Howells #include <linux/key-type.h>
188c3e34a4SDavid Howells #include <linux/ctype.h>
198c3e34a4SDavid Howells #include <linux/slab.h>
208c3e34a4SDavid Howells #include <net/sock.h>
218c3e34a4SDavid Howells #include <net/af_rxrpc.h>
228c3e34a4SDavid Howells #include <keys/rxrpc-type.h>
238c3e34a4SDavid Howells #include <keys/user-type.h>
248c3e34a4SDavid Howells #include "ar-internal.h"
258c3e34a4SDavid Howells 
268c3e34a4SDavid Howells static int rxrpc_preparse(struct key_preparsed_payload *);
278c3e34a4SDavid Howells static void rxrpc_free_preparse(struct key_preparsed_payload *);
288c3e34a4SDavid Howells static void rxrpc_destroy(struct key *);
298c3e34a4SDavid Howells static void rxrpc_describe(const struct key *, struct seq_file *);
30d3ec10aaSWaiman Long static long rxrpc_read(const struct key *, char *, size_t);
318c3e34a4SDavid Howells 
328c3e34a4SDavid Howells /*
338c3e34a4SDavid Howells  * rxrpc defined keys take an arbitrary string as the description and an
348c3e34a4SDavid Howells  * arbitrary blob of data as the payload
358c3e34a4SDavid Howells  */
368c3e34a4SDavid Howells struct key_type key_type_rxrpc = {
378c3e34a4SDavid Howells 	.name		= "rxrpc",
389b242610SDavid Howells 	.flags		= KEY_TYPE_NET_DOMAIN,
398c3e34a4SDavid Howells 	.preparse	= rxrpc_preparse,
408c3e34a4SDavid Howells 	.free_preparse	= rxrpc_free_preparse,
418c3e34a4SDavid Howells 	.instantiate	= generic_key_instantiate,
428c3e34a4SDavid Howells 	.destroy	= rxrpc_destroy,
438c3e34a4SDavid Howells 	.describe	= rxrpc_describe,
448c3e34a4SDavid Howells 	.read		= rxrpc_read,
458c3e34a4SDavid Howells };
468c3e34a4SDavid Howells EXPORT_SYMBOL(key_type_rxrpc);
478c3e34a4SDavid Howells 
488c3e34a4SDavid Howells /*
498c3e34a4SDavid Howells  * parse an RxKAD type XDR format token
508c3e34a4SDavid Howells  * - the caller guarantees we have at least 4 words
518c3e34a4SDavid Howells  */
rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload * prep,size_t datalen,const __be32 * xdr,unsigned int toklen)528c3e34a4SDavid Howells static int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep,
538c3e34a4SDavid Howells 				    size_t datalen,
548c3e34a4SDavid Howells 				    const __be32 *xdr, unsigned int toklen)
558c3e34a4SDavid Howells {
568c3e34a4SDavid Howells 	struct rxrpc_key_token *token, **pptoken;
5710674a03SBaolin Wang 	time64_t expiry;
588c3e34a4SDavid Howells 	size_t plen;
598c3e34a4SDavid Howells 	u32 tktlen;
608c3e34a4SDavid Howells 
618c3e34a4SDavid Howells 	_enter(",{%x,%x,%x,%x},%u",
628c3e34a4SDavid Howells 	       ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]),
638c3e34a4SDavid Howells 	       toklen);
648c3e34a4SDavid Howells 
658c3e34a4SDavid Howells 	if (toklen <= 8 * 4)
668c3e34a4SDavid Howells 		return -EKEYREJECTED;
678c3e34a4SDavid Howells 	tktlen = ntohl(xdr[7]);
688c3e34a4SDavid Howells 	_debug("tktlen: %x", tktlen);
698c3e34a4SDavid Howells 	if (tktlen > AFSTOKEN_RK_TIX_MAX)
708c3e34a4SDavid Howells 		return -EKEYREJECTED;
718c3e34a4SDavid Howells 	if (toklen < 8 * 4 + tktlen)
728c3e34a4SDavid Howells 		return -EKEYREJECTED;
738c3e34a4SDavid Howells 
748c3e34a4SDavid Howells 	plen = sizeof(*token) + sizeof(*token->kad) + tktlen;
758c3e34a4SDavid Howells 	prep->quotalen = datalen + plen;
768c3e34a4SDavid Howells 
778c3e34a4SDavid Howells 	plen -= sizeof(*token);
788c3e34a4SDavid Howells 	token = kzalloc(sizeof(*token), GFP_KERNEL);
798c3e34a4SDavid Howells 	if (!token)
808c3e34a4SDavid Howells 		return -ENOMEM;
818c3e34a4SDavid Howells 
828c3e34a4SDavid Howells 	token->kad = kzalloc(plen, GFP_KERNEL);
838c3e34a4SDavid Howells 	if (!token->kad) {
848c3e34a4SDavid Howells 		kfree(token);
858c3e34a4SDavid Howells 		return -ENOMEM;
868c3e34a4SDavid Howells 	}
878c3e34a4SDavid Howells 
888c3e34a4SDavid Howells 	token->security_index	= RXRPC_SECURITY_RXKAD;
898c3e34a4SDavid Howells 	token->kad->ticket_len	= tktlen;
908c3e34a4SDavid Howells 	token->kad->vice_id	= ntohl(xdr[0]);
918c3e34a4SDavid Howells 	token->kad->kvno	= ntohl(xdr[1]);
928c3e34a4SDavid Howells 	token->kad->start	= ntohl(xdr[4]);
938c3e34a4SDavid Howells 	token->kad->expiry	= ntohl(xdr[5]);
948c3e34a4SDavid Howells 	token->kad->primary_flag = ntohl(xdr[6]);
958c3e34a4SDavid Howells 	memcpy(&token->kad->session_key, &xdr[2], 8);
968c3e34a4SDavid Howells 	memcpy(&token->kad->ticket, &xdr[8], tktlen);
978c3e34a4SDavid Howells 
988c3e34a4SDavid Howells 	_debug("SCIX: %u", token->security_index);
998c3e34a4SDavid Howells 	_debug("TLEN: %u", token->kad->ticket_len);
1008c3e34a4SDavid Howells 	_debug("EXPY: %x", token->kad->expiry);
1018c3e34a4SDavid Howells 	_debug("KVNO: %u", token->kad->kvno);
1028c3e34a4SDavid Howells 	_debug("PRIM: %u", token->kad->primary_flag);
1038c3e34a4SDavid Howells 	_debug("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x",
1048c3e34a4SDavid Howells 	       token->kad->session_key[0], token->kad->session_key[1],
1058c3e34a4SDavid Howells 	       token->kad->session_key[2], token->kad->session_key[3],
1068c3e34a4SDavid Howells 	       token->kad->session_key[4], token->kad->session_key[5],
1078c3e34a4SDavid Howells 	       token->kad->session_key[6], token->kad->session_key[7]);
1088c3e34a4SDavid Howells 	if (token->kad->ticket_len >= 8)
1098c3e34a4SDavid Howells 		_debug("TCKT: %02x%02x%02x%02x%02x%02x%02x%02x",
1108c3e34a4SDavid Howells 		       token->kad->ticket[0], token->kad->ticket[1],
1118c3e34a4SDavid Howells 		       token->kad->ticket[2], token->kad->ticket[3],
1128c3e34a4SDavid Howells 		       token->kad->ticket[4], token->kad->ticket[5],
1138c3e34a4SDavid Howells 		       token->kad->ticket[6], token->kad->ticket[7]);
1148c3e34a4SDavid Howells 
1158c3e34a4SDavid Howells 	/* count the number of tokens attached */
1168c3e34a4SDavid Howells 	prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1);
1178c3e34a4SDavid Howells 
1188c3e34a4SDavid Howells 	/* attach the data */
1198c3e34a4SDavid Howells 	for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0];
1208c3e34a4SDavid Howells 	     *pptoken;
1218c3e34a4SDavid Howells 	     pptoken = &(*pptoken)->next)
1228c3e34a4SDavid Howells 		continue;
1238c3e34a4SDavid Howells 	*pptoken = token;
12410674a03SBaolin Wang 	expiry = rxrpc_u32_to_time64(token->kad->expiry);
12510674a03SBaolin Wang 	if (expiry < prep->expiry)
12610674a03SBaolin Wang 		prep->expiry = expiry;
1278c3e34a4SDavid Howells 
1288c3e34a4SDavid Howells 	_leave(" = 0");
1298c3e34a4SDavid Howells 	return 0;
1308c3e34a4SDavid Howells }
1318c3e34a4SDavid Howells 
1328c3e34a4SDavid Howells /*
1338c3e34a4SDavid Howells  * attempt to parse the data as the XDR format
1348c3e34a4SDavid Howells  * - the caller guarantees we have more than 7 words
1358c3e34a4SDavid Howells  */
rxrpc_preparse_xdr(struct key_preparsed_payload * prep)1368c3e34a4SDavid Howells static int rxrpc_preparse_xdr(struct key_preparsed_payload *prep)
1378c3e34a4SDavid Howells {
1384c20c333SDavid Howells 	const __be32 *xdr = prep->data, *token, *p;
1398c3e34a4SDavid Howells 	const char *cp;
1405f2f9765SDavid Howells 	unsigned int len, paddedlen, loop, ntoken, toklen, sec_ix;
1418c3e34a4SDavid Howells 	size_t datalen = prep->datalen;
1429a0e6464SDavid Howells 	int ret, ret2;
1438c3e34a4SDavid Howells 
1448c3e34a4SDavid Howells 	_enter(",{%x,%x,%x,%x},%zu",
1458c3e34a4SDavid Howells 	       ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]),
1468c3e34a4SDavid Howells 	       prep->datalen);
1478c3e34a4SDavid Howells 
1488c3e34a4SDavid Howells 	if (datalen > AFSTOKEN_LENGTH_MAX)
1498c3e34a4SDavid Howells 		goto not_xdr;
1508c3e34a4SDavid Howells 
1518c3e34a4SDavid Howells 	/* XDR is an array of __be32's */
1528c3e34a4SDavid Howells 	if (datalen & 3)
1538c3e34a4SDavid Howells 		goto not_xdr;
1548c3e34a4SDavid Howells 
1558c3e34a4SDavid Howells 	/* the flags should be 0 (the setpag bit must be handled by
1568c3e34a4SDavid Howells 	 * userspace) */
1578c3e34a4SDavid Howells 	if (ntohl(*xdr++) != 0)
1588c3e34a4SDavid Howells 		goto not_xdr;
1598c3e34a4SDavid Howells 	datalen -= 4;
1608c3e34a4SDavid Howells 
1618c3e34a4SDavid Howells 	/* check the cell name */
1628c3e34a4SDavid Howells 	len = ntohl(*xdr++);
1638c3e34a4SDavid Howells 	if (len < 1 || len > AFSTOKEN_CELL_MAX)
1648c3e34a4SDavid Howells 		goto not_xdr;
1658c3e34a4SDavid Howells 	datalen -= 4;
1665f2f9765SDavid Howells 	paddedlen = (len + 3) & ~3;
1675f2f9765SDavid Howells 	if (paddedlen > datalen)
1688c3e34a4SDavid Howells 		goto not_xdr;
1698c3e34a4SDavid Howells 
1708c3e34a4SDavid Howells 	cp = (const char *) xdr;
1718c3e34a4SDavid Howells 	for (loop = 0; loop < len; loop++)
1728c3e34a4SDavid Howells 		if (!isprint(cp[loop]))
1738c3e34a4SDavid Howells 			goto not_xdr;
1745f2f9765SDavid Howells 	for (; loop < paddedlen; loop++)
1758c3e34a4SDavid Howells 		if (cp[loop])
1768c3e34a4SDavid Howells 			goto not_xdr;
1778c3e34a4SDavid Howells 	_debug("cellname: [%u/%u] '%*.*s'",
1785f2f9765SDavid Howells 	       len, paddedlen, len, len, (const char *) xdr);
1795f2f9765SDavid Howells 	datalen -= paddedlen;
1805f2f9765SDavid Howells 	xdr += paddedlen >> 2;
1818c3e34a4SDavid Howells 
1828c3e34a4SDavid Howells 	/* get the token count */
1838c3e34a4SDavid Howells 	if (datalen < 12)
1848c3e34a4SDavid Howells 		goto not_xdr;
1858c3e34a4SDavid Howells 	ntoken = ntohl(*xdr++);
1868c3e34a4SDavid Howells 	datalen -= 4;
1878c3e34a4SDavid Howells 	_debug("ntoken: %x", ntoken);
1888c3e34a4SDavid Howells 	if (ntoken < 1 || ntoken > AFSTOKEN_MAX)
1898c3e34a4SDavid Howells 		goto not_xdr;
1908c3e34a4SDavid Howells 
1918c3e34a4SDavid Howells 	/* check each token wrapper */
1924c20c333SDavid Howells 	p = xdr;
1938c3e34a4SDavid Howells 	loop = ntoken;
1948c3e34a4SDavid Howells 	do {
1958c3e34a4SDavid Howells 		if (datalen < 8)
1968c3e34a4SDavid Howells 			goto not_xdr;
1974c20c333SDavid Howells 		toklen = ntohl(*p++);
1984c20c333SDavid Howells 		sec_ix = ntohl(*p);
1998c3e34a4SDavid Howells 		datalen -= 4;
2008c3e34a4SDavid Howells 		_debug("token: [%x/%zx] %x", toklen, datalen, sec_ix);
2015f2f9765SDavid Howells 		paddedlen = (toklen + 3) & ~3;
2025f2f9765SDavid Howells 		if (toklen < 20 || toklen > datalen || paddedlen > datalen)
2038c3e34a4SDavid Howells 			goto not_xdr;
2045f2f9765SDavid Howells 		datalen -= paddedlen;
2054c20c333SDavid Howells 		p += paddedlen >> 2;
2068c3e34a4SDavid Howells 
2078c3e34a4SDavid Howells 	} while (--loop > 0);
2088c3e34a4SDavid Howells 
2098c3e34a4SDavid Howells 	_debug("remainder: %zu", datalen);
2108c3e34a4SDavid Howells 	if (datalen != 0)
2118c3e34a4SDavid Howells 		goto not_xdr;
2128c3e34a4SDavid Howells 
2138c3e34a4SDavid Howells 	/* okay: we're going to assume it's valid XDR format
2148c3e34a4SDavid Howells 	 * - we ignore the cellname, relying on the key to be correctly named
2158c3e34a4SDavid Howells 	 */
2169a0e6464SDavid Howells 	ret = -EPROTONOSUPPORT;
2178c3e34a4SDavid Howells 	do {
2188c3e34a4SDavid Howells 		toklen = ntohl(*xdr++);
2194c20c333SDavid Howells 		token = xdr;
2204c20c333SDavid Howells 		xdr += (toklen + 3) / 4;
2214c20c333SDavid Howells 
2224c20c333SDavid Howells 		sec_ix = ntohl(*token++);
2238c3e34a4SDavid Howells 		toklen -= 4;
2248c3e34a4SDavid Howells 
2254c20c333SDavid Howells 		_debug("TOKEN type=%x len=%x", sec_ix, toklen);
2268c3e34a4SDavid Howells 
2278c3e34a4SDavid Howells 		switch (sec_ix) {
2288c3e34a4SDavid Howells 		case RXRPC_SECURITY_RXKAD:
2299a0e6464SDavid Howells 			ret2 = rxrpc_preparse_xdr_rxkad(prep, datalen, token, toklen);
2308c3e34a4SDavid Howells 			break;
2318c3e34a4SDavid Howells 		default:
2329a0e6464SDavid Howells 			ret2 = -EPROTONOSUPPORT;
2339a0e6464SDavid Howells 			break;
2349a0e6464SDavid Howells 		}
2359a0e6464SDavid Howells 
2369a0e6464SDavid Howells 		switch (ret2) {
2379a0e6464SDavid Howells 		case 0:
2389a0e6464SDavid Howells 			ret = 0;
2399a0e6464SDavid Howells 			break;
2409a0e6464SDavid Howells 		case -EPROTONOSUPPORT:
2419a0e6464SDavid Howells 			break;
2429a0e6464SDavid Howells 		case -ENOPKG:
2439a0e6464SDavid Howells 			if (ret != 0)
2449a0e6464SDavid Howells 				ret = -ENOPKG;
2459a0e6464SDavid Howells 			break;
2469a0e6464SDavid Howells 		default:
2479a0e6464SDavid Howells 			ret = ret2;
2488c3e34a4SDavid Howells 			goto error;
2498c3e34a4SDavid Howells 		}
2508c3e34a4SDavid Howells 
2518c3e34a4SDavid Howells 	} while (--ntoken > 0);
2528c3e34a4SDavid Howells 
2539a0e6464SDavid Howells error:
2549a0e6464SDavid Howells 	_leave(" = %d", ret);
2559a0e6464SDavid Howells 	return ret;
2568c3e34a4SDavid Howells 
2578c3e34a4SDavid Howells not_xdr:
2588c3e34a4SDavid Howells 	_leave(" = -EPROTO");
2598c3e34a4SDavid Howells 	return -EPROTO;
2608c3e34a4SDavid Howells }
2618c3e34a4SDavid Howells 
2628c3e34a4SDavid Howells /*
2638c3e34a4SDavid Howells  * Preparse an rxrpc defined key.
2648c3e34a4SDavid Howells  *
2658c3e34a4SDavid Howells  * Data should be of the form:
2668c3e34a4SDavid Howells  *	OFFSET	LEN	CONTENT
2678c3e34a4SDavid Howells  *	0	4	key interface version number
2688c3e34a4SDavid Howells  *	4	2	security index (type)
2698c3e34a4SDavid Howells  *	6	2	ticket length
2708c3e34a4SDavid Howells  *	8	4	key expiry time (time_t)
2718c3e34a4SDavid Howells  *	12	4	kvno
2728c3e34a4SDavid Howells  *	16	8	session key
2738c3e34a4SDavid Howells  *	24	[len]	ticket
2748c3e34a4SDavid Howells  *
2758c3e34a4SDavid Howells  * if no data is provided, then a no-security key is made
2768c3e34a4SDavid Howells  */
rxrpc_preparse(struct key_preparsed_payload * prep)2778c3e34a4SDavid Howells static int rxrpc_preparse(struct key_preparsed_payload *prep)
2788c3e34a4SDavid Howells {
2798c3e34a4SDavid Howells 	const struct rxrpc_key_data_v1 *v1;
2808c3e34a4SDavid Howells 	struct rxrpc_key_token *token, **pp;
28110674a03SBaolin Wang 	time64_t expiry;
2828c3e34a4SDavid Howells 	size_t plen;
2838c3e34a4SDavid Howells 	u32 kver;
2848c3e34a4SDavid Howells 	int ret;
2858c3e34a4SDavid Howells 
2868c3e34a4SDavid Howells 	_enter("%zu", prep->datalen);
2878c3e34a4SDavid Howells 
2888c3e34a4SDavid Howells 	/* handle a no-security key */
2898c3e34a4SDavid Howells 	if (!prep->data && prep->datalen == 0)
2908c3e34a4SDavid Howells 		return 0;
2918c3e34a4SDavid Howells 
2928c3e34a4SDavid Howells 	/* determine if the XDR payload format is being used */
2938c3e34a4SDavid Howells 	if (prep->datalen > 7 * 4) {
2948c3e34a4SDavid Howells 		ret = rxrpc_preparse_xdr(prep);
2958c3e34a4SDavid Howells 		if (ret != -EPROTO)
2968c3e34a4SDavid Howells 			return ret;
2978c3e34a4SDavid Howells 	}
2988c3e34a4SDavid Howells 
2998c3e34a4SDavid Howells 	/* get the key interface version number */
3008c3e34a4SDavid Howells 	ret = -EINVAL;
3018c3e34a4SDavid Howells 	if (prep->datalen <= 4 || !prep->data)
3028c3e34a4SDavid Howells 		goto error;
3038c3e34a4SDavid Howells 	memcpy(&kver, prep->data, sizeof(kver));
3048c3e34a4SDavid Howells 	prep->data += sizeof(kver);
3058c3e34a4SDavid Howells 	prep->datalen -= sizeof(kver);
3068c3e34a4SDavid Howells 
3078c3e34a4SDavid Howells 	_debug("KEY I/F VERSION: %u", kver);
3088c3e34a4SDavid Howells 
3098c3e34a4SDavid Howells 	ret = -EKEYREJECTED;
3108c3e34a4SDavid Howells 	if (kver != 1)
3118c3e34a4SDavid Howells 		goto error;
3128c3e34a4SDavid Howells 
3138c3e34a4SDavid Howells 	/* deal with a version 1 key */
3148c3e34a4SDavid Howells 	ret = -EINVAL;
3158c3e34a4SDavid Howells 	if (prep->datalen < sizeof(*v1))
3168c3e34a4SDavid Howells 		goto error;
3178c3e34a4SDavid Howells 
3188c3e34a4SDavid Howells 	v1 = prep->data;
3198c3e34a4SDavid Howells 	if (prep->datalen != sizeof(*v1) + v1->ticket_length)
3208c3e34a4SDavid Howells 		goto error;
3218c3e34a4SDavid Howells 
3228c3e34a4SDavid Howells 	_debug("SCIX: %u", v1->security_index);
3238c3e34a4SDavid Howells 	_debug("TLEN: %u", v1->ticket_length);
3248c3e34a4SDavid Howells 	_debug("EXPY: %x", v1->expiry);
3258c3e34a4SDavid Howells 	_debug("KVNO: %u", v1->kvno);
3268c3e34a4SDavid Howells 	_debug("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x",
3278c3e34a4SDavid Howells 	       v1->session_key[0], v1->session_key[1],
3288c3e34a4SDavid Howells 	       v1->session_key[2], v1->session_key[3],
3298c3e34a4SDavid Howells 	       v1->session_key[4], v1->session_key[5],
3308c3e34a4SDavid Howells 	       v1->session_key[6], v1->session_key[7]);
3318c3e34a4SDavid Howells 	if (v1->ticket_length >= 8)
3328c3e34a4SDavid Howells 		_debug("TCKT: %02x%02x%02x%02x%02x%02x%02x%02x",
3338c3e34a4SDavid Howells 		       v1->ticket[0], v1->ticket[1],
3348c3e34a4SDavid Howells 		       v1->ticket[2], v1->ticket[3],
3358c3e34a4SDavid Howells 		       v1->ticket[4], v1->ticket[5],
3368c3e34a4SDavid Howells 		       v1->ticket[6], v1->ticket[7]);
3378c3e34a4SDavid Howells 
3388c3e34a4SDavid Howells 	ret = -EPROTONOSUPPORT;
3398c3e34a4SDavid Howells 	if (v1->security_index != RXRPC_SECURITY_RXKAD)
3408c3e34a4SDavid Howells 		goto error;
3418c3e34a4SDavid Howells 
3428c3e34a4SDavid Howells 	plen = sizeof(*token->kad) + v1->ticket_length;
3438c3e34a4SDavid Howells 	prep->quotalen = plen + sizeof(*token);
3448c3e34a4SDavid Howells 
3458c3e34a4SDavid Howells 	ret = -ENOMEM;
3468c3e34a4SDavid Howells 	token = kzalloc(sizeof(*token), GFP_KERNEL);
3478c3e34a4SDavid Howells 	if (!token)
3488c3e34a4SDavid Howells 		goto error;
3498c3e34a4SDavid Howells 	token->kad = kzalloc(plen, GFP_KERNEL);
3508c3e34a4SDavid Howells 	if (!token->kad)
3518c3e34a4SDavid Howells 		goto error_free;
3528c3e34a4SDavid Howells 
3538c3e34a4SDavid Howells 	token->security_index		= RXRPC_SECURITY_RXKAD;
3548c3e34a4SDavid Howells 	token->kad->ticket_len		= v1->ticket_length;
3558c3e34a4SDavid Howells 	token->kad->expiry		= v1->expiry;
3568c3e34a4SDavid Howells 	token->kad->kvno		= v1->kvno;
3578c3e34a4SDavid Howells 	memcpy(&token->kad->session_key, &v1->session_key, 8);
3588c3e34a4SDavid Howells 	memcpy(&token->kad->ticket, v1->ticket, v1->ticket_length);
3598c3e34a4SDavid Howells 
3608c3e34a4SDavid Howells 	/* count the number of tokens attached */
3618c3e34a4SDavid Howells 	prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1);
3628c3e34a4SDavid Howells 
3638c3e34a4SDavid Howells 	/* attach the data */
3648c3e34a4SDavid Howells 	pp = (struct rxrpc_key_token **)&prep->payload.data[0];
3658c3e34a4SDavid Howells 	while (*pp)
3668c3e34a4SDavid Howells 		pp = &(*pp)->next;
3678c3e34a4SDavid Howells 	*pp = token;
36810674a03SBaolin Wang 	expiry = rxrpc_u32_to_time64(token->kad->expiry);
36910674a03SBaolin Wang 	if (expiry < prep->expiry)
37010674a03SBaolin Wang 		prep->expiry = expiry;
3718c3e34a4SDavid Howells 	token = NULL;
3728c3e34a4SDavid Howells 	ret = 0;
3738c3e34a4SDavid Howells 
3748c3e34a4SDavid Howells error_free:
3758c3e34a4SDavid Howells 	kfree(token);
3768c3e34a4SDavid Howells error:
3778c3e34a4SDavid Howells 	return ret;
3788c3e34a4SDavid Howells }
3798c3e34a4SDavid Howells 
3808c3e34a4SDavid Howells /*
3818c3e34a4SDavid Howells  * Free token list.
3828c3e34a4SDavid Howells  */
rxrpc_free_token_list(struct rxrpc_key_token * token)3838c3e34a4SDavid Howells static void rxrpc_free_token_list(struct rxrpc_key_token *token)
3848c3e34a4SDavid Howells {
3858c3e34a4SDavid Howells 	struct rxrpc_key_token *next;
3868c3e34a4SDavid Howells 
3878c3e34a4SDavid Howells 	for (; token; token = next) {
3888c3e34a4SDavid Howells 		next = token->next;
3898c3e34a4SDavid Howells 		switch (token->security_index) {
3908c3e34a4SDavid Howells 		case RXRPC_SECURITY_RXKAD:
3918c3e34a4SDavid Howells 			kfree(token->kad);
3928c3e34a4SDavid Howells 			break;
3938c3e34a4SDavid Howells 		default:
3948c3e34a4SDavid Howells 			pr_err("Unknown token type %x on rxrpc key\n",
3958c3e34a4SDavid Howells 			       token->security_index);
3968c3e34a4SDavid Howells 			BUG();
3978c3e34a4SDavid Howells 		}
3988c3e34a4SDavid Howells 
3998c3e34a4SDavid Howells 		kfree(token);
4008c3e34a4SDavid Howells 	}
4018c3e34a4SDavid Howells }
4028c3e34a4SDavid Howells 
4038c3e34a4SDavid Howells /*
4048c3e34a4SDavid Howells  * Clean up preparse data.
4058c3e34a4SDavid Howells  */
rxrpc_free_preparse(struct key_preparsed_payload * prep)4068c3e34a4SDavid Howells static void rxrpc_free_preparse(struct key_preparsed_payload *prep)
4078c3e34a4SDavid Howells {
4088c3e34a4SDavid Howells 	rxrpc_free_token_list(prep->payload.data[0]);
4098c3e34a4SDavid Howells }
4108c3e34a4SDavid Howells 
4118c3e34a4SDavid Howells /*
4128c3e34a4SDavid Howells  * dispose of the data dangling from the corpse of a rxrpc key
4138c3e34a4SDavid Howells  */
rxrpc_destroy(struct key * key)4148c3e34a4SDavid Howells static void rxrpc_destroy(struct key *key)
4158c3e34a4SDavid Howells {
4168c3e34a4SDavid Howells 	rxrpc_free_token_list(key->payload.data[0]);
4178c3e34a4SDavid Howells }
4188c3e34a4SDavid Howells 
4198c3e34a4SDavid Howells /*
4208c3e34a4SDavid Howells  * describe the rxrpc key
4218c3e34a4SDavid Howells  */
rxrpc_describe(const struct key * key,struct seq_file * m)4228c3e34a4SDavid Howells static void rxrpc_describe(const struct key *key, struct seq_file *m)
4238c3e34a4SDavid Howells {
4240727d3ecSDavid Howells 	const struct rxrpc_key_token *token;
4250727d3ecSDavid Howells 	const char *sep = ": ";
4260727d3ecSDavid Howells 
4270727d3ecSDavid Howells 	seq_puts(m, key->description);
4280727d3ecSDavid Howells 
4290727d3ecSDavid Howells 	for (token = key->payload.data[0]; token; token = token->next) {
4300727d3ecSDavid Howells 		seq_puts(m, sep);
4310727d3ecSDavid Howells 
4320727d3ecSDavid Howells 		switch (token->security_index) {
4330727d3ecSDavid Howells 		case RXRPC_SECURITY_RXKAD:
4340727d3ecSDavid Howells 			seq_puts(m, "ka");
4350727d3ecSDavid Howells 			break;
4360727d3ecSDavid Howells 		default: /* we have a ticket we can't encode */
4370727d3ecSDavid Howells 			seq_printf(m, "%u", token->security_index);
4380727d3ecSDavid Howells 			break;
4390727d3ecSDavid Howells 		}
4400727d3ecSDavid Howells 
4410727d3ecSDavid Howells 		sep = " ";
4420727d3ecSDavid Howells 	}
4430727d3ecSDavid Howells }
4440727d3ecSDavid Howells 
4450727d3ecSDavid Howells /*
4468c3e34a4SDavid Howells  * grab the security key for a socket
4478c3e34a4SDavid Howells  */
rxrpc_request_key(struct rxrpc_sock * rx,sockptr_t optval,int optlen)448a7b75c5aSChristoph Hellwig int rxrpc_request_key(struct rxrpc_sock *rx, sockptr_t optval, int optlen)
4498c3e34a4SDavid Howells {
4508c3e34a4SDavid Howells 	struct key *key;
4518c3e34a4SDavid Howells 	char *description;
4528c3e34a4SDavid Howells 
4538c3e34a4SDavid Howells 	_enter("");
4548c3e34a4SDavid Howells 
45538b1dc47SDavid Howells 	if (optlen <= 0 || optlen > PAGE_SIZE - 1 || rx->securities)
4568c3e34a4SDavid Howells 		return -EINVAL;
4578c3e34a4SDavid Howells 
458a7b75c5aSChristoph Hellwig 	description = memdup_sockptr_nul(optval, optlen);
4598c3e34a4SDavid Howells 	if (IS_ERR(description))
4608c3e34a4SDavid Howells 		return PTR_ERR(description);
4618c3e34a4SDavid Howells 
462028db3e2SLinus Torvalds 	key = request_key_net(&key_type_rxrpc, description, sock_net(&rx->sk), NULL);
4638c3e34a4SDavid Howells 	if (IS_ERR(key)) {
4648c3e34a4SDavid Howells 		kfree(description);
4658c3e34a4SDavid Howells 		_leave(" = %ld", PTR_ERR(key));
4668c3e34a4SDavid Howells 		return PTR_ERR(key);
4678c3e34a4SDavid Howells 	}
4688c3e34a4SDavid Howells 
4698c3e34a4SDavid Howells 	rx->key = key;
4708c3e34a4SDavid Howells 	kfree(description);
4718c3e34a4SDavid Howells 	_leave(" = 0 [key %x]", key->serial);
4728c3e34a4SDavid Howells 	return 0;
4738c3e34a4SDavid Howells }
4748c3e34a4SDavid Howells 
4758c3e34a4SDavid Howells /*
4768c3e34a4SDavid Howells  * generate a server data key
4778c3e34a4SDavid Howells  */
rxrpc_get_server_data_key(struct rxrpc_connection * conn,const void * session_key,time64_t expiry,u32 kvno)4788c3e34a4SDavid Howells int rxrpc_get_server_data_key(struct rxrpc_connection *conn,
4798c3e34a4SDavid Howells 			      const void *session_key,
48010674a03SBaolin Wang 			      time64_t expiry,
4818c3e34a4SDavid Howells 			      u32 kvno)
4828c3e34a4SDavid Howells {
4838c3e34a4SDavid Howells 	const struct cred *cred = current_cred();
4848c3e34a4SDavid Howells 	struct key *key;
4858c3e34a4SDavid Howells 	int ret;
4868c3e34a4SDavid Howells 
4878c3e34a4SDavid Howells 	struct {
4888c3e34a4SDavid Howells 		u32 kver;
4898c3e34a4SDavid Howells 		struct rxrpc_key_data_v1 v1;
4908c3e34a4SDavid Howells 	} data;
4918c3e34a4SDavid Howells 
4928c3e34a4SDavid Howells 	_enter("");
4938c3e34a4SDavid Howells 
4948c3e34a4SDavid Howells 	key = key_alloc(&key_type_rxrpc, "x",
495028db3e2SLinus Torvalds 			GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0,
4968c3e34a4SDavid Howells 			KEY_ALLOC_NOT_IN_QUOTA, NULL);
4978c3e34a4SDavid Howells 	if (IS_ERR(key)) {
4988c3e34a4SDavid Howells 		_leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
4998c3e34a4SDavid Howells 		return -ENOMEM;
5008c3e34a4SDavid Howells 	}
5018c3e34a4SDavid Howells 
5028c3e34a4SDavid Howells 	_debug("key %d", key_serial(key));
5038c3e34a4SDavid Howells 
5048c3e34a4SDavid Howells 	data.kver = 1;
5058c3e34a4SDavid Howells 	data.v1.security_index = RXRPC_SECURITY_RXKAD;
5068c3e34a4SDavid Howells 	data.v1.ticket_length = 0;
50710674a03SBaolin Wang 	data.v1.expiry = rxrpc_time64_to_u32(expiry);
5088c3e34a4SDavid Howells 	data.v1.kvno = 0;
5098c3e34a4SDavid Howells 
5108c3e34a4SDavid Howells 	memcpy(&data.v1.session_key, session_key, sizeof(data.v1.session_key));
5118c3e34a4SDavid Howells 
5128c3e34a4SDavid Howells 	ret = key_instantiate_and_link(key, &data, sizeof(data), NULL, NULL);
5138c3e34a4SDavid Howells 	if (ret < 0)
5148c3e34a4SDavid Howells 		goto error;
5158c3e34a4SDavid Howells 
5162cc80086SDavid Howells 	conn->key = key;
5178c3e34a4SDavid Howells 	_leave(" = 0 [%d]", key_serial(key));
5188c3e34a4SDavid Howells 	return 0;
5198c3e34a4SDavid Howells 
5208c3e34a4SDavid Howells error:
5218c3e34a4SDavid Howells 	key_revoke(key);
5228c3e34a4SDavid Howells 	key_put(key);
5238c3e34a4SDavid Howells 	_leave(" = -ENOMEM [ins %d]", ret);
5248c3e34a4SDavid Howells 	return -ENOMEM;
5258c3e34a4SDavid Howells }
5268c3e34a4SDavid Howells EXPORT_SYMBOL(rxrpc_get_server_data_key);
5278c3e34a4SDavid Howells 
5288c3e34a4SDavid Howells /**
5298c3e34a4SDavid Howells  * rxrpc_get_null_key - Generate a null RxRPC key
5308c3e34a4SDavid Howells  * @keyname: The name to give the key.
5318c3e34a4SDavid Howells  *
5328c3e34a4SDavid Howells  * Generate a null RxRPC key that can be used to indicate anonymous security is
5338c3e34a4SDavid Howells  * required for a particular domain.
5348c3e34a4SDavid Howells  */
rxrpc_get_null_key(const char * keyname)5358c3e34a4SDavid Howells struct key *rxrpc_get_null_key(const char *keyname)
5368c3e34a4SDavid Howells {
5378c3e34a4SDavid Howells 	const struct cred *cred = current_cred();
5388c3e34a4SDavid Howells 	struct key *key;
5398c3e34a4SDavid Howells 	int ret;
5408c3e34a4SDavid Howells 
5418c3e34a4SDavid Howells 	key = key_alloc(&key_type_rxrpc, keyname,
5428c3e34a4SDavid Howells 			GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
543028db3e2SLinus Torvalds 			KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, NULL);
5448c3e34a4SDavid Howells 	if (IS_ERR(key))
5458c3e34a4SDavid Howells 		return key;
5468c3e34a4SDavid Howells 
5478c3e34a4SDavid Howells 	ret = key_instantiate_and_link(key, NULL, 0, NULL, NULL);
5488c3e34a4SDavid Howells 	if (ret < 0) {
5498c3e34a4SDavid Howells 		key_revoke(key);
5508c3e34a4SDavid Howells 		key_put(key);
5518c3e34a4SDavid Howells 		return ERR_PTR(ret);
5528c3e34a4SDavid Howells 	}
5538c3e34a4SDavid Howells 
5548c3e34a4SDavid Howells 	return key;
5558c3e34a4SDavid Howells }
5568c3e34a4SDavid Howells EXPORT_SYMBOL(rxrpc_get_null_key);
5578c3e34a4SDavid Howells 
5588c3e34a4SDavid Howells /*
5598c3e34a4SDavid Howells  * read the contents of an rxrpc key
5608c3e34a4SDavid Howells  * - this returns the result in XDR form
5618c3e34a4SDavid Howells  */
rxrpc_read(const struct key * key,char * buffer,size_t buflen)5628c3e34a4SDavid Howells static long rxrpc_read(const struct key *key,
563d3ec10aaSWaiman Long 		       char *buffer, size_t buflen)
5648c3e34a4SDavid Howells {
5658c3e34a4SDavid Howells 	const struct rxrpc_key_token *token;
5668c3e34a4SDavid Howells 	size_t size;
567d3ec10aaSWaiman Long 	__be32 *xdr, *oldxdr;
5688c3e34a4SDavid Howells 	u32 cnlen, toksize, ntoks, tok, zero;
5698c3e34a4SDavid Howells 	u16 toksizes[AFSTOKEN_MAX];
5708c3e34a4SDavid Howells 
5718c3e34a4SDavid Howells 	_enter("");
5728c3e34a4SDavid Howells 
5738c3e34a4SDavid Howells 	/* we don't know what form we should return non-AFS keys in */
5748c3e34a4SDavid Howells 	if (memcmp(key->description, "afs@", 4) != 0)
5758c3e34a4SDavid Howells 		return -EOPNOTSUPP;
5768c3e34a4SDavid Howells 	cnlen = strlen(key->description + 4);
5778c3e34a4SDavid Howells 
5788c3e34a4SDavid Howells #define RND(X) (((X) + 3) & ~3)
5798c3e34a4SDavid Howells 
5808c3e34a4SDavid Howells 	/* AFS keys we return in XDR form, so we need to work out the size of
5818c3e34a4SDavid Howells 	 * the XDR */
5828c3e34a4SDavid Howells 	size = 2 * 4;	/* flags, cellname len */
5838c3e34a4SDavid Howells 	size += RND(cnlen);	/* cellname */
5848c3e34a4SDavid Howells 	size += 1 * 4;	/* token count */
5858c3e34a4SDavid Howells 
5868c3e34a4SDavid Howells 	ntoks = 0;
5878c3e34a4SDavid Howells 	for (token = key->payload.data[0]; token; token = token->next) {
5888c3e34a4SDavid Howells 		toksize = 4;	/* sec index */
5898c3e34a4SDavid Howells 
5908c3e34a4SDavid Howells 		switch (token->security_index) {
5918c3e34a4SDavid Howells 		case RXRPC_SECURITY_RXKAD:
59256305118SMarc Dionne 			toksize += 8 * 4;	/* viceid, kvno, key*2, begin,
5938c3e34a4SDavid Howells 						 * end, primary, tktlen */
594d2ae4e91SDavid Howells 			if (!token->no_leak_key)
5958c3e34a4SDavid Howells 				toksize += RND(token->kad->ticket_len);
5968c3e34a4SDavid Howells 			break;
5978c3e34a4SDavid Howells 
5988c3e34a4SDavid Howells 		default: /* we have a ticket we can't encode */
5999a059cd5SDavid Howells 			pr_err("Unsupported key token type (%u)\n",
6009a059cd5SDavid Howells 			       token->security_index);
601d52e419aSDavid Howells 			return -ENOPKG;
6028c3e34a4SDavid Howells 		}
6038c3e34a4SDavid Howells 
6048c3e34a4SDavid Howells 		_debug("token[%u]: toksize=%u", ntoks, toksize);
60584924aacSDavid Howells 		if (WARN_ON(toksize > AFSTOKEN_LENGTH_MAX))
60684924aacSDavid Howells 			return -EIO;
6078c3e34a4SDavid Howells 
6088c3e34a4SDavid Howells 		toksizes[ntoks++] = toksize;
6098c3e34a4SDavid Howells 		size += toksize + 4; /* each token has a length word */
6108c3e34a4SDavid Howells 	}
6118c3e34a4SDavid Howells 
6128c3e34a4SDavid Howells #undef RND
6138c3e34a4SDavid Howells 
6148c3e34a4SDavid Howells 	if (!buffer || buflen < size)
6158c3e34a4SDavid Howells 		return size;
6168c3e34a4SDavid Howells 
617d3ec10aaSWaiman Long 	xdr = (__be32 *)buffer;
6188c3e34a4SDavid Howells 	zero = 0;
6198c3e34a4SDavid Howells #define ENCODE(x)				\
6208c3e34a4SDavid Howells 	do {					\
621d3ec10aaSWaiman Long 		*xdr++ = htonl(x);		\
6228c3e34a4SDavid Howells 	} while(0)
6238c3e34a4SDavid Howells #define ENCODE_DATA(l, s)						\
6248c3e34a4SDavid Howells 	do {								\
6258c3e34a4SDavid Howells 		u32 _l = (l);						\
6268c3e34a4SDavid Howells 		ENCODE(l);						\
627d3ec10aaSWaiman Long 		memcpy(xdr, (s), _l);					\
628d3ec10aaSWaiman Long 		if (_l & 3)						\
629d3ec10aaSWaiman Long 			memcpy((u8 *)xdr + _l, &zero, 4 - (_l & 3));	\
6308c3e34a4SDavid Howells 		xdr += (_l + 3) >> 2;					\
6318c3e34a4SDavid Howells 	} while(0)
63256305118SMarc Dionne #define ENCODE_BYTES(l, s)						\
63356305118SMarc Dionne 	do {								\
63456305118SMarc Dionne 		u32 _l = (l);						\
63556305118SMarc Dionne 		memcpy(xdr, (s), _l);					\
63656305118SMarc Dionne 		if (_l & 3)						\
63756305118SMarc Dionne 			memcpy((u8 *)xdr + _l, &zero, 4 - (_l & 3));	\
63856305118SMarc Dionne 		xdr += (_l + 3) >> 2;					\
63956305118SMarc Dionne 	} while(0)
6408c3e34a4SDavid Howells #define ENCODE64(x)					\
6418c3e34a4SDavid Howells 	do {						\
6428c3e34a4SDavid Howells 		__be64 y = cpu_to_be64(x);		\
643d3ec10aaSWaiman Long 		memcpy(xdr, &y, 8);			\
6448c3e34a4SDavid Howells 		xdr += 8 >> 2;				\
6458c3e34a4SDavid Howells 	} while(0)
6468c3e34a4SDavid Howells #define ENCODE_STR(s)				\
6478c3e34a4SDavid Howells 	do {					\
6488c3e34a4SDavid Howells 		const char *_s = (s);		\
6498c3e34a4SDavid Howells 		ENCODE_DATA(strlen(_s), _s);	\
6508c3e34a4SDavid Howells 	} while(0)
6518c3e34a4SDavid Howells 
6528c3e34a4SDavid Howells 	ENCODE(0);					/* flags */
6538c3e34a4SDavid Howells 	ENCODE_DATA(cnlen, key->description + 4);	/* cellname */
6548c3e34a4SDavid Howells 	ENCODE(ntoks);
6558c3e34a4SDavid Howells 
6568c3e34a4SDavid Howells 	tok = 0;
6578c3e34a4SDavid Howells 	for (token = key->payload.data[0]; token; token = token->next) {
6588c3e34a4SDavid Howells 		toksize = toksizes[tok++];
6598c3e34a4SDavid Howells 		ENCODE(toksize);
6608c3e34a4SDavid Howells 		oldxdr = xdr;
6618c3e34a4SDavid Howells 		ENCODE(token->security_index);
6628c3e34a4SDavid Howells 
6638c3e34a4SDavid Howells 		switch (token->security_index) {
6648c3e34a4SDavid Howells 		case RXRPC_SECURITY_RXKAD:
6658c3e34a4SDavid Howells 			ENCODE(token->kad->vice_id);
6668c3e34a4SDavid Howells 			ENCODE(token->kad->kvno);
66756305118SMarc Dionne 			ENCODE_BYTES(8, token->kad->session_key);
6688c3e34a4SDavid Howells 			ENCODE(token->kad->start);
6698c3e34a4SDavid Howells 			ENCODE(token->kad->expiry);
6708c3e34a4SDavid Howells 			ENCODE(token->kad->primary_flag);
671d2ae4e91SDavid Howells 			if (token->no_leak_key)
672d2ae4e91SDavid Howells 				ENCODE(0);
673d2ae4e91SDavid Howells 			else
6748c3e34a4SDavid Howells 				ENCODE_DATA(token->kad->ticket_len, token->kad->ticket);
6758c3e34a4SDavid Howells 			break;
6768c3e34a4SDavid Howells 
6778c3e34a4SDavid Howells 		default:
678d52e419aSDavid Howells 			pr_err("Unsupported key token type (%u)\n",
679d52e419aSDavid Howells 			       token->security_index);
680d52e419aSDavid Howells 			return -ENOPKG;
6818c3e34a4SDavid Howells 		}
6828c3e34a4SDavid Howells 
683*fadfc57cSMarc Dionne 		if (WARN_ON((unsigned long)xdr - (unsigned long)oldxdr !=
68484924aacSDavid Howells 			    toksize))
68584924aacSDavid Howells 			return -EIO;
6868c3e34a4SDavid Howells 	}
6878c3e34a4SDavid Howells 
6888c3e34a4SDavid Howells #undef ENCODE_STR
6898c3e34a4SDavid Howells #undef ENCODE_DATA
6908c3e34a4SDavid Howells #undef ENCODE64
6918c3e34a4SDavid Howells #undef ENCODE
6928c3e34a4SDavid Howells 
69384924aacSDavid Howells 	if (WARN_ON(tok != ntoks))
69484924aacSDavid Howells 		return -EIO;
69584924aacSDavid Howells 	if (WARN_ON((unsigned long)xdr - (unsigned long)buffer != size))
69684924aacSDavid Howells 		return -EIO;
6978c3e34a4SDavid Howells 	_leave(" = %zu", size);
6988c3e34a4SDavid Howells 	return size;
6998c3e34a4SDavid Howells }
700