root/security/selinux/netport.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sel_netport_hashfn
  2. sel_netport_find
  3. sel_netport_insert
  4. sel_netport_sid_slow
  5. sel_netport_sid
  6. sel_netport_flush
  7. sel_netport_init

   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 
  38 struct sel_netport_bkt {
  39         int size;
  40         struct list_head list;
  41 };
  42 
  43 struct 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 
  56 static LIST_HEAD(sel_netport_list);
  57 static DEFINE_SPINLOCK(sel_netport_lock);
  58 static 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  */
  69 static 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  */
  84 static 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  */
 105 static 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 quering 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  */
 138 static 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 
 163 out:
 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  */
 184 int 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  */
 207 void 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 
 224 static __init int sel_netport_init(void)
 225 {
 226         int iter;
 227 
 228         if (!selinux_enabled)
 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);

/* [<][>][^][v][top][bottom][index][help] */