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