linux/security/selinux/netnode.c
<<
>>
Prefs
   1/*
   2 * Network node table
   3 *
   4 * SELinux must keep a mapping of network nodes to labels/SIDs.  This
   5 * mapping is maintained as part of the normal policy but a fast cache is
   6 * needed to reduce the lookup overhead since most of these queries happen on
   7 * a per-packet basis.
   8 *
   9 * Author: Paul Moore <paul.moore@hp.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/*
  18 * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
  19 *
  20 * This program is free software: you can redistribute it and/or modify
  21 * it under the terms of version 2 of the GNU General Public License as
  22 * published by the Free Software Foundation.
  23 *
  24 * This program is distributed in the hope that it will be useful,
  25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27 * GNU General Public License for more details.
  28 *
  29 */
  30
  31#include <linux/types.h>
  32#include <linux/rcupdate.h>
  33#include <linux/list.h>
  34#include <linux/slab.h>
  35#include <linux/spinlock.h>
  36#include <linux/in.h>
  37#include <linux/in6.h>
  38#include <linux/ip.h>
  39#include <linux/ipv6.h>
  40#include <net/ip.h>
  41#include <net/ipv6.h>
  42
  43#include "netnode.h"
  44#include "objsec.h"
  45
  46#define SEL_NETNODE_HASH_SIZE       256
  47#define SEL_NETNODE_HASH_BKT_LIMIT   16
  48
  49struct sel_netnode_bkt {
  50        unsigned int size;
  51        struct list_head list;
  52};
  53
  54struct sel_netnode {
  55        struct netnode_security_struct nsec;
  56
  57        struct list_head list;
  58        struct rcu_head rcu;
  59};
  60
  61/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason
  62 * for this is that I suspect most users will not make heavy use of both
  63 * address families at the same time so one table will usually end up wasted,
  64 * if this becomes a problem we can always add a hash table for each address
  65 * family later */
  66
  67static LIST_HEAD(sel_netnode_list);
  68static DEFINE_SPINLOCK(sel_netnode_lock);
  69static struct sel_netnode_bkt sel_netnode_hash[SEL_NETNODE_HASH_SIZE];
  70
  71/**
  72 * sel_netnode_free - Frees a node entry
  73 * @p: the entry's RCU field
  74 *
  75 * Description:
  76 * This function is designed to be used as a callback to the call_rcu()
  77 * function so that memory allocated to a hash table node entry can be
  78 * released safely.
  79 *
  80 */
  81static void sel_netnode_free(struct rcu_head *p)
  82{
  83        struct sel_netnode *node = container_of(p, struct sel_netnode, rcu);
  84        kfree(node);
  85}
  86
  87/**
  88 * sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table
  89 * @addr: IPv4 address
  90 *
  91 * Description:
  92 * This is the IPv4 hashing function for the node interface table, it returns
  93 * the bucket number for the given IP address.
  94 *
  95 */
  96static unsigned int sel_netnode_hashfn_ipv4(__be32 addr)
  97{
  98        /* at some point we should determine if the mismatch in byte order
  99         * affects the hash function dramatically */
 100        return (addr & (SEL_NETNODE_HASH_SIZE - 1));
 101}
 102
 103/**
 104 * sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table
 105 * @addr: IPv6 address
 106 *
 107 * Description:
 108 * This is the IPv6 hashing function for the node interface table, it returns
 109 * the bucket number for the given IP address.
 110 *
 111 */
 112static unsigned int sel_netnode_hashfn_ipv6(const struct in6_addr *addr)
 113{
 114        /* just hash the least significant 32 bits to keep things fast (they
 115         * are the most likely to be different anyway), we can revisit this
 116         * later if needed */
 117        return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1));
 118}
 119
 120/**
 121 * sel_netnode_find - Search for a node record
 122 * @addr: IP address
 123 * @family: address family
 124 *
 125 * Description:
 126 * Search the network node table and return the record matching @addr.  If an
 127 * entry can not be found in the table return NULL.
 128 *
 129 */
 130static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
 131{
 132        unsigned int idx;
 133        struct sel_netnode *node;
 134
 135        switch (family) {
 136        case PF_INET:
 137                idx = sel_netnode_hashfn_ipv4(*(__be32 *)addr);
 138                break;
 139        case PF_INET6:
 140                idx = sel_netnode_hashfn_ipv6(addr);
 141                break;
 142        default:
 143                BUG();
 144        }
 145
 146        list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list)
 147                if (node->nsec.family == family)
 148                        switch (family) {
 149                        case PF_INET:
 150                                if (node->nsec.addr.ipv4 == *(__be32 *)addr)
 151                                        return node;
 152                                break;
 153                        case PF_INET6:
 154                                if (ipv6_addr_equal(&node->nsec.addr.ipv6,
 155                                                    addr))
 156                                        return node;
 157                                break;
 158                        }
 159
 160        return NULL;
 161}
 162
 163/**
 164 * sel_netnode_insert - Insert a new node into the table
 165 * @node: the new node record
 166 *
 167 * Description:
 168 * Add a new node record to the network address hash table.
 169 *
 170 */
 171static void sel_netnode_insert(struct sel_netnode *node)
 172{
 173        unsigned int idx;
 174
 175        switch (node->nsec.family) {
 176        case PF_INET:
 177                idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4);
 178                break;
 179        case PF_INET6:
 180                idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6);
 181                break;
 182        default:
 183                BUG();
 184        }
 185
 186        /* we need to impose a limit on the growth of the hash table so check
 187         * this bucket to make sure it is within the specified bounds */
 188        list_add_rcu(&node->list, &sel_netnode_hash[idx].list);
 189        if (sel_netnode_hash[idx].size == SEL_NETNODE_HASH_BKT_LIMIT) {
 190                struct sel_netnode *tail;
 191                tail = list_entry(
 192                        rcu_dereference(sel_netnode_hash[idx].list.prev),
 193                        struct sel_netnode, list);
 194                list_del_rcu(&tail->list);
 195                call_rcu(&tail->rcu, sel_netnode_free);
 196        } else
 197                sel_netnode_hash[idx].size++;
 198}
 199
 200/**
 201 * sel_netnode_sid_slow - Lookup the SID of a network address using the policy
 202 * @addr: the IP address
 203 * @family: the address family
 204 * @sid: node SID
 205 *
 206 * Description:
 207 * This function determines the SID of a network address by quering the
 208 * security policy.  The result is added to the network address table to
 209 * speedup future queries.  Returns zero on success, negative values on
 210 * failure.
 211 *
 212 */
 213static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
 214{
 215        int ret = -ENOMEM;
 216        struct sel_netnode *node;
 217        struct sel_netnode *new = NULL;
 218
 219        spin_lock_bh(&sel_netnode_lock);
 220        node = sel_netnode_find(addr, family);
 221        if (node != NULL) {
 222                *sid = node->nsec.sid;
 223                spin_unlock_bh(&sel_netnode_lock);
 224                return 0;
 225        }
 226        new = kzalloc(sizeof(*new), GFP_ATOMIC);
 227        if (new == NULL)
 228                goto out;
 229        switch (family) {
 230        case PF_INET:
 231                ret = security_node_sid(PF_INET,
 232                                        addr, sizeof(struct in_addr), sid);
 233                new->nsec.addr.ipv4 = *(__be32 *)addr;
 234                break;
 235        case PF_INET6:
 236                ret = security_node_sid(PF_INET6,
 237                                        addr, sizeof(struct in6_addr), sid);
 238                ipv6_addr_copy(&new->nsec.addr.ipv6, addr);
 239                break;
 240        default:
 241                BUG();
 242        }
 243        if (ret != 0)
 244                goto out;
 245
 246        new->nsec.family = family;
 247        new->nsec.sid = *sid;
 248        sel_netnode_insert(new);
 249
 250out:
 251        spin_unlock_bh(&sel_netnode_lock);
 252        if (unlikely(ret)) {
 253                printk(KERN_WARNING
 254                       "SELinux: failure in sel_netnode_sid_slow(),"
 255                       " unable to determine network node label\n");
 256                kfree(new);
 257        }
 258        return ret;
 259}
 260
 261/**
 262 * sel_netnode_sid - Lookup the SID of a network address
 263 * @addr: the IP address
 264 * @family: the address family
 265 * @sid: node SID
 266 *
 267 * Description:
 268 * This function determines the SID of a network address using the fastest
 269 * method possible.  First the address table is queried, but if an entry
 270 * can't be found then the policy is queried and the result is added to the
 271 * table to speedup future queries.  Returns zero on success, negative values
 272 * on failure.
 273 *
 274 */
 275int sel_netnode_sid(void *addr, u16 family, u32 *sid)
 276{
 277        struct sel_netnode *node;
 278
 279        rcu_read_lock();
 280        node = sel_netnode_find(addr, family);
 281        if (node != NULL) {
 282                *sid = node->nsec.sid;
 283                rcu_read_unlock();
 284                return 0;
 285        }
 286        rcu_read_unlock();
 287
 288        return sel_netnode_sid_slow(addr, family, sid);
 289}
 290
 291/**
 292 * sel_netnode_flush - Flush the entire network address table
 293 *
 294 * Description:
 295 * Remove all entries from the network address table.
 296 *
 297 */
 298static void sel_netnode_flush(void)
 299{
 300        unsigned int idx;
 301        struct sel_netnode *node, *node_tmp;
 302
 303        spin_lock_bh(&sel_netnode_lock);
 304        for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) {
 305                list_for_each_entry_safe(node, node_tmp,
 306                                         &sel_netnode_hash[idx].list, list) {
 307                                list_del_rcu(&node->list);
 308                                call_rcu(&node->rcu, sel_netnode_free);
 309                }
 310                sel_netnode_hash[idx].size = 0;
 311        }
 312        spin_unlock_bh(&sel_netnode_lock);
 313}
 314
 315static int sel_netnode_avc_callback(u32 event, u32 ssid, u32 tsid,
 316                                    u16 class, u32 perms, u32 *retained)
 317{
 318        if (event == AVC_CALLBACK_RESET) {
 319                sel_netnode_flush();
 320                synchronize_net();
 321        }
 322        return 0;
 323}
 324
 325static __init int sel_netnode_init(void)
 326{
 327        int iter;
 328        int ret;
 329
 330        if (!selinux_enabled)
 331                return 0;
 332
 333        for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) {
 334                INIT_LIST_HEAD(&sel_netnode_hash[iter].list);
 335                sel_netnode_hash[iter].size = 0;
 336        }
 337
 338        ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET,
 339                               SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
 340        if (ret != 0)
 341                panic("avc_add_callback() failed, error %d\n", ret);
 342
 343        return ret;
 344}
 345
 346__initcall(sel_netnode_init);
 347