File Index
Files and directories
Sign in
FILE

netport.c

File preview with syntax highlighting.

linux-6.19.14/security/selinux/netport.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Network port table
  4 *
  5 * SELinux must keep a mapping of network ports to labels/SIDs.  This
  6 * mapping is maintained as part of the normal policy but a fast cache is
  7 * needed to reduce the lookup overhead.
  8 *
  9 * Author: Paul Moore <paul@paul-moore.com>
 10 *
 11 * This code is heavily based on the "netif" concept originally developed by
 12 * James Morris <jmorris@redhat.com>
 13 *   (see security/selinux/netif.c for more information)
 14 */
 15
 16/*
 17 * (c) Copyright Hewlett-Packard Development Company, L.P., 2008
 18 */
 19
 20#include <linux/types.h>
 21#include <linux/rcupdate.h>
 22#include <linux/list.h>
 23#include <linux/slab.h>
 24#include <linux/spinlock.h>
 25#include <linux/in.h>
 26#include <linux/in6.h>
 27#include <linux/ip.h>
 28#include <linux/ipv6.h>
 29#include <net/ip.h>
 30#include <net/ipv6.h>
 31
 32#include "initcalls.h"
 33#include "netport.h"
 34#include "objsec.h"
 35
 36#define SEL_NETPORT_HASH_SIZE       256
 37#define SEL_NETPORT_HASH_BKT_LIMIT   16
 38
 39struct sel_netport_bkt {
 40	int size;
 41	struct list_head list;
 42};
 43
 44struct sel_netport {
 45	struct netport_security_struct psec;
 46
 47	struct list_head list;
 48	struct rcu_head rcu;
 49};
 50
 51static DEFINE_SPINLOCK(sel_netport_lock);
 52static struct sel_netport_bkt sel_netport_hash[SEL_NETPORT_HASH_SIZE];
 53
 54/**
 55 * sel_netport_hashfn - Hashing function for the port table
 56 * @pnum: port number
 57 *
 58 * Description:
 59 * This is the hashing function for the port table, it returns the bucket
 60 * number for the given port.
 61 *
 62 */
 63static unsigned int sel_netport_hashfn(u16 pnum)
 64{
 65	return (pnum & (SEL_NETPORT_HASH_SIZE - 1));
 66}
 67
 68/**
 69 * sel_netport_find - Search for a port record
 70 * @protocol: protocol
 71 * @pnum: port
 72 *
 73 * Description:
 74 * Search the network port table and return the matching record.  If an entry
 75 * can not be found in the table return NULL.
 76 *
 77 */
 78static struct sel_netport *sel_netport_find(u8 protocol, u16 pnum)
 79{
 80	unsigned int idx;
 81	struct sel_netport *port;
 82
 83	idx = sel_netport_hashfn(pnum);
 84	list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list)
 85		if (port->psec.port == pnum && port->psec.protocol == protocol)
 86			return port;
 87
 88	return NULL;
 89}
 90
 91/**
 92 * sel_netport_insert - Insert a new port into the table
 93 * @port: the new port record
 94 *
 95 * Description:
 96 * Add a new port record to the network address hash table.
 97 *
 98 */
 99static void sel_netport_insert(struct sel_netport *port)
100{
101	unsigned int idx;
102
103	/* we need to impose a limit on the growth of the hash table so check
104	 * this bucket to make sure it is within the specified bounds */
105	idx = sel_netport_hashfn(port->psec.port);
106	list_add_rcu(&port->list, &sel_netport_hash[idx].list);
107	if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) {
108		struct sel_netport *tail;
109		tail = list_entry(
110			rcu_dereference_protected(
111				list_tail_rcu(&sel_netport_hash[idx].list),
112				lockdep_is_held(&sel_netport_lock)),
113			struct sel_netport, list);
114		list_del_rcu(&tail->list);
115		kfree_rcu(tail, rcu);
116	} else
117		sel_netport_hash[idx].size++;
118}
119
120/**
121 * sel_netport_sid_slow - Lookup the SID of a network address using the policy
122 * @protocol: protocol
123 * @pnum: port
124 * @sid: port SID
125 *
126 * Description:
127 * This function determines the SID of a network port by querying the security
128 * policy.  The result is added to the network port table to speedup future
129 * queries.  Returns zero on success, negative values on failure.
130 *
131 */
132static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)
133{
134	int ret;
135	struct sel_netport *port;
136	struct sel_netport *new;
137
138	spin_lock_bh(&sel_netport_lock);
139	port = sel_netport_find(protocol, pnum);
140	if (port != NULL) {
141		*sid = port->psec.sid;
142		spin_unlock_bh(&sel_netport_lock);
143		return 0;
144	}
145
146	ret = security_port_sid(protocol, pnum, sid);
147	if (ret != 0)
148		goto out;
149
150	/* If this memory allocation fails still return 0. The SID
151	 * is valid, it just won't be added to the cache.
152	 */
153	new = kmalloc(sizeof(*new), GFP_ATOMIC);
154	if (new) {
155		new->psec.port = pnum;
156		new->psec.protocol = protocol;
157		new->psec.sid = *sid;
158		sel_netport_insert(new);
159	}
160
161out:
162	spin_unlock_bh(&sel_netport_lock);
163	if (unlikely(ret))
164		pr_warn("SELinux: failure in %s(), unable to determine network port label\n",
165			__func__);
166	return ret;
167}
168
169/**
170 * sel_netport_sid - Lookup the SID of a network port
171 * @protocol: protocol
172 * @pnum: port
173 * @sid: port SID
174 *
175 * Description:
176 * This function determines the SID of a network port using the fastest method
177 * possible.  First the port table is queried, but if an entry can't be found
178 * then the policy is queried and the result is added to the table to speedup
179 * future queries.  Returns zero on success, negative values on failure.
180 *
181 */
182int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid)
183{
184	struct sel_netport *port;
185
186	rcu_read_lock();
187	port = sel_netport_find(protocol, pnum);
188	if (likely(port != NULL)) {
189		*sid = port->psec.sid;
190		rcu_read_unlock();
191		return 0;
192	}
193	rcu_read_unlock();
194
195	return sel_netport_sid_slow(protocol, pnum, sid);
196}
197
198/**
199 * sel_netport_flush - Flush the entire network port table
200 *
201 * Description:
202 * Remove all entries from the network address table.
203 *
204 */
205void sel_netport_flush(void)
206{
207	unsigned int idx;
208	struct sel_netport *port, *port_tmp;
209
210	spin_lock_bh(&sel_netport_lock);
211	for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) {
212		list_for_each_entry_safe(port, port_tmp,
213					 &sel_netport_hash[idx].list, list) {
214			list_del_rcu(&port->list);
215			kfree_rcu(port, rcu);
216		}
217		sel_netport_hash[idx].size = 0;
218	}
219	spin_unlock_bh(&sel_netport_lock);
220}
221
222int __init sel_netport_init(void)
223{
224	int iter;
225
226	if (!selinux_enabled_boot)
227		return 0;
228
229	for (iter = 0; iter < SEL_NETPORT_HASH_SIZE; iter++) {
230		INIT_LIST_HEAD(&sel_netport_hash[iter].list);
231		sel_netport_hash[iter].size = 0;
232	}
233
234	return 0;
235}
~/ Read-only file preview.