xref: /openbmc/linux/net/rxrpc/security.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28c3e34a4SDavid Howells /* RxRPC security handling
38c3e34a4SDavid Howells  *
48c3e34a4SDavid Howells  * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
58c3e34a4SDavid Howells  * Written by David Howells (dhowells@redhat.com)
68c3e34a4SDavid Howells  */
78c3e34a4SDavid Howells 
88c3e34a4SDavid Howells #include <linux/module.h>
98c3e34a4SDavid Howells #include <linux/net.h>
108c3e34a4SDavid Howells #include <linux/skbuff.h>
118c3e34a4SDavid Howells #include <linux/udp.h>
128c3e34a4SDavid Howells #include <linux/crypto.h>
138c3e34a4SDavid Howells #include <net/sock.h>
148c3e34a4SDavid Howells #include <net/af_rxrpc.h>
158c3e34a4SDavid Howells #include <keys/rxrpc-type.h>
168c3e34a4SDavid Howells #include "ar-internal.h"
178c3e34a4SDavid Howells 
188c3e34a4SDavid Howells static const struct rxrpc_security *rxrpc_security_types[] = {
198c3e34a4SDavid Howells 	[RXRPC_SECURITY_NONE]	= &rxrpc_no_security,
208c3e34a4SDavid Howells #ifdef CONFIG_RXKAD
218c3e34a4SDavid Howells 	[RXRPC_SECURITY_RXKAD]	= &rxkad,
228c3e34a4SDavid Howells #endif
238c3e34a4SDavid Howells };
248c3e34a4SDavid Howells 
rxrpc_init_security(void)258c3e34a4SDavid Howells int __init rxrpc_init_security(void)
268c3e34a4SDavid Howells {
278c3e34a4SDavid Howells 	int i, ret;
288c3e34a4SDavid Howells 
298c3e34a4SDavid Howells 	for (i = 0; i < ARRAY_SIZE(rxrpc_security_types); i++) {
308c3e34a4SDavid Howells 		if (rxrpc_security_types[i]) {
318c3e34a4SDavid Howells 			ret = rxrpc_security_types[i]->init();
328c3e34a4SDavid Howells 			if (ret < 0)
338c3e34a4SDavid Howells 				goto failed;
348c3e34a4SDavid Howells 		}
358c3e34a4SDavid Howells 	}
368c3e34a4SDavid Howells 
378c3e34a4SDavid Howells 	return 0;
388c3e34a4SDavid Howells 
398c3e34a4SDavid Howells failed:
408c3e34a4SDavid Howells 	for (i--; i >= 0; i--)
418c3e34a4SDavid Howells 		if (rxrpc_security_types[i])
428c3e34a4SDavid Howells 			rxrpc_security_types[i]->exit();
438c3e34a4SDavid Howells 	return ret;
448c3e34a4SDavid Howells }
458c3e34a4SDavid Howells 
rxrpc_exit_security(void)468c3e34a4SDavid Howells void rxrpc_exit_security(void)
478c3e34a4SDavid Howells {
488c3e34a4SDavid Howells 	int i;
498c3e34a4SDavid Howells 
508c3e34a4SDavid Howells 	for (i = 0; i < ARRAY_SIZE(rxrpc_security_types); i++)
518c3e34a4SDavid Howells 		if (rxrpc_security_types[i])
528c3e34a4SDavid Howells 			rxrpc_security_types[i]->exit();
538c3e34a4SDavid Howells }
548c3e34a4SDavid Howells 
558c3e34a4SDavid Howells /*
568c3e34a4SDavid Howells  * look up an rxrpc security module
578c3e34a4SDavid Howells  */
rxrpc_security_lookup(u8 security_index)5812da59fcSDavid Howells const struct rxrpc_security *rxrpc_security_lookup(u8 security_index)
598c3e34a4SDavid Howells {
608c3e34a4SDavid Howells 	if (security_index >= ARRAY_SIZE(rxrpc_security_types))
618c3e34a4SDavid Howells 		return NULL;
628c3e34a4SDavid Howells 	return rxrpc_security_types[security_index];
638c3e34a4SDavid Howells }
648c3e34a4SDavid Howells 
658c3e34a4SDavid Howells /*
66f3441d41SDavid Howells  * Initialise the security on a client call.
67f3441d41SDavid Howells  */
rxrpc_init_client_call_security(struct rxrpc_call * call)68f3441d41SDavid Howells int rxrpc_init_client_call_security(struct rxrpc_call *call)
69f3441d41SDavid Howells {
70fdb99487SDavid Howells 	const struct rxrpc_security *sec = &rxrpc_no_security;
71f3441d41SDavid Howells 	struct rxrpc_key_token *token;
72f3441d41SDavid Howells 	struct key *key = call->key;
73f3441d41SDavid Howells 	int ret;
74f3441d41SDavid Howells 
75f3441d41SDavid Howells 	if (!key)
76fdb99487SDavid Howells 		goto found;
77f3441d41SDavid Howells 
78f3441d41SDavid Howells 	ret = key_validate(key);
79f3441d41SDavid Howells 	if (ret < 0)
80f3441d41SDavid Howells 		return ret;
81f3441d41SDavid Howells 
82f3441d41SDavid Howells 	for (token = key->payload.data[0]; token; token = token->next) {
83f3441d41SDavid Howells 		sec = rxrpc_security_lookup(token->security_index);
84f3441d41SDavid Howells 		if (sec)
85f3441d41SDavid Howells 			goto found;
86f3441d41SDavid Howells 	}
87f3441d41SDavid Howells 	return -EKEYREJECTED;
88f3441d41SDavid Howells 
89f3441d41SDavid Howells found:
90f3441d41SDavid Howells 	call->security = sec;
91fdb99487SDavid Howells 	call->security_ix = sec->security_index;
92f3441d41SDavid Howells 	return 0;
93f3441d41SDavid Howells }
94f3441d41SDavid Howells 
95f3441d41SDavid Howells /*
968c3e34a4SDavid Howells  * initialise the security on a client connection
978c3e34a4SDavid Howells  */
rxrpc_init_client_conn_security(struct rxrpc_connection * conn)988c3e34a4SDavid Howells int rxrpc_init_client_conn_security(struct rxrpc_connection *conn)
998c3e34a4SDavid Howells {
1008c3e34a4SDavid Howells 	struct rxrpc_key_token *token;
1012cc80086SDavid Howells 	struct key *key = conn->key;
1029d35d880SDavid Howells 	int ret = 0;
1038c3e34a4SDavid Howells 
1048c3e34a4SDavid Howells 	_enter("{%d},{%x}", conn->debug_id, key_serial(key));
1058c3e34a4SDavid Howells 
10641057ebdSDavid Howells 	for (token = key->payload.data[0]; token; token = token->next) {
1079d35d880SDavid Howells 		if (token->security_index == conn->security->security_index)
10841057ebdSDavid Howells 			goto found;
10941057ebdSDavid Howells 	}
1108c3e34a4SDavid Howells 	return -EKEYREJECTED;
1118c3e34a4SDavid Howells 
11241057ebdSDavid Howells found:
1139d35d880SDavid Howells 	mutex_lock(&conn->security_lock);
1149d35d880SDavid Howells 	if (conn->state == RXRPC_CONN_CLIENT_UNSECURED) {
11541057ebdSDavid Howells 		ret = conn->security->init_connection_security(conn, token);
1169d35d880SDavid Howells 		if (ret == 0) {
1179d35d880SDavid Howells 			spin_lock(&conn->state_lock);
1189d35d880SDavid Howells 			if (conn->state == RXRPC_CONN_CLIENT_UNSECURED)
1199d35d880SDavid Howells 				conn->state = RXRPC_CONN_CLIENT;
1209d35d880SDavid Howells 			spin_unlock(&conn->state_lock);
1218c3e34a4SDavid Howells 		}
1229d35d880SDavid Howells 	}
1239d35d880SDavid Howells 	mutex_unlock(&conn->security_lock);
1249d35d880SDavid Howells 	return ret;
1258c3e34a4SDavid Howells }
1268c3e34a4SDavid Howells 
1278c3e34a4SDavid Howells /*
128ec832bd0SDavid Howells  * Set the ops a server connection.
1298c3e34a4SDavid Howells  */
rxrpc_get_incoming_security(struct rxrpc_sock * rx,struct sk_buff * skb)130ec832bd0SDavid Howells const struct rxrpc_security *rxrpc_get_incoming_security(struct rxrpc_sock *rx,
131063c60d3SDavid Howells 							 struct sk_buff *skb)
1328c3e34a4SDavid Howells {
1338c3e34a4SDavid Howells 	const struct rxrpc_security *sec;
134063c60d3SDavid Howells 	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
1358c3e34a4SDavid Howells 
1368c3e34a4SDavid Howells 	_enter("");
1378c3e34a4SDavid Howells 
138063c60d3SDavid Howells 	sec = rxrpc_security_lookup(sp->hdr.securityIndex);
1398c3e34a4SDavid Howells 	if (!sec) {
14057af281eSDavid Howells 		rxrpc_direct_abort(skb, rxrpc_abort_unsupported_security,
14157af281eSDavid Howells 				   RX_INVALID_OPERATION, -EKEYREJECTED);
142ec832bd0SDavid Howells 		return NULL;
1438c3e34a4SDavid Howells 	}
1448c3e34a4SDavid Howells 
145ec832bd0SDavid Howells 	if (sp->hdr.securityIndex != RXRPC_SECURITY_NONE &&
146ec832bd0SDavid Howells 	    !rx->securities) {
14757af281eSDavid Howells 		rxrpc_direct_abort(skb, rxrpc_abort_no_service_key,
14857af281eSDavid Howells 				   sec->no_key_abort, -EKEYREJECTED);
149ec832bd0SDavid Howells 		return NULL;
1508c3e34a4SDavid Howells 	}
1518c3e34a4SDavid Howells 
152ec832bd0SDavid Howells 	return sec;
153ec832bd0SDavid Howells }
154ec832bd0SDavid Howells 
155ec832bd0SDavid Howells /*
156ec832bd0SDavid Howells  * Find the security key for a server connection.
157ec832bd0SDavid Howells  */
rxrpc_look_up_server_security(struct rxrpc_connection * conn,struct sk_buff * skb,u32 kvno,u32 enctype)158ec832bd0SDavid Howells struct key *rxrpc_look_up_server_security(struct rxrpc_connection *conn,
159ec832bd0SDavid Howells 					  struct sk_buff *skb,
160ec832bd0SDavid Howells 					  u32 kvno, u32 enctype)
161ec832bd0SDavid Howells {
162ec832bd0SDavid Howells 	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
163ec832bd0SDavid Howells 	struct rxrpc_sock *rx;
164ec832bd0SDavid Howells 	struct key *key = ERR_PTR(-EKEYREJECTED);
165ec832bd0SDavid Howells 	key_ref_t kref = NULL;
166ec832bd0SDavid Howells 	char kdesc[5 + 1 + 3 + 1 + 12 + 1 + 12 + 1];
167ec832bd0SDavid Howells 	int ret;
168ec832bd0SDavid Howells 
169ec832bd0SDavid Howells 	_enter("");
170ec832bd0SDavid Howells 
171ec832bd0SDavid Howells 	if (enctype)
172ec832bd0SDavid Howells 		sprintf(kdesc, "%u:%u:%u:%u",
173ec832bd0SDavid Howells 			sp->hdr.serviceId, sp->hdr.securityIndex, kvno, enctype);
174ec832bd0SDavid Howells 	else if (kvno)
175ec832bd0SDavid Howells 		sprintf(kdesc, "%u:%u:%u",
176ec832bd0SDavid Howells 			sp->hdr.serviceId, sp->hdr.securityIndex, kvno);
177ec832bd0SDavid Howells 	else
178ec832bd0SDavid Howells 		sprintf(kdesc, "%u:%u",
179ec832bd0SDavid Howells 			sp->hdr.serviceId, sp->hdr.securityIndex);
180ec832bd0SDavid Howells 
181*42f229c3SDavid Howells 	read_lock(&conn->local->services_lock);
182ec832bd0SDavid Howells 
183*42f229c3SDavid Howells 	rx = conn->local->service;
184ec832bd0SDavid Howells 	if (!rx)
185ec832bd0SDavid Howells 		goto out;
186ec832bd0SDavid Howells 
1878c3e34a4SDavid Howells 	/* look through the service's keyring */
1888c3e34a4SDavid Howells 	kref = keyring_search(make_key_ref(rx->securities, 1UL),
189dcf49dbcSDavid Howells 			      &key_type_rxrpc_s, kdesc, true);
1908c3e34a4SDavid Howells 	if (IS_ERR(kref)) {
191ec832bd0SDavid Howells 		key = ERR_CAST(kref);
192ec832bd0SDavid Howells 		goto out;
193ec832bd0SDavid Howells 	}
194ec832bd0SDavid Howells 
195ec832bd0SDavid Howells 	key = key_ref_to_ptr(kref);
196ec832bd0SDavid Howells 
197ec832bd0SDavid Howells 	ret = key_validate(key);
198ec832bd0SDavid Howells 	if (ret < 0) {
199ec832bd0SDavid Howells 		key_put(key);
200ec832bd0SDavid Howells 		key = ERR_PTR(ret);
201ec832bd0SDavid Howells 		goto out;
2028c3e34a4SDavid Howells 	}
2038c3e34a4SDavid Howells 
204063c60d3SDavid Howells out:
205*42f229c3SDavid Howells 	read_unlock(&conn->local->services_lock);
206ec832bd0SDavid Howells 	return key;
2078c3e34a4SDavid Howells }
208