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