| 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | /* Out of band message handling (e.g. challenge-response) |
| 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/gfp.h> |
| 12 | #include <linux/skbuff.h> |
| 13 | #include <linux/export.h> |
| 14 | #include <linux/sched/signal.h> |
| 15 | #include <net/sock.h> |
| 16 | #include <net/af_rxrpc.h> |
| 17 | #include "ar-internal.h" |
| 18 | |
| 19 | enum rxrpc_oob_command { |
| 20 | RXRPC_OOB_CMD_UNSET, |
| 21 | RXRPC_OOB_CMD_RESPOND, |
| 22 | } __mode(byte); |
| 23 | |
| 24 | struct rxrpc_oob_params { |
| 25 | u64 oob_id; /* ID number of message if reply */ |
| 26 | s32 abort_code; |
| 27 | enum rxrpc_oob_command command; |
| 28 | bool have_oob_id:1; |
| 29 | }; |
| 30 | |
| 31 | /* |
| 32 | * Post an out-of-band message for attention by the socket or kernel service |
| 33 | * associated with a reference call. |
| 34 | */ |
| 35 | void rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb) |
| 36 | { |
| 37 | struct rxrpc_skb_priv *sp = rxrpc_skb(skb); |
| 38 | struct rxrpc_sock *rx; |
| 39 | struct sock *sk; |
| 40 | |
| 41 | rcu_read_lock(); |
| 42 | |
| 43 | rx = rcu_dereference(call->socket); |
| 44 | if (rx) { |
| 45 | sk = &rx->sk; |
| 46 | spin_lock_irq(lock: &rx->recvmsg_lock); |
| 47 | |
| 48 | if (sk->sk_state < RXRPC_CLOSE) { |
| 49 | skb->skb_mstamp_ns = rx->oob_id_counter++; |
| 50 | rxrpc_get_skb(skb, rxrpc_skb_get_post_oob); |
| 51 | skb_queue_tail(list: &rx->recvmsg_oobq, newsk: skb); |
| 52 | |
| 53 | trace_rxrpc_notify_socket(debug_id: call->debug_id, serial: sp->hdr.serial); |
| 54 | if (rx->app_ops) |
| 55 | rx->app_ops->notify_oob(sk, skb); |
| 56 | } |
| 57 | |
| 58 | spin_unlock_irq(lock: &rx->recvmsg_lock); |
| 59 | if (!rx->app_ops && !sock_flag(sk, flag: SOCK_DEAD)) |
| 60 | sk->sk_data_ready(sk); |
| 61 | } |
| 62 | |
| 63 | rcu_read_unlock(); |
| 64 | } |
| 65 | |
| 66 | /* |
| 67 | * Locate the OOB message to respond to by its ID. |
| 68 | */ |
| 69 | static struct sk_buff *rxrpc_find_pending_oob(struct rxrpc_sock *rx, u64 oob_id) |
| 70 | { |
| 71 | struct rb_node *p; |
| 72 | struct sk_buff *skb; |
| 73 | |
| 74 | p = rx->pending_oobq.rb_node; |
| 75 | while (p) { |
| 76 | skb = rb_entry(p, struct sk_buff, rbnode); |
| 77 | |
| 78 | if (oob_id < skb->skb_mstamp_ns) |
| 79 | p = p->rb_left; |
| 80 | else if (oob_id > skb->skb_mstamp_ns) |
| 81 | p = p->rb_right; |
| 82 | else |
| 83 | return skb; |
| 84 | } |
| 85 | |
| 86 | return NULL; |
| 87 | } |
| 88 | |
| 89 | /* |
| 90 | * Add an OOB message into the pending-response set. We always assign the next |
| 91 | * value from a 64-bit counter to the oob_id, so just assume we're always going |
| 92 | * to be on the right-hand edge of the tree and that the counter won't wrap. |
| 93 | * The tree is also given a ref to the message. |
| 94 | */ |
| 95 | void rxrpc_add_pending_oob(struct rxrpc_sock *rx, struct sk_buff *skb) |
| 96 | { |
| 97 | struct rb_node **pp = &rx->pending_oobq.rb_node, *p = NULL; |
| 98 | |
| 99 | while (*pp) { |
| 100 | p = *pp; |
| 101 | pp = &(*pp)->rb_right; |
| 102 | } |
| 103 | |
| 104 | rb_link_node(node: &skb->rbnode, parent: p, rb_link: pp); |
| 105 | rb_insert_color(&skb->rbnode, &rx->pending_oobq); |
| 106 | } |
| 107 | |
| 108 | /* |
| 109 | * Extract control messages from the sendmsg() control buffer. |
| 110 | */ |
| 111 | static int rxrpc_sendmsg_oob_cmsg(struct msghdr *msg, struct rxrpc_oob_params *p) |
| 112 | { |
| 113 | struct cmsghdr *cmsg; |
| 114 | int len; |
| 115 | |
| 116 | if (msg->msg_controllen == 0) |
| 117 | return -EINVAL; |
| 118 | |
| 119 | for_each_cmsghdr(cmsg, msg) { |
| 120 | if (!CMSG_OK(msg, cmsg)) |
| 121 | return -EINVAL; |
| 122 | |
| 123 | len = cmsg->cmsg_len - sizeof(struct cmsghdr); |
| 124 | _debug("CMSG %d, %d, %d" , |
| 125 | cmsg->cmsg_level, cmsg->cmsg_type, len); |
| 126 | |
| 127 | if (cmsg->cmsg_level != SOL_RXRPC) |
| 128 | continue; |
| 129 | |
| 130 | switch (cmsg->cmsg_type) { |
| 131 | case RXRPC_OOB_ID: |
| 132 | if (len != sizeof(p->oob_id) || p->have_oob_id) |
| 133 | return -EINVAL; |
| 134 | memcpy(&p->oob_id, CMSG_DATA(cmsg), sizeof(p->oob_id)); |
| 135 | p->have_oob_id = true; |
| 136 | break; |
| 137 | case RXRPC_RESPOND: |
| 138 | if (p->command != RXRPC_OOB_CMD_UNSET) |
| 139 | return -EINVAL; |
| 140 | p->command = RXRPC_OOB_CMD_RESPOND; |
| 141 | break; |
| 142 | case RXRPC_ABORT: |
| 143 | if (len != sizeof(p->abort_code) || p->abort_code) |
| 144 | return -EINVAL; |
| 145 | memcpy(&p->abort_code, CMSG_DATA(cmsg), sizeof(p->abort_code)); |
| 146 | if (p->abort_code == 0) |
| 147 | return -EINVAL; |
| 148 | break; |
| 149 | case RXRPC_RESP_RXGK_APPDATA: |
| 150 | if (p->command != RXRPC_OOB_CMD_RESPOND) |
| 151 | return -EINVAL; |
| 152 | break; |
| 153 | default: |
| 154 | return -EINVAL; |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | switch (p->command) { |
| 159 | case RXRPC_OOB_CMD_RESPOND: |
| 160 | if (!p->have_oob_id) |
| 161 | return -EBADSLT; |
| 162 | break; |
| 163 | default: |
| 164 | return -EINVAL; |
| 165 | } |
| 166 | |
| 167 | return 0; |
| 168 | } |
| 169 | |
| 170 | /* |
| 171 | * Allow userspace to respond to an OOB using sendmsg(). |
| 172 | */ |
| 173 | static int rxrpc_respond_to_oob(struct rxrpc_sock *rx, |
| 174 | struct rxrpc_oob_params *p, |
| 175 | struct msghdr *msg) |
| 176 | { |
| 177 | struct rxrpc_connection *conn; |
| 178 | struct rxrpc_skb_priv *sp; |
| 179 | struct sk_buff *skb; |
| 180 | int ret; |
| 181 | |
| 182 | skb = rxrpc_find_pending_oob(rx, oob_id: p->oob_id); |
| 183 | if (skb) |
| 184 | rb_erase(&skb->rbnode, &rx->pending_oobq); |
| 185 | release_sock(sk: &rx->sk); |
| 186 | if (!skb) |
| 187 | return -EBADSLT; |
| 188 | |
| 189 | sp = rxrpc_skb(skb); |
| 190 | |
| 191 | switch (p->command) { |
| 192 | case RXRPC_OOB_CMD_RESPOND: |
| 193 | ret = -EPROTO; |
| 194 | if (skb->mark != RXRPC_OOB_CHALLENGE) |
| 195 | break; |
| 196 | conn = sp->chall.conn; |
| 197 | ret = -EOPNOTSUPP; |
| 198 | if (!conn->security->sendmsg_respond_to_challenge) |
| 199 | break; |
| 200 | if (p->abort_code) { |
| 201 | rxrpc_abort_conn(conn, NULL, abort_code: p->abort_code, err: -ECONNABORTED, |
| 202 | why: rxrpc_abort_response_sendmsg); |
| 203 | ret = 0; |
| 204 | } else { |
| 205 | ret = conn->security->sendmsg_respond_to_challenge(skb, msg); |
| 206 | } |
| 207 | break; |
| 208 | default: |
| 209 | ret = -EINVAL; |
| 210 | break; |
| 211 | } |
| 212 | |
| 213 | rxrpc_free_skb(skb, rxrpc_skb_put_oob); |
| 214 | return ret; |
| 215 | } |
| 216 | |
| 217 | /* |
| 218 | * Send an out-of-band message or respond to a received out-of-band message. |
| 219 | * - caller gives us the socket lock |
| 220 | * - the socket may be either a client socket or a server socket |
| 221 | */ |
| 222 | int rxrpc_sendmsg_oob(struct rxrpc_sock *rx, struct msghdr *msg, size_t len) |
| 223 | { |
| 224 | struct rxrpc_oob_params p = {}; |
| 225 | int ret; |
| 226 | |
| 227 | _enter("" ); |
| 228 | |
| 229 | ret = rxrpc_sendmsg_oob_cmsg(msg, p: &p); |
| 230 | if (ret < 0) |
| 231 | goto error_release_sock; |
| 232 | |
| 233 | if (p.have_oob_id) |
| 234 | return rxrpc_respond_to_oob(rx, p: &p, msg); |
| 235 | |
| 236 | release_sock(sk: &rx->sk); |
| 237 | |
| 238 | switch (p.command) { |
| 239 | default: |
| 240 | ret = -EINVAL; |
| 241 | break; |
| 242 | } |
| 243 | |
| 244 | _leave(" = %d" , ret); |
| 245 | return ret; |
| 246 | |
| 247 | error_release_sock: |
| 248 | release_sock(sk: &rx->sk); |
| 249 | return ret; |
| 250 | } |
| 251 | |
| 252 | /** |
| 253 | * rxrpc_kernel_query_oob - Query the parameters of an out-of-band message |
| 254 | * @oob: The message to query |
| 255 | * @_peer: Where to return the peer record |
| 256 | * @_peer_appdata: The application data attached to a peer record |
| 257 | * |
| 258 | * Extract useful parameters from an out-of-band message. The source peer |
| 259 | * parameters are returned through the argument list and the message type is |
| 260 | * returned. |
| 261 | * |
| 262 | * Return: |
| 263 | * * %RXRPC_OOB_CHALLENGE - Challenge wanting a response. |
| 264 | */ |
| 265 | enum rxrpc_oob_type rxrpc_kernel_query_oob(struct sk_buff *oob, |
| 266 | struct rxrpc_peer **_peer, |
| 267 | unsigned long *_peer_appdata) |
| 268 | { |
| 269 | struct rxrpc_skb_priv *sp = rxrpc_skb(oob); |
| 270 | enum rxrpc_oob_type type = oob->mark; |
| 271 | |
| 272 | switch (type) { |
| 273 | case RXRPC_OOB_CHALLENGE: |
| 274 | *_peer = sp->chall.conn->peer; |
| 275 | *_peer_appdata = sp->chall.conn->peer->app_data; |
| 276 | break; |
| 277 | default: |
| 278 | WARN_ON_ONCE(1); |
| 279 | *_peer = NULL; |
| 280 | *_peer_appdata = 0; |
| 281 | break; |
| 282 | } |
| 283 | |
| 284 | return type; |
| 285 | } |
| 286 | EXPORT_SYMBOL(rxrpc_kernel_query_oob); |
| 287 | |
| 288 | /** |
| 289 | * rxrpc_kernel_dequeue_oob - Dequeue and return the front OOB message |
| 290 | * @sock: The socket to query |
| 291 | * @_type: Where to return the message type |
| 292 | * |
| 293 | * Dequeue the front OOB message, if there is one, and return it and |
| 294 | * its type. |
| 295 | * |
| 296 | * Return: The sk_buff representing the OOB message or %NULL if the queue was |
| 297 | * empty. |
| 298 | */ |
| 299 | struct sk_buff *rxrpc_kernel_dequeue_oob(struct socket *sock, |
| 300 | enum rxrpc_oob_type *_type) |
| 301 | { |
| 302 | struct rxrpc_sock *rx = rxrpc_sk(sock->sk); |
| 303 | struct sk_buff *oob; |
| 304 | |
| 305 | oob = skb_dequeue(list: &rx->recvmsg_oobq); |
| 306 | if (oob) |
| 307 | *_type = oob->mark; |
| 308 | return oob; |
| 309 | } |
| 310 | EXPORT_SYMBOL(rxrpc_kernel_dequeue_oob); |
| 311 | |
| 312 | /** |
| 313 | * rxrpc_kernel_free_oob - Free an out-of-band message |
| 314 | * @oob: The OOB message to free |
| 315 | * |
| 316 | * Free an OOB message along with any resources it holds. |
| 317 | */ |
| 318 | void rxrpc_kernel_free_oob(struct sk_buff *oob) |
| 319 | { |
| 320 | struct rxrpc_skb_priv *sp = rxrpc_skb(oob); |
| 321 | |
| 322 | switch (oob->mark) { |
| 323 | case RXRPC_OOB_CHALLENGE: |
| 324 | rxrpc_put_connection(sp->chall.conn, rxrpc_conn_put_oob); |
| 325 | break; |
| 326 | } |
| 327 | |
| 328 | rxrpc_free_skb(oob, rxrpc_skb_put_purge_oob); |
| 329 | } |
| 330 | EXPORT_SYMBOL(rxrpc_kernel_free_oob); |
| 331 | |
| 332 | /** |
| 333 | * rxrpc_kernel_query_challenge - Query the parameters of a challenge |
| 334 | * @challenge: The challenge to query |
| 335 | * @_peer: Where to return the peer record |
| 336 | * @_peer_appdata: The application data attached to a peer record |
| 337 | * @_service_id: Where to return the connection service ID |
| 338 | * @_security_index: Where to return the connection security index |
| 339 | * |
| 340 | * Extract useful parameters from a CHALLENGE message. |
| 341 | */ |
| 342 | void rxrpc_kernel_query_challenge(struct sk_buff *challenge, |
| 343 | struct rxrpc_peer **_peer, |
| 344 | unsigned long *_peer_appdata, |
| 345 | u16 *_service_id, u8 *_security_index) |
| 346 | { |
| 347 | struct rxrpc_skb_priv *sp = rxrpc_skb(challenge); |
| 348 | |
| 349 | *_peer = sp->chall.conn->peer; |
| 350 | *_peer_appdata = sp->chall.conn->peer->app_data; |
| 351 | *_service_id = sp->hdr.serviceId; |
| 352 | *_security_index = sp->hdr.securityIndex; |
| 353 | } |
| 354 | EXPORT_SYMBOL(rxrpc_kernel_query_challenge); |
| 355 | |
| 356 | /** |
| 357 | * rxrpc_kernel_reject_challenge - Allow a kernel service to reject a challenge |
| 358 | * @challenge: The challenge to be rejected |
| 359 | * @abort_code: The abort code to stick into the ABORT packet |
| 360 | * @error: Local error value |
| 361 | * @why: Indication as to why. |
| 362 | * |
| 363 | * Allow a kernel service to reject a challenge by aborting the connection if |
| 364 | * it's still in an abortable state. The error is returned so this function |
| 365 | * can be used with a return statement. |
| 366 | * |
| 367 | * Return: The %error parameter. |
| 368 | */ |
| 369 | int rxrpc_kernel_reject_challenge(struct sk_buff *challenge, u32 abort_code, |
| 370 | int error, enum rxrpc_abort_reason why) |
| 371 | { |
| 372 | struct rxrpc_skb_priv *sp = rxrpc_skb(challenge); |
| 373 | |
| 374 | _enter("{%x},%d,%d,%u" , sp->hdr.serial, abort_code, error, why); |
| 375 | |
| 376 | rxrpc_abort_conn(conn: sp->chall.conn, NULL, abort_code, err: error, why); |
| 377 | return error; |
| 378 | } |
| 379 | EXPORT_SYMBOL(rxrpc_kernel_reject_challenge); |
| 380 | |