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 */ 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 */ 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