18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* RxRPC key management
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * RxRPC keys should have a description of describing their purpose:
88c2ecf20Sopenharmony_ci *	"afs@CAMBRIDGE.REDHAT.COM>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <crypto/skcipher.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/net.h>
168c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
178c2ecf20Sopenharmony_ci#include <linux/key-type.h>
188c2ecf20Sopenharmony_ci#include <linux/ctype.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <net/sock.h>
218c2ecf20Sopenharmony_ci#include <net/af_rxrpc.h>
228c2ecf20Sopenharmony_ci#include <keys/rxrpc-type.h>
238c2ecf20Sopenharmony_ci#include <keys/user-type.h>
248c2ecf20Sopenharmony_ci#include "ar-internal.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int rxrpc_vet_description_s(const char *);
278c2ecf20Sopenharmony_cistatic int rxrpc_preparse(struct key_preparsed_payload *);
288c2ecf20Sopenharmony_cistatic int rxrpc_preparse_s(struct key_preparsed_payload *);
298c2ecf20Sopenharmony_cistatic void rxrpc_free_preparse(struct key_preparsed_payload *);
308c2ecf20Sopenharmony_cistatic void rxrpc_free_preparse_s(struct key_preparsed_payload *);
318c2ecf20Sopenharmony_cistatic void rxrpc_destroy(struct key *);
328c2ecf20Sopenharmony_cistatic void rxrpc_destroy_s(struct key *);
338c2ecf20Sopenharmony_cistatic void rxrpc_describe(const struct key *, struct seq_file *);
348c2ecf20Sopenharmony_cistatic long rxrpc_read(const struct key *, char *, size_t);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/*
378c2ecf20Sopenharmony_ci * rxrpc defined keys take an arbitrary string as the description and an
388c2ecf20Sopenharmony_ci * arbitrary blob of data as the payload
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_cistruct key_type key_type_rxrpc = {
418c2ecf20Sopenharmony_ci	.name		= "rxrpc",
428c2ecf20Sopenharmony_ci	.flags		= KEY_TYPE_NET_DOMAIN,
438c2ecf20Sopenharmony_ci	.preparse	= rxrpc_preparse,
448c2ecf20Sopenharmony_ci	.free_preparse	= rxrpc_free_preparse,
458c2ecf20Sopenharmony_ci	.instantiate	= generic_key_instantiate,
468c2ecf20Sopenharmony_ci	.destroy	= rxrpc_destroy,
478c2ecf20Sopenharmony_ci	.describe	= rxrpc_describe,
488c2ecf20Sopenharmony_ci	.read		= rxrpc_read,
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(key_type_rxrpc);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/*
538c2ecf20Sopenharmony_ci * rxrpc server defined keys take "<serviceId>:<securityIndex>" as the
548c2ecf20Sopenharmony_ci * description and an 8-byte decryption key as the payload
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_cistruct key_type key_type_rxrpc_s = {
578c2ecf20Sopenharmony_ci	.name		= "rxrpc_s",
588c2ecf20Sopenharmony_ci	.flags		= KEY_TYPE_NET_DOMAIN,
598c2ecf20Sopenharmony_ci	.vet_description = rxrpc_vet_description_s,
608c2ecf20Sopenharmony_ci	.preparse	= rxrpc_preparse_s,
618c2ecf20Sopenharmony_ci	.free_preparse	= rxrpc_free_preparse_s,
628c2ecf20Sopenharmony_ci	.instantiate	= generic_key_instantiate,
638c2ecf20Sopenharmony_ci	.destroy	= rxrpc_destroy_s,
648c2ecf20Sopenharmony_ci	.describe	= rxrpc_describe,
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/*
688c2ecf20Sopenharmony_ci * Vet the description for an RxRPC server key
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_cistatic int rxrpc_vet_description_s(const char *desc)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	unsigned long num;
738c2ecf20Sopenharmony_ci	char *p;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	num = simple_strtoul(desc, &p, 10);
768c2ecf20Sopenharmony_ci	if (*p != ':' || num > 65535)
778c2ecf20Sopenharmony_ci		return -EINVAL;
788c2ecf20Sopenharmony_ci	num = simple_strtoul(p + 1, &p, 10);
798c2ecf20Sopenharmony_ci	if (*p || num < 1 || num > 255)
808c2ecf20Sopenharmony_ci		return -EINVAL;
818c2ecf20Sopenharmony_ci	return 0;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/*
858c2ecf20Sopenharmony_ci * parse an RxKAD type XDR format token
868c2ecf20Sopenharmony_ci * - the caller guarantees we have at least 4 words
878c2ecf20Sopenharmony_ci */
888c2ecf20Sopenharmony_cistatic int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep,
898c2ecf20Sopenharmony_ci				    size_t datalen,
908c2ecf20Sopenharmony_ci				    const __be32 *xdr, unsigned int toklen)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct rxrpc_key_token *token, **pptoken;
938c2ecf20Sopenharmony_ci	time64_t expiry;
948c2ecf20Sopenharmony_ci	size_t plen;
958c2ecf20Sopenharmony_ci	u32 tktlen;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	_enter(",{%x,%x,%x,%x},%u",
988c2ecf20Sopenharmony_ci	       ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]),
998c2ecf20Sopenharmony_ci	       toklen);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (toklen <= 8 * 4)
1028c2ecf20Sopenharmony_ci		return -EKEYREJECTED;
1038c2ecf20Sopenharmony_ci	tktlen = ntohl(xdr[7]);
1048c2ecf20Sopenharmony_ci	_debug("tktlen: %x", tktlen);
1058c2ecf20Sopenharmony_ci	if (tktlen > AFSTOKEN_RK_TIX_MAX)
1068c2ecf20Sopenharmony_ci		return -EKEYREJECTED;
1078c2ecf20Sopenharmony_ci	if (toklen < 8 * 4 + tktlen)
1088c2ecf20Sopenharmony_ci		return -EKEYREJECTED;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	plen = sizeof(*token) + sizeof(*token->kad) + tktlen;
1118c2ecf20Sopenharmony_ci	prep->quotalen = datalen + plen;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	plen -= sizeof(*token);
1148c2ecf20Sopenharmony_ci	token = kzalloc(sizeof(*token), GFP_KERNEL);
1158c2ecf20Sopenharmony_ci	if (!token)
1168c2ecf20Sopenharmony_ci		return -ENOMEM;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	token->kad = kzalloc(plen, GFP_KERNEL);
1198c2ecf20Sopenharmony_ci	if (!token->kad) {
1208c2ecf20Sopenharmony_ci		kfree(token);
1218c2ecf20Sopenharmony_ci		return -ENOMEM;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	token->security_index	= RXRPC_SECURITY_RXKAD;
1258c2ecf20Sopenharmony_ci	token->kad->ticket_len	= tktlen;
1268c2ecf20Sopenharmony_ci	token->kad->vice_id	= ntohl(xdr[0]);
1278c2ecf20Sopenharmony_ci	token->kad->kvno	= ntohl(xdr[1]);
1288c2ecf20Sopenharmony_ci	token->kad->start	= ntohl(xdr[4]);
1298c2ecf20Sopenharmony_ci	token->kad->expiry	= ntohl(xdr[5]);
1308c2ecf20Sopenharmony_ci	token->kad->primary_flag = ntohl(xdr[6]);
1318c2ecf20Sopenharmony_ci	memcpy(&token->kad->session_key, &xdr[2], 8);
1328c2ecf20Sopenharmony_ci	memcpy(&token->kad->ticket, &xdr[8], tktlen);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	_debug("SCIX: %u", token->security_index);
1358c2ecf20Sopenharmony_ci	_debug("TLEN: %u", token->kad->ticket_len);
1368c2ecf20Sopenharmony_ci	_debug("EXPY: %x", token->kad->expiry);
1378c2ecf20Sopenharmony_ci	_debug("KVNO: %u", token->kad->kvno);
1388c2ecf20Sopenharmony_ci	_debug("PRIM: %u", token->kad->primary_flag);
1398c2ecf20Sopenharmony_ci	_debug("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x",
1408c2ecf20Sopenharmony_ci	       token->kad->session_key[0], token->kad->session_key[1],
1418c2ecf20Sopenharmony_ci	       token->kad->session_key[2], token->kad->session_key[3],
1428c2ecf20Sopenharmony_ci	       token->kad->session_key[4], token->kad->session_key[5],
1438c2ecf20Sopenharmony_ci	       token->kad->session_key[6], token->kad->session_key[7]);
1448c2ecf20Sopenharmony_ci	if (token->kad->ticket_len >= 8)
1458c2ecf20Sopenharmony_ci		_debug("TCKT: %02x%02x%02x%02x%02x%02x%02x%02x",
1468c2ecf20Sopenharmony_ci		       token->kad->ticket[0], token->kad->ticket[1],
1478c2ecf20Sopenharmony_ci		       token->kad->ticket[2], token->kad->ticket[3],
1488c2ecf20Sopenharmony_ci		       token->kad->ticket[4], token->kad->ticket[5],
1498c2ecf20Sopenharmony_ci		       token->kad->ticket[6], token->kad->ticket[7]);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* count the number of tokens attached */
1528c2ecf20Sopenharmony_ci	prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/* attach the data */
1558c2ecf20Sopenharmony_ci	for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0];
1568c2ecf20Sopenharmony_ci	     *pptoken;
1578c2ecf20Sopenharmony_ci	     pptoken = &(*pptoken)->next)
1588c2ecf20Sopenharmony_ci		continue;
1598c2ecf20Sopenharmony_ci	*pptoken = token;
1608c2ecf20Sopenharmony_ci	expiry = rxrpc_u32_to_time64(token->kad->expiry);
1618c2ecf20Sopenharmony_ci	if (expiry < prep->expiry)
1628c2ecf20Sopenharmony_ci		prep->expiry = expiry;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	_leave(" = 0");
1658c2ecf20Sopenharmony_ci	return 0;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic void rxrpc_free_krb5_principal(struct krb5_principal *princ)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	int loop;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (princ->name_parts) {
1738c2ecf20Sopenharmony_ci		for (loop = princ->n_name_parts - 1; loop >= 0; loop--)
1748c2ecf20Sopenharmony_ci			kfree(princ->name_parts[loop]);
1758c2ecf20Sopenharmony_ci		kfree(princ->name_parts);
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci	kfree(princ->realm);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic void rxrpc_free_krb5_tagged(struct krb5_tagged_data *td)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	kfree(td->data);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci/*
1868c2ecf20Sopenharmony_ci * free up an RxK5 token
1878c2ecf20Sopenharmony_ci */
1888c2ecf20Sopenharmony_cistatic void rxrpc_rxk5_free(struct rxk5_key *rxk5)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	int loop;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	rxrpc_free_krb5_principal(&rxk5->client);
1938c2ecf20Sopenharmony_ci	rxrpc_free_krb5_principal(&rxk5->server);
1948c2ecf20Sopenharmony_ci	rxrpc_free_krb5_tagged(&rxk5->session);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (rxk5->addresses) {
1978c2ecf20Sopenharmony_ci		for (loop = rxk5->n_addresses - 1; loop >= 0; loop--)
1988c2ecf20Sopenharmony_ci			rxrpc_free_krb5_tagged(&rxk5->addresses[loop]);
1998c2ecf20Sopenharmony_ci		kfree(rxk5->addresses);
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci	if (rxk5->authdata) {
2028c2ecf20Sopenharmony_ci		for (loop = rxk5->n_authdata - 1; loop >= 0; loop--)
2038c2ecf20Sopenharmony_ci			rxrpc_free_krb5_tagged(&rxk5->authdata[loop]);
2048c2ecf20Sopenharmony_ci		kfree(rxk5->authdata);
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	kfree(rxk5->ticket);
2088c2ecf20Sopenharmony_ci	kfree(rxk5->ticket2);
2098c2ecf20Sopenharmony_ci	kfree(rxk5);
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci/*
2138c2ecf20Sopenharmony_ci * extract a krb5 principal
2148c2ecf20Sopenharmony_ci */
2158c2ecf20Sopenharmony_cistatic int rxrpc_krb5_decode_principal(struct krb5_principal *princ,
2168c2ecf20Sopenharmony_ci				       const __be32 **_xdr,
2178c2ecf20Sopenharmony_ci				       unsigned int *_toklen)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	const __be32 *xdr = *_xdr;
2208c2ecf20Sopenharmony_ci	unsigned int toklen = *_toklen, n_parts, loop, tmp, paddedlen;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	/* there must be at least one name, and at least #names+1 length
2238c2ecf20Sopenharmony_ci	 * words */
2248c2ecf20Sopenharmony_ci	if (toklen <= 12)
2258c2ecf20Sopenharmony_ci		return -EINVAL;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	_enter(",{%x,%x,%x},%u",
2288c2ecf20Sopenharmony_ci	       ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), toklen);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	n_parts = ntohl(*xdr++);
2318c2ecf20Sopenharmony_ci	toklen -= 4;
2328c2ecf20Sopenharmony_ci	if (n_parts <= 0 || n_parts > AFSTOKEN_K5_COMPONENTS_MAX)
2338c2ecf20Sopenharmony_ci		return -EINVAL;
2348c2ecf20Sopenharmony_ci	princ->n_name_parts = n_parts;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	if (toklen <= (n_parts + 1) * 4)
2378c2ecf20Sopenharmony_ci		return -EINVAL;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	princ->name_parts = kcalloc(n_parts, sizeof(char *), GFP_KERNEL);
2408c2ecf20Sopenharmony_ci	if (!princ->name_parts)
2418c2ecf20Sopenharmony_ci		return -ENOMEM;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	for (loop = 0; loop < n_parts; loop++) {
2448c2ecf20Sopenharmony_ci		if (toklen < 4)
2458c2ecf20Sopenharmony_ci			return -EINVAL;
2468c2ecf20Sopenharmony_ci		tmp = ntohl(*xdr++);
2478c2ecf20Sopenharmony_ci		toklen -= 4;
2488c2ecf20Sopenharmony_ci		if (tmp <= 0 || tmp > AFSTOKEN_STRING_MAX)
2498c2ecf20Sopenharmony_ci			return -EINVAL;
2508c2ecf20Sopenharmony_ci		paddedlen = (tmp + 3) & ~3;
2518c2ecf20Sopenharmony_ci		if (paddedlen > toklen)
2528c2ecf20Sopenharmony_ci			return -EINVAL;
2538c2ecf20Sopenharmony_ci		princ->name_parts[loop] = kmalloc(tmp + 1, GFP_KERNEL);
2548c2ecf20Sopenharmony_ci		if (!princ->name_parts[loop])
2558c2ecf20Sopenharmony_ci			return -ENOMEM;
2568c2ecf20Sopenharmony_ci		memcpy(princ->name_parts[loop], xdr, tmp);
2578c2ecf20Sopenharmony_ci		princ->name_parts[loop][tmp] = 0;
2588c2ecf20Sopenharmony_ci		toklen -= paddedlen;
2598c2ecf20Sopenharmony_ci		xdr += paddedlen >> 2;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (toklen < 4)
2638c2ecf20Sopenharmony_ci		return -EINVAL;
2648c2ecf20Sopenharmony_ci	tmp = ntohl(*xdr++);
2658c2ecf20Sopenharmony_ci	toklen -= 4;
2668c2ecf20Sopenharmony_ci	if (tmp <= 0 || tmp > AFSTOKEN_K5_REALM_MAX)
2678c2ecf20Sopenharmony_ci		return -EINVAL;
2688c2ecf20Sopenharmony_ci	paddedlen = (tmp + 3) & ~3;
2698c2ecf20Sopenharmony_ci	if (paddedlen > toklen)
2708c2ecf20Sopenharmony_ci		return -EINVAL;
2718c2ecf20Sopenharmony_ci	princ->realm = kmalloc(tmp + 1, GFP_KERNEL);
2728c2ecf20Sopenharmony_ci	if (!princ->realm)
2738c2ecf20Sopenharmony_ci		return -ENOMEM;
2748c2ecf20Sopenharmony_ci	memcpy(princ->realm, xdr, tmp);
2758c2ecf20Sopenharmony_ci	princ->realm[tmp] = 0;
2768c2ecf20Sopenharmony_ci	toklen -= paddedlen;
2778c2ecf20Sopenharmony_ci	xdr += paddedlen >> 2;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	_debug("%s/...@%s", princ->name_parts[0], princ->realm);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	*_xdr = xdr;
2828c2ecf20Sopenharmony_ci	*_toklen = toklen;
2838c2ecf20Sopenharmony_ci	_leave(" = 0 [toklen=%u]", toklen);
2848c2ecf20Sopenharmony_ci	return 0;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci/*
2888c2ecf20Sopenharmony_ci * extract a piece of krb5 tagged data
2898c2ecf20Sopenharmony_ci */
2908c2ecf20Sopenharmony_cistatic int rxrpc_krb5_decode_tagged_data(struct krb5_tagged_data *td,
2918c2ecf20Sopenharmony_ci					 size_t max_data_size,
2928c2ecf20Sopenharmony_ci					 const __be32 **_xdr,
2938c2ecf20Sopenharmony_ci					 unsigned int *_toklen)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	const __be32 *xdr = *_xdr;
2968c2ecf20Sopenharmony_ci	unsigned int toklen = *_toklen, len, paddedlen;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* there must be at least one tag and one length word */
2998c2ecf20Sopenharmony_ci	if (toklen <= 8)
3008c2ecf20Sopenharmony_ci		return -EINVAL;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	_enter(",%zu,{%x,%x},%u",
3038c2ecf20Sopenharmony_ci	       max_data_size, ntohl(xdr[0]), ntohl(xdr[1]), toklen);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	td->tag = ntohl(*xdr++);
3068c2ecf20Sopenharmony_ci	len = ntohl(*xdr++);
3078c2ecf20Sopenharmony_ci	toklen -= 8;
3088c2ecf20Sopenharmony_ci	if (len > max_data_size)
3098c2ecf20Sopenharmony_ci		return -EINVAL;
3108c2ecf20Sopenharmony_ci	paddedlen = (len + 3) & ~3;
3118c2ecf20Sopenharmony_ci	if (paddedlen > toklen)
3128c2ecf20Sopenharmony_ci		return -EINVAL;
3138c2ecf20Sopenharmony_ci	td->data_len = len;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (len > 0) {
3168c2ecf20Sopenharmony_ci		td->data = kmemdup(xdr, len, GFP_KERNEL);
3178c2ecf20Sopenharmony_ci		if (!td->data)
3188c2ecf20Sopenharmony_ci			return -ENOMEM;
3198c2ecf20Sopenharmony_ci		toklen -= paddedlen;
3208c2ecf20Sopenharmony_ci		xdr += paddedlen >> 2;
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	_debug("tag %x len %x", td->tag, td->data_len);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	*_xdr = xdr;
3268c2ecf20Sopenharmony_ci	*_toklen = toklen;
3278c2ecf20Sopenharmony_ci	_leave(" = 0 [toklen=%u]", toklen);
3288c2ecf20Sopenharmony_ci	return 0;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci/*
3328c2ecf20Sopenharmony_ci * extract an array of tagged data
3338c2ecf20Sopenharmony_ci */
3348c2ecf20Sopenharmony_cistatic int rxrpc_krb5_decode_tagged_array(struct krb5_tagged_data **_td,
3358c2ecf20Sopenharmony_ci					  u8 *_n_elem,
3368c2ecf20Sopenharmony_ci					  u8 max_n_elem,
3378c2ecf20Sopenharmony_ci					  size_t max_elem_size,
3388c2ecf20Sopenharmony_ci					  const __be32 **_xdr,
3398c2ecf20Sopenharmony_ci					  unsigned int *_toklen)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct krb5_tagged_data *td;
3428c2ecf20Sopenharmony_ci	const __be32 *xdr = *_xdr;
3438c2ecf20Sopenharmony_ci	unsigned int toklen = *_toklen, n_elem, loop;
3448c2ecf20Sopenharmony_ci	int ret;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	/* there must be at least one count */
3478c2ecf20Sopenharmony_ci	if (toklen < 4)
3488c2ecf20Sopenharmony_ci		return -EINVAL;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	_enter(",,%u,%zu,{%x},%u",
3518c2ecf20Sopenharmony_ci	       max_n_elem, max_elem_size, ntohl(xdr[0]), toklen);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	n_elem = ntohl(*xdr++);
3548c2ecf20Sopenharmony_ci	toklen -= 4;
3558c2ecf20Sopenharmony_ci	if (n_elem > max_n_elem)
3568c2ecf20Sopenharmony_ci		return -EINVAL;
3578c2ecf20Sopenharmony_ci	*_n_elem = n_elem;
3588c2ecf20Sopenharmony_ci	if (n_elem > 0) {
3598c2ecf20Sopenharmony_ci		if (toklen <= (n_elem + 1) * 4)
3608c2ecf20Sopenharmony_ci			return -EINVAL;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		_debug("n_elem %d", n_elem);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		td = kcalloc(n_elem, sizeof(struct krb5_tagged_data),
3658c2ecf20Sopenharmony_ci			     GFP_KERNEL);
3668c2ecf20Sopenharmony_ci		if (!td)
3678c2ecf20Sopenharmony_ci			return -ENOMEM;
3688c2ecf20Sopenharmony_ci		*_td = td;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci		for (loop = 0; loop < n_elem; loop++) {
3718c2ecf20Sopenharmony_ci			ret = rxrpc_krb5_decode_tagged_data(&td[loop],
3728c2ecf20Sopenharmony_ci							    max_elem_size,
3738c2ecf20Sopenharmony_ci							    &xdr, &toklen);
3748c2ecf20Sopenharmony_ci			if (ret < 0)
3758c2ecf20Sopenharmony_ci				return ret;
3768c2ecf20Sopenharmony_ci		}
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	*_xdr = xdr;
3808c2ecf20Sopenharmony_ci	*_toklen = toklen;
3818c2ecf20Sopenharmony_ci	_leave(" = 0 [toklen=%u]", toklen);
3828c2ecf20Sopenharmony_ci	return 0;
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci/*
3868c2ecf20Sopenharmony_ci * extract a krb5 ticket
3878c2ecf20Sopenharmony_ci */
3888c2ecf20Sopenharmony_cistatic int rxrpc_krb5_decode_ticket(u8 **_ticket, u16 *_tktlen,
3898c2ecf20Sopenharmony_ci				    const __be32 **_xdr, unsigned int *_toklen)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	const __be32 *xdr = *_xdr;
3928c2ecf20Sopenharmony_ci	unsigned int toklen = *_toklen, len, paddedlen;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/* there must be at least one length word */
3958c2ecf20Sopenharmony_ci	if (toklen <= 4)
3968c2ecf20Sopenharmony_ci		return -EINVAL;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	_enter(",{%x},%u", ntohl(xdr[0]), toklen);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	len = ntohl(*xdr++);
4018c2ecf20Sopenharmony_ci	toklen -= 4;
4028c2ecf20Sopenharmony_ci	if (len > AFSTOKEN_K5_TIX_MAX)
4038c2ecf20Sopenharmony_ci		return -EINVAL;
4048c2ecf20Sopenharmony_ci	paddedlen = (len + 3) & ~3;
4058c2ecf20Sopenharmony_ci	if (paddedlen > toklen)
4068c2ecf20Sopenharmony_ci		return -EINVAL;
4078c2ecf20Sopenharmony_ci	*_tktlen = len;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	_debug("ticket len %u", len);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (len > 0) {
4128c2ecf20Sopenharmony_ci		*_ticket = kmemdup(xdr, len, GFP_KERNEL);
4138c2ecf20Sopenharmony_ci		if (!*_ticket)
4148c2ecf20Sopenharmony_ci			return -ENOMEM;
4158c2ecf20Sopenharmony_ci		toklen -= paddedlen;
4168c2ecf20Sopenharmony_ci		xdr += paddedlen >> 2;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	*_xdr = xdr;
4208c2ecf20Sopenharmony_ci	*_toklen = toklen;
4218c2ecf20Sopenharmony_ci	_leave(" = 0 [toklen=%u]", toklen);
4228c2ecf20Sopenharmony_ci	return 0;
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci/*
4268c2ecf20Sopenharmony_ci * parse an RxK5 type XDR format token
4278c2ecf20Sopenharmony_ci * - the caller guarantees we have at least 4 words
4288c2ecf20Sopenharmony_ci */
4298c2ecf20Sopenharmony_cistatic int rxrpc_preparse_xdr_rxk5(struct key_preparsed_payload *prep,
4308c2ecf20Sopenharmony_ci				   size_t datalen,
4318c2ecf20Sopenharmony_ci				   const __be32 *xdr, unsigned int toklen)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct rxrpc_key_token *token, **pptoken;
4348c2ecf20Sopenharmony_ci	struct rxk5_key *rxk5;
4358c2ecf20Sopenharmony_ci	const __be32 *end_xdr = xdr + (toklen >> 2);
4368c2ecf20Sopenharmony_ci	time64_t expiry;
4378c2ecf20Sopenharmony_ci	int ret;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	_enter(",{%x,%x,%x,%x},%u",
4408c2ecf20Sopenharmony_ci	       ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]),
4418c2ecf20Sopenharmony_ci	       toklen);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	/* reserve some payload space for this subkey - the length of the token
4448c2ecf20Sopenharmony_ci	 * is a reasonable approximation */
4458c2ecf20Sopenharmony_ci	prep->quotalen = datalen + toklen;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	token = kzalloc(sizeof(*token), GFP_KERNEL);
4488c2ecf20Sopenharmony_ci	if (!token)
4498c2ecf20Sopenharmony_ci		return -ENOMEM;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	rxk5 = kzalloc(sizeof(*rxk5), GFP_KERNEL);
4528c2ecf20Sopenharmony_ci	if (!rxk5) {
4538c2ecf20Sopenharmony_ci		kfree(token);
4548c2ecf20Sopenharmony_ci		return -ENOMEM;
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	token->security_index = RXRPC_SECURITY_RXK5;
4588c2ecf20Sopenharmony_ci	token->k5 = rxk5;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* extract the principals */
4618c2ecf20Sopenharmony_ci	ret = rxrpc_krb5_decode_principal(&rxk5->client, &xdr, &toklen);
4628c2ecf20Sopenharmony_ci	if (ret < 0)
4638c2ecf20Sopenharmony_ci		goto error;
4648c2ecf20Sopenharmony_ci	ret = rxrpc_krb5_decode_principal(&rxk5->server, &xdr, &toklen);
4658c2ecf20Sopenharmony_ci	if (ret < 0)
4668c2ecf20Sopenharmony_ci		goto error;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	/* extract the session key and the encoding type (the tag field ->
4698c2ecf20Sopenharmony_ci	 * ENCTYPE_xxx) */
4708c2ecf20Sopenharmony_ci	ret = rxrpc_krb5_decode_tagged_data(&rxk5->session, AFSTOKEN_DATA_MAX,
4718c2ecf20Sopenharmony_ci					    &xdr, &toklen);
4728c2ecf20Sopenharmony_ci	if (ret < 0)
4738c2ecf20Sopenharmony_ci		goto error;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	if (toklen < 4 * 8 + 2 * 4)
4768c2ecf20Sopenharmony_ci		goto inval;
4778c2ecf20Sopenharmony_ci	rxk5->authtime	= be64_to_cpup((const __be64 *) xdr);
4788c2ecf20Sopenharmony_ci	xdr += 2;
4798c2ecf20Sopenharmony_ci	rxk5->starttime	= be64_to_cpup((const __be64 *) xdr);
4808c2ecf20Sopenharmony_ci	xdr += 2;
4818c2ecf20Sopenharmony_ci	rxk5->endtime	= be64_to_cpup((const __be64 *) xdr);
4828c2ecf20Sopenharmony_ci	xdr += 2;
4838c2ecf20Sopenharmony_ci	rxk5->renew_till = be64_to_cpup((const __be64 *) xdr);
4848c2ecf20Sopenharmony_ci	xdr += 2;
4858c2ecf20Sopenharmony_ci	rxk5->is_skey = ntohl(*xdr++);
4868c2ecf20Sopenharmony_ci	rxk5->flags = ntohl(*xdr++);
4878c2ecf20Sopenharmony_ci	toklen -= 4 * 8 + 2 * 4;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	_debug("times: a=%llx s=%llx e=%llx rt=%llx",
4908c2ecf20Sopenharmony_ci	       rxk5->authtime, rxk5->starttime, rxk5->endtime,
4918c2ecf20Sopenharmony_ci	       rxk5->renew_till);
4928c2ecf20Sopenharmony_ci	_debug("is_skey=%x flags=%x", rxk5->is_skey, rxk5->flags);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	/* extract the permitted client addresses */
4958c2ecf20Sopenharmony_ci	ret = rxrpc_krb5_decode_tagged_array(&rxk5->addresses,
4968c2ecf20Sopenharmony_ci					     &rxk5->n_addresses,
4978c2ecf20Sopenharmony_ci					     AFSTOKEN_K5_ADDRESSES_MAX,
4988c2ecf20Sopenharmony_ci					     AFSTOKEN_DATA_MAX,
4998c2ecf20Sopenharmony_ci					     &xdr, &toklen);
5008c2ecf20Sopenharmony_ci	if (ret < 0)
5018c2ecf20Sopenharmony_ci		goto error;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	ASSERTCMP((end_xdr - xdr) << 2, ==, toklen);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	/* extract the tickets */
5068c2ecf20Sopenharmony_ci	ret = rxrpc_krb5_decode_ticket(&rxk5->ticket, &rxk5->ticket_len,
5078c2ecf20Sopenharmony_ci				       &xdr, &toklen);
5088c2ecf20Sopenharmony_ci	if (ret < 0)
5098c2ecf20Sopenharmony_ci		goto error;
5108c2ecf20Sopenharmony_ci	ret = rxrpc_krb5_decode_ticket(&rxk5->ticket2, &rxk5->ticket2_len,
5118c2ecf20Sopenharmony_ci				       &xdr, &toklen);
5128c2ecf20Sopenharmony_ci	if (ret < 0)
5138c2ecf20Sopenharmony_ci		goto error;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	ASSERTCMP((end_xdr - xdr) << 2, ==, toklen);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* extract the typed auth data */
5188c2ecf20Sopenharmony_ci	ret = rxrpc_krb5_decode_tagged_array(&rxk5->authdata,
5198c2ecf20Sopenharmony_ci					     &rxk5->n_authdata,
5208c2ecf20Sopenharmony_ci					     AFSTOKEN_K5_AUTHDATA_MAX,
5218c2ecf20Sopenharmony_ci					     AFSTOKEN_BDATALN_MAX,
5228c2ecf20Sopenharmony_ci					     &xdr, &toklen);
5238c2ecf20Sopenharmony_ci	if (ret < 0)
5248c2ecf20Sopenharmony_ci		goto error;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	ASSERTCMP((end_xdr - xdr) << 2, ==, toklen);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (toklen != 0)
5298c2ecf20Sopenharmony_ci		goto inval;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	/* attach the payload */
5328c2ecf20Sopenharmony_ci	for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0];
5338c2ecf20Sopenharmony_ci	     *pptoken;
5348c2ecf20Sopenharmony_ci	     pptoken = &(*pptoken)->next)
5358c2ecf20Sopenharmony_ci		continue;
5368c2ecf20Sopenharmony_ci	*pptoken = token;
5378c2ecf20Sopenharmony_ci	expiry = rxrpc_u32_to_time64(token->k5->endtime);
5388c2ecf20Sopenharmony_ci	if (expiry < prep->expiry)
5398c2ecf20Sopenharmony_ci		prep->expiry = expiry;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	_leave(" = 0");
5428c2ecf20Sopenharmony_ci	return 0;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ciinval:
5458c2ecf20Sopenharmony_ci	ret = -EINVAL;
5468c2ecf20Sopenharmony_cierror:
5478c2ecf20Sopenharmony_ci	rxrpc_rxk5_free(rxk5);
5488c2ecf20Sopenharmony_ci	kfree(token);
5498c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
5508c2ecf20Sopenharmony_ci	return ret;
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci/*
5548c2ecf20Sopenharmony_ci * attempt to parse the data as the XDR format
5558c2ecf20Sopenharmony_ci * - the caller guarantees we have more than 7 words
5568c2ecf20Sopenharmony_ci */
5578c2ecf20Sopenharmony_cistatic int rxrpc_preparse_xdr(struct key_preparsed_payload *prep)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	const __be32 *xdr = prep->data, *token;
5608c2ecf20Sopenharmony_ci	const char *cp;
5618c2ecf20Sopenharmony_ci	unsigned int len, paddedlen, loop, ntoken, toklen, sec_ix;
5628c2ecf20Sopenharmony_ci	size_t datalen = prep->datalen;
5638c2ecf20Sopenharmony_ci	int ret;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	_enter(",{%x,%x,%x,%x},%zu",
5668c2ecf20Sopenharmony_ci	       ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]),
5678c2ecf20Sopenharmony_ci	       prep->datalen);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	if (datalen > AFSTOKEN_LENGTH_MAX)
5708c2ecf20Sopenharmony_ci		goto not_xdr;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	/* XDR is an array of __be32's */
5738c2ecf20Sopenharmony_ci	if (datalen & 3)
5748c2ecf20Sopenharmony_ci		goto not_xdr;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	/* the flags should be 0 (the setpag bit must be handled by
5778c2ecf20Sopenharmony_ci	 * userspace) */
5788c2ecf20Sopenharmony_ci	if (ntohl(*xdr++) != 0)
5798c2ecf20Sopenharmony_ci		goto not_xdr;
5808c2ecf20Sopenharmony_ci	datalen -= 4;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	/* check the cell name */
5838c2ecf20Sopenharmony_ci	len = ntohl(*xdr++);
5848c2ecf20Sopenharmony_ci	if (len < 1 || len > AFSTOKEN_CELL_MAX)
5858c2ecf20Sopenharmony_ci		goto not_xdr;
5868c2ecf20Sopenharmony_ci	datalen -= 4;
5878c2ecf20Sopenharmony_ci	paddedlen = (len + 3) & ~3;
5888c2ecf20Sopenharmony_ci	if (paddedlen > datalen)
5898c2ecf20Sopenharmony_ci		goto not_xdr;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	cp = (const char *) xdr;
5928c2ecf20Sopenharmony_ci	for (loop = 0; loop < len; loop++)
5938c2ecf20Sopenharmony_ci		if (!isprint(cp[loop]))
5948c2ecf20Sopenharmony_ci			goto not_xdr;
5958c2ecf20Sopenharmony_ci	for (; loop < paddedlen; loop++)
5968c2ecf20Sopenharmony_ci		if (cp[loop])
5978c2ecf20Sopenharmony_ci			goto not_xdr;
5988c2ecf20Sopenharmony_ci	_debug("cellname: [%u/%u] '%*.*s'",
5998c2ecf20Sopenharmony_ci	       len, paddedlen, len, len, (const char *) xdr);
6008c2ecf20Sopenharmony_ci	datalen -= paddedlen;
6018c2ecf20Sopenharmony_ci	xdr += paddedlen >> 2;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	/* get the token count */
6048c2ecf20Sopenharmony_ci	if (datalen < 12)
6058c2ecf20Sopenharmony_ci		goto not_xdr;
6068c2ecf20Sopenharmony_ci	ntoken = ntohl(*xdr++);
6078c2ecf20Sopenharmony_ci	datalen -= 4;
6088c2ecf20Sopenharmony_ci	_debug("ntoken: %x", ntoken);
6098c2ecf20Sopenharmony_ci	if (ntoken < 1 || ntoken > AFSTOKEN_MAX)
6108c2ecf20Sopenharmony_ci		goto not_xdr;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	/* check each token wrapper */
6138c2ecf20Sopenharmony_ci	token = xdr;
6148c2ecf20Sopenharmony_ci	loop = ntoken;
6158c2ecf20Sopenharmony_ci	do {
6168c2ecf20Sopenharmony_ci		if (datalen < 8)
6178c2ecf20Sopenharmony_ci			goto not_xdr;
6188c2ecf20Sopenharmony_ci		toklen = ntohl(*xdr++);
6198c2ecf20Sopenharmony_ci		sec_ix = ntohl(*xdr);
6208c2ecf20Sopenharmony_ci		datalen -= 4;
6218c2ecf20Sopenharmony_ci		_debug("token: [%x/%zx] %x", toklen, datalen, sec_ix);
6228c2ecf20Sopenharmony_ci		paddedlen = (toklen + 3) & ~3;
6238c2ecf20Sopenharmony_ci		if (toklen < 20 || toklen > datalen || paddedlen > datalen)
6248c2ecf20Sopenharmony_ci			goto not_xdr;
6258c2ecf20Sopenharmony_ci		datalen -= paddedlen;
6268c2ecf20Sopenharmony_ci		xdr += paddedlen >> 2;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	} while (--loop > 0);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	_debug("remainder: %zu", datalen);
6318c2ecf20Sopenharmony_ci	if (datalen != 0)
6328c2ecf20Sopenharmony_ci		goto not_xdr;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	/* okay: we're going to assume it's valid XDR format
6358c2ecf20Sopenharmony_ci	 * - we ignore the cellname, relying on the key to be correctly named
6368c2ecf20Sopenharmony_ci	 */
6378c2ecf20Sopenharmony_ci	do {
6388c2ecf20Sopenharmony_ci		xdr = token;
6398c2ecf20Sopenharmony_ci		toklen = ntohl(*xdr++);
6408c2ecf20Sopenharmony_ci		token = xdr + ((toklen + 3) >> 2);
6418c2ecf20Sopenharmony_ci		sec_ix = ntohl(*xdr++);
6428c2ecf20Sopenharmony_ci		toklen -= 4;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci		_debug("TOKEN type=%u [%p-%p]", sec_ix, xdr, token);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci		switch (sec_ix) {
6478c2ecf20Sopenharmony_ci		case RXRPC_SECURITY_RXKAD:
6488c2ecf20Sopenharmony_ci			ret = rxrpc_preparse_xdr_rxkad(prep, datalen, xdr, toklen);
6498c2ecf20Sopenharmony_ci			if (ret != 0)
6508c2ecf20Sopenharmony_ci				goto error;
6518c2ecf20Sopenharmony_ci			break;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci		case RXRPC_SECURITY_RXK5:
6548c2ecf20Sopenharmony_ci			ret = rxrpc_preparse_xdr_rxk5(prep, datalen, xdr, toklen);
6558c2ecf20Sopenharmony_ci			if (ret != 0)
6568c2ecf20Sopenharmony_ci				goto error;
6578c2ecf20Sopenharmony_ci			break;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci		default:
6608c2ecf20Sopenharmony_ci			ret = -EPROTONOSUPPORT;
6618c2ecf20Sopenharmony_ci			goto error;
6628c2ecf20Sopenharmony_ci		}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	} while (--ntoken > 0);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	_leave(" = 0");
6678c2ecf20Sopenharmony_ci	return 0;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cinot_xdr:
6708c2ecf20Sopenharmony_ci	_leave(" = -EPROTO");
6718c2ecf20Sopenharmony_ci	return -EPROTO;
6728c2ecf20Sopenharmony_cierror:
6738c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
6748c2ecf20Sopenharmony_ci	return ret;
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci/*
6788c2ecf20Sopenharmony_ci * Preparse an rxrpc defined key.
6798c2ecf20Sopenharmony_ci *
6808c2ecf20Sopenharmony_ci * Data should be of the form:
6818c2ecf20Sopenharmony_ci *	OFFSET	LEN	CONTENT
6828c2ecf20Sopenharmony_ci *	0	4	key interface version number
6838c2ecf20Sopenharmony_ci *	4	2	security index (type)
6848c2ecf20Sopenharmony_ci *	6	2	ticket length
6858c2ecf20Sopenharmony_ci *	8	4	key expiry time (time_t)
6868c2ecf20Sopenharmony_ci *	12	4	kvno
6878c2ecf20Sopenharmony_ci *	16	8	session key
6888c2ecf20Sopenharmony_ci *	24	[len]	ticket
6898c2ecf20Sopenharmony_ci *
6908c2ecf20Sopenharmony_ci * if no data is provided, then a no-security key is made
6918c2ecf20Sopenharmony_ci */
6928c2ecf20Sopenharmony_cistatic int rxrpc_preparse(struct key_preparsed_payload *prep)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	const struct rxrpc_key_data_v1 *v1;
6958c2ecf20Sopenharmony_ci	struct rxrpc_key_token *token, **pp;
6968c2ecf20Sopenharmony_ci	time64_t expiry;
6978c2ecf20Sopenharmony_ci	size_t plen;
6988c2ecf20Sopenharmony_ci	u32 kver;
6998c2ecf20Sopenharmony_ci	int ret;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	_enter("%zu", prep->datalen);
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	/* handle a no-security key */
7048c2ecf20Sopenharmony_ci	if (!prep->data && prep->datalen == 0)
7058c2ecf20Sopenharmony_ci		return 0;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	/* determine if the XDR payload format is being used */
7088c2ecf20Sopenharmony_ci	if (prep->datalen > 7 * 4) {
7098c2ecf20Sopenharmony_ci		ret = rxrpc_preparse_xdr(prep);
7108c2ecf20Sopenharmony_ci		if (ret != -EPROTO)
7118c2ecf20Sopenharmony_ci			return ret;
7128c2ecf20Sopenharmony_ci	}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	/* get the key interface version number */
7158c2ecf20Sopenharmony_ci	ret = -EINVAL;
7168c2ecf20Sopenharmony_ci	if (prep->datalen <= 4 || !prep->data)
7178c2ecf20Sopenharmony_ci		goto error;
7188c2ecf20Sopenharmony_ci	memcpy(&kver, prep->data, sizeof(kver));
7198c2ecf20Sopenharmony_ci	prep->data += sizeof(kver);
7208c2ecf20Sopenharmony_ci	prep->datalen -= sizeof(kver);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	_debug("KEY I/F VERSION: %u", kver);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	ret = -EKEYREJECTED;
7258c2ecf20Sopenharmony_ci	if (kver != 1)
7268c2ecf20Sopenharmony_ci		goto error;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	/* deal with a version 1 key */
7298c2ecf20Sopenharmony_ci	ret = -EINVAL;
7308c2ecf20Sopenharmony_ci	if (prep->datalen < sizeof(*v1))
7318c2ecf20Sopenharmony_ci		goto error;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	v1 = prep->data;
7348c2ecf20Sopenharmony_ci	if (prep->datalen != sizeof(*v1) + v1->ticket_length)
7358c2ecf20Sopenharmony_ci		goto error;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	_debug("SCIX: %u", v1->security_index);
7388c2ecf20Sopenharmony_ci	_debug("TLEN: %u", v1->ticket_length);
7398c2ecf20Sopenharmony_ci	_debug("EXPY: %x", v1->expiry);
7408c2ecf20Sopenharmony_ci	_debug("KVNO: %u", v1->kvno);
7418c2ecf20Sopenharmony_ci	_debug("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x",
7428c2ecf20Sopenharmony_ci	       v1->session_key[0], v1->session_key[1],
7438c2ecf20Sopenharmony_ci	       v1->session_key[2], v1->session_key[3],
7448c2ecf20Sopenharmony_ci	       v1->session_key[4], v1->session_key[5],
7458c2ecf20Sopenharmony_ci	       v1->session_key[6], v1->session_key[7]);
7468c2ecf20Sopenharmony_ci	if (v1->ticket_length >= 8)
7478c2ecf20Sopenharmony_ci		_debug("TCKT: %02x%02x%02x%02x%02x%02x%02x%02x",
7488c2ecf20Sopenharmony_ci		       v1->ticket[0], v1->ticket[1],
7498c2ecf20Sopenharmony_ci		       v1->ticket[2], v1->ticket[3],
7508c2ecf20Sopenharmony_ci		       v1->ticket[4], v1->ticket[5],
7518c2ecf20Sopenharmony_ci		       v1->ticket[6], v1->ticket[7]);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	ret = -EPROTONOSUPPORT;
7548c2ecf20Sopenharmony_ci	if (v1->security_index != RXRPC_SECURITY_RXKAD)
7558c2ecf20Sopenharmony_ci		goto error;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	plen = sizeof(*token->kad) + v1->ticket_length;
7588c2ecf20Sopenharmony_ci	prep->quotalen = plen + sizeof(*token);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	ret = -ENOMEM;
7618c2ecf20Sopenharmony_ci	token = kzalloc(sizeof(*token), GFP_KERNEL);
7628c2ecf20Sopenharmony_ci	if (!token)
7638c2ecf20Sopenharmony_ci		goto error;
7648c2ecf20Sopenharmony_ci	token->kad = kzalloc(plen, GFP_KERNEL);
7658c2ecf20Sopenharmony_ci	if (!token->kad)
7668c2ecf20Sopenharmony_ci		goto error_free;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	token->security_index		= RXRPC_SECURITY_RXKAD;
7698c2ecf20Sopenharmony_ci	token->kad->ticket_len		= v1->ticket_length;
7708c2ecf20Sopenharmony_ci	token->kad->expiry		= v1->expiry;
7718c2ecf20Sopenharmony_ci	token->kad->kvno		= v1->kvno;
7728c2ecf20Sopenharmony_ci	memcpy(&token->kad->session_key, &v1->session_key, 8);
7738c2ecf20Sopenharmony_ci	memcpy(&token->kad->ticket, v1->ticket, v1->ticket_length);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	/* count the number of tokens attached */
7768c2ecf20Sopenharmony_ci	prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	/* attach the data */
7798c2ecf20Sopenharmony_ci	pp = (struct rxrpc_key_token **)&prep->payload.data[0];
7808c2ecf20Sopenharmony_ci	while (*pp)
7818c2ecf20Sopenharmony_ci		pp = &(*pp)->next;
7828c2ecf20Sopenharmony_ci	*pp = token;
7838c2ecf20Sopenharmony_ci	expiry = rxrpc_u32_to_time64(token->kad->expiry);
7848c2ecf20Sopenharmony_ci	if (expiry < prep->expiry)
7858c2ecf20Sopenharmony_ci		prep->expiry = expiry;
7868c2ecf20Sopenharmony_ci	token = NULL;
7878c2ecf20Sopenharmony_ci	ret = 0;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_cierror_free:
7908c2ecf20Sopenharmony_ci	kfree(token);
7918c2ecf20Sopenharmony_cierror:
7928c2ecf20Sopenharmony_ci	return ret;
7938c2ecf20Sopenharmony_ci}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci/*
7968c2ecf20Sopenharmony_ci * Free token list.
7978c2ecf20Sopenharmony_ci */
7988c2ecf20Sopenharmony_cistatic void rxrpc_free_token_list(struct rxrpc_key_token *token)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	struct rxrpc_key_token *next;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	for (; token; token = next) {
8038c2ecf20Sopenharmony_ci		next = token->next;
8048c2ecf20Sopenharmony_ci		switch (token->security_index) {
8058c2ecf20Sopenharmony_ci		case RXRPC_SECURITY_RXKAD:
8068c2ecf20Sopenharmony_ci			kfree(token->kad);
8078c2ecf20Sopenharmony_ci			break;
8088c2ecf20Sopenharmony_ci		case RXRPC_SECURITY_RXK5:
8098c2ecf20Sopenharmony_ci			if (token->k5)
8108c2ecf20Sopenharmony_ci				rxrpc_rxk5_free(token->k5);
8118c2ecf20Sopenharmony_ci			break;
8128c2ecf20Sopenharmony_ci		default:
8138c2ecf20Sopenharmony_ci			pr_err("Unknown token type %x on rxrpc key\n",
8148c2ecf20Sopenharmony_ci			       token->security_index);
8158c2ecf20Sopenharmony_ci			BUG();
8168c2ecf20Sopenharmony_ci		}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci		kfree(token);
8198c2ecf20Sopenharmony_ci	}
8208c2ecf20Sopenharmony_ci}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci/*
8238c2ecf20Sopenharmony_ci * Clean up preparse data.
8248c2ecf20Sopenharmony_ci */
8258c2ecf20Sopenharmony_cistatic void rxrpc_free_preparse(struct key_preparsed_payload *prep)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	rxrpc_free_token_list(prep->payload.data[0]);
8288c2ecf20Sopenharmony_ci}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci/*
8318c2ecf20Sopenharmony_ci * Preparse a server secret key.
8328c2ecf20Sopenharmony_ci *
8338c2ecf20Sopenharmony_ci * The data should be the 8-byte secret key.
8348c2ecf20Sopenharmony_ci */
8358c2ecf20Sopenharmony_cistatic int rxrpc_preparse_s(struct key_preparsed_payload *prep)
8368c2ecf20Sopenharmony_ci{
8378c2ecf20Sopenharmony_ci	struct crypto_skcipher *ci;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	_enter("%zu", prep->datalen);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	if (prep->datalen != 8)
8428c2ecf20Sopenharmony_ci		return -EINVAL;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	memcpy(&prep->payload.data[2], prep->data, 8);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	ci = crypto_alloc_skcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC);
8478c2ecf20Sopenharmony_ci	if (IS_ERR(ci)) {
8488c2ecf20Sopenharmony_ci		_leave(" = %ld", PTR_ERR(ci));
8498c2ecf20Sopenharmony_ci		return PTR_ERR(ci);
8508c2ecf20Sopenharmony_ci	}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	if (crypto_skcipher_setkey(ci, prep->data, 8) < 0)
8538c2ecf20Sopenharmony_ci		BUG();
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	prep->payload.data[0] = ci;
8568c2ecf20Sopenharmony_ci	_leave(" = 0");
8578c2ecf20Sopenharmony_ci	return 0;
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci/*
8618c2ecf20Sopenharmony_ci * Clean up preparse data.
8628c2ecf20Sopenharmony_ci */
8638c2ecf20Sopenharmony_cistatic void rxrpc_free_preparse_s(struct key_preparsed_payload *prep)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	if (prep->payload.data[0])
8668c2ecf20Sopenharmony_ci		crypto_free_skcipher(prep->payload.data[0]);
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci/*
8708c2ecf20Sopenharmony_ci * dispose of the data dangling from the corpse of a rxrpc key
8718c2ecf20Sopenharmony_ci */
8728c2ecf20Sopenharmony_cistatic void rxrpc_destroy(struct key *key)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci	rxrpc_free_token_list(key->payload.data[0]);
8758c2ecf20Sopenharmony_ci}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci/*
8788c2ecf20Sopenharmony_ci * dispose of the data dangling from the corpse of a rxrpc key
8798c2ecf20Sopenharmony_ci */
8808c2ecf20Sopenharmony_cistatic void rxrpc_destroy_s(struct key *key)
8818c2ecf20Sopenharmony_ci{
8828c2ecf20Sopenharmony_ci	if (key->payload.data[0]) {
8838c2ecf20Sopenharmony_ci		crypto_free_skcipher(key->payload.data[0]);
8848c2ecf20Sopenharmony_ci		key->payload.data[0] = NULL;
8858c2ecf20Sopenharmony_ci	}
8868c2ecf20Sopenharmony_ci}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci/*
8898c2ecf20Sopenharmony_ci * describe the rxrpc key
8908c2ecf20Sopenharmony_ci */
8918c2ecf20Sopenharmony_cistatic void rxrpc_describe(const struct key *key, struct seq_file *m)
8928c2ecf20Sopenharmony_ci{
8938c2ecf20Sopenharmony_ci	seq_puts(m, key->description);
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci/*
8978c2ecf20Sopenharmony_ci * grab the security key for a socket
8988c2ecf20Sopenharmony_ci */
8998c2ecf20Sopenharmony_ciint rxrpc_request_key(struct rxrpc_sock *rx, sockptr_t optval, int optlen)
9008c2ecf20Sopenharmony_ci{
9018c2ecf20Sopenharmony_ci	struct key *key;
9028c2ecf20Sopenharmony_ci	char *description;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	_enter("");
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	if (optlen <= 0 || optlen > PAGE_SIZE - 1 || rx->securities)
9078c2ecf20Sopenharmony_ci		return -EINVAL;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	description = memdup_sockptr_nul(optval, optlen);
9108c2ecf20Sopenharmony_ci	if (IS_ERR(description))
9118c2ecf20Sopenharmony_ci		return PTR_ERR(description);
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	key = request_key_net(&key_type_rxrpc, description, sock_net(&rx->sk), NULL);
9148c2ecf20Sopenharmony_ci	if (IS_ERR(key)) {
9158c2ecf20Sopenharmony_ci		kfree(description);
9168c2ecf20Sopenharmony_ci		_leave(" = %ld", PTR_ERR(key));
9178c2ecf20Sopenharmony_ci		return PTR_ERR(key);
9188c2ecf20Sopenharmony_ci	}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	rx->key = key;
9218c2ecf20Sopenharmony_ci	kfree(description);
9228c2ecf20Sopenharmony_ci	_leave(" = 0 [key %x]", key->serial);
9238c2ecf20Sopenharmony_ci	return 0;
9248c2ecf20Sopenharmony_ci}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci/*
9278c2ecf20Sopenharmony_ci * grab the security keyring for a server socket
9288c2ecf20Sopenharmony_ci */
9298c2ecf20Sopenharmony_ciint rxrpc_server_keyring(struct rxrpc_sock *rx, sockptr_t optval, int optlen)
9308c2ecf20Sopenharmony_ci{
9318c2ecf20Sopenharmony_ci	struct key *key;
9328c2ecf20Sopenharmony_ci	char *description;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	_enter("");
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	if (optlen <= 0 || optlen > PAGE_SIZE - 1)
9378c2ecf20Sopenharmony_ci		return -EINVAL;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	description = memdup_sockptr_nul(optval, optlen);
9408c2ecf20Sopenharmony_ci	if (IS_ERR(description))
9418c2ecf20Sopenharmony_ci		return PTR_ERR(description);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	key = request_key(&key_type_keyring, description, NULL);
9448c2ecf20Sopenharmony_ci	if (IS_ERR(key)) {
9458c2ecf20Sopenharmony_ci		kfree(description);
9468c2ecf20Sopenharmony_ci		_leave(" = %ld", PTR_ERR(key));
9478c2ecf20Sopenharmony_ci		return PTR_ERR(key);
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	rx->securities = key;
9518c2ecf20Sopenharmony_ci	kfree(description);
9528c2ecf20Sopenharmony_ci	_leave(" = 0 [key %x]", key->serial);
9538c2ecf20Sopenharmony_ci	return 0;
9548c2ecf20Sopenharmony_ci}
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci/*
9578c2ecf20Sopenharmony_ci * generate a server data key
9588c2ecf20Sopenharmony_ci */
9598c2ecf20Sopenharmony_ciint rxrpc_get_server_data_key(struct rxrpc_connection *conn,
9608c2ecf20Sopenharmony_ci			      const void *session_key,
9618c2ecf20Sopenharmony_ci			      time64_t expiry,
9628c2ecf20Sopenharmony_ci			      u32 kvno)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	const struct cred *cred = current_cred();
9658c2ecf20Sopenharmony_ci	struct key *key;
9668c2ecf20Sopenharmony_ci	int ret;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	struct {
9698c2ecf20Sopenharmony_ci		u32 kver;
9708c2ecf20Sopenharmony_ci		struct rxrpc_key_data_v1 v1;
9718c2ecf20Sopenharmony_ci	} data;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	_enter("");
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	key = key_alloc(&key_type_rxrpc, "x",
9768c2ecf20Sopenharmony_ci			GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0,
9778c2ecf20Sopenharmony_ci			KEY_ALLOC_NOT_IN_QUOTA, NULL);
9788c2ecf20Sopenharmony_ci	if (IS_ERR(key)) {
9798c2ecf20Sopenharmony_ci		_leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
9808c2ecf20Sopenharmony_ci		return -ENOMEM;
9818c2ecf20Sopenharmony_ci	}
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	_debug("key %d", key_serial(key));
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	data.kver = 1;
9868c2ecf20Sopenharmony_ci	data.v1.security_index = RXRPC_SECURITY_RXKAD;
9878c2ecf20Sopenharmony_ci	data.v1.ticket_length = 0;
9888c2ecf20Sopenharmony_ci	data.v1.expiry = rxrpc_time64_to_u32(expiry);
9898c2ecf20Sopenharmony_ci	data.v1.kvno = 0;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	memcpy(&data.v1.session_key, session_key, sizeof(data.v1.session_key));
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	ret = key_instantiate_and_link(key, &data, sizeof(data), NULL, NULL);
9948c2ecf20Sopenharmony_ci	if (ret < 0)
9958c2ecf20Sopenharmony_ci		goto error;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	conn->params.key = key;
9988c2ecf20Sopenharmony_ci	_leave(" = 0 [%d]", key_serial(key));
9998c2ecf20Sopenharmony_ci	return 0;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_cierror:
10028c2ecf20Sopenharmony_ci	key_revoke(key);
10038c2ecf20Sopenharmony_ci	key_put(key);
10048c2ecf20Sopenharmony_ci	_leave(" = -ENOMEM [ins %d]", ret);
10058c2ecf20Sopenharmony_ci	return -ENOMEM;
10068c2ecf20Sopenharmony_ci}
10078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rxrpc_get_server_data_key);
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci/**
10108c2ecf20Sopenharmony_ci * rxrpc_get_null_key - Generate a null RxRPC key
10118c2ecf20Sopenharmony_ci * @keyname: The name to give the key.
10128c2ecf20Sopenharmony_ci *
10138c2ecf20Sopenharmony_ci * Generate a null RxRPC key that can be used to indicate anonymous security is
10148c2ecf20Sopenharmony_ci * required for a particular domain.
10158c2ecf20Sopenharmony_ci */
10168c2ecf20Sopenharmony_cistruct key *rxrpc_get_null_key(const char *keyname)
10178c2ecf20Sopenharmony_ci{
10188c2ecf20Sopenharmony_ci	const struct cred *cred = current_cred();
10198c2ecf20Sopenharmony_ci	struct key *key;
10208c2ecf20Sopenharmony_ci	int ret;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	key = key_alloc(&key_type_rxrpc, keyname,
10238c2ecf20Sopenharmony_ci			GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
10248c2ecf20Sopenharmony_ci			KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, NULL);
10258c2ecf20Sopenharmony_ci	if (IS_ERR(key))
10268c2ecf20Sopenharmony_ci		return key;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	ret = key_instantiate_and_link(key, NULL, 0, NULL, NULL);
10298c2ecf20Sopenharmony_ci	if (ret < 0) {
10308c2ecf20Sopenharmony_ci		key_revoke(key);
10318c2ecf20Sopenharmony_ci		key_put(key);
10328c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
10338c2ecf20Sopenharmony_ci	}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	return key;
10368c2ecf20Sopenharmony_ci}
10378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rxrpc_get_null_key);
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci/*
10408c2ecf20Sopenharmony_ci * read the contents of an rxrpc key
10418c2ecf20Sopenharmony_ci * - this returns the result in XDR form
10428c2ecf20Sopenharmony_ci */
10438c2ecf20Sopenharmony_cistatic long rxrpc_read(const struct key *key,
10448c2ecf20Sopenharmony_ci		       char *buffer, size_t buflen)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	const struct rxrpc_key_token *token;
10478c2ecf20Sopenharmony_ci	const struct krb5_principal *princ;
10488c2ecf20Sopenharmony_ci	size_t size;
10498c2ecf20Sopenharmony_ci	__be32 *xdr, *oldxdr;
10508c2ecf20Sopenharmony_ci	u32 cnlen, toksize, ntoks, tok, zero;
10518c2ecf20Sopenharmony_ci	u16 toksizes[AFSTOKEN_MAX];
10528c2ecf20Sopenharmony_ci	int loop;
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	_enter("");
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	/* we don't know what form we should return non-AFS keys in */
10578c2ecf20Sopenharmony_ci	if (memcmp(key->description, "afs@", 4) != 0)
10588c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
10598c2ecf20Sopenharmony_ci	cnlen = strlen(key->description + 4);
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci#define RND(X) (((X) + 3) & ~3)
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	/* AFS keys we return in XDR form, so we need to work out the size of
10648c2ecf20Sopenharmony_ci	 * the XDR */
10658c2ecf20Sopenharmony_ci	size = 2 * 4;	/* flags, cellname len */
10668c2ecf20Sopenharmony_ci	size += RND(cnlen);	/* cellname */
10678c2ecf20Sopenharmony_ci	size += 1 * 4;	/* token count */
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	ntoks = 0;
10708c2ecf20Sopenharmony_ci	for (token = key->payload.data[0]; token; token = token->next) {
10718c2ecf20Sopenharmony_ci		toksize = 4;	/* sec index */
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci		switch (token->security_index) {
10748c2ecf20Sopenharmony_ci		case RXRPC_SECURITY_RXKAD:
10758c2ecf20Sopenharmony_ci			toksize += 8 * 4;	/* viceid, kvno, key*2, begin,
10768c2ecf20Sopenharmony_ci						 * end, primary, tktlen */
10778c2ecf20Sopenharmony_ci			toksize += RND(token->kad->ticket_len);
10788c2ecf20Sopenharmony_ci			break;
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci		case RXRPC_SECURITY_RXK5:
10818c2ecf20Sopenharmony_ci			princ = &token->k5->client;
10828c2ecf20Sopenharmony_ci			toksize += 4 + princ->n_name_parts * 4;
10838c2ecf20Sopenharmony_ci			for (loop = 0; loop < princ->n_name_parts; loop++)
10848c2ecf20Sopenharmony_ci				toksize += RND(strlen(princ->name_parts[loop]));
10858c2ecf20Sopenharmony_ci			toksize += 4 + RND(strlen(princ->realm));
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci			princ = &token->k5->server;
10888c2ecf20Sopenharmony_ci			toksize += 4 + princ->n_name_parts * 4;
10898c2ecf20Sopenharmony_ci			for (loop = 0; loop < princ->n_name_parts; loop++)
10908c2ecf20Sopenharmony_ci				toksize += RND(strlen(princ->name_parts[loop]));
10918c2ecf20Sopenharmony_ci			toksize += 4 + RND(strlen(princ->realm));
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci			toksize += 8 + RND(token->k5->session.data_len);
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci			toksize += 4 * 8 + 2 * 4;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci			toksize += 4 + token->k5->n_addresses * 8;
10988c2ecf20Sopenharmony_ci			for (loop = 0; loop < token->k5->n_addresses; loop++)
10998c2ecf20Sopenharmony_ci				toksize += RND(token->k5->addresses[loop].data_len);
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci			toksize += 4 + RND(token->k5->ticket_len);
11028c2ecf20Sopenharmony_ci			toksize += 4 + RND(token->k5->ticket2_len);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci			toksize += 4 + token->k5->n_authdata * 8;
11058c2ecf20Sopenharmony_ci			for (loop = 0; loop < token->k5->n_authdata; loop++)
11068c2ecf20Sopenharmony_ci				toksize += RND(token->k5->authdata[loop].data_len);
11078c2ecf20Sopenharmony_ci			break;
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci		default: /* we have a ticket we can't encode */
11108c2ecf20Sopenharmony_ci			pr_err("Unsupported key token type (%u)\n",
11118c2ecf20Sopenharmony_ci			       token->security_index);
11128c2ecf20Sopenharmony_ci			return -ENOPKG;
11138c2ecf20Sopenharmony_ci		}
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci		_debug("token[%u]: toksize=%u", ntoks, toksize);
11168c2ecf20Sopenharmony_ci		ASSERTCMP(toksize, <=, AFSTOKEN_LENGTH_MAX);
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci		toksizes[ntoks++] = toksize;
11198c2ecf20Sopenharmony_ci		size += toksize + 4; /* each token has a length word */
11208c2ecf20Sopenharmony_ci	}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci#undef RND
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	if (!buffer || buflen < size)
11258c2ecf20Sopenharmony_ci		return size;
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	xdr = (__be32 *)buffer;
11288c2ecf20Sopenharmony_ci	zero = 0;
11298c2ecf20Sopenharmony_ci#define ENCODE(x)				\
11308c2ecf20Sopenharmony_ci	do {					\
11318c2ecf20Sopenharmony_ci		*xdr++ = htonl(x);		\
11328c2ecf20Sopenharmony_ci	} while(0)
11338c2ecf20Sopenharmony_ci#define ENCODE_DATA(l, s)						\
11348c2ecf20Sopenharmony_ci	do {								\
11358c2ecf20Sopenharmony_ci		u32 _l = (l);						\
11368c2ecf20Sopenharmony_ci		ENCODE(l);						\
11378c2ecf20Sopenharmony_ci		memcpy(xdr, (s), _l);					\
11388c2ecf20Sopenharmony_ci		if (_l & 3)						\
11398c2ecf20Sopenharmony_ci			memcpy((u8 *)xdr + _l, &zero, 4 - (_l & 3));	\
11408c2ecf20Sopenharmony_ci		xdr += (_l + 3) >> 2;					\
11418c2ecf20Sopenharmony_ci	} while(0)
11428c2ecf20Sopenharmony_ci#define ENCODE_BYTES(l, s)						\
11438c2ecf20Sopenharmony_ci	do {								\
11448c2ecf20Sopenharmony_ci		u32 _l = (l);						\
11458c2ecf20Sopenharmony_ci		memcpy(xdr, (s), _l);					\
11468c2ecf20Sopenharmony_ci		if (_l & 3)						\
11478c2ecf20Sopenharmony_ci			memcpy((u8 *)xdr + _l, &zero, 4 - (_l & 3));	\
11488c2ecf20Sopenharmony_ci		xdr += (_l + 3) >> 2;					\
11498c2ecf20Sopenharmony_ci	} while(0)
11508c2ecf20Sopenharmony_ci#define ENCODE64(x)					\
11518c2ecf20Sopenharmony_ci	do {						\
11528c2ecf20Sopenharmony_ci		__be64 y = cpu_to_be64(x);		\
11538c2ecf20Sopenharmony_ci		memcpy(xdr, &y, 8);			\
11548c2ecf20Sopenharmony_ci		xdr += 8 >> 2;				\
11558c2ecf20Sopenharmony_ci	} while(0)
11568c2ecf20Sopenharmony_ci#define ENCODE_STR(s)				\
11578c2ecf20Sopenharmony_ci	do {					\
11588c2ecf20Sopenharmony_ci		const char *_s = (s);		\
11598c2ecf20Sopenharmony_ci		ENCODE_DATA(strlen(_s), _s);	\
11608c2ecf20Sopenharmony_ci	} while(0)
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	ENCODE(0);					/* flags */
11638c2ecf20Sopenharmony_ci	ENCODE_DATA(cnlen, key->description + 4);	/* cellname */
11648c2ecf20Sopenharmony_ci	ENCODE(ntoks);
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	tok = 0;
11678c2ecf20Sopenharmony_ci	for (token = key->payload.data[0]; token; token = token->next) {
11688c2ecf20Sopenharmony_ci		toksize = toksizes[tok++];
11698c2ecf20Sopenharmony_ci		ENCODE(toksize);
11708c2ecf20Sopenharmony_ci		oldxdr = xdr;
11718c2ecf20Sopenharmony_ci		ENCODE(token->security_index);
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci		switch (token->security_index) {
11748c2ecf20Sopenharmony_ci		case RXRPC_SECURITY_RXKAD:
11758c2ecf20Sopenharmony_ci			ENCODE(token->kad->vice_id);
11768c2ecf20Sopenharmony_ci			ENCODE(token->kad->kvno);
11778c2ecf20Sopenharmony_ci			ENCODE_BYTES(8, token->kad->session_key);
11788c2ecf20Sopenharmony_ci			ENCODE(token->kad->start);
11798c2ecf20Sopenharmony_ci			ENCODE(token->kad->expiry);
11808c2ecf20Sopenharmony_ci			ENCODE(token->kad->primary_flag);
11818c2ecf20Sopenharmony_ci			ENCODE_DATA(token->kad->ticket_len, token->kad->ticket);
11828c2ecf20Sopenharmony_ci			break;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci		case RXRPC_SECURITY_RXK5:
11858c2ecf20Sopenharmony_ci			princ = &token->k5->client;
11868c2ecf20Sopenharmony_ci			ENCODE(princ->n_name_parts);
11878c2ecf20Sopenharmony_ci			for (loop = 0; loop < princ->n_name_parts; loop++)
11888c2ecf20Sopenharmony_ci				ENCODE_STR(princ->name_parts[loop]);
11898c2ecf20Sopenharmony_ci			ENCODE_STR(princ->realm);
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci			princ = &token->k5->server;
11928c2ecf20Sopenharmony_ci			ENCODE(princ->n_name_parts);
11938c2ecf20Sopenharmony_ci			for (loop = 0; loop < princ->n_name_parts; loop++)
11948c2ecf20Sopenharmony_ci				ENCODE_STR(princ->name_parts[loop]);
11958c2ecf20Sopenharmony_ci			ENCODE_STR(princ->realm);
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci			ENCODE(token->k5->session.tag);
11988c2ecf20Sopenharmony_ci			ENCODE_DATA(token->k5->session.data_len,
11998c2ecf20Sopenharmony_ci				    token->k5->session.data);
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci			ENCODE64(token->k5->authtime);
12028c2ecf20Sopenharmony_ci			ENCODE64(token->k5->starttime);
12038c2ecf20Sopenharmony_ci			ENCODE64(token->k5->endtime);
12048c2ecf20Sopenharmony_ci			ENCODE64(token->k5->renew_till);
12058c2ecf20Sopenharmony_ci			ENCODE(token->k5->is_skey);
12068c2ecf20Sopenharmony_ci			ENCODE(token->k5->flags);
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci			ENCODE(token->k5->n_addresses);
12098c2ecf20Sopenharmony_ci			for (loop = 0; loop < token->k5->n_addresses; loop++) {
12108c2ecf20Sopenharmony_ci				ENCODE(token->k5->addresses[loop].tag);
12118c2ecf20Sopenharmony_ci				ENCODE_DATA(token->k5->addresses[loop].data_len,
12128c2ecf20Sopenharmony_ci					    token->k5->addresses[loop].data);
12138c2ecf20Sopenharmony_ci			}
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci			ENCODE_DATA(token->k5->ticket_len, token->k5->ticket);
12168c2ecf20Sopenharmony_ci			ENCODE_DATA(token->k5->ticket2_len, token->k5->ticket2);
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci			ENCODE(token->k5->n_authdata);
12198c2ecf20Sopenharmony_ci			for (loop = 0; loop < token->k5->n_authdata; loop++) {
12208c2ecf20Sopenharmony_ci				ENCODE(token->k5->authdata[loop].tag);
12218c2ecf20Sopenharmony_ci				ENCODE_DATA(token->k5->authdata[loop].data_len,
12228c2ecf20Sopenharmony_ci					    token->k5->authdata[loop].data);
12238c2ecf20Sopenharmony_ci			}
12248c2ecf20Sopenharmony_ci			break;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci		default:
12278c2ecf20Sopenharmony_ci			pr_err("Unsupported key token type (%u)\n",
12288c2ecf20Sopenharmony_ci			       token->security_index);
12298c2ecf20Sopenharmony_ci			return -ENOPKG;
12308c2ecf20Sopenharmony_ci		}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci		ASSERTCMP((unsigned long)xdr - (unsigned long)oldxdr, ==,
12338c2ecf20Sopenharmony_ci			  toksize);
12348c2ecf20Sopenharmony_ci	}
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci#undef ENCODE_STR
12378c2ecf20Sopenharmony_ci#undef ENCODE_DATA
12388c2ecf20Sopenharmony_ci#undef ENCODE64
12398c2ecf20Sopenharmony_ci#undef ENCODE
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	ASSERTCMP(tok, ==, ntoks);
12428c2ecf20Sopenharmony_ci	ASSERTCMP((char __user *) xdr - buffer, ==, size);
12438c2ecf20Sopenharmony_ci	_leave(" = %zu", size);
12448c2ecf20Sopenharmony_ci	return size;
12458c2ecf20Sopenharmony_ci}
1246