xref: /linux/net/rxrpc/rxgk_app.c (revision cbf658dd09419f1ef9de11b9604e950bdd5c170b)
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  */
rxgk_yfs_decode_ticket(struct rxrpc_connection * conn,struct sk_buff * skb,unsigned int ticket_offset,unsigned int ticket_len,struct key ** _key)42 int 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 
165 error_key:
166 	key_put(key);
167 error:
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  */
rxgk_extract_token(struct rxrpc_connection * conn,struct sk_buff * skb,unsigned int token_offset,unsigned int token_len,struct key ** _key)184 int 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 
258 cant_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 
275 cant_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 
287 temporary_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 
294 short_packet:
295 	return rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
296 				rxgk_abort_resp_tok_short);
297 }
298