File Index
Files and directories
Sign in
FILE

rxgk_app.c

File preview with syntax highlighting.

linux-6.19.14/net/rxrpc/rxgk_app.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/* Application-specific bits for GSSAPI-based RxRPC security
  3 *
  4 * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
  5 * Written by David Howells (dhowells@redhat.com)
  6 */
  7
  8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  9
 10#include <linux/net.h>
 11#include <linux/skbuff.h>
 12#include <linux/slab.h>
 13#include <linux/key-type.h>
 14#include "ar-internal.h"
 15#include "rxgk_common.h"
 16
 17/*
 18 * Decode a default-style YFS ticket in a response and turn it into an
 19 * rxrpc-type key.
 20 *
 21 * struct rxgk_key {
 22 *	afs_uint32	enctype;
 23 *	opaque		key<>;
 24 * };
 25 *
 26 * struct RXGK_AuthName {
 27 *	afs_int32	kind;
 28 *	opaque		data<AUTHDATAMAX>;
 29 *	opaque		display<AUTHPRINTABLEMAX>;
 30 * };
 31 *
 32 * struct RXGK_Token {
 33 *	rxgk_key		K0;
 34 *	RXGK_Level		level;
 35 *	rxgkTime		starttime;
 36 *	afs_int32		lifetime;
 37 *	afs_int32		bytelife;
 38 *	rxgkTime		expirationtime;
 39 *	struct RXGK_AuthName	identities<>;
 40 * };
 41 */
 42int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
 43			   unsigned int ticket_offset, unsigned int ticket_len,
 44			   struct key **_key)
 45{
 46	struct rxrpc_key_token *token;
 47	const struct cred *cred = current_cred(); // TODO - use socket creds
 48	struct key *key;
 49	size_t pre_ticket_len, payload_len;
 50	unsigned int klen, enctype;
 51	void *payload, *ticket;
 52	__be32 *t, *p, *q, tmp[2];
 53	int ret;
 54
 55	_enter("");
 56
 57	if (ticket_len < 10 * sizeof(__be32))
 58		return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
 59					rxgk_abort_resp_short_yfs_tkt);
 60
 61	/* Get the session key length */
 62	ret = skb_copy_bits(skb, ticket_offset, tmp, sizeof(tmp));
 63	if (ret < 0)
 64		return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
 65					rxgk_abort_resp_short_yfs_klen);
 66	enctype = ntohl(tmp[0]);
 67	klen = ntohl(tmp[1]);
 68
 69	if (klen > ticket_len - 10 * sizeof(__be32))
 70		return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
 71					rxgk_abort_resp_short_yfs_key);
 72
 73	pre_ticket_len = ((5 + 14) * sizeof(__be32) +
 74			  xdr_round_up(klen) +
 75			  sizeof(__be32));
 76	payload_len = pre_ticket_len + xdr_round_up(ticket_len);
 77
 78	payload = kzalloc(payload_len, GFP_NOFS);
 79	if (!payload)
 80		return -ENOMEM;
 81
 82	/* We need to fill out the XDR form for a key payload that we can pass
 83	 * to add_key().  Start by copying in the ticket so that we can parse
 84	 * it.
 85	 */
 86	ticket = payload + pre_ticket_len;
 87	ret = skb_copy_bits(skb, ticket_offset, ticket, ticket_len);
 88	if (ret < 0) {
 89		ret = rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
 90				       rxgk_abort_resp_short_yfs_tkt);
 91		goto error;
 92	}
 93
 94	/* Fill out the form header. */
 95	p = payload;
 96	p[0] = htonl(0); /* Flags */
 97	p[1] = htonl(1); /* len(cellname) */
 98	p[2] = htonl(0x20000000); /* Cellname " " */
 99	p[3] = htonl(1); /* #tokens */
100	p[4] = htonl(15 * sizeof(__be32) + xdr_round_up(klen) +
101		     xdr_round_up(ticket_len)); /* Token len */
102
103	/* Now fill in the body.  Most of this we can just scrape directly from
104	 * the ticket.
105	 */
106	t = ticket + sizeof(__be32) * 2 + xdr_round_up(klen);
107	q = payload + 5 * sizeof(__be32);
108	q[0]  = htonl(RXRPC_SECURITY_YFS_RXGK);
109	q[1]  = t[1];		/* begintime - msw */
110	q[2]  = t[2];		/* - lsw */
111	q[3]  = t[5];		/* endtime - msw */
112	q[4]  = t[6];		/* - lsw */
113	q[5]  = 0;		/* level - msw */
114	q[6]  = t[0];		/* - lsw */
115	q[7]  = 0;		/* lifetime - msw */
116	q[8]  = t[3];		/* - lsw */
117	q[9]  = 0;		/* bytelife - msw */
118	q[10] = t[4];		/* - lsw */
119	q[11] = 0;		/* enctype - msw */
120	q[12] = htonl(enctype);	/* - lsw */
121	q[13] = htonl(klen);	/* Key length */
122
123	q += 14;
124
125	memcpy(q, ticket + sizeof(__be32) * 2, klen);
126	q += xdr_round_up(klen) / 4;
127	q[0] = htonl(ticket_len);
128	q++;
129	if (WARN_ON((unsigned long)q != (unsigned long)ticket)) {
130		ret = -EIO;
131		goto error;
132	}
133
134	/* Ticket read in with skb_copy_bits above */
135	q += xdr_round_up(ticket_len) / 4;
136	if (WARN_ON((unsigned long)q - (unsigned long)payload != payload_len)) {
137		ret = -EIO;
138		goto error;
139	}
140
141	/* Now turn that into a key. */
142	key = key_alloc(&key_type_rxrpc, "x",
143			GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, // TODO: Use socket owner
144			KEY_USR_VIEW,
145			KEY_ALLOC_NOT_IN_QUOTA, NULL);
146	if (IS_ERR(key)) {
147		_leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
148		ret = PTR_ERR(key);
149		goto error;
150	}
151
152	_debug("key %d", key_serial(key));
153
154	ret = key_instantiate_and_link(key, payload, payload_len, NULL, NULL);
155	if (ret < 0)
156		goto error_key;
157
158	token = key->payload.data[0];
159	token->no_leak_key = true;
160	*_key = key;
161	key = NULL;
162	ret = 0;
163	goto error;
164
165error_key:
166	key_put(key);
167error:
168	kfree_sensitive(payload);
169	_leave(" = %d", ret);
170	return ret;
171}
172
173/*
174 * Extract the token and set up a session key from the details.
175 *
176 * struct RXGK_TokenContainer {
177 *	afs_int32	kvno;
178 *	afs_int32	enctype;
179 *	opaque		encrypted_token<>;
180 * };
181 *
182 * [tools.ietf.org/html/draft-wilkinson-afs3-rxgk-afs-08 sec 6.1]
183 */
184int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb,
185		       unsigned int token_offset, unsigned int token_len,
186		       struct key **_key)
187{
188	const struct krb5_enctype *krb5;
189	const struct krb5_buffer *server_secret;
190	struct crypto_aead *token_enc = NULL;
191	struct key *server_key;
192	unsigned int ticket_offset, ticket_len;
193	u32 kvno, enctype;
194	int ret, ec = 0;
195
196	struct {
197		__be32 kvno;
198		__be32 enctype;
199		__be32 token_len;
200	} container;
201
202	if (token_len < sizeof(container))
203		goto short_packet;
204
205	/* Decode the RXGK_TokenContainer object.  This tells us which server
206	 * key we should be using.  We can then fetch the key, get the secret
207	 * and set up the crypto to extract the token.
208	 */
209	if (skb_copy_bits(skb, token_offset, &container, sizeof(container)) < 0)
210		goto short_packet;
211
212	kvno		= ntohl(container.kvno);
213	enctype		= ntohl(container.enctype);
214	ticket_len	= ntohl(container.token_len);
215	ticket_offset	= token_offset + sizeof(container);
216
217	if (xdr_round_up(ticket_len) > token_len - sizeof(container))
218		goto short_packet;
219
220	_debug("KVNO %u", kvno);
221	_debug("ENC  %u", enctype);
222	_debug("TLEN %u", ticket_len);
223
224	server_key = rxrpc_look_up_server_security(conn, skb, kvno, enctype);
225	if (IS_ERR(server_key))
226		goto cant_get_server_key;
227
228	down_read(&server_key->sem);
229	server_secret = (const void *)&server_key->payload.data[2];
230	ret = rxgk_set_up_token_cipher(server_secret, &token_enc, enctype, &krb5, GFP_NOFS);
231	up_read(&server_key->sem);
232	key_put(server_key);
233	if (ret < 0)
234		goto cant_get_token;
235
236	/* We can now decrypt and parse the token/ticket.  This allows us to
237	 * gain access to K0, from which we can derive the transport key and
238	 * thence decode the authenticator.
239	 */
240	ret = rxgk_decrypt_skb(krb5, token_enc, skb,
241			       &ticket_offset, &ticket_len, &ec);
242	crypto_free_aead(token_enc);
243	token_enc = NULL;
244	if (ret < 0) {
245		if (ret != -ENOMEM)
246			return rxrpc_abort_conn(conn, skb, ec, ret,
247						rxgk_abort_resp_tok_dec);
248	}
249
250	ret = conn->security->default_decode_ticket(conn, skb, ticket_offset,
251						    ticket_len, _key);
252	if (ret < 0)
253		goto cant_get_token;
254
255	_leave(" = 0");
256	return ret;
257
258cant_get_server_key:
259	ret = PTR_ERR(server_key);
260	switch (ret) {
261	case -ENOMEM:
262		goto temporary_error;
263	case -ENOKEY:
264	case -EKEYREJECTED:
265	case -EKEYEXPIRED:
266	case -EKEYREVOKED:
267	case -EPERM:
268		return rxrpc_abort_conn(conn, skb, RXGK_BADKEYNO, -EKEYREJECTED,
269					rxgk_abort_resp_tok_nokey);
270	default:
271		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EKEYREJECTED,
272					rxgk_abort_resp_tok_keyerr);
273	}
274
275cant_get_token:
276	switch (ret) {
277	case -ENOMEM:
278		goto temporary_error;
279	case -EINVAL:
280		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EKEYREJECTED,
281					rxgk_abort_resp_tok_internal_error);
282	case -ENOPKG:
283		return rxrpc_abort_conn(conn, skb, KRB5_PROG_KEYTYPE_NOSUPP,
284					-EKEYREJECTED, rxgk_abort_resp_tok_nopkg);
285	}
286
287temporary_error:
288	/* Ignore the response packet if we got a temporary error such as
289	 * ENOMEM.  We just want to send the challenge again.  Note that we
290	 * also come out this way if the ticket decryption fails.
291	 */
292	return ret;
293
294short_packet:
295	return rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
296				rxgk_abort_resp_tok_short);
297}
~/ Read-only file preview.