/*1* Network port table2*3* SELinux must keep a mapping of network ports to labels/SIDs. This4* mapping is maintained as part of the normal policy but a fast cache is5* needed to reduce the lookup overhead.6*7* Author: Paul Moore <[email protected]>8*9* This code is heavily based on the "netif" concept originally developed by10* James Morris <[email protected]>11* (see security/selinux/netif.c for more information)12*13*/1415/*16* (c) Copyright Hewlett-Packard Development Company, L.P., 200817*18* This program is free software: you can redistribute it and/or modify19* it under the terms of version 2 of the GNU General Public License as20* published by the Free Software Foundation.21*22* This program is distributed in the hope that it will be useful,23* but WITHOUT ANY WARRANTY; without even the implied warranty of24* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the25* GNU General Public License for more details.26*27*/2829#include <linux/types.h>30#include <linux/rcupdate.h>31#include <linux/list.h>32#include <linux/slab.h>33#include <linux/spinlock.h>34#include <linux/in.h>35#include <linux/in6.h>36#include <linux/ip.h>37#include <linux/ipv6.h>38#include <net/ip.h>39#include <net/ipv6.h>4041#include "netport.h"42#include "objsec.h"4344#define SEL_NETPORT_HASH_SIZE 25645#define SEL_NETPORT_HASH_BKT_LIMIT 164647struct sel_netport_bkt {48int size;49struct list_head list;50};5152struct sel_netport {53struct netport_security_struct psec;5455struct list_head list;56struct rcu_head rcu;57};5859/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason60* for this is that I suspect most users will not make heavy use of both61* address families at the same time so one table will usually end up wasted,62* if this becomes a problem we can always add a hash table for each address63* family later */6465static LIST_HEAD(sel_netport_list);66static DEFINE_SPINLOCK(sel_netport_lock);67static struct sel_netport_bkt sel_netport_hash[SEL_NETPORT_HASH_SIZE];6869/**70* sel_netport_free - Frees a port entry71* @p: the entry's RCU field72*73* Description:74* This function is designed to be used as a callback to the call_rcu()75* function so that memory allocated to a hash table port entry can be76* released safely.77*78*/79static void sel_netport_free(struct rcu_head *p)80{81struct sel_netport *port = container_of(p, struct sel_netport, rcu);82kfree(port);83}8485/**86* sel_netport_hashfn - Hashing function for the port table87* @pnum: port number88*89* Description:90* This is the hashing function for the port table, it returns the bucket91* number for the given port.92*93*/94static unsigned int sel_netport_hashfn(u16 pnum)95{96return (pnum & (SEL_NETPORT_HASH_SIZE - 1));97}9899/**100* sel_netport_find - Search for a port record101* @protocol: protocol102* @port: pnum103*104* Description:105* Search the network port table and return the matching record. If an entry106* can not be found in the table return NULL.107*108*/109static struct sel_netport *sel_netport_find(u8 protocol, u16 pnum)110{111unsigned int idx;112struct sel_netport *port;113114idx = sel_netport_hashfn(pnum);115list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list)116if (port->psec.port == pnum && port->psec.protocol == protocol)117return port;118119return NULL;120}121122/**123* sel_netport_insert - Insert a new port into the table124* @port: the new port record125*126* Description:127* Add a new port record to the network address hash table.128*129*/130static void sel_netport_insert(struct sel_netport *port)131{132unsigned int idx;133134/* we need to impose a limit on the growth of the hash table so check135* this bucket to make sure it is within the specified bounds */136idx = sel_netport_hashfn(port->psec.port);137list_add_rcu(&port->list, &sel_netport_hash[idx].list);138if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) {139struct sel_netport *tail;140tail = list_entry(141rcu_dereference(sel_netport_hash[idx].list.prev),142struct sel_netport, list);143list_del_rcu(&tail->list);144call_rcu(&tail->rcu, sel_netport_free);145} else146sel_netport_hash[idx].size++;147}148149/**150* sel_netport_sid_slow - Lookup the SID of a network address using the policy151* @protocol: protocol152* @pnum: port153* @sid: port SID154*155* Description:156* This function determines the SID of a network port by quering the security157* policy. The result is added to the network port table to speedup future158* queries. Returns zero on success, negative values on failure.159*160*/161static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)162{163int ret = -ENOMEM;164struct sel_netport *port;165struct sel_netport *new = NULL;166167spin_lock_bh(&sel_netport_lock);168port = sel_netport_find(protocol, pnum);169if (port != NULL) {170*sid = port->psec.sid;171spin_unlock_bh(&sel_netport_lock);172return 0;173}174new = kzalloc(sizeof(*new), GFP_ATOMIC);175if (new == NULL)176goto out;177ret = security_port_sid(protocol, pnum, sid);178if (ret != 0)179goto out;180181new->psec.port = pnum;182new->psec.protocol = protocol;183new->psec.sid = *sid;184sel_netport_insert(new);185186out:187spin_unlock_bh(&sel_netport_lock);188if (unlikely(ret)) {189printk(KERN_WARNING190"SELinux: failure in sel_netport_sid_slow(),"191" unable to determine network port label\n");192kfree(new);193}194return ret;195}196197/**198* sel_netport_sid - Lookup the SID of a network port199* @protocol: protocol200* @pnum: port201* @sid: port SID202*203* Description:204* This function determines the SID of a network port using the fastest method205* possible. First the port table is queried, but if an entry can't be found206* then the policy is queried and the result is added to the table to speedup207* future queries. Returns zero on success, negative values on failure.208*209*/210int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid)211{212struct sel_netport *port;213214rcu_read_lock();215port = sel_netport_find(protocol, pnum);216if (port != NULL) {217*sid = port->psec.sid;218rcu_read_unlock();219return 0;220}221rcu_read_unlock();222223return sel_netport_sid_slow(protocol, pnum, sid);224}225226/**227* sel_netport_flush - Flush the entire network port table228*229* Description:230* Remove all entries from the network address table.231*232*/233static void sel_netport_flush(void)234{235unsigned int idx;236struct sel_netport *port, *port_tmp;237238spin_lock_bh(&sel_netport_lock);239for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) {240list_for_each_entry_safe(port, port_tmp,241&sel_netport_hash[idx].list, list) {242list_del_rcu(&port->list);243call_rcu(&port->rcu, sel_netport_free);244}245sel_netport_hash[idx].size = 0;246}247spin_unlock_bh(&sel_netport_lock);248}249250static int sel_netport_avc_callback(u32 event, u32 ssid, u32 tsid,251u16 class, u32 perms, u32 *retained)252{253if (event == AVC_CALLBACK_RESET) {254sel_netport_flush();255synchronize_net();256}257return 0;258}259260static __init int sel_netport_init(void)261{262int iter;263int ret;264265if (!selinux_enabled)266return 0;267268for (iter = 0; iter < SEL_NETPORT_HASH_SIZE; iter++) {269INIT_LIST_HEAD(&sel_netport_hash[iter].list);270sel_netport_hash[iter].size = 0;271}272273ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET,274SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);275if (ret != 0)276panic("avc_add_callback() failed, error %d\n", ret);277278return ret;279}280281__initcall(sel_netport_init);282283284