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