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

calipso: Add a label cache.

This works in exactly the same way as the CIPSO label cache.
The idea is to allow the lsm to cache the result of a secattr
lookup so that it doesn't need to perform the lookup for
every skbuff.

It introduces two sysctl controls:
calipso_cache_enable - enables/disables the cache.
calipso_cache_bucket_size - sets the size of a cache bucket.

Signed-off-by: Huw Davies <huw@codeweavers.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>

authored by

Huw Davies and committed by
Paul Moore
4fee5242 2e532b70

+360 -12
+6
include/net/calipso.h
··· 62 62 struct rcu_head rcu; 63 63 }; 64 64 65 + /* 66 + * Sysctl Variables 67 + */ 68 + extern int calipso_cache_enabled; 69 + extern int calipso_cache_bucketsize; 70 + 65 71 #ifdef CONFIG_NETLABEL 66 72 int __init calipso_init(void); 67 73 void calipso_exit(void);
+7 -2
include/net/netlabel.h
··· 235 235 * @skbuff_optptr: find option in packet 236 236 * @skbuff_setattr: set the skbuff's attr 237 237 * @skbuff_delattr: remove the skbuff's attr 238 + * @cache_invalidate: invalidate cache 239 + * @cache_add: add cache entry 238 240 * 239 241 * Description: 240 242 * This structure is filled out by the CALIPSO engine and passed ··· 271 269 const struct calipso_doi *doi_def, 272 270 const struct netlbl_lsm_secattr *secattr); 273 271 int (*skbuff_delattr)(struct sk_buff *skb); 272 + void (*cache_invalidate)(void); 273 + int (*cache_add)(const unsigned char *calipso_ptr, 274 + const struct netlbl_lsm_secattr *secattr); 274 275 }; 275 276 276 277 /* ··· 499 494 * LSM label mapping cache operations 500 495 */ 501 496 void netlbl_cache_invalidate(void); 502 - int netlbl_cache_add(const struct sk_buff *skb, 497 + int netlbl_cache_add(const struct sk_buff *skb, u16 family, 503 498 const struct netlbl_lsm_secattr *secattr); 504 499 505 500 /* ··· 652 647 { 653 648 return; 654 649 } 655 - static inline int netlbl_cache_add(const struct sk_buff *skb, 650 + static inline int netlbl_cache_add(const struct sk_buff *skb, u16 family, 656 651 const struct netlbl_lsm_secattr *secattr) 657 652 { 658 653 return 0;
+262 -2
net/ipv6/calipso.c
··· 72 72 static DEFINE_SPINLOCK(calipso_doi_list_lock); 73 73 static LIST_HEAD(calipso_doi_list); 74 74 75 + /* Label mapping cache */ 76 + int calipso_cache_enabled = 1; 77 + int calipso_cache_bucketsize = 10; 78 + #define CALIPSO_CACHE_BUCKETBITS 7 79 + #define CALIPSO_CACHE_BUCKETS BIT(CALIPSO_CACHE_BUCKETBITS) 80 + #define CALIPSO_CACHE_REORDERLIMIT 10 81 + struct calipso_map_cache_bkt { 82 + spinlock_t lock; 83 + u32 size; 84 + struct list_head list; 85 + }; 86 + 87 + struct calipso_map_cache_entry { 88 + u32 hash; 89 + unsigned char *key; 90 + size_t key_len; 91 + 92 + struct netlbl_lsm_cache *lsm_data; 93 + 94 + u32 activity; 95 + struct list_head list; 96 + }; 97 + 98 + static struct calipso_map_cache_bkt *calipso_cache; 99 + 100 + /* Label Mapping Cache Functions 101 + */ 102 + 103 + /** 104 + * calipso_cache_entry_free - Frees a cache entry 105 + * @entry: the entry to free 106 + * 107 + * Description: 108 + * This function frees the memory associated with a cache entry including the 109 + * LSM cache data if there are no longer any users, i.e. reference count == 0. 110 + * 111 + */ 112 + static void calipso_cache_entry_free(struct calipso_map_cache_entry *entry) 113 + { 114 + if (entry->lsm_data) 115 + netlbl_secattr_cache_free(entry->lsm_data); 116 + kfree(entry->key); 117 + kfree(entry); 118 + } 119 + 120 + /** 121 + * calipso_map_cache_hash - Hashing function for the CALIPSO cache 122 + * @key: the hash key 123 + * @key_len: the length of the key in bytes 124 + * 125 + * Description: 126 + * The CALIPSO tag hashing function. Returns a 32-bit hash value. 127 + * 128 + */ 129 + static u32 calipso_map_cache_hash(const unsigned char *key, u32 key_len) 130 + { 131 + return jhash(key, key_len, 0); 132 + } 133 + 134 + /** 135 + * calipso_cache_init - Initialize the CALIPSO cache 136 + * 137 + * Description: 138 + * Initializes the CALIPSO label mapping cache, this function should be called 139 + * before any of the other functions defined in this file. Returns zero on 140 + * success, negative values on error. 141 + * 142 + */ 143 + static int __init calipso_cache_init(void) 144 + { 145 + u32 iter; 146 + 147 + calipso_cache = kcalloc(CALIPSO_CACHE_BUCKETS, 148 + sizeof(struct calipso_map_cache_bkt), 149 + GFP_KERNEL); 150 + if (!calipso_cache) 151 + return -ENOMEM; 152 + 153 + for (iter = 0; iter < CALIPSO_CACHE_BUCKETS; iter++) { 154 + spin_lock_init(&calipso_cache[iter].lock); 155 + calipso_cache[iter].size = 0; 156 + INIT_LIST_HEAD(&calipso_cache[iter].list); 157 + } 158 + 159 + return 0; 160 + } 161 + 162 + /** 163 + * calipso_cache_invalidate - Invalidates the current CALIPSO cache 164 + * 165 + * Description: 166 + * Invalidates and frees any entries in the CALIPSO cache. Returns zero on 167 + * success and negative values on failure. 168 + * 169 + */ 170 + static void calipso_cache_invalidate(void) 171 + { 172 + struct calipso_map_cache_entry *entry, *tmp_entry; 173 + u32 iter; 174 + 175 + for (iter = 0; iter < CALIPSO_CACHE_BUCKETS; iter++) { 176 + spin_lock_bh(&calipso_cache[iter].lock); 177 + list_for_each_entry_safe(entry, 178 + tmp_entry, 179 + &calipso_cache[iter].list, list) { 180 + list_del(&entry->list); 181 + calipso_cache_entry_free(entry); 182 + } 183 + calipso_cache[iter].size = 0; 184 + spin_unlock_bh(&calipso_cache[iter].lock); 185 + } 186 + } 187 + 188 + /** 189 + * calipso_cache_check - Check the CALIPSO cache for a label mapping 190 + * @key: the buffer to check 191 + * @key_len: buffer length in bytes 192 + * @secattr: the security attribute struct to use 193 + * 194 + * Description: 195 + * This function checks the cache to see if a label mapping already exists for 196 + * the given key. If there is a match then the cache is adjusted and the 197 + * @secattr struct is populated with the correct LSM security attributes. The 198 + * cache is adjusted in the following manner if the entry is not already the 199 + * first in the cache bucket: 200 + * 201 + * 1. The cache entry's activity counter is incremented 202 + * 2. The previous (higher ranking) entry's activity counter is decremented 203 + * 3. If the difference between the two activity counters is geater than 204 + * CALIPSO_CACHE_REORDERLIMIT the two entries are swapped 205 + * 206 + * Returns zero on success, -ENOENT for a cache miss, and other negative values 207 + * on error. 208 + * 209 + */ 210 + static int calipso_cache_check(const unsigned char *key, 211 + u32 key_len, 212 + struct netlbl_lsm_secattr *secattr) 213 + { 214 + u32 bkt; 215 + struct calipso_map_cache_entry *entry; 216 + struct calipso_map_cache_entry *prev_entry = NULL; 217 + u32 hash; 218 + 219 + if (!calipso_cache_enabled) 220 + return -ENOENT; 221 + 222 + hash = calipso_map_cache_hash(key, key_len); 223 + bkt = hash & (CALIPSO_CACHE_BUCKETS - 1); 224 + spin_lock_bh(&calipso_cache[bkt].lock); 225 + list_for_each_entry(entry, &calipso_cache[bkt].list, list) { 226 + if (entry->hash == hash && 227 + entry->key_len == key_len && 228 + memcmp(entry->key, key, key_len) == 0) { 229 + entry->activity += 1; 230 + atomic_inc(&entry->lsm_data->refcount); 231 + secattr->cache = entry->lsm_data; 232 + secattr->flags |= NETLBL_SECATTR_CACHE; 233 + secattr->type = NETLBL_NLTYPE_CALIPSO; 234 + if (!prev_entry) { 235 + spin_unlock_bh(&calipso_cache[bkt].lock); 236 + return 0; 237 + } 238 + 239 + if (prev_entry->activity > 0) 240 + prev_entry->activity -= 1; 241 + if (entry->activity > prev_entry->activity && 242 + entry->activity - prev_entry->activity > 243 + CALIPSO_CACHE_REORDERLIMIT) { 244 + __list_del(entry->list.prev, entry->list.next); 245 + __list_add(&entry->list, 246 + prev_entry->list.prev, 247 + &prev_entry->list); 248 + } 249 + 250 + spin_unlock_bh(&calipso_cache[bkt].lock); 251 + return 0; 252 + } 253 + prev_entry = entry; 254 + } 255 + spin_unlock_bh(&calipso_cache[bkt].lock); 256 + 257 + return -ENOENT; 258 + } 259 + 260 + /** 261 + * calipso_cache_add - Add an entry to the CALIPSO cache 262 + * @calipso_ptr: the CALIPSO option 263 + * @secattr: the packet's security attributes 264 + * 265 + * Description: 266 + * Add a new entry into the CALIPSO label mapping cache. Add the new entry to 267 + * head of the cache bucket's list, if the cache bucket is out of room remove 268 + * the last entry in the list first. It is important to note that there is 269 + * currently no checking for duplicate keys. Returns zero on success, 270 + * negative values on failure. The key stored starts at calipso_ptr + 2, 271 + * i.e. the type and length bytes are not stored, this corresponds to 272 + * calipso_ptr[1] bytes of data. 273 + * 274 + */ 275 + static int calipso_cache_add(const unsigned char *calipso_ptr, 276 + const struct netlbl_lsm_secattr *secattr) 277 + { 278 + int ret_val = -EPERM; 279 + u32 bkt; 280 + struct calipso_map_cache_entry *entry = NULL; 281 + struct calipso_map_cache_entry *old_entry = NULL; 282 + u32 calipso_ptr_len; 283 + 284 + if (!calipso_cache_enabled || calipso_cache_bucketsize <= 0) 285 + return 0; 286 + 287 + calipso_ptr_len = calipso_ptr[1]; 288 + 289 + entry = kzalloc(sizeof(*entry), GFP_ATOMIC); 290 + if (!entry) 291 + return -ENOMEM; 292 + entry->key = kmemdup(calipso_ptr + 2, calipso_ptr_len, GFP_ATOMIC); 293 + if (!entry->key) { 294 + ret_val = -ENOMEM; 295 + goto cache_add_failure; 296 + } 297 + entry->key_len = calipso_ptr_len; 298 + entry->hash = calipso_map_cache_hash(calipso_ptr, calipso_ptr_len); 299 + atomic_inc(&secattr->cache->refcount); 300 + entry->lsm_data = secattr->cache; 301 + 302 + bkt = entry->hash & (CALIPSO_CACHE_BUCKETS - 1); 303 + spin_lock_bh(&calipso_cache[bkt].lock); 304 + if (calipso_cache[bkt].size < calipso_cache_bucketsize) { 305 + list_add(&entry->list, &calipso_cache[bkt].list); 306 + calipso_cache[bkt].size += 1; 307 + } else { 308 + old_entry = list_entry(calipso_cache[bkt].list.prev, 309 + struct calipso_map_cache_entry, list); 310 + list_del(&old_entry->list); 311 + list_add(&entry->list, &calipso_cache[bkt].list); 312 + calipso_cache_entry_free(old_entry); 313 + } 314 + spin_unlock_bh(&calipso_cache[bkt].lock); 315 + 316 + return 0; 317 + 318 + cache_add_failure: 319 + if (entry) 320 + calipso_cache_entry_free(entry); 321 + return ret_val; 322 + } 323 + 75 324 /* DOI List Functions 76 325 */ 77 326 ··· 1038 789 if (cat_len + 8 > len) 1039 790 return -EINVAL; 1040 791 792 + if (calipso_cache_check(calipso + 2, calipso[1], secattr) == 0) 793 + return 0; 794 + 1041 795 doi = get_unaligned_be32(calipso + 2); 1042 796 rcu_read_lock(); 1043 797 doi_def = calipso_doi_search(doi); ··· 1443 1191 .skbuff_optptr = calipso_skbuff_optptr, 1444 1192 .skbuff_setattr = calipso_skbuff_setattr, 1445 1193 .skbuff_delattr = calipso_skbuff_delattr, 1194 + .cache_invalidate = calipso_cache_invalidate, 1195 + .cache_add = calipso_cache_add 1446 1196 }; 1447 1197 1448 1198 /** ··· 1457 1203 */ 1458 1204 int __init calipso_init(void) 1459 1205 { 1460 - netlbl_calipso_ops_register(&ops); 1461 - return 0; 1206 + int ret_val; 1207 + 1208 + ret_val = calipso_cache_init(); 1209 + if (!ret_val) 1210 + netlbl_calipso_ops_register(&ops); 1211 + return ret_val; 1462 1212 } 1463 1213 1464 1214 void calipso_exit(void) 1465 1215 { 1466 1216 netlbl_calipso_ops_register(NULL); 1217 + calipso_cache_invalidate(); 1218 + kfree(calipso_cache); 1467 1219 }
+19
net/ipv6/sysctl_net_ipv6.c
··· 15 15 #include <net/ipv6.h> 16 16 #include <net/addrconf.h> 17 17 #include <net/inet_frag.h> 18 + #ifdef CONFIG_NETLABEL 19 + #include <net/calipso.h> 20 + #endif 18 21 19 22 static int one = 1; 20 23 static int auto_flowlabels_min; ··· 109 106 .proc_handler = proc_dointvec_minmax, 110 107 .extra1 = &one 111 108 }, 109 + #ifdef CONFIG_NETLABEL 110 + { 111 + .procname = "calipso_cache_enable", 112 + .data = &calipso_cache_enabled, 113 + .maxlen = sizeof(int), 114 + .mode = 0644, 115 + .proc_handler = proc_dointvec, 116 + }, 117 + { 118 + .procname = "calipso_cache_bucket_size", 119 + .data = &calipso_cache_bucketsize, 120 + .maxlen = sizeof(int), 121 + .mode = 0644, 122 + .proc_handler = proc_dointvec, 123 + }, 124 + #endif /* CONFIG_NETLABEL */ 112 125 { } 113 126 }; 114 127
+38
net/netlabel/netlabel_calipso.c
··· 700 700 ret_val = ops->skbuff_delattr(skb); 701 701 return ret_val; 702 702 } 703 + 704 + /** 705 + * calipso_cache_invalidate - Invalidates the current CALIPSO cache 706 + * 707 + * Description: 708 + * Invalidates and frees any entries in the CALIPSO cache. Returns zero on 709 + * success and negative values on failure. 710 + * 711 + */ 712 + void calipso_cache_invalidate(void) 713 + { 714 + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); 715 + 716 + if (ops) 717 + ops->cache_invalidate(); 718 + } 719 + 720 + /** 721 + * calipso_cache_add - Add an entry to the CALIPSO cache 722 + * @calipso_ptr: the CALIPSO option 723 + * @secattr: the packet's security attributes 724 + * 725 + * Description: 726 + * Add a new entry into the CALIPSO label mapping cache. 727 + * Returns zero on success, negative values on failure. 728 + * 729 + */ 730 + int calipso_cache_add(const unsigned char *calipso_ptr, 731 + const struct netlbl_lsm_secattr *secattr) 732 + 733 + { 734 + int ret_val = -ENOMSG; 735 + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); 736 + 737 + if (ops) 738 + ret_val = ops->cache_add(calipso_ptr, secattr); 739 + return ret_val; 740 + }
+3
net/netlabel/netlabel_calipso.h
··· 144 144 const struct calipso_doi *doi_def, 145 145 const struct netlbl_lsm_secattr *secattr); 146 146 int calipso_skbuff_delattr(struct sk_buff *skb); 147 + void calipso_cache_invalidate(void); 148 + int calipso_cache_add(const unsigned char *calipso_ptr, 149 + const struct netlbl_lsm_secattr *secattr); 147 150 148 151 #endif
+19 -5
net/netlabel/netlabel_kapi.c
··· 1281 1281 void netlbl_cache_invalidate(void) 1282 1282 { 1283 1283 cipso_v4_cache_invalidate(); 1284 + #if IS_ENABLED(CONFIG_IPV6) 1285 + calipso_cache_invalidate(); 1286 + #endif /* IPv6 */ 1284 1287 } 1285 1288 1286 1289 /** 1287 1290 * netlbl_cache_add - Add an entry to a NetLabel protocol cache 1288 1291 * @skb: the packet 1292 + * @family: the family 1289 1293 * @secattr: the packet's security attributes 1290 1294 * 1291 1295 * Description: ··· 1298 1294 * values on error. 1299 1295 * 1300 1296 */ 1301 - int netlbl_cache_add(const struct sk_buff *skb, 1297 + int netlbl_cache_add(const struct sk_buff *skb, u16 family, 1302 1298 const struct netlbl_lsm_secattr *secattr) 1303 1299 { 1304 1300 unsigned char *ptr; ··· 1306 1302 if ((secattr->flags & NETLBL_SECATTR_CACHE) == 0) 1307 1303 return -ENOMSG; 1308 1304 1309 - ptr = cipso_v4_optptr(skb); 1310 - if (ptr) 1311 - return cipso_v4_cache_add(ptr, secattr); 1312 - 1305 + switch (family) { 1306 + case AF_INET: 1307 + ptr = cipso_v4_optptr(skb); 1308 + if (ptr) 1309 + return cipso_v4_cache_add(ptr, secattr); 1310 + break; 1311 + #if IS_ENABLED(CONFIG_IPV6) 1312 + case AF_INET6: 1313 + ptr = calipso_optptr(skb); 1314 + if (ptr) 1315 + return calipso_cache_add(ptr, secattr); 1316 + break; 1317 + #endif /* IPv6 */ 1318 + } 1313 1319 return -ENOMSG; 1314 1320 } 1315 1321
+6 -3
security/selinux/netlabel.c
··· 54 54 * 55 55 */ 56 56 static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb, 57 + u16 family, 57 58 struct netlbl_lsm_secattr *secattr, 58 59 u32 *sid) 59 60 { ··· 64 63 if (rc == 0 && 65 64 (secattr->flags & NETLBL_SECATTR_CACHEABLE) && 66 65 (secattr->flags & NETLBL_SECATTR_CACHE)) 67 - netlbl_cache_add(skb, secattr); 66 + netlbl_cache_add(skb, family, secattr); 68 67 69 68 return rc; 70 69 } ··· 215 214 netlbl_secattr_init(&secattr); 216 215 rc = netlbl_skbuff_getattr(skb, family, &secattr); 217 216 if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) 218 - rc = selinux_netlbl_sidlookup_cached(skb, &secattr, sid); 217 + rc = selinux_netlbl_sidlookup_cached(skb, family, 218 + &secattr, sid); 219 219 else 220 220 *sid = SECSID_NULL; 221 221 *type = secattr.type; ··· 384 382 netlbl_secattr_init(&secattr); 385 383 rc = netlbl_skbuff_getattr(skb, family, &secattr); 386 384 if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) 387 - rc = selinux_netlbl_sidlookup_cached(skb, &secattr, &nlbl_sid); 385 + rc = selinux_netlbl_sidlookup_cached(skb, family, 386 + &secattr, &nlbl_sid); 388 387 else 389 388 nlbl_sid = SECINITSID_UNLABELED; 390 389 netlbl_secattr_destroy(&secattr);