Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

SELinux: add netport.[ch]

Thank you, git.

Signed-off-by: James Morris <jmorris@namei.org>

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