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