162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* RxRPC security handling
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/net.h>
1062306a36Sopenharmony_ci#include <linux/skbuff.h>
1162306a36Sopenharmony_ci#include <linux/udp.h>
1262306a36Sopenharmony_ci#include <linux/crypto.h>
1362306a36Sopenharmony_ci#include <net/sock.h>
1462306a36Sopenharmony_ci#include <net/af_rxrpc.h>
1562306a36Sopenharmony_ci#include <keys/rxrpc-type.h>
1662306a36Sopenharmony_ci#include "ar-internal.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic const struct rxrpc_security *rxrpc_security_types[] = {
1962306a36Sopenharmony_ci	[RXRPC_SECURITY_NONE]	= &rxrpc_no_security,
2062306a36Sopenharmony_ci#ifdef CONFIG_RXKAD
2162306a36Sopenharmony_ci	[RXRPC_SECURITY_RXKAD]	= &rxkad,
2262306a36Sopenharmony_ci#endif
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciint __init rxrpc_init_security(void)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	int i, ret;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(rxrpc_security_types); i++) {
3062306a36Sopenharmony_ci		if (rxrpc_security_types[i]) {
3162306a36Sopenharmony_ci			ret = rxrpc_security_types[i]->init();
3262306a36Sopenharmony_ci			if (ret < 0)
3362306a36Sopenharmony_ci				goto failed;
3462306a36Sopenharmony_ci		}
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return 0;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cifailed:
4062306a36Sopenharmony_ci	for (i--; i >= 0; i--)
4162306a36Sopenharmony_ci		if (rxrpc_security_types[i])
4262306a36Sopenharmony_ci			rxrpc_security_types[i]->exit();
4362306a36Sopenharmony_ci	return ret;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_civoid rxrpc_exit_security(void)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	int i;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(rxrpc_security_types); i++)
5162306a36Sopenharmony_ci		if (rxrpc_security_types[i])
5262306a36Sopenharmony_ci			rxrpc_security_types[i]->exit();
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/*
5662306a36Sopenharmony_ci * look up an rxrpc security module
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_ciconst struct rxrpc_security *rxrpc_security_lookup(u8 security_index)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	if (security_index >= ARRAY_SIZE(rxrpc_security_types))
6162306a36Sopenharmony_ci		return NULL;
6262306a36Sopenharmony_ci	return rxrpc_security_types[security_index];
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/*
6662306a36Sopenharmony_ci * Initialise the security on a client call.
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_ciint rxrpc_init_client_call_security(struct rxrpc_call *call)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	const struct rxrpc_security *sec = &rxrpc_no_security;
7162306a36Sopenharmony_ci	struct rxrpc_key_token *token;
7262306a36Sopenharmony_ci	struct key *key = call->key;
7362306a36Sopenharmony_ci	int ret;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (!key)
7662306a36Sopenharmony_ci		goto found;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	ret = key_validate(key);
7962306a36Sopenharmony_ci	if (ret < 0)
8062306a36Sopenharmony_ci		return ret;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	for (token = key->payload.data[0]; token; token = token->next) {
8362306a36Sopenharmony_ci		sec = rxrpc_security_lookup(token->security_index);
8462306a36Sopenharmony_ci		if (sec)
8562306a36Sopenharmony_ci			goto found;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci	return -EKEYREJECTED;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cifound:
9062306a36Sopenharmony_ci	call->security = sec;
9162306a36Sopenharmony_ci	call->security_ix = sec->security_index;
9262306a36Sopenharmony_ci	return 0;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/*
9662306a36Sopenharmony_ci * initialise the security on a client connection
9762306a36Sopenharmony_ci */
9862306a36Sopenharmony_ciint rxrpc_init_client_conn_security(struct rxrpc_connection *conn)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct rxrpc_key_token *token;
10162306a36Sopenharmony_ci	struct key *key = conn->key;
10262306a36Sopenharmony_ci	int ret = 0;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	_enter("{%d},{%x}", conn->debug_id, key_serial(key));
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	for (token = key->payload.data[0]; token; token = token->next) {
10762306a36Sopenharmony_ci		if (token->security_index == conn->security->security_index)
10862306a36Sopenharmony_ci			goto found;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci	return -EKEYREJECTED;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cifound:
11362306a36Sopenharmony_ci	mutex_lock(&conn->security_lock);
11462306a36Sopenharmony_ci	if (conn->state == RXRPC_CONN_CLIENT_UNSECURED) {
11562306a36Sopenharmony_ci		ret = conn->security->init_connection_security(conn, token);
11662306a36Sopenharmony_ci		if (ret == 0) {
11762306a36Sopenharmony_ci			spin_lock(&conn->state_lock);
11862306a36Sopenharmony_ci			if (conn->state == RXRPC_CONN_CLIENT_UNSECURED)
11962306a36Sopenharmony_ci				conn->state = RXRPC_CONN_CLIENT;
12062306a36Sopenharmony_ci			spin_unlock(&conn->state_lock);
12162306a36Sopenharmony_ci		}
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci	mutex_unlock(&conn->security_lock);
12462306a36Sopenharmony_ci	return ret;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/*
12862306a36Sopenharmony_ci * Set the ops a server connection.
12962306a36Sopenharmony_ci */
13062306a36Sopenharmony_ciconst struct rxrpc_security *rxrpc_get_incoming_security(struct rxrpc_sock *rx,
13162306a36Sopenharmony_ci							 struct sk_buff *skb)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	const struct rxrpc_security *sec;
13462306a36Sopenharmony_ci	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	_enter("");
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	sec = rxrpc_security_lookup(sp->hdr.securityIndex);
13962306a36Sopenharmony_ci	if (!sec) {
14062306a36Sopenharmony_ci		rxrpc_direct_abort(skb, rxrpc_abort_unsupported_security,
14162306a36Sopenharmony_ci				   RX_INVALID_OPERATION, -EKEYREJECTED);
14262306a36Sopenharmony_ci		return NULL;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (sp->hdr.securityIndex != RXRPC_SECURITY_NONE &&
14662306a36Sopenharmony_ci	    !rx->securities) {
14762306a36Sopenharmony_ci		rxrpc_direct_abort(skb, rxrpc_abort_no_service_key,
14862306a36Sopenharmony_ci				   sec->no_key_abort, -EKEYREJECTED);
14962306a36Sopenharmony_ci		return NULL;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return sec;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/*
15662306a36Sopenharmony_ci * Find the security key for a server connection.
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_cistruct key *rxrpc_look_up_server_security(struct rxrpc_connection *conn,
15962306a36Sopenharmony_ci					  struct sk_buff *skb,
16062306a36Sopenharmony_ci					  u32 kvno, u32 enctype)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
16362306a36Sopenharmony_ci	struct rxrpc_sock *rx;
16462306a36Sopenharmony_ci	struct key *key = ERR_PTR(-EKEYREJECTED);
16562306a36Sopenharmony_ci	key_ref_t kref = NULL;
16662306a36Sopenharmony_ci	char kdesc[5 + 1 + 3 + 1 + 12 + 1 + 12 + 1];
16762306a36Sopenharmony_ci	int ret;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	_enter("");
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (enctype)
17262306a36Sopenharmony_ci		sprintf(kdesc, "%u:%u:%u:%u",
17362306a36Sopenharmony_ci			sp->hdr.serviceId, sp->hdr.securityIndex, kvno, enctype);
17462306a36Sopenharmony_ci	else if (kvno)
17562306a36Sopenharmony_ci		sprintf(kdesc, "%u:%u:%u",
17662306a36Sopenharmony_ci			sp->hdr.serviceId, sp->hdr.securityIndex, kvno);
17762306a36Sopenharmony_ci	else
17862306a36Sopenharmony_ci		sprintf(kdesc, "%u:%u",
17962306a36Sopenharmony_ci			sp->hdr.serviceId, sp->hdr.securityIndex);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	read_lock(&conn->local->services_lock);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	rx = conn->local->service;
18462306a36Sopenharmony_ci	if (!rx)
18562306a36Sopenharmony_ci		goto out;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* look through the service's keyring */
18862306a36Sopenharmony_ci	kref = keyring_search(make_key_ref(rx->securities, 1UL),
18962306a36Sopenharmony_ci			      &key_type_rxrpc_s, kdesc, true);
19062306a36Sopenharmony_ci	if (IS_ERR(kref)) {
19162306a36Sopenharmony_ci		key = ERR_CAST(kref);
19262306a36Sopenharmony_ci		goto out;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	key = key_ref_to_ptr(kref);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	ret = key_validate(key);
19862306a36Sopenharmony_ci	if (ret < 0) {
19962306a36Sopenharmony_ci		key_put(key);
20062306a36Sopenharmony_ci		key = ERR_PTR(ret);
20162306a36Sopenharmony_ci		goto out;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ciout:
20562306a36Sopenharmony_ci	read_unlock(&conn->local->services_lock);
20662306a36Sopenharmony_ci	return key;
20762306a36Sopenharmony_ci}
208