1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* Cache manager security. 3 * 4 * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. 5 * Written by David Howells (dhowells@redhat.com) 6 */ 7 8 #include <linux/slab.h> 9 #include <crypto/krb5.h> 10 #include "internal.h" 11 #include "afs_cm.h" 12 #include "afs_fs.h" 13 #include "protocol_yfs.h" 14 #define RXRPC_TRACE_ONLY_DEFINE_ENUMS 15 #include <trace/events/rxrpc.h> 16 17 #define RXGK_SERVER_ENC_TOKEN 1036U // 0x40c 18 #define xdr_round_up(x) (round_up((x), sizeof(__be32))) 19 #define xdr_len_object(x) (4 + round_up((x), sizeof(__be32))) 20 21 #ifdef CONFIG_RXGK 22 static int afs_create_yfs_cm_token(struct sk_buff *challenge, 23 struct afs_server *server); 24 #endif 25 26 /* 27 * Respond to an RxGK challenge, adding appdata. 28 */ 29 static int afs_respond_to_challenge(struct sk_buff *challenge) 30 { 31 #ifdef CONFIG_RXGK 32 struct krb5_buffer appdata = {}; 33 struct afs_server *server; 34 #endif 35 struct rxrpc_peer *peer; 36 unsigned long peer_data; 37 u16 service_id; 38 u8 security_index; 39 40 rxrpc_kernel_query_challenge(challenge, &peer, &peer_data, 41 &service_id, &security_index); 42 43 _enter("%u,%u", service_id, security_index); 44 45 switch (service_id) { 46 /* We don't send CM_SERVICE RPCs, so don't expect a challenge 47 * therefrom. 48 */ 49 case FS_SERVICE: 50 case VL_SERVICE: 51 case YFS_FS_SERVICE: 52 case YFS_VL_SERVICE: 53 break; 54 default: 55 pr_warn("Can't respond to unknown challenge %u:%u", 56 service_id, security_index); 57 return rxrpc_kernel_reject_challenge(challenge, RX_USER_ABORT, -EPROTO, 58 afs_abort_unsupported_sec_class); 59 } 60 61 switch (security_index) { 62 #ifdef CONFIG_RXKAD 63 case RXRPC_SECURITY_RXKAD: 64 return rxkad_kernel_respond_to_challenge(challenge); 65 #endif 66 67 #ifdef CONFIG_RXGK 68 case RXRPC_SECURITY_RXGK: 69 return rxgk_kernel_respond_to_challenge(challenge, &appdata); 70 71 case RXRPC_SECURITY_YFS_RXGK: 72 switch (service_id) { 73 case FS_SERVICE: 74 case YFS_FS_SERVICE: 75 server = (struct afs_server *)peer_data; 76 if (!server->cm_rxgk_appdata.data) { 77 mutex_lock(&server->cm_token_lock); 78 if (!server->cm_rxgk_appdata.data) 79 afs_create_yfs_cm_token(challenge, server); 80 mutex_unlock(&server->cm_token_lock); 81 } 82 if (server->cm_rxgk_appdata.data) 83 appdata = server->cm_rxgk_appdata; 84 break; 85 } 86 return rxgk_kernel_respond_to_challenge(challenge, &appdata); 87 #endif 88 89 default: 90 return rxrpc_kernel_reject_challenge(challenge, RX_USER_ABORT, -EPROTO, 91 afs_abort_unsupported_sec_class); 92 } 93 } 94 95 /* 96 * Process the OOB message queue, processing challenge packets. 97 */ 98 void afs_process_oob_queue(struct work_struct *work) 99 { 100 struct afs_net *net = container_of(work, struct afs_net, rx_oob_work); 101 struct sk_buff *oob; 102 enum rxrpc_oob_type type; 103 104 while (READ_ONCE(net->live) && 105 (oob = rxrpc_kernel_dequeue_oob(net->socket, &type))) { 106 switch (type) { 107 case RXRPC_OOB_CHALLENGE: 108 afs_respond_to_challenge(oob); 109 break; 110 } 111 rxrpc_kernel_free_oob(oob); 112 } 113 } 114 115 #ifdef CONFIG_RXGK 116 /* 117 * Create a securities keyring for the cache manager and attach a key to it for 118 * the RxGK tokens we want to use to secure the callback connection back from 119 * the fileserver. 120 */ 121 int afs_create_token_key(struct afs_net *net, struct socket *socket) 122 { 123 const struct krb5_enctype *krb5; 124 struct key *ring; 125 key_ref_t key; 126 char K0[32], *desc; 127 int ret; 128 129 ring = keyring_alloc("kafs", 130 GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(), 131 KEY_POS_SEARCH | KEY_POS_WRITE | 132 KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH, 133 KEY_ALLOC_NOT_IN_QUOTA, 134 NULL, NULL); 135 if (IS_ERR(ring)) 136 return PTR_ERR(ring); 137 138 ret = rxrpc_sock_set_security_keyring(socket->sk, ring); 139 if (ret < 0) 140 goto out; 141 142 ret = -ENOPKG; 143 krb5 = crypto_krb5_find_enctype(KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96); 144 if (!krb5) 145 goto out; 146 147 if (WARN_ON_ONCE(krb5->key_len > sizeof(K0))) 148 goto out; 149 150 ret = -ENOMEM; 151 desc = kasprintf(GFP_KERNEL, "%u:%u:%u:%u", 152 YFS_CM_SERVICE, RXRPC_SECURITY_YFS_RXGK, 1, krb5->etype); 153 if (!desc) 154 goto out; 155 156 wait_for_random_bytes(); 157 get_random_bytes(K0, krb5->key_len); 158 159 key = key_create(make_key_ref(ring, true), 160 "rxrpc_s", desc, 161 K0, krb5->key_len, 162 KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | KEY_USR_VIEW, 163 KEY_ALLOC_NOT_IN_QUOTA); 164 kfree(desc); 165 if (IS_ERR(key)) { 166 ret = PTR_ERR(key); 167 goto out; 168 } 169 170 net->fs_cm_token_key = key_ref_to_ptr(key); 171 ret = 0; 172 out: 173 key_put(ring); 174 return ret; 175 } 176 177 /* 178 * Create an YFS RxGK GSS token to use as a ticket to the specified fileserver. 179 */ 180 static int afs_create_yfs_cm_token(struct sk_buff *challenge, 181 struct afs_server *server) 182 { 183 const struct krb5_enctype *conn_krb5, *token_krb5; 184 const struct krb5_buffer *token_key; 185 struct crypto_aead *aead; 186 struct scatterlist sg; 187 struct afs_net *net = server->cell->net; 188 const struct key *key = net->fs_cm_token_key; 189 size_t keysize, uuidsize, authsize, toksize, encsize, contsize, adatasize, offset; 190 __be32 caps[1] = { 191 [0] = htonl(AFS_CAP_ERROR_TRANSLATION), 192 }; 193 __be32 *xdr; 194 void *appdata, *K0, *encbase; 195 u32 enctype; 196 int ret; 197 198 if (!key) 199 return -ENOKEY; 200 201 /* Assume that the fileserver is happy to use the same encoding type as 202 * we were told to use by the token obtained by the user. 203 */ 204 enctype = rxgk_kernel_query_challenge(challenge); 205 206 conn_krb5 = crypto_krb5_find_enctype(enctype); 207 if (!conn_krb5) 208 return -ENOPKG; 209 token_krb5 = key->payload.data[0]; 210 token_key = (const struct krb5_buffer *)&key->payload.data[2]; 211 212 /* struct rxgk_key { 213 * afs_uint32 enctype; 214 * opaque key<>; 215 * }; 216 */ 217 keysize = 4 + xdr_len_object(conn_krb5->key_len); 218 219 /* struct RXGK_AuthName { 220 * afs_int32 kind; 221 * opaque data<AUTHDATAMAX>; 222 * opaque display<AUTHPRINTABLEMAX>; 223 * }; 224 */ 225 uuidsize = sizeof(server->uuid); 226 authsize = 4 + xdr_len_object(uuidsize) + xdr_len_object(0); 227 228 /* struct RXGK_Token { 229 * rxgk_key K0; 230 * RXGK_Level level; 231 * rxgkTime starttime; 232 * afs_int32 lifetime; 233 * afs_int32 bytelife; 234 * rxgkTime expirationtime; 235 * struct RXGK_AuthName identities<>; 236 * }; 237 */ 238 toksize = keysize + 8 + 4 + 4 + 8 + xdr_len_object(authsize); 239 240 offset = 0; 241 encsize = crypto_krb5_how_much_buffer(token_krb5, KRB5_ENCRYPT_MODE, toksize, &offset); 242 243 /* struct RXGK_TokenContainer { 244 * afs_int32 kvno; 245 * afs_int32 enctype; 246 * opaque encrypted_token<>; 247 * }; 248 */ 249 contsize = 4 + 4 + xdr_len_object(encsize); 250 251 /* struct YFSAppData { 252 * opr_uuid initiatorUuid; 253 * opr_uuid acceptorUuid; 254 * Capabilities caps; 255 * afs_int32 enctype; 256 * opaque callbackKey<>; 257 * opaque callbackToken<>; 258 * }; 259 */ 260 adatasize = 16 + 16 + 261 xdr_len_object(sizeof(caps)) + 262 4 + 263 xdr_len_object(conn_krb5->key_len) + 264 xdr_len_object(contsize); 265 266 ret = -ENOMEM; 267 appdata = kzalloc(adatasize, GFP_KERNEL); 268 if (!appdata) 269 goto out; 270 xdr = appdata; 271 272 memcpy(xdr, &net->uuid, 16); /* appdata.initiatorUuid */ 273 xdr += 16 / 4; 274 memcpy(xdr, &server->uuid, 16); /* appdata.acceptorUuid */ 275 xdr += 16 / 4; 276 *xdr++ = htonl(ARRAY_SIZE(caps)); /* appdata.caps.len */ 277 memcpy(xdr, &caps, sizeof(caps)); /* appdata.caps */ 278 xdr += ARRAY_SIZE(caps); 279 *xdr++ = htonl(conn_krb5->etype); /* appdata.enctype */ 280 281 *xdr++ = htonl(conn_krb5->key_len); /* appdata.callbackKey.len */ 282 K0 = xdr; 283 get_random_bytes(K0, conn_krb5->key_len); /* appdata.callbackKey.data */ 284 xdr += xdr_round_up(conn_krb5->key_len) / 4; 285 286 *xdr++ = htonl(contsize); /* appdata.callbackToken.len */ 287 *xdr++ = htonl(1); /* cont.kvno */ 288 *xdr++ = htonl(token_krb5->etype); /* cont.enctype */ 289 *xdr++ = htonl(encsize); /* cont.encrypted_token.len */ 290 291 encbase = xdr; 292 xdr += offset / 4; 293 *xdr++ = htonl(conn_krb5->etype); /* token.K0.enctype */ 294 *xdr++ = htonl(conn_krb5->key_len); /* token.K0.key.len */ 295 memcpy(xdr, K0, conn_krb5->key_len); /* token.K0.key.data */ 296 xdr += xdr_round_up(conn_krb5->key_len) / 4; 297 298 *xdr++ = htonl(RXRPC_SECURITY_ENCRYPT); /* token.level */ 299 *xdr++ = htonl(0); /* token.starttime */ 300 *xdr++ = htonl(0); /* " */ 301 *xdr++ = htonl(0); /* token.lifetime */ 302 *xdr++ = htonl(0); /* token.bytelife */ 303 *xdr++ = htonl(0); /* token.expirationtime */ 304 *xdr++ = htonl(0); /* " */ 305 *xdr++ = htonl(1); /* token.identities.count */ 306 *xdr++ = htonl(0); /* token.identities[0].kind */ 307 *xdr++ = htonl(uuidsize); /* token.identities[0].data.len */ 308 memcpy(xdr, &server->uuid, uuidsize); 309 xdr += xdr_round_up(uuidsize) / 4; 310 *xdr++ = htonl(0); /* token.identities[0].display.len */ 311 312 xdr = encbase + xdr_round_up(encsize); 313 314 if ((unsigned long)xdr - (unsigned long)appdata != adatasize) 315 pr_err("Appdata size incorrect %lx != %zx\n", 316 (unsigned long)xdr - (unsigned long)appdata, adatasize); 317 318 aead = crypto_krb5_prepare_encryption(token_krb5, token_key, RXGK_SERVER_ENC_TOKEN, 319 GFP_KERNEL); 320 if (IS_ERR(aead)) { 321 ret = PTR_ERR(aead); 322 goto out_token; 323 } 324 325 sg_init_one(&sg, encbase, encsize); 326 ret = crypto_krb5_encrypt(token_krb5, aead, &sg, 1, encsize, offset, toksize, false); 327 if (ret < 0) 328 goto out_aead; 329 330 server->cm_rxgk_appdata.len = adatasize; 331 server->cm_rxgk_appdata.data = appdata; 332 appdata = NULL; 333 334 out_aead: 335 crypto_free_aead(aead); 336 out_token: 337 kfree(appdata); 338 out: 339 return ret; 340 } 341 #endif /* CONFIG_RXGK */ 342