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 ((oob = rxrpc_kernel_dequeue_oob(net->socket, &type))) { 105 switch (type) { 106 case RXRPC_OOB_CHALLENGE: 107 afs_respond_to_challenge(oob); 108 break; 109 } 110 rxrpc_kernel_free_oob(oob); 111 } 112 } 113 114 #ifdef CONFIG_RXGK 115 /* 116 * Create a securities keyring for the cache manager and attach a key to it for 117 * the RxGK tokens we want to use to secure the callback connection back from 118 * the fileserver. 119 */ 120 int afs_create_token_key(struct afs_net *net, struct socket *socket) 121 { 122 const struct krb5_enctype *krb5; 123 struct key *ring; 124 key_ref_t key; 125 char K0[32], *desc; 126 int ret; 127 128 ring = keyring_alloc("kafs", 129 GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(), 130 KEY_POS_SEARCH | KEY_POS_WRITE | 131 KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH, 132 KEY_ALLOC_NOT_IN_QUOTA, 133 NULL, NULL); 134 if (IS_ERR(ring)) 135 return PTR_ERR(ring); 136 137 ret = rxrpc_sock_set_security_keyring(socket->sk, ring); 138 if (ret < 0) 139 goto out; 140 141 ret = -ENOPKG; 142 krb5 = crypto_krb5_find_enctype(KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96); 143 if (!krb5) 144 goto out; 145 146 if (WARN_ON_ONCE(krb5->key_len > sizeof(K0))) 147 goto out; 148 149 ret = -ENOMEM; 150 desc = kasprintf(GFP_KERNEL, "%u:%u:%u:%u", 151 YFS_CM_SERVICE, RXRPC_SECURITY_YFS_RXGK, 1, krb5->etype); 152 if (!desc) 153 goto out; 154 155 wait_for_random_bytes(); 156 get_random_bytes(K0, krb5->key_len); 157 158 key = key_create(make_key_ref(ring, true), 159 "rxrpc_s", desc, 160 K0, krb5->key_len, 161 KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | KEY_USR_VIEW, 162 KEY_ALLOC_NOT_IN_QUOTA); 163 kfree(desc); 164 if (IS_ERR(key)) { 165 ret = PTR_ERR(key); 166 goto out; 167 } 168 169 net->fs_cm_token_key = key_ref_to_ptr(key); 170 ret = 0; 171 out: 172 key_put(ring); 173 return ret; 174 } 175 176 /* 177 * Create an YFS RxGK GSS token to use as a ticket to the specified fileserver. 178 */ 179 static int afs_create_yfs_cm_token(struct sk_buff *challenge, 180 struct afs_server *server) 181 { 182 const struct krb5_enctype *conn_krb5, *token_krb5; 183 const struct krb5_buffer *token_key; 184 struct crypto_aead *aead; 185 struct scatterlist sg; 186 struct afs_net *net = server->cell->net; 187 const struct key *key = net->fs_cm_token_key; 188 size_t keysize, uuidsize, authsize, toksize, encsize, contsize, adatasize, offset; 189 __be32 caps[1] = { 190 [0] = htonl(AFS_CAP_ERROR_TRANSLATION), 191 }; 192 __be32 *xdr; 193 void *appdata, *K0, *encbase; 194 u32 enctype; 195 int ret; 196 197 if (!key) 198 return -ENOKEY; 199 200 /* Assume that the fileserver is happy to use the same encoding type as 201 * we were told to use by the token obtained by the user. 202 */ 203 enctype = rxgk_kernel_query_challenge(challenge); 204 205 conn_krb5 = crypto_krb5_find_enctype(enctype); 206 if (!conn_krb5) 207 return -ENOPKG; 208 token_krb5 = key->payload.data[0]; 209 token_key = (const struct krb5_buffer *)&key->payload.data[2]; 210 211 /* struct rxgk_key { 212 * afs_uint32 enctype; 213 * opaque key<>; 214 * }; 215 */ 216 keysize = 4 + xdr_len_object(conn_krb5->key_len); 217 218 /* struct RXGK_AuthName { 219 * afs_int32 kind; 220 * opaque data<AUTHDATAMAX>; 221 * opaque display<AUTHPRINTABLEMAX>; 222 * }; 223 */ 224 uuidsize = sizeof(server->uuid); 225 authsize = 4 + xdr_len_object(uuidsize) + xdr_len_object(0); 226 227 /* struct RXGK_Token { 228 * rxgk_key K0; 229 * RXGK_Level level; 230 * rxgkTime starttime; 231 * afs_int32 lifetime; 232 * afs_int32 bytelife; 233 * rxgkTime expirationtime; 234 * struct RXGK_AuthName identities<>; 235 * }; 236 */ 237 toksize = keysize + 8 + 4 + 4 + 8 + xdr_len_object(authsize); 238 239 offset = 0; 240 encsize = crypto_krb5_how_much_buffer(token_krb5, KRB5_ENCRYPT_MODE, toksize, &offset); 241 242 /* struct RXGK_TokenContainer { 243 * afs_int32 kvno; 244 * afs_int32 enctype; 245 * opaque encrypted_token<>; 246 * }; 247 */ 248 contsize = 4 + 4 + xdr_len_object(encsize); 249 250 /* struct YFSAppData { 251 * opr_uuid initiatorUuid; 252 * opr_uuid acceptorUuid; 253 * Capabilities caps; 254 * afs_int32 enctype; 255 * opaque callbackKey<>; 256 * opaque callbackToken<>; 257 * }; 258 */ 259 adatasize = 16 + 16 + 260 xdr_len_object(sizeof(caps)) + 261 4 + 262 xdr_len_object(conn_krb5->key_len) + 263 xdr_len_object(contsize); 264 265 ret = -ENOMEM; 266 appdata = kzalloc(adatasize, GFP_KERNEL); 267 if (!appdata) 268 goto out; 269 xdr = appdata; 270 271 memcpy(xdr, &net->uuid, 16); /* appdata.initiatorUuid */ 272 xdr += 16 / 4; 273 memcpy(xdr, &server->uuid, 16); /* appdata.acceptorUuid */ 274 xdr += 16 / 4; 275 *xdr++ = htonl(ARRAY_SIZE(caps)); /* appdata.caps.len */ 276 memcpy(xdr, &caps, sizeof(caps)); /* appdata.caps */ 277 xdr += ARRAY_SIZE(caps); 278 *xdr++ = htonl(conn_krb5->etype); /* appdata.enctype */ 279 280 *xdr++ = htonl(conn_krb5->key_len); /* appdata.callbackKey.len */ 281 K0 = xdr; 282 get_random_bytes(K0, conn_krb5->key_len); /* appdata.callbackKey.data */ 283 xdr += xdr_round_up(conn_krb5->key_len) / 4; 284 285 *xdr++ = htonl(contsize); /* appdata.callbackToken.len */ 286 *xdr++ = htonl(1); /* cont.kvno */ 287 *xdr++ = htonl(token_krb5->etype); /* cont.enctype */ 288 *xdr++ = htonl(encsize); /* cont.encrypted_token.len */ 289 290 encbase = xdr; 291 xdr += offset / 4; 292 *xdr++ = htonl(conn_krb5->etype); /* token.K0.enctype */ 293 *xdr++ = htonl(conn_krb5->key_len); /* token.K0.key.len */ 294 memcpy(xdr, K0, conn_krb5->key_len); /* token.K0.key.data */ 295 xdr += xdr_round_up(conn_krb5->key_len) / 4; 296 297 *xdr++ = htonl(RXRPC_SECURITY_ENCRYPT); /* token.level */ 298 *xdr++ = htonl(0); /* token.starttime */ 299 *xdr++ = htonl(0); /* " */ 300 *xdr++ = htonl(0); /* token.lifetime */ 301 *xdr++ = htonl(0); /* token.bytelife */ 302 *xdr++ = htonl(0); /* token.expirationtime */ 303 *xdr++ = htonl(0); /* " */ 304 *xdr++ = htonl(1); /* token.identities.count */ 305 *xdr++ = htonl(0); /* token.identities[0].kind */ 306 *xdr++ = htonl(uuidsize); /* token.identities[0].data.len */ 307 memcpy(xdr, &server->uuid, uuidsize); 308 xdr += xdr_round_up(uuidsize) / 4; 309 *xdr++ = htonl(0); /* token.identities[0].display.len */ 310 311 xdr = encbase + xdr_round_up(encsize); 312 313 if ((unsigned long)xdr - (unsigned long)appdata != adatasize) 314 pr_err("Appdata size incorrect %lx != %zx\n", 315 (unsigned long)xdr - (unsigned long)appdata, adatasize); 316 317 aead = crypto_krb5_prepare_encryption(token_krb5, token_key, RXGK_SERVER_ENC_TOKEN, 318 GFP_KERNEL); 319 if (IS_ERR(aead)) { 320 ret = PTR_ERR(aead); 321 goto out_token; 322 } 323 324 sg_init_one(&sg, encbase, encsize); 325 ret = crypto_krb5_encrypt(token_krb5, aead, &sg, 1, encsize, offset, toksize, false); 326 if (ret < 0) 327 goto out_aead; 328 329 server->cm_rxgk_appdata.len = adatasize; 330 server->cm_rxgk_appdata.data = appdata; 331 appdata = NULL; 332 333 out_aead: 334 crypto_free_aead(aead); 335 out_token: 336 kfree(appdata); 337 out: 338 return ret; 339 } 340 #endif /* CONFIG_RXGK */ 341