File Index
Files and directories
Sign in
FILE

oob.c

File preview with syntax highlighting.

linux-6.19.14/net/rxrpc/oob.c C
  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
 19enum rxrpc_oob_command {
 20	RXRPC_OOB_CMD_UNSET,
 21	RXRPC_OOB_CMD_RESPOND,
 22} __mode(byte);
 23
 24struct 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 */
 35void 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(&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(&rx->recvmsg_oobq, skb);
 52
 53			trace_rxrpc_notify_socket(call->debug_id, sp->hdr.serial);
 54			if (rx->app_ops)
 55				rx->app_ops->notify_oob(sk, skb);
 56		}
 57
 58		spin_unlock_irq(&rx->recvmsg_lock);
 59		if (!rx->app_ops && !sock_flag(sk, 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 */
 69static 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 */
 95void 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(&skb->rbnode, p, pp);
105	rb_insert_color(&skb->rbnode, &rx->pending_oobq);
106}
107
108/*
109 * Extract control messages from the sendmsg() control buffer.
110 */
111static 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 */
173static 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, p->oob_id);
183	if (skb)
184		rb_erase(&skb->rbnode, &rx->pending_oobq);
185	release_sock(&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, p->abort_code, -ECONNABORTED,
202					 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 */
222int 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);
230	if (ret < 0)
231		goto error_release_sock;
232
233	if (p.have_oob_id)
234		return rxrpc_respond_to_oob(rx, &p, msg);
235
236	release_sock(&rx->sk);
237
238	switch (p.command) {
239	default:
240		ret = -EINVAL;
241		break;
242	}
243
244	_leave(" = %d", ret);
245	return ret;
246
247error_release_sock:
248	release_sock(&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 */
265enum 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}
286EXPORT_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 */
299struct 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(&rx->recvmsg_oobq);
306	if (oob)
307		*_type = oob->mark;
308	return oob;
309}
310EXPORT_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 */
318void 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}
330EXPORT_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 */
342void 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}
354EXPORT_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 */
369int 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(sp->chall.conn, NULL, abort_code, error, why);
377	return error;
378}
379EXPORT_SYMBOL(rxrpc_kernel_reject_challenge);
~/ Read-only file preview.