linux/security/selinux/netport.c
<<
>>
Prefs
   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 "netport.h"
  33#include "objsec.h"
  34
  35#define SEL_NETPORT_HASH_SIZE       256
  36#define SEL_NETPORT_HASH_BKT_LIMIT   16
  37
  38struct sel_netport_bkt {
  39        int size;
  40        struct list_head list;
  41};
  42
  43struct sel_netport {
  44        struct netport_security_struct psec;
  45
  46        struct list_head list;
  47        struct rcu_head rcu;
  48};
  49
  50/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason
  51 * for this is that I suspect most users will not make heavy use of both
  52 * address families at the same time so one table will usually end up wasted,
  53 * if this becomes a problem we can always add a hash table for each address
  54 * family later */
  55
  56static DEFINE_SPINLOCK(sel_netport_lock);
  57static struct sel_netport_bkt sel_netport_hash[SEL_NETPORT_HASH_SIZE];
  58
  59/**
  60 * sel_netport_hashfn - Hashing function for the port table
  61 * @pnum: port number
  62 *
  63 * Description:
  64 * This is the hashing function for the port table, it returns the bucket
  65 * number for the given port.
  66 *
  67 */
  68static unsigned int sel_netport_hashfn(u16 pnum)
  69{
  70        return (pnum & (SEL_NETPORT_HASH_SIZE - 1));
  71}
  72
  73/**
  74 * sel_netport_find - Search for a port record
  75 * @protocol: protocol
  76 * @port: pnum
  77 *
  78 * Description:
  79 * Search the network port table and return the matching record.  If an entry
  80 * can not be found in the table return NULL.
  81 *
  82 */
  83static struct sel_netport *sel_netport_find(u8 protocol, u16 pnum)
  84{
  85        unsigned int idx;
  86        struct sel_netport *port;
  87
  88        idx = sel_netport_hashfn(pnum);
  89        list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list)
  90                if (port->psec.port == pnum && port->psec.protocol == protocol)
  91                        return port;
  92
  93        return NULL;
  94}
  95
  96/**
  97 * sel_netport_insert - Insert a new port into the table
  98 * @port: the new port record
  99 *
 100 * Description:
 101 * Add a new port record to the network address hash table.
 102 *
 103 */
 104static void sel_netport_insert(struct sel_netport *port)
 105{
 106        unsigned int idx;
 107
 108        /* we need to impose a limit on the growth of the hash table so check
 109         * this bucket to make sure it is within the specified bounds */
 110        idx = sel_netport_hashfn(port->psec.port);
 111        list_add_rcu(&port->list, &sel_netport_hash[idx].list);
 112        if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) {
 113                struct sel_netport *tail;
 114                tail = list_entry(
 115                        rcu_dereference_protected(
 116                                sel_netport_hash[idx].list.prev,
 117                                lockdep_is_held(&sel_netport_lock)),
 118                        struct sel_netport, list);
 119                list_del_rcu(&tail->list);
 120                kfree_rcu(tail, rcu);
 121        } else
 122                sel_netport_hash[idx].size++;
 123}
 124
 125/**
 126 * sel_netport_sid_slow - Lookup the SID of a network address using the policy
 127 * @protocol: protocol
 128 * @pnum: port
 129 * @sid: port SID
 130 *
 131 * Description:
 132 * This function determines the SID of a network port by querying the security
 133 * policy.  The result is added to the network port table to speedup future
 134 * queries.  Returns zero on success, negative values on failure.
 135 *
 136 */
 137static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)
 138{
 139        int ret;
 140        struct sel_netport *port;
 141        struct sel_netport *new;
 142
 143        spin_lock_bh(&sel_netport_lock);
 144        port = sel_netport_find(protocol, pnum);
 145        if (port != NULL) {
 146                *sid = port->psec.sid;
 147                spin_unlock_bh(&sel_netport_lock);
 148                return 0;
 149        }
 150
 151        ret = security_port_sid(&selinux_state, protocol, pnum, sid);
 152        if (ret != 0)
 153                goto out;
 154        new = kzalloc(sizeof(*new), GFP_ATOMIC);
 155        if (new) {
 156                new->psec.port = pnum;
 157                new->psec.protocol = protocol;
 158                new->psec.sid = *sid;
 159                sel_netport_insert(new);
 160        }
 161
 162out:
 163        spin_unlock_bh(&sel_netport_lock);
 164        if (unlikely(ret))
 165                pr_warn("SELinux: failure in %s(), unable to determine network port label\n",
 166                        __func__);
 167        return ret;
 168}
 169
 170/**
 171 * sel_netport_sid - Lookup the SID of a network port
 172 * @protocol: protocol
 173 * @pnum: port
 174 * @sid: port SID
 175 *
 176 * Description:
 177 * This function determines the SID of a network port using the fastest method
 178 * possible.  First the port table is queried, but if an entry can't be found
 179 * then the policy is queried and the result is added to the table to speedup
 180 * future queries.  Returns zero on success, negative values on failure.
 181 *
 182 */
 183int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid)
 184{
 185        struct sel_netport *port;
 186
 187        rcu_read_lock();
 188        port = sel_netport_find(protocol, pnum);
 189        if (port != NULL) {
 190                *sid = port->psec.sid;
 191                rcu_read_unlock();
 192                return 0;
 193        }
 194        rcu_read_unlock();
 195
 196        return sel_netport_sid_slow(protocol, pnum, sid);
 197}
 198
 199/**
 200 * sel_netport_flush - Flush the entire network port table
 201 *
 202 * Description:
 203 * Remove all entries from the network address table.
 204 *
 205 */
 206void sel_netport_flush(void)
 207{
 208        unsigned int idx;
 209        struct sel_netport *port, *port_tmp;
 210
 211        spin_lock_bh(&sel_netport_lock);
 212        for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) {
 213                list_for_each_entry_safe(port, port_tmp,
 214                                         &sel_netport_hash[idx].list, list) {
 215                        list_del_rcu(&port->list);
 216                        kfree_rcu(port, rcu);
 217                }
 218                sel_netport_hash[idx].size = 0;
 219        }
 220        spin_unlock_bh(&sel_netport_lock);
 221}
 222
 223static __init int sel_netport_init(void)
 224{
 225        int iter;
 226
 227        if (!selinux_enabled_boot)
 228                return 0;
 229
 230        for (iter = 0; iter < SEL_NETPORT_HASH_SIZE; iter++) {
 231                INIT_LIST_HEAD(&sel_netport_hash[iter].list);
 232                sel_netport_hash[iter].size = 0;
 233        }
 234
 235        return 0;
 236}
 237
 238__initcall(sel_netport_init);
 239