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