1 /* 2 * Network port table 3 * 4 * SELinux must keep a mapping of network ports 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. 7 * 8 * Author: Paul Moore <paul.moore@hp.com> 9 * 10 * This code is heavily based on the "netif" concept originally developed by 11 * James Morris <jmorris@redhat.com> 12 * (see security/selinux/netif.c for more information) 13 * 14 */ 15 16 /* 17 * (c) Copyright Hewlett-Packard Development Company, L.P., 2008 18 * 19 * This program is free software: you can redistribute it and/or modify 20 * it under the terms of version 2 of the GNU General Public License as 21 * published by the Free Software Foundation. 22 * 23 * This program is distributed in the hope that it will be useful, 24 * but WITHOUT ANY WARRANTY; without even the implied warranty of 25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 * GNU General Public License for more details. 27 * 28 */ 29 30 #include <linux/types.h> 31 #include <linux/rcupdate.h> 32 #include <linux/list.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> 40 41 #include "netport.h" 42 #include "objsec.h" 43 44 #define SEL_NETPORT_HASH_SIZE 256 45 #define SEL_NETPORT_HASH_BKT_LIMIT 16 46 47 struct sel_netport_bkt { 48 int size; 49 struct list_head list; 50 }; 51 52 struct sel_netport { 53 struct netport_security_struct psec; 54 55 struct list_head list; 56 struct rcu_head rcu; 57 }; 58 59 /* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason 60 * for this is that I suspect most users will not make heavy use of both 61 * 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 address 63 * family later */ 64 65 static LIST_HEAD(sel_netport_list); 66 static DEFINE_SPINLOCK(sel_netport_lock); 67 static struct sel_netport_bkt sel_netport_hash[SEL_NETPORT_HASH_SIZE]; 68 69 /** 70 * sel_netport_free - Frees a port entry 71 * @p: the entry's RCU field 72 * 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 be 76 * released safely. 77 * 78 */ 79 static void sel_netport_free(struct rcu_head *p) 80 { 81 struct sel_netport *port = container_of(p, struct sel_netport, rcu); 82 kfree(port); 83 } 84 85 /** 86 * sel_netport_hashfn - Hashing function for the port table 87 * @pnum: port number 88 * 89 * Description: 90 * This is the hashing function for the port table, it returns the bucket 91 * number for the given port. 92 * 93 */ 94 static unsigned int sel_netport_hashfn(u16 pnum) 95 { 96 return (pnum & (SEL_NETPORT_HASH_SIZE - 1)); 97 } 98 99 /** 100 * sel_netport_find - Search for a port record 101 * @protocol: protocol 102 * @port: pnum 103 * 104 * Description: 105 * Search the network port table and return the matching record. If an entry 106 * can not be found in the table return NULL. 107 * 108 */ 109 static struct sel_netport *sel_netport_find(u8 protocol, u16 pnum) 110 { 111 unsigned int idx; 112 struct sel_netport *port; 113 114 idx = sel_netport_hashfn(pnum); 115 list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list) 116 if (port->psec.port == pnum && port->psec.protocol == protocol) 117 return port; 118 119 return NULL; 120 } 121 122 /** 123 * sel_netport_insert - Insert a new port into the table 124 * @port: the new port record 125 * 126 * Description: 127 * Add a new port record to the network address hash table. 128 * 129 */ 130 static void sel_netport_insert(struct sel_netport *port) 131 { 132 unsigned int idx; 133 134 /* we need to impose a limit on the growth of the hash table so check 135 * this bucket to make sure it is within the specified bounds */ 136 idx = sel_netport_hashfn(port->psec.port); 137 list_add_rcu(&port->list, &sel_netport_hash[idx].list); 138 if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) { 139 struct sel_netport *tail; 140 tail = list_entry( 141 rcu_dereference(sel_netport_hash[idx].list.prev), 142 struct sel_netport, list); 143 list_del_rcu(&tail->list); 144 call_rcu(&tail->rcu, sel_netport_free); 145 } else 146 sel_netport_hash[idx].size++; 147 } 148 149 /** 150 * sel_netport_sid_slow - Lookup the SID of a network address using the policy 151 * @protocol: protocol 152 * @pnum: port 153 * @sid: port SID 154 * 155 * Description: 156 * This function determines the SID of a network port by quering the security 157 * policy. The result is added to the network port table to speedup future 158 * queries. Returns zero on success, negative values on failure. 159 * 160 */ 161 static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid) 162 { 163 int ret = -ENOMEM; 164 struct sel_netport *port; 165 struct sel_netport *new = NULL; 166 167 spin_lock_bh(&sel_netport_lock); 168 port = sel_netport_find(protocol, pnum); 169 if (port != NULL) { 170 *sid = port->psec.sid; 171 spin_unlock_bh(&sel_netport_lock); 172 return 0; 173 } 174 new = kzalloc(sizeof(*new), GFP_ATOMIC); 175 if (new == NULL) 176 goto out; 177 ret = security_port_sid(protocol, pnum, sid); 178 if (ret != 0) 179 goto out; 180 181 new->psec.port = pnum; 182 new->psec.protocol = protocol; 183 new->psec.sid = *sid; 184 sel_netport_insert(new); 185 186 out: 187 spin_unlock_bh(&sel_netport_lock); 188 if (unlikely(ret)) { 189 printk(KERN_WARNING 190 "SELinux: failure in sel_netport_sid_slow()," 191 " unable to determine network port label\n"); 192 kfree(new); 193 } 194 return ret; 195 } 196 197 /** 198 * sel_netport_sid - Lookup the SID of a network port 199 * @protocol: protocol 200 * @pnum: port 201 * @sid: port SID 202 * 203 * Description: 204 * This function determines the SID of a network port using the fastest method 205 * possible. First the port table is queried, but if an entry can't be found 206 * then the policy is queried and the result is added to the table to speedup 207 * future queries. Returns zero on success, negative values on failure. 208 * 209 */ 210 int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid) 211 { 212 struct sel_netport *port; 213 214 rcu_read_lock(); 215 port = sel_netport_find(protocol, pnum); 216 if (port != NULL) { 217 *sid = port->psec.sid; 218 rcu_read_unlock(); 219 return 0; 220 } 221 rcu_read_unlock(); 222 223 return sel_netport_sid_slow(protocol, pnum, sid); 224 } 225 226 /** 227 * sel_netport_flush - Flush the entire network port table 228 * 229 * Description: 230 * Remove all entries from the network address table. 231 * 232 */ 233 static void sel_netport_flush(void) 234 { 235 unsigned int idx; 236 struct sel_netport *port, *port_tmp; 237 238 spin_lock_bh(&sel_netport_lock); 239 for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) { 240 list_for_each_entry_safe(port, port_tmp, 241 &sel_netport_hash[idx].list, list) { 242 list_del_rcu(&port->list); 243 call_rcu(&port->rcu, sel_netport_free); 244 } 245 sel_netport_hash[idx].size = 0; 246 } 247 spin_unlock_bh(&sel_netport_lock); 248 } 249 250 static int sel_netport_avc_callback(u32 event, u32 ssid, u32 tsid, 251 u16 class, u32 perms, u32 *retained) 252 { 253 if (event == AVC_CALLBACK_RESET) { 254 sel_netport_flush(); 255 synchronize_net(); 256 } 257 return 0; 258 } 259 260 static __init int sel_netport_init(void) 261 { 262 int iter; 263 int ret; 264 265 if (!selinux_enabled) 266 return 0; 267 268 for (iter = 0; iter < SEL_NETPORT_HASH_SIZE; iter++) { 269 INIT_LIST_HEAD(&sel_netport_hash[iter].list); 270 sel_netport_hash[iter].size = 0; 271 } 272 273 ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET, 274 SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); 275 if (ret != 0) 276 panic("avc_add_callback() failed, error %d\n", ret); 277 278 return ret; 279 } 280 281 __initcall(sel_netport_init); 282