1446fda4fSPaul Moore /* 2446fda4fSPaul Moore * CIPSO - Commercial IP Security Option 3446fda4fSPaul Moore * 4446fda4fSPaul Moore * This is an implementation of the CIPSO 2.2 protocol as specified in 5446fda4fSPaul Moore * draft-ietf-cipso-ipsecurity-01.txt with additional tag types as found in 6446fda4fSPaul Moore * FIPS-188, copies of both documents can be found in the Documentation 7446fda4fSPaul Moore * directory. While CIPSO never became a full IETF RFC standard many vendors 8446fda4fSPaul Moore * have chosen to adopt the protocol and over the years it has become a 9446fda4fSPaul Moore * de-facto standard for labeled networking. 10446fda4fSPaul Moore * 11446fda4fSPaul Moore * Author: Paul Moore <paul.moore@hp.com> 12446fda4fSPaul Moore * 13446fda4fSPaul Moore */ 14446fda4fSPaul Moore 15446fda4fSPaul Moore /* 16446fda4fSPaul Moore * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 17446fda4fSPaul Moore * 18446fda4fSPaul Moore * This program is free software; you can redistribute it and/or modify 19446fda4fSPaul Moore * it under the terms of the GNU General Public License as published by 20446fda4fSPaul Moore * the Free Software Foundation; either version 2 of the License, or 21446fda4fSPaul Moore * (at your option) any later version. 22446fda4fSPaul Moore * 23446fda4fSPaul Moore * This program is distributed in the hope that it will be useful, 24446fda4fSPaul Moore * but WITHOUT ANY WARRANTY; without even the implied warranty of 25446fda4fSPaul Moore * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 26446fda4fSPaul Moore * the GNU General Public License for more details. 27446fda4fSPaul Moore * 28446fda4fSPaul Moore * You should have received a copy of the GNU General Public License 29446fda4fSPaul Moore * along with this program; if not, write to the Free Software 30446fda4fSPaul Moore * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 31446fda4fSPaul Moore * 32446fda4fSPaul Moore */ 33446fda4fSPaul Moore 34446fda4fSPaul Moore #include <linux/init.h> 35446fda4fSPaul Moore #include <linux/types.h> 36446fda4fSPaul Moore #include <linux/rcupdate.h> 37446fda4fSPaul Moore #include <linux/list.h> 38446fda4fSPaul Moore #include <linux/spinlock.h> 39446fda4fSPaul Moore #include <linux/string.h> 40446fda4fSPaul Moore #include <linux/jhash.h> 41446fda4fSPaul Moore #include <net/ip.h> 42446fda4fSPaul Moore #include <net/icmp.h> 43446fda4fSPaul Moore #include <net/tcp.h> 44446fda4fSPaul Moore #include <net/netlabel.h> 45446fda4fSPaul Moore #include <net/cipso_ipv4.h> 46ffb733c6Spaul.moore@hp.com #include <asm/atomic.h> 47446fda4fSPaul Moore #include <asm/bug.h> 48446fda4fSPaul Moore 49446fda4fSPaul Moore struct cipso_v4_domhsh_entry { 50446fda4fSPaul Moore char *domain; 51446fda4fSPaul Moore u32 valid; 52446fda4fSPaul Moore struct list_head list; 53446fda4fSPaul Moore struct rcu_head rcu; 54446fda4fSPaul Moore }; 55446fda4fSPaul Moore 56446fda4fSPaul Moore /* List of available DOI definitions */ 57446fda4fSPaul Moore /* XXX - Updates should be minimal so having a single lock for the 58446fda4fSPaul Moore * cipso_v4_doi_list and the cipso_v4_doi_list->dom_list should be 59446fda4fSPaul Moore * okay. */ 60446fda4fSPaul Moore /* XXX - This currently assumes a minimal number of different DOIs in use, 61446fda4fSPaul Moore * if in practice there are a lot of different DOIs this list should 62446fda4fSPaul Moore * probably be turned into a hash table or something similar so we 63446fda4fSPaul Moore * can do quick lookups. */ 648ce11e6aSAdrian Bunk static DEFINE_SPINLOCK(cipso_v4_doi_list_lock); 65446fda4fSPaul Moore static struct list_head cipso_v4_doi_list = LIST_HEAD_INIT(cipso_v4_doi_list); 66446fda4fSPaul Moore 67446fda4fSPaul Moore /* Label mapping cache */ 68446fda4fSPaul Moore int cipso_v4_cache_enabled = 1; 69446fda4fSPaul Moore int cipso_v4_cache_bucketsize = 10; 70446fda4fSPaul Moore #define CIPSO_V4_CACHE_BUCKETBITS 7 71446fda4fSPaul Moore #define CIPSO_V4_CACHE_BUCKETS (1 << CIPSO_V4_CACHE_BUCKETBITS) 72446fda4fSPaul Moore #define CIPSO_V4_CACHE_REORDERLIMIT 10 73446fda4fSPaul Moore struct cipso_v4_map_cache_bkt { 74446fda4fSPaul Moore spinlock_t lock; 75446fda4fSPaul Moore u32 size; 76446fda4fSPaul Moore struct list_head list; 77446fda4fSPaul Moore }; 78446fda4fSPaul Moore struct cipso_v4_map_cache_entry { 79446fda4fSPaul Moore u32 hash; 80446fda4fSPaul Moore unsigned char *key; 81446fda4fSPaul Moore size_t key_len; 82446fda4fSPaul Moore 83ffb733c6Spaul.moore@hp.com struct netlbl_lsm_cache *lsm_data; 84446fda4fSPaul Moore 85446fda4fSPaul Moore u32 activity; 86446fda4fSPaul Moore struct list_head list; 87446fda4fSPaul Moore }; 88446fda4fSPaul Moore static struct cipso_v4_map_cache_bkt *cipso_v4_cache = NULL; 89446fda4fSPaul Moore 90446fda4fSPaul Moore /* Restricted bitmap (tag #1) flags */ 91446fda4fSPaul Moore int cipso_v4_rbm_optfmt = 0; 92446fda4fSPaul Moore int cipso_v4_rbm_strictvalid = 1; 93446fda4fSPaul Moore 94446fda4fSPaul Moore /* 95446fda4fSPaul Moore * Helper Functions 96446fda4fSPaul Moore */ 97446fda4fSPaul Moore 98446fda4fSPaul Moore /** 99446fda4fSPaul Moore * cipso_v4_bitmap_walk - Walk a bitmap looking for a bit 100446fda4fSPaul Moore * @bitmap: the bitmap 101446fda4fSPaul Moore * @bitmap_len: length in bits 102446fda4fSPaul Moore * @offset: starting offset 103446fda4fSPaul Moore * @state: if non-zero, look for a set (1) bit else look for a cleared (0) bit 104446fda4fSPaul Moore * 105446fda4fSPaul Moore * Description: 106446fda4fSPaul Moore * Starting at @offset, walk the bitmap from left to right until either the 107446fda4fSPaul Moore * desired bit is found or we reach the end. Return the bit offset, -1 if 108446fda4fSPaul Moore * not found, or -2 if error. 109446fda4fSPaul Moore */ 110446fda4fSPaul Moore static int cipso_v4_bitmap_walk(const unsigned char *bitmap, 111446fda4fSPaul Moore u32 bitmap_len, 112446fda4fSPaul Moore u32 offset, 113446fda4fSPaul Moore u8 state) 114446fda4fSPaul Moore { 115446fda4fSPaul Moore u32 bit_spot; 116446fda4fSPaul Moore u32 byte_offset; 117446fda4fSPaul Moore unsigned char bitmask; 118446fda4fSPaul Moore unsigned char byte; 119446fda4fSPaul Moore 120446fda4fSPaul Moore /* gcc always rounds to zero when doing integer division */ 121446fda4fSPaul Moore byte_offset = offset / 8; 122446fda4fSPaul Moore byte = bitmap[byte_offset]; 123446fda4fSPaul Moore bit_spot = offset; 124446fda4fSPaul Moore bitmask = 0x80 >> (offset % 8); 125446fda4fSPaul Moore 126446fda4fSPaul Moore while (bit_spot < bitmap_len) { 127446fda4fSPaul Moore if ((state && (byte & bitmask) == bitmask) || 128446fda4fSPaul Moore (state == 0 && (byte & bitmask) == 0)) 129446fda4fSPaul Moore return bit_spot; 130446fda4fSPaul Moore 131446fda4fSPaul Moore bit_spot++; 132446fda4fSPaul Moore bitmask >>= 1; 133446fda4fSPaul Moore if (bitmask == 0) { 134446fda4fSPaul Moore byte = bitmap[++byte_offset]; 135446fda4fSPaul Moore bitmask = 0x80; 136446fda4fSPaul Moore } 137446fda4fSPaul Moore } 138446fda4fSPaul Moore 139446fda4fSPaul Moore return -1; 140446fda4fSPaul Moore } 141446fda4fSPaul Moore 142446fda4fSPaul Moore /** 143446fda4fSPaul Moore * cipso_v4_bitmap_setbit - Sets a single bit in a bitmap 144446fda4fSPaul Moore * @bitmap: the bitmap 145446fda4fSPaul Moore * @bit: the bit 146446fda4fSPaul Moore * @state: if non-zero, set the bit (1) else clear the bit (0) 147446fda4fSPaul Moore * 148446fda4fSPaul Moore * Description: 149446fda4fSPaul Moore * Set a single bit in the bitmask. Returns zero on success, negative values 150446fda4fSPaul Moore * on error. 151446fda4fSPaul Moore */ 152446fda4fSPaul Moore static void cipso_v4_bitmap_setbit(unsigned char *bitmap, 153446fda4fSPaul Moore u32 bit, 154446fda4fSPaul Moore u8 state) 155446fda4fSPaul Moore { 156446fda4fSPaul Moore u32 byte_spot; 157446fda4fSPaul Moore u8 bitmask; 158446fda4fSPaul Moore 159446fda4fSPaul Moore /* gcc always rounds to zero when doing integer division */ 160446fda4fSPaul Moore byte_spot = bit / 8; 161446fda4fSPaul Moore bitmask = 0x80 >> (bit % 8); 162446fda4fSPaul Moore if (state) 163446fda4fSPaul Moore bitmap[byte_spot] |= bitmask; 164446fda4fSPaul Moore else 165446fda4fSPaul Moore bitmap[byte_spot] &= ~bitmask; 166446fda4fSPaul Moore } 167446fda4fSPaul Moore 168446fda4fSPaul Moore /** 169446fda4fSPaul Moore * cipso_v4_doi_domhsh_free - Frees a domain list entry 170446fda4fSPaul Moore * @entry: the entry's RCU field 171446fda4fSPaul Moore * 172446fda4fSPaul Moore * Description: 173446fda4fSPaul Moore * This function is designed to be used as a callback to the call_rcu() 174446fda4fSPaul Moore * function so that the memory allocated to a domain list entry can be released 175446fda4fSPaul Moore * safely. 176446fda4fSPaul Moore * 177446fda4fSPaul Moore */ 178446fda4fSPaul Moore static void cipso_v4_doi_domhsh_free(struct rcu_head *entry) 179446fda4fSPaul Moore { 180446fda4fSPaul Moore struct cipso_v4_domhsh_entry *ptr; 181446fda4fSPaul Moore 182446fda4fSPaul Moore ptr = container_of(entry, struct cipso_v4_domhsh_entry, rcu); 183446fda4fSPaul Moore kfree(ptr->domain); 184446fda4fSPaul Moore kfree(ptr); 185446fda4fSPaul Moore } 186446fda4fSPaul Moore 187446fda4fSPaul Moore /** 188446fda4fSPaul Moore * cipso_v4_cache_entry_free - Frees a cache entry 189446fda4fSPaul Moore * @entry: the entry to free 190446fda4fSPaul Moore * 191446fda4fSPaul Moore * Description: 192ffb733c6Spaul.moore@hp.com * This function frees the memory associated with a cache entry including the 193ffb733c6Spaul.moore@hp.com * LSM cache data if there are no longer any users, i.e. reference count == 0. 194446fda4fSPaul Moore * 195446fda4fSPaul Moore */ 196446fda4fSPaul Moore static void cipso_v4_cache_entry_free(struct cipso_v4_map_cache_entry *entry) 197446fda4fSPaul Moore { 198ffb733c6Spaul.moore@hp.com if (entry->lsm_data) 199ffb733c6Spaul.moore@hp.com netlbl_secattr_cache_free(entry->lsm_data); 200446fda4fSPaul Moore kfree(entry->key); 201446fda4fSPaul Moore kfree(entry); 202446fda4fSPaul Moore } 203446fda4fSPaul Moore 204446fda4fSPaul Moore /** 205446fda4fSPaul Moore * cipso_v4_map_cache_hash - Hashing function for the CIPSO cache 206446fda4fSPaul Moore * @key: the hash key 207446fda4fSPaul Moore * @key_len: the length of the key in bytes 208446fda4fSPaul Moore * 209446fda4fSPaul Moore * Description: 210446fda4fSPaul Moore * The CIPSO tag hashing function. Returns a 32-bit hash value. 211446fda4fSPaul Moore * 212446fda4fSPaul Moore */ 213446fda4fSPaul Moore static u32 cipso_v4_map_cache_hash(const unsigned char *key, u32 key_len) 214446fda4fSPaul Moore { 215446fda4fSPaul Moore return jhash(key, key_len, 0); 216446fda4fSPaul Moore } 217446fda4fSPaul Moore 218446fda4fSPaul Moore /* 219446fda4fSPaul Moore * Label Mapping Cache Functions 220446fda4fSPaul Moore */ 221446fda4fSPaul Moore 222446fda4fSPaul Moore /** 223446fda4fSPaul Moore * cipso_v4_cache_init - Initialize the CIPSO cache 224446fda4fSPaul Moore * 225446fda4fSPaul Moore * Description: 226446fda4fSPaul Moore * Initializes the CIPSO label mapping cache, this function should be called 227446fda4fSPaul Moore * before any of the other functions defined in this file. Returns zero on 228446fda4fSPaul Moore * success, negative values on error. 229446fda4fSPaul Moore * 230446fda4fSPaul Moore */ 231446fda4fSPaul Moore static int cipso_v4_cache_init(void) 232446fda4fSPaul Moore { 233446fda4fSPaul Moore u32 iter; 234446fda4fSPaul Moore 235446fda4fSPaul Moore cipso_v4_cache = kcalloc(CIPSO_V4_CACHE_BUCKETS, 236446fda4fSPaul Moore sizeof(struct cipso_v4_map_cache_bkt), 237446fda4fSPaul Moore GFP_KERNEL); 238446fda4fSPaul Moore if (cipso_v4_cache == NULL) 239446fda4fSPaul Moore return -ENOMEM; 240446fda4fSPaul Moore 241446fda4fSPaul Moore for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) { 242446fda4fSPaul Moore spin_lock_init(&cipso_v4_cache[iter].lock); 243446fda4fSPaul Moore cipso_v4_cache[iter].size = 0; 244446fda4fSPaul Moore INIT_LIST_HEAD(&cipso_v4_cache[iter].list); 245446fda4fSPaul Moore } 246446fda4fSPaul Moore 247446fda4fSPaul Moore return 0; 248446fda4fSPaul Moore } 249446fda4fSPaul Moore 250446fda4fSPaul Moore /** 251446fda4fSPaul Moore * cipso_v4_cache_invalidate - Invalidates the current CIPSO cache 252446fda4fSPaul Moore * 253446fda4fSPaul Moore * Description: 254446fda4fSPaul Moore * Invalidates and frees any entries in the CIPSO cache. Returns zero on 255446fda4fSPaul Moore * success and negative values on failure. 256446fda4fSPaul Moore * 257446fda4fSPaul Moore */ 258446fda4fSPaul Moore void cipso_v4_cache_invalidate(void) 259446fda4fSPaul Moore { 260446fda4fSPaul Moore struct cipso_v4_map_cache_entry *entry, *tmp_entry; 261446fda4fSPaul Moore u32 iter; 262446fda4fSPaul Moore 263446fda4fSPaul Moore for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) { 264609c92feSPaul Moore spin_lock_bh(&cipso_v4_cache[iter].lock); 265446fda4fSPaul Moore list_for_each_entry_safe(entry, 266446fda4fSPaul Moore tmp_entry, 267446fda4fSPaul Moore &cipso_v4_cache[iter].list, list) { 268446fda4fSPaul Moore list_del(&entry->list); 269446fda4fSPaul Moore cipso_v4_cache_entry_free(entry); 270446fda4fSPaul Moore } 271446fda4fSPaul Moore cipso_v4_cache[iter].size = 0; 272609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[iter].lock); 273446fda4fSPaul Moore } 274446fda4fSPaul Moore 275446fda4fSPaul Moore return; 276446fda4fSPaul Moore } 277446fda4fSPaul Moore 278446fda4fSPaul Moore /** 279446fda4fSPaul Moore * cipso_v4_cache_check - Check the CIPSO cache for a label mapping 280446fda4fSPaul Moore * @key: the buffer to check 281446fda4fSPaul Moore * @key_len: buffer length in bytes 282446fda4fSPaul Moore * @secattr: the security attribute struct to use 283446fda4fSPaul Moore * 284446fda4fSPaul Moore * Description: 285446fda4fSPaul Moore * This function checks the cache to see if a label mapping already exists for 286446fda4fSPaul Moore * the given key. If there is a match then the cache is adjusted and the 287446fda4fSPaul Moore * @secattr struct is populated with the correct LSM security attributes. The 288446fda4fSPaul Moore * cache is adjusted in the following manner if the entry is not already the 289446fda4fSPaul Moore * first in the cache bucket: 290446fda4fSPaul Moore * 291446fda4fSPaul Moore * 1. The cache entry's activity counter is incremented 292446fda4fSPaul Moore * 2. The previous (higher ranking) entry's activity counter is decremented 293446fda4fSPaul Moore * 3. If the difference between the two activity counters is geater than 294446fda4fSPaul Moore * CIPSO_V4_CACHE_REORDERLIMIT the two entries are swapped 295446fda4fSPaul Moore * 296446fda4fSPaul Moore * Returns zero on success, -ENOENT for a cache miss, and other negative values 297446fda4fSPaul Moore * on error. 298446fda4fSPaul Moore * 299446fda4fSPaul Moore */ 300446fda4fSPaul Moore static int cipso_v4_cache_check(const unsigned char *key, 301446fda4fSPaul Moore u32 key_len, 302446fda4fSPaul Moore struct netlbl_lsm_secattr *secattr) 303446fda4fSPaul Moore { 304446fda4fSPaul Moore u32 bkt; 305446fda4fSPaul Moore struct cipso_v4_map_cache_entry *entry; 306446fda4fSPaul Moore struct cipso_v4_map_cache_entry *prev_entry = NULL; 307446fda4fSPaul Moore u32 hash; 308446fda4fSPaul Moore 309446fda4fSPaul Moore if (!cipso_v4_cache_enabled) 310446fda4fSPaul Moore return -ENOENT; 311446fda4fSPaul Moore 312446fda4fSPaul Moore hash = cipso_v4_map_cache_hash(key, key_len); 313446fda4fSPaul Moore bkt = hash & (CIPSO_V4_CACHE_BUCKETBITS - 1); 314609c92feSPaul Moore spin_lock_bh(&cipso_v4_cache[bkt].lock); 315446fda4fSPaul Moore list_for_each_entry(entry, &cipso_v4_cache[bkt].list, list) { 316446fda4fSPaul Moore if (entry->hash == hash && 317446fda4fSPaul Moore entry->key_len == key_len && 318446fda4fSPaul Moore memcmp(entry->key, key, key_len) == 0) { 319446fda4fSPaul Moore entry->activity += 1; 320ffb733c6Spaul.moore@hp.com atomic_inc(&entry->lsm_data->refcount); 321ffb733c6Spaul.moore@hp.com secattr->cache = entry->lsm_data; 322701a90baSPaul Moore secattr->flags |= NETLBL_SECATTR_CACHE; 323446fda4fSPaul Moore if (prev_entry == NULL) { 324609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[bkt].lock); 325446fda4fSPaul Moore return 0; 326446fda4fSPaul Moore } 327446fda4fSPaul Moore 328446fda4fSPaul Moore if (prev_entry->activity > 0) 329446fda4fSPaul Moore prev_entry->activity -= 1; 330446fda4fSPaul Moore if (entry->activity > prev_entry->activity && 331446fda4fSPaul Moore entry->activity - prev_entry->activity > 332446fda4fSPaul Moore CIPSO_V4_CACHE_REORDERLIMIT) { 333446fda4fSPaul Moore __list_del(entry->list.prev, entry->list.next); 334446fda4fSPaul Moore __list_add(&entry->list, 335446fda4fSPaul Moore prev_entry->list.prev, 336446fda4fSPaul Moore &prev_entry->list); 337446fda4fSPaul Moore } 338446fda4fSPaul Moore 339609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[bkt].lock); 340446fda4fSPaul Moore return 0; 341446fda4fSPaul Moore } 342446fda4fSPaul Moore prev_entry = entry; 343446fda4fSPaul Moore } 344609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[bkt].lock); 345446fda4fSPaul Moore 346446fda4fSPaul Moore return -ENOENT; 347446fda4fSPaul Moore } 348446fda4fSPaul Moore 349446fda4fSPaul Moore /** 350446fda4fSPaul Moore * cipso_v4_cache_add - Add an entry to the CIPSO cache 351446fda4fSPaul Moore * @skb: the packet 352446fda4fSPaul Moore * @secattr: the packet's security attributes 353446fda4fSPaul Moore * 354446fda4fSPaul Moore * Description: 355446fda4fSPaul Moore * Add a new entry into the CIPSO label mapping cache. Add the new entry to 356446fda4fSPaul Moore * head of the cache bucket's list, if the cache bucket is out of room remove 357446fda4fSPaul Moore * the last entry in the list first. It is important to note that there is 358446fda4fSPaul Moore * currently no checking for duplicate keys. Returns zero on success, 359446fda4fSPaul Moore * negative values on failure. 360446fda4fSPaul Moore * 361446fda4fSPaul Moore */ 362446fda4fSPaul Moore int cipso_v4_cache_add(const struct sk_buff *skb, 363446fda4fSPaul Moore const struct netlbl_lsm_secattr *secattr) 364446fda4fSPaul Moore { 365446fda4fSPaul Moore int ret_val = -EPERM; 366446fda4fSPaul Moore u32 bkt; 367446fda4fSPaul Moore struct cipso_v4_map_cache_entry *entry = NULL; 368446fda4fSPaul Moore struct cipso_v4_map_cache_entry *old_entry = NULL; 369446fda4fSPaul Moore unsigned char *cipso_ptr; 370446fda4fSPaul Moore u32 cipso_ptr_len; 371446fda4fSPaul Moore 372446fda4fSPaul Moore if (!cipso_v4_cache_enabled || cipso_v4_cache_bucketsize <= 0) 373446fda4fSPaul Moore return 0; 374446fda4fSPaul Moore 375446fda4fSPaul Moore cipso_ptr = CIPSO_V4_OPTPTR(skb); 376446fda4fSPaul Moore cipso_ptr_len = cipso_ptr[1]; 377446fda4fSPaul Moore 378446fda4fSPaul Moore entry = kzalloc(sizeof(*entry), GFP_ATOMIC); 379446fda4fSPaul Moore if (entry == NULL) 380446fda4fSPaul Moore return -ENOMEM; 381fac5d731SArnaldo Carvalho de Melo entry->key = kmemdup(cipso_ptr, cipso_ptr_len, GFP_ATOMIC); 382446fda4fSPaul Moore if (entry->key == NULL) { 383446fda4fSPaul Moore ret_val = -ENOMEM; 384446fda4fSPaul Moore goto cache_add_failure; 385446fda4fSPaul Moore } 386446fda4fSPaul Moore entry->key_len = cipso_ptr_len; 387446fda4fSPaul Moore entry->hash = cipso_v4_map_cache_hash(cipso_ptr, cipso_ptr_len); 388ffb733c6Spaul.moore@hp.com atomic_inc(&secattr->cache->refcount); 389ffb733c6Spaul.moore@hp.com entry->lsm_data = secattr->cache; 390446fda4fSPaul Moore 391446fda4fSPaul Moore bkt = entry->hash & (CIPSO_V4_CACHE_BUCKETBITS - 1); 392609c92feSPaul Moore spin_lock_bh(&cipso_v4_cache[bkt].lock); 393446fda4fSPaul Moore if (cipso_v4_cache[bkt].size < cipso_v4_cache_bucketsize) { 394446fda4fSPaul Moore list_add(&entry->list, &cipso_v4_cache[bkt].list); 395446fda4fSPaul Moore cipso_v4_cache[bkt].size += 1; 396446fda4fSPaul Moore } else { 397446fda4fSPaul Moore old_entry = list_entry(cipso_v4_cache[bkt].list.prev, 398446fda4fSPaul Moore struct cipso_v4_map_cache_entry, list); 399446fda4fSPaul Moore list_del(&old_entry->list); 400446fda4fSPaul Moore list_add(&entry->list, &cipso_v4_cache[bkt].list); 401446fda4fSPaul Moore cipso_v4_cache_entry_free(old_entry); 402446fda4fSPaul Moore } 403609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[bkt].lock); 404446fda4fSPaul Moore 405446fda4fSPaul Moore return 0; 406446fda4fSPaul Moore 407446fda4fSPaul Moore cache_add_failure: 408446fda4fSPaul Moore if (entry) 409446fda4fSPaul Moore cipso_v4_cache_entry_free(entry); 410446fda4fSPaul Moore return ret_val; 411446fda4fSPaul Moore } 412446fda4fSPaul Moore 413446fda4fSPaul Moore /* 414446fda4fSPaul Moore * DOI List Functions 415446fda4fSPaul Moore */ 416446fda4fSPaul Moore 417446fda4fSPaul Moore /** 418446fda4fSPaul Moore * cipso_v4_doi_search - Searches for a DOI definition 419446fda4fSPaul Moore * @doi: the DOI to search for 420446fda4fSPaul Moore * 421446fda4fSPaul Moore * Description: 422446fda4fSPaul Moore * Search the DOI definition list for a DOI definition with a DOI value that 423446fda4fSPaul Moore * matches @doi. The caller is responsibile for calling rcu_read_[un]lock(). 424446fda4fSPaul Moore * Returns a pointer to the DOI definition on success and NULL on failure. 425446fda4fSPaul Moore */ 426446fda4fSPaul Moore static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi) 427446fda4fSPaul Moore { 428446fda4fSPaul Moore struct cipso_v4_doi *iter; 429446fda4fSPaul Moore 430446fda4fSPaul Moore list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list) 431446fda4fSPaul Moore if (iter->doi == doi && iter->valid) 432446fda4fSPaul Moore return iter; 433446fda4fSPaul Moore return NULL; 434446fda4fSPaul Moore } 435446fda4fSPaul Moore 436446fda4fSPaul Moore /** 437446fda4fSPaul Moore * cipso_v4_doi_add - Add a new DOI to the CIPSO protocol engine 438446fda4fSPaul Moore * @doi_def: the DOI structure 439446fda4fSPaul Moore * 440446fda4fSPaul Moore * Description: 441446fda4fSPaul Moore * The caller defines a new DOI for use by the CIPSO engine and calls this 442446fda4fSPaul Moore * function to add it to the list of acceptable domains. The caller must 443446fda4fSPaul Moore * ensure that the mapping table specified in @doi_def->map meets all of the 444446fda4fSPaul Moore * requirements of the mapping type (see cipso_ipv4.h for details). Returns 445446fda4fSPaul Moore * zero on success and non-zero on failure. 446446fda4fSPaul Moore * 447446fda4fSPaul Moore */ 448446fda4fSPaul Moore int cipso_v4_doi_add(struct cipso_v4_doi *doi_def) 449446fda4fSPaul Moore { 4506ce61a7cSPaul Moore u32 iter; 4516ce61a7cSPaul Moore 452446fda4fSPaul Moore if (doi_def == NULL || doi_def->doi == CIPSO_V4_DOI_UNKNOWN) 453446fda4fSPaul Moore return -EINVAL; 4546ce61a7cSPaul Moore for (iter = 0; iter < CIPSO_V4_TAG_MAXCNT; iter++) { 4556ce61a7cSPaul Moore switch (doi_def->tags[iter]) { 4566ce61a7cSPaul Moore case CIPSO_V4_TAG_RBITMAP: 4576ce61a7cSPaul Moore break; 4586ce61a7cSPaul Moore case CIPSO_V4_TAG_INVALID: 4596ce61a7cSPaul Moore if (iter == 0) 4606ce61a7cSPaul Moore return -EINVAL; 4616ce61a7cSPaul Moore break; 4626ce61a7cSPaul Moore default: 4636ce61a7cSPaul Moore return -EINVAL; 4646ce61a7cSPaul Moore } 4656ce61a7cSPaul Moore } 466446fda4fSPaul Moore 467446fda4fSPaul Moore doi_def->valid = 1; 468446fda4fSPaul Moore INIT_RCU_HEAD(&doi_def->rcu); 469446fda4fSPaul Moore INIT_LIST_HEAD(&doi_def->dom_list); 470446fda4fSPaul Moore 471446fda4fSPaul Moore rcu_read_lock(); 472446fda4fSPaul Moore if (cipso_v4_doi_search(doi_def->doi) != NULL) 473446fda4fSPaul Moore goto doi_add_failure_rlock; 474446fda4fSPaul Moore spin_lock(&cipso_v4_doi_list_lock); 475446fda4fSPaul Moore if (cipso_v4_doi_search(doi_def->doi) != NULL) 476446fda4fSPaul Moore goto doi_add_failure_slock; 477446fda4fSPaul Moore list_add_tail_rcu(&doi_def->list, &cipso_v4_doi_list); 478446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 479446fda4fSPaul Moore rcu_read_unlock(); 480446fda4fSPaul Moore 481446fda4fSPaul Moore return 0; 482446fda4fSPaul Moore 483446fda4fSPaul Moore doi_add_failure_slock: 484446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 485446fda4fSPaul Moore doi_add_failure_rlock: 486446fda4fSPaul Moore rcu_read_unlock(); 487446fda4fSPaul Moore return -EEXIST; 488446fda4fSPaul Moore } 489446fda4fSPaul Moore 490446fda4fSPaul Moore /** 491446fda4fSPaul Moore * cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine 492446fda4fSPaul Moore * @doi: the DOI value 49332f50cdeSPaul Moore * @audit_secid: the LSM secid to use in the audit message 494446fda4fSPaul Moore * @callback: the DOI cleanup/free callback 495446fda4fSPaul Moore * 496446fda4fSPaul Moore * Description: 497446fda4fSPaul Moore * Removes a DOI definition from the CIPSO engine, @callback is called to 498446fda4fSPaul Moore * free any memory. The NetLabel routines will be called to release their own 499446fda4fSPaul Moore * LSM domain mappings as well as our own domain list. Returns zero on 500446fda4fSPaul Moore * success and negative values on failure. 501446fda4fSPaul Moore * 502446fda4fSPaul Moore */ 50332f50cdeSPaul Moore int cipso_v4_doi_remove(u32 doi, 50495d4e6beSPaul Moore struct netlbl_audit *audit_info, 50532f50cdeSPaul Moore void (*callback) (struct rcu_head * head)) 506446fda4fSPaul Moore { 507446fda4fSPaul Moore struct cipso_v4_doi *doi_def; 508446fda4fSPaul Moore struct cipso_v4_domhsh_entry *dom_iter; 509446fda4fSPaul Moore 510446fda4fSPaul Moore rcu_read_lock(); 511446fda4fSPaul Moore if (cipso_v4_doi_search(doi) != NULL) { 512446fda4fSPaul Moore spin_lock(&cipso_v4_doi_list_lock); 513446fda4fSPaul Moore doi_def = cipso_v4_doi_search(doi); 514446fda4fSPaul Moore if (doi_def == NULL) { 515446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 516446fda4fSPaul Moore rcu_read_unlock(); 517446fda4fSPaul Moore return -ENOENT; 518446fda4fSPaul Moore } 519446fda4fSPaul Moore doi_def->valid = 0; 520446fda4fSPaul Moore list_del_rcu(&doi_def->list); 521446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 522446fda4fSPaul Moore list_for_each_entry_rcu(dom_iter, &doi_def->dom_list, list) 523446fda4fSPaul Moore if (dom_iter->valid) 52432f50cdeSPaul Moore netlbl_domhsh_remove(dom_iter->domain, 52595d4e6beSPaul Moore audit_info); 526446fda4fSPaul Moore cipso_v4_cache_invalidate(); 527446fda4fSPaul Moore rcu_read_unlock(); 528446fda4fSPaul Moore 529446fda4fSPaul Moore call_rcu(&doi_def->rcu, callback); 530446fda4fSPaul Moore return 0; 531446fda4fSPaul Moore } 532446fda4fSPaul Moore rcu_read_unlock(); 533446fda4fSPaul Moore 534446fda4fSPaul Moore return -ENOENT; 535446fda4fSPaul Moore } 536446fda4fSPaul Moore 537446fda4fSPaul Moore /** 538446fda4fSPaul Moore * cipso_v4_doi_getdef - Returns a pointer to a valid DOI definition 539446fda4fSPaul Moore * @doi: the DOI value 540446fda4fSPaul Moore * 541446fda4fSPaul Moore * Description: 542446fda4fSPaul Moore * Searches for a valid DOI definition and if one is found it is returned to 543446fda4fSPaul Moore * the caller. Otherwise NULL is returned. The caller must ensure that 544446fda4fSPaul Moore * rcu_read_lock() is held while accessing the returned definition. 545446fda4fSPaul Moore * 546446fda4fSPaul Moore */ 547446fda4fSPaul Moore struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi) 548446fda4fSPaul Moore { 549446fda4fSPaul Moore return cipso_v4_doi_search(doi); 550446fda4fSPaul Moore } 551446fda4fSPaul Moore 552446fda4fSPaul Moore /** 553fcd48280SPaul Moore * cipso_v4_doi_walk - Iterate through the DOI definitions 554fcd48280SPaul Moore * @skip_cnt: skip past this number of DOI definitions, updated 555fcd48280SPaul Moore * @callback: callback for each DOI definition 556fcd48280SPaul Moore * @cb_arg: argument for the callback function 557446fda4fSPaul Moore * 558446fda4fSPaul Moore * Description: 559fcd48280SPaul Moore * Iterate over the DOI definition list, skipping the first @skip_cnt entries. 560fcd48280SPaul Moore * For each entry call @callback, if @callback returns a negative value stop 561fcd48280SPaul Moore * 'walking' through the list and return. Updates the value in @skip_cnt upon 562fcd48280SPaul Moore * return. Returns zero on success, negative values on failure. 563446fda4fSPaul Moore * 564446fda4fSPaul Moore */ 565fcd48280SPaul Moore int cipso_v4_doi_walk(u32 *skip_cnt, 566fcd48280SPaul Moore int (*callback) (struct cipso_v4_doi *doi_def, void *arg), 567fcd48280SPaul Moore void *cb_arg) 568446fda4fSPaul Moore { 569fcd48280SPaul Moore int ret_val = -ENOENT; 570446fda4fSPaul Moore u32 doi_cnt = 0; 571fcd48280SPaul Moore struct cipso_v4_doi *iter_doi; 572446fda4fSPaul Moore 573446fda4fSPaul Moore rcu_read_lock(); 574fcd48280SPaul Moore list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list) 575fcd48280SPaul Moore if (iter_doi->valid) { 576fcd48280SPaul Moore if (doi_cnt++ < *skip_cnt) 577fcd48280SPaul Moore continue; 578fcd48280SPaul Moore ret_val = callback(iter_doi, cb_arg); 579fcd48280SPaul Moore if (ret_val < 0) { 580fcd48280SPaul Moore doi_cnt--; 581fcd48280SPaul Moore goto doi_walk_return; 582446fda4fSPaul Moore } 583446fda4fSPaul Moore } 584446fda4fSPaul Moore 585fcd48280SPaul Moore doi_walk_return: 586446fda4fSPaul Moore rcu_read_unlock(); 587fcd48280SPaul Moore *skip_cnt = doi_cnt; 588fcd48280SPaul Moore return ret_val; 589446fda4fSPaul Moore } 590446fda4fSPaul Moore 591446fda4fSPaul Moore /** 592446fda4fSPaul Moore * cipso_v4_doi_domhsh_add - Adds a domain entry to a DOI definition 593446fda4fSPaul Moore * @doi_def: the DOI definition 594446fda4fSPaul Moore * @domain: the domain to add 595446fda4fSPaul Moore * 596446fda4fSPaul Moore * Description: 597446fda4fSPaul Moore * Adds the @domain to the the DOI specified by @doi_def, this function 598446fda4fSPaul Moore * should only be called by external functions (i.e. NetLabel). This function 599446fda4fSPaul Moore * does allocate memory. Returns zero on success, negative values on failure. 600446fda4fSPaul Moore * 601446fda4fSPaul Moore */ 602446fda4fSPaul Moore int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain) 603446fda4fSPaul Moore { 604446fda4fSPaul Moore struct cipso_v4_domhsh_entry *iter; 605446fda4fSPaul Moore struct cipso_v4_domhsh_entry *new_dom; 606446fda4fSPaul Moore 607446fda4fSPaul Moore new_dom = kzalloc(sizeof(*new_dom), GFP_KERNEL); 608446fda4fSPaul Moore if (new_dom == NULL) 609446fda4fSPaul Moore return -ENOMEM; 610446fda4fSPaul Moore if (domain) { 611446fda4fSPaul Moore new_dom->domain = kstrdup(domain, GFP_KERNEL); 612446fda4fSPaul Moore if (new_dom->domain == NULL) { 613446fda4fSPaul Moore kfree(new_dom); 614446fda4fSPaul Moore return -ENOMEM; 615446fda4fSPaul Moore } 616446fda4fSPaul Moore } 617446fda4fSPaul Moore new_dom->valid = 1; 618446fda4fSPaul Moore INIT_RCU_HEAD(&new_dom->rcu); 619446fda4fSPaul Moore 620446fda4fSPaul Moore rcu_read_lock(); 621446fda4fSPaul Moore spin_lock(&cipso_v4_doi_list_lock); 622446fda4fSPaul Moore list_for_each_entry_rcu(iter, &doi_def->dom_list, list) 623446fda4fSPaul Moore if (iter->valid && 624446fda4fSPaul Moore ((domain != NULL && iter->domain != NULL && 625446fda4fSPaul Moore strcmp(iter->domain, domain) == 0) || 626446fda4fSPaul Moore (domain == NULL && iter->domain == NULL))) { 627446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 628446fda4fSPaul Moore rcu_read_unlock(); 629446fda4fSPaul Moore kfree(new_dom->domain); 630446fda4fSPaul Moore kfree(new_dom); 631446fda4fSPaul Moore return -EEXIST; 632446fda4fSPaul Moore } 633446fda4fSPaul Moore list_add_tail_rcu(&new_dom->list, &doi_def->dom_list); 634446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 635446fda4fSPaul Moore rcu_read_unlock(); 636446fda4fSPaul Moore 637446fda4fSPaul Moore return 0; 638446fda4fSPaul Moore } 639446fda4fSPaul Moore 640446fda4fSPaul Moore /** 641446fda4fSPaul Moore * cipso_v4_doi_domhsh_remove - Removes a domain entry from a DOI definition 642446fda4fSPaul Moore * @doi_def: the DOI definition 643446fda4fSPaul Moore * @domain: the domain to remove 644446fda4fSPaul Moore * 645446fda4fSPaul Moore * Description: 646446fda4fSPaul Moore * Removes the @domain from the DOI specified by @doi_def, this function 647446fda4fSPaul Moore * should only be called by external functions (i.e. NetLabel). Returns zero 648446fda4fSPaul Moore * on success and negative values on error. 649446fda4fSPaul Moore * 650446fda4fSPaul Moore */ 651446fda4fSPaul Moore int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def, 652446fda4fSPaul Moore const char *domain) 653446fda4fSPaul Moore { 654446fda4fSPaul Moore struct cipso_v4_domhsh_entry *iter; 655446fda4fSPaul Moore 656446fda4fSPaul Moore rcu_read_lock(); 657446fda4fSPaul Moore spin_lock(&cipso_v4_doi_list_lock); 658446fda4fSPaul Moore list_for_each_entry_rcu(iter, &doi_def->dom_list, list) 659446fda4fSPaul Moore if (iter->valid && 660446fda4fSPaul Moore ((domain != NULL && iter->domain != NULL && 661446fda4fSPaul Moore strcmp(iter->domain, domain) == 0) || 662446fda4fSPaul Moore (domain == NULL && iter->domain == NULL))) { 663446fda4fSPaul Moore iter->valid = 0; 664446fda4fSPaul Moore list_del_rcu(&iter->list); 665446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 666446fda4fSPaul Moore rcu_read_unlock(); 667446fda4fSPaul Moore call_rcu(&iter->rcu, cipso_v4_doi_domhsh_free); 668446fda4fSPaul Moore 669446fda4fSPaul Moore return 0; 670446fda4fSPaul Moore } 671446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 672446fda4fSPaul Moore rcu_read_unlock(); 673446fda4fSPaul Moore 674446fda4fSPaul Moore return -ENOENT; 675446fda4fSPaul Moore } 676446fda4fSPaul Moore 677446fda4fSPaul Moore /* 678446fda4fSPaul Moore * Label Mapping Functions 679446fda4fSPaul Moore */ 680446fda4fSPaul Moore 681446fda4fSPaul Moore /** 682446fda4fSPaul Moore * cipso_v4_map_lvl_valid - Checks to see if the given level is understood 683446fda4fSPaul Moore * @doi_def: the DOI definition 684446fda4fSPaul Moore * @level: the level to check 685446fda4fSPaul Moore * 686446fda4fSPaul Moore * Description: 687446fda4fSPaul Moore * Checks the given level against the given DOI definition and returns a 688446fda4fSPaul Moore * negative value if the level does not have a valid mapping and a zero value 689446fda4fSPaul Moore * if the level is defined by the DOI. 690446fda4fSPaul Moore * 691446fda4fSPaul Moore */ 692446fda4fSPaul Moore static int cipso_v4_map_lvl_valid(const struct cipso_v4_doi *doi_def, u8 level) 693446fda4fSPaul Moore { 694446fda4fSPaul Moore switch (doi_def->type) { 695446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 696446fda4fSPaul Moore return 0; 697446fda4fSPaul Moore case CIPSO_V4_MAP_STD: 698446fda4fSPaul Moore if (doi_def->map.std->lvl.cipso[level] < CIPSO_V4_INV_LVL) 699446fda4fSPaul Moore return 0; 700446fda4fSPaul Moore break; 701446fda4fSPaul Moore } 702446fda4fSPaul Moore 703446fda4fSPaul Moore return -EFAULT; 704446fda4fSPaul Moore } 705446fda4fSPaul Moore 706446fda4fSPaul Moore /** 707446fda4fSPaul Moore * cipso_v4_map_lvl_hton - Perform a level mapping from the host to the network 708446fda4fSPaul Moore * @doi_def: the DOI definition 709446fda4fSPaul Moore * @host_lvl: the host MLS level 710446fda4fSPaul Moore * @net_lvl: the network/CIPSO MLS level 711446fda4fSPaul Moore * 712446fda4fSPaul Moore * Description: 713446fda4fSPaul Moore * Perform a label mapping to translate a local MLS level to the correct 714446fda4fSPaul Moore * CIPSO level using the given DOI definition. Returns zero on success, 715446fda4fSPaul Moore * negative values otherwise. 716446fda4fSPaul Moore * 717446fda4fSPaul Moore */ 718446fda4fSPaul Moore static int cipso_v4_map_lvl_hton(const struct cipso_v4_doi *doi_def, 719446fda4fSPaul Moore u32 host_lvl, 720446fda4fSPaul Moore u32 *net_lvl) 721446fda4fSPaul Moore { 722446fda4fSPaul Moore switch (doi_def->type) { 723446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 724446fda4fSPaul Moore *net_lvl = host_lvl; 725446fda4fSPaul Moore return 0; 726446fda4fSPaul Moore case CIPSO_V4_MAP_STD: 727446fda4fSPaul Moore if (host_lvl < doi_def->map.std->lvl.local_size) { 728446fda4fSPaul Moore *net_lvl = doi_def->map.std->lvl.local[host_lvl]; 729446fda4fSPaul Moore return 0; 730446fda4fSPaul Moore } 731446fda4fSPaul Moore break; 732446fda4fSPaul Moore } 733446fda4fSPaul Moore 734446fda4fSPaul Moore return -EINVAL; 735446fda4fSPaul Moore } 736446fda4fSPaul Moore 737446fda4fSPaul Moore /** 738446fda4fSPaul Moore * cipso_v4_map_lvl_ntoh - Perform a level mapping from the network to the host 739446fda4fSPaul Moore * @doi_def: the DOI definition 740446fda4fSPaul Moore * @net_lvl: the network/CIPSO MLS level 741446fda4fSPaul Moore * @host_lvl: the host MLS level 742446fda4fSPaul Moore * 743446fda4fSPaul Moore * Description: 744446fda4fSPaul Moore * Perform a label mapping to translate a CIPSO level to the correct local MLS 745446fda4fSPaul Moore * level using the given DOI definition. Returns zero on success, negative 746446fda4fSPaul Moore * values otherwise. 747446fda4fSPaul Moore * 748446fda4fSPaul Moore */ 749446fda4fSPaul Moore static int cipso_v4_map_lvl_ntoh(const struct cipso_v4_doi *doi_def, 750446fda4fSPaul Moore u32 net_lvl, 751446fda4fSPaul Moore u32 *host_lvl) 752446fda4fSPaul Moore { 753446fda4fSPaul Moore struct cipso_v4_std_map_tbl *map_tbl; 754446fda4fSPaul Moore 755446fda4fSPaul Moore switch (doi_def->type) { 756446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 757446fda4fSPaul Moore *host_lvl = net_lvl; 758446fda4fSPaul Moore return 0; 759446fda4fSPaul Moore case CIPSO_V4_MAP_STD: 760446fda4fSPaul Moore map_tbl = doi_def->map.std; 761446fda4fSPaul Moore if (net_lvl < map_tbl->lvl.cipso_size && 762446fda4fSPaul Moore map_tbl->lvl.cipso[net_lvl] < CIPSO_V4_INV_LVL) { 763446fda4fSPaul Moore *host_lvl = doi_def->map.std->lvl.cipso[net_lvl]; 764446fda4fSPaul Moore return 0; 765446fda4fSPaul Moore } 766446fda4fSPaul Moore break; 767446fda4fSPaul Moore } 768446fda4fSPaul Moore 769446fda4fSPaul Moore return -EINVAL; 770446fda4fSPaul Moore } 771446fda4fSPaul Moore 772446fda4fSPaul Moore /** 773446fda4fSPaul Moore * cipso_v4_map_cat_rbm_valid - Checks to see if the category bitmap is valid 774446fda4fSPaul Moore * @doi_def: the DOI definition 775446fda4fSPaul Moore * @bitmap: category bitmap 776446fda4fSPaul Moore * @bitmap_len: bitmap length in bytes 777446fda4fSPaul Moore * 778446fda4fSPaul Moore * Description: 779446fda4fSPaul Moore * Checks the given category bitmap against the given DOI definition and 780446fda4fSPaul Moore * returns a negative value if any of the categories in the bitmap do not have 781446fda4fSPaul Moore * a valid mapping and a zero value if all of the categories are valid. 782446fda4fSPaul Moore * 783446fda4fSPaul Moore */ 784446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_valid(const struct cipso_v4_doi *doi_def, 785446fda4fSPaul Moore const unsigned char *bitmap, 786446fda4fSPaul Moore u32 bitmap_len) 787446fda4fSPaul Moore { 788446fda4fSPaul Moore int cat = -1; 789446fda4fSPaul Moore u32 bitmap_len_bits = bitmap_len * 8; 790044a68edSPaul Moore u32 cipso_cat_size; 791044a68edSPaul Moore u32 *cipso_array; 792446fda4fSPaul Moore 793446fda4fSPaul Moore switch (doi_def->type) { 794446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 795446fda4fSPaul Moore return 0; 796446fda4fSPaul Moore case CIPSO_V4_MAP_STD: 797044a68edSPaul Moore cipso_cat_size = doi_def->map.std->cat.cipso_size; 798044a68edSPaul Moore cipso_array = doi_def->map.std->cat.cipso; 799446fda4fSPaul Moore for (;;) { 800446fda4fSPaul Moore cat = cipso_v4_bitmap_walk(bitmap, 801446fda4fSPaul Moore bitmap_len_bits, 802446fda4fSPaul Moore cat + 1, 803446fda4fSPaul Moore 1); 804446fda4fSPaul Moore if (cat < 0) 805446fda4fSPaul Moore break; 806446fda4fSPaul Moore if (cat >= cipso_cat_size || 807446fda4fSPaul Moore cipso_array[cat] >= CIPSO_V4_INV_CAT) 808446fda4fSPaul Moore return -EFAULT; 809446fda4fSPaul Moore } 810446fda4fSPaul Moore 811446fda4fSPaul Moore if (cat == -1) 812446fda4fSPaul Moore return 0; 813446fda4fSPaul Moore break; 814446fda4fSPaul Moore } 815446fda4fSPaul Moore 816446fda4fSPaul Moore return -EFAULT; 817446fda4fSPaul Moore } 818446fda4fSPaul Moore 819446fda4fSPaul Moore /** 820446fda4fSPaul Moore * cipso_v4_map_cat_rbm_hton - Perform a category mapping from host to network 821446fda4fSPaul Moore * @doi_def: the DOI definition 822446fda4fSPaul Moore * @host_cat: the category bitmap in host format 823446fda4fSPaul Moore * @host_cat_len: the length of the host's category bitmap in bytes 824446fda4fSPaul Moore * @net_cat: the zero'd out category bitmap in network/CIPSO format 825446fda4fSPaul Moore * @net_cat_len: the length of the CIPSO bitmap in bytes 826446fda4fSPaul Moore * 827446fda4fSPaul Moore * Description: 828446fda4fSPaul Moore * Perform a label mapping to translate a local MLS category bitmap to the 829446fda4fSPaul Moore * correct CIPSO bitmap using the given DOI definition. Returns the minimum 830446fda4fSPaul Moore * size in bytes of the network bitmap on success, negative values otherwise. 831446fda4fSPaul Moore * 832446fda4fSPaul Moore */ 833446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def, 834446fda4fSPaul Moore const unsigned char *host_cat, 835446fda4fSPaul Moore u32 host_cat_len, 836446fda4fSPaul Moore unsigned char *net_cat, 837446fda4fSPaul Moore u32 net_cat_len) 838446fda4fSPaul Moore { 839446fda4fSPaul Moore int host_spot = -1; 840446fda4fSPaul Moore u32 net_spot; 841446fda4fSPaul Moore u32 net_spot_max = 0; 842446fda4fSPaul Moore u32 host_clen_bits = host_cat_len * 8; 843446fda4fSPaul Moore u32 net_clen_bits = net_cat_len * 8; 844044a68edSPaul Moore u32 host_cat_size; 845044a68edSPaul Moore u32 *host_cat_array; 846446fda4fSPaul Moore 847446fda4fSPaul Moore switch (doi_def->type) { 848446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 849ea614d7fSPaul Moore net_spot_max = host_cat_len; 850ea614d7fSPaul Moore while (net_spot_max > 0 && host_cat[net_spot_max - 1] == 0) 851446fda4fSPaul Moore net_spot_max--; 852446fda4fSPaul Moore if (net_spot_max > net_cat_len) 853446fda4fSPaul Moore return -EINVAL; 854446fda4fSPaul Moore memcpy(net_cat, host_cat, net_spot_max); 855446fda4fSPaul Moore return net_spot_max; 856446fda4fSPaul Moore case CIPSO_V4_MAP_STD: 857044a68edSPaul Moore host_cat_size = doi_def->map.std->cat.local_size; 858044a68edSPaul Moore host_cat_array = doi_def->map.std->cat.local; 859446fda4fSPaul Moore for (;;) { 860446fda4fSPaul Moore host_spot = cipso_v4_bitmap_walk(host_cat, 861446fda4fSPaul Moore host_clen_bits, 862446fda4fSPaul Moore host_spot + 1, 863446fda4fSPaul Moore 1); 864446fda4fSPaul Moore if (host_spot < 0) 865446fda4fSPaul Moore break; 866446fda4fSPaul Moore if (host_spot >= host_cat_size) 867446fda4fSPaul Moore return -EPERM; 868446fda4fSPaul Moore 869446fda4fSPaul Moore net_spot = host_cat_array[host_spot]; 870446fda4fSPaul Moore if (net_spot >= net_clen_bits) 871446fda4fSPaul Moore return -ENOSPC; 872446fda4fSPaul Moore cipso_v4_bitmap_setbit(net_cat, net_spot, 1); 873446fda4fSPaul Moore 874446fda4fSPaul Moore if (net_spot > net_spot_max) 875446fda4fSPaul Moore net_spot_max = net_spot; 876446fda4fSPaul Moore } 877446fda4fSPaul Moore 878446fda4fSPaul Moore if (host_spot == -2) 879446fda4fSPaul Moore return -EFAULT; 880446fda4fSPaul Moore 881446fda4fSPaul Moore if (++net_spot_max % 8) 882446fda4fSPaul Moore return net_spot_max / 8 + 1; 883446fda4fSPaul Moore return net_spot_max / 8; 884446fda4fSPaul Moore } 885446fda4fSPaul Moore 886446fda4fSPaul Moore return -EINVAL; 887446fda4fSPaul Moore } 888446fda4fSPaul Moore 889446fda4fSPaul Moore /** 890446fda4fSPaul Moore * cipso_v4_map_cat_rbm_ntoh - Perform a category mapping from network to host 891446fda4fSPaul Moore * @doi_def: the DOI definition 892446fda4fSPaul Moore * @net_cat: the category bitmap in network/CIPSO format 893446fda4fSPaul Moore * @net_cat_len: the length of the CIPSO bitmap in bytes 894446fda4fSPaul Moore * @host_cat: the zero'd out category bitmap in host format 895446fda4fSPaul Moore * @host_cat_len: the length of the host's category bitmap in bytes 896446fda4fSPaul Moore * 897446fda4fSPaul Moore * Description: 898446fda4fSPaul Moore * Perform a label mapping to translate a CIPSO bitmap to the correct local 899446fda4fSPaul Moore * MLS category bitmap using the given DOI definition. Returns the minimum 900446fda4fSPaul Moore * size in bytes of the host bitmap on success, negative values otherwise. 901446fda4fSPaul Moore * 902446fda4fSPaul Moore */ 903446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def, 904446fda4fSPaul Moore const unsigned char *net_cat, 905446fda4fSPaul Moore u32 net_cat_len, 906446fda4fSPaul Moore unsigned char *host_cat, 907446fda4fSPaul Moore u32 host_cat_len) 908446fda4fSPaul Moore { 909446fda4fSPaul Moore u32 host_spot; 910446fda4fSPaul Moore u32 host_spot_max = 0; 911446fda4fSPaul Moore int net_spot = -1; 912446fda4fSPaul Moore u32 net_clen_bits = net_cat_len * 8; 913446fda4fSPaul Moore u32 host_clen_bits = host_cat_len * 8; 914044a68edSPaul Moore u32 net_cat_size; 915044a68edSPaul Moore u32 *net_cat_array; 916446fda4fSPaul Moore 917446fda4fSPaul Moore switch (doi_def->type) { 918446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 919446fda4fSPaul Moore if (net_cat_len > host_cat_len) 920446fda4fSPaul Moore return -EINVAL; 921446fda4fSPaul Moore memcpy(host_cat, net_cat, net_cat_len); 922446fda4fSPaul Moore return net_cat_len; 923446fda4fSPaul Moore case CIPSO_V4_MAP_STD: 924044a68edSPaul Moore net_cat_size = doi_def->map.std->cat.cipso_size; 925044a68edSPaul Moore net_cat_array = doi_def->map.std->cat.cipso; 926446fda4fSPaul Moore for (;;) { 927446fda4fSPaul Moore net_spot = cipso_v4_bitmap_walk(net_cat, 928446fda4fSPaul Moore net_clen_bits, 929446fda4fSPaul Moore net_spot + 1, 930446fda4fSPaul Moore 1); 931446fda4fSPaul Moore if (net_spot < 0) 932446fda4fSPaul Moore break; 933446fda4fSPaul Moore if (net_spot >= net_cat_size || 934446fda4fSPaul Moore net_cat_array[net_spot] >= CIPSO_V4_INV_CAT) 935446fda4fSPaul Moore return -EPERM; 936446fda4fSPaul Moore 937446fda4fSPaul Moore host_spot = net_cat_array[net_spot]; 938446fda4fSPaul Moore if (host_spot >= host_clen_bits) 939446fda4fSPaul Moore return -ENOSPC; 940446fda4fSPaul Moore cipso_v4_bitmap_setbit(host_cat, host_spot, 1); 941446fda4fSPaul Moore 942446fda4fSPaul Moore if (host_spot > host_spot_max) 943446fda4fSPaul Moore host_spot_max = host_spot; 944446fda4fSPaul Moore } 945446fda4fSPaul Moore 946446fda4fSPaul Moore if (net_spot == -2) 947446fda4fSPaul Moore return -EFAULT; 948446fda4fSPaul Moore 949446fda4fSPaul Moore if (++host_spot_max % 8) 950446fda4fSPaul Moore return host_spot_max / 8 + 1; 951446fda4fSPaul Moore return host_spot_max / 8; 952446fda4fSPaul Moore } 953446fda4fSPaul Moore 954446fda4fSPaul Moore return -EINVAL; 955446fda4fSPaul Moore } 956446fda4fSPaul Moore 957446fda4fSPaul Moore /* 958446fda4fSPaul Moore * Protocol Handling Functions 959446fda4fSPaul Moore */ 960446fda4fSPaul Moore 961*91b1ed0aSPaul Moore #define CIPSO_V4_OPT_LEN_MAX 40 962446fda4fSPaul Moore #define CIPSO_V4_HDR_LEN 6 963446fda4fSPaul Moore 964446fda4fSPaul Moore /** 965446fda4fSPaul Moore * cipso_v4_gentag_hdr - Generate a CIPSO option header 966446fda4fSPaul Moore * @doi_def: the DOI definition 967*91b1ed0aSPaul Moore * @len: the total tag length in bytes, not including this header 968446fda4fSPaul Moore * @buf: the CIPSO option buffer 969446fda4fSPaul Moore * 970446fda4fSPaul Moore * Description: 971*91b1ed0aSPaul Moore * Write a CIPSO header into the beginning of @buffer. 972446fda4fSPaul Moore * 973446fda4fSPaul Moore */ 974*91b1ed0aSPaul Moore static void cipso_v4_gentag_hdr(const struct cipso_v4_doi *doi_def, 975*91b1ed0aSPaul Moore unsigned char *buf, 976*91b1ed0aSPaul Moore u32 len) 977446fda4fSPaul Moore { 978446fda4fSPaul Moore buf[0] = IPOPT_CIPSO; 979446fda4fSPaul Moore buf[1] = CIPSO_V4_HDR_LEN + len; 980714e85beSAl Viro *(__be32 *)&buf[2] = htonl(doi_def->doi); 981446fda4fSPaul Moore } 982446fda4fSPaul Moore 983446fda4fSPaul Moore /** 984446fda4fSPaul Moore * cipso_v4_gentag_rbm - Generate a CIPSO restricted bitmap tag (type #1) 985446fda4fSPaul Moore * @doi_def: the DOI definition 986446fda4fSPaul Moore * @secattr: the security attributes 987446fda4fSPaul Moore * @buffer: the option buffer 988446fda4fSPaul Moore * @buffer_len: length of buffer in bytes 989446fda4fSPaul Moore * 990446fda4fSPaul Moore * Description: 991446fda4fSPaul Moore * Generate a CIPSO option using the restricted bitmap tag, tag type #1. The 992446fda4fSPaul Moore * actual buffer length may be larger than the indicated size due to 993*91b1ed0aSPaul Moore * translation between host and network category bitmaps. Returns the size of 994*91b1ed0aSPaul Moore * the tag on success, negative values on failure. 995446fda4fSPaul Moore * 996446fda4fSPaul Moore */ 997446fda4fSPaul Moore static int cipso_v4_gentag_rbm(const struct cipso_v4_doi *doi_def, 998446fda4fSPaul Moore const struct netlbl_lsm_secattr *secattr, 999*91b1ed0aSPaul Moore unsigned char *buffer, 1000*91b1ed0aSPaul Moore u32 buffer_len) 1001446fda4fSPaul Moore { 1002701a90baSPaul Moore int ret_val; 1003*91b1ed0aSPaul Moore u32 tag_len; 1004446fda4fSPaul Moore u32 level; 1005446fda4fSPaul Moore 1006701a90baSPaul Moore if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0) 1007701a90baSPaul Moore return -EPERM; 1008701a90baSPaul Moore 1009*91b1ed0aSPaul Moore ret_val = cipso_v4_map_lvl_hton(doi_def, secattr->mls_lvl, &level); 1010*91b1ed0aSPaul Moore if (ret_val != 0) 1011*91b1ed0aSPaul Moore return ret_val; 1012446fda4fSPaul Moore 1013*91b1ed0aSPaul Moore if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { 1014446fda4fSPaul Moore ret_val = cipso_v4_map_cat_rbm_hton(doi_def, 1015446fda4fSPaul Moore secattr->mls_cat, 1016446fda4fSPaul Moore secattr->mls_cat_len, 1017*91b1ed0aSPaul Moore &buffer[4], 1018*91b1ed0aSPaul Moore buffer_len - 4); 1019446fda4fSPaul Moore if (ret_val < 0) 1020*91b1ed0aSPaul Moore return ret_val; 1021446fda4fSPaul Moore 1022446fda4fSPaul Moore /* This will send packets using the "optimized" format when 1023446fda4fSPaul Moore * possibile as specified in section 3.4.2.6 of the 1024446fda4fSPaul Moore * CIPSO draft. */ 1025701a90baSPaul Moore if (cipso_v4_rbm_optfmt && ret_val > 0 && ret_val <= 10) 1026*91b1ed0aSPaul Moore tag_len = 14; 1027701a90baSPaul Moore else 1028*91b1ed0aSPaul Moore tag_len = 4 + ret_val; 1029*91b1ed0aSPaul Moore } else 1030*91b1ed0aSPaul Moore tag_len = 4; 1031446fda4fSPaul Moore 1032*91b1ed0aSPaul Moore buffer[0] = 0x01; 1033*91b1ed0aSPaul Moore buffer[1] = tag_len; 1034*91b1ed0aSPaul Moore buffer[3] = level; 1035446fda4fSPaul Moore 1036*91b1ed0aSPaul Moore return tag_len; 1037446fda4fSPaul Moore } 1038446fda4fSPaul Moore 1039446fda4fSPaul Moore /** 1040446fda4fSPaul Moore * cipso_v4_parsetag_rbm - Parse a CIPSO restricted bitmap tag 1041446fda4fSPaul Moore * @doi_def: the DOI definition 1042446fda4fSPaul Moore * @tag: the CIPSO tag 1043446fda4fSPaul Moore * @secattr: the security attributes 1044446fda4fSPaul Moore * 1045446fda4fSPaul Moore * Description: 1046446fda4fSPaul Moore * Parse a CIPSO restricted bitmap tag (tag type #1) and return the security 1047446fda4fSPaul Moore * attributes in @secattr. Return zero on success, negatives values on 1048446fda4fSPaul Moore * failure. 1049446fda4fSPaul Moore * 1050446fda4fSPaul Moore */ 1051446fda4fSPaul Moore static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def, 1052446fda4fSPaul Moore const unsigned char *tag, 1053446fda4fSPaul Moore struct netlbl_lsm_secattr *secattr) 1054446fda4fSPaul Moore { 1055446fda4fSPaul Moore int ret_val; 1056446fda4fSPaul Moore u8 tag_len = tag[1]; 1057446fda4fSPaul Moore u32 level; 1058446fda4fSPaul Moore 1059446fda4fSPaul Moore ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level); 1060446fda4fSPaul Moore if (ret_val != 0) 1061446fda4fSPaul Moore return ret_val; 1062446fda4fSPaul Moore secattr->mls_lvl = level; 1063701a90baSPaul Moore secattr->flags |= NETLBL_SECATTR_MLS_LVL; 1064446fda4fSPaul Moore 1065446fda4fSPaul Moore if (tag_len > 4) { 1066446fda4fSPaul Moore switch (doi_def->type) { 1067446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 1068446fda4fSPaul Moore secattr->mls_cat_len = tag_len - 4; 1069446fda4fSPaul Moore break; 1070446fda4fSPaul Moore case CIPSO_V4_MAP_STD: 1071446fda4fSPaul Moore secattr->mls_cat_len = 1072446fda4fSPaul Moore doi_def->map.std->cat.local_size; 1073446fda4fSPaul Moore break; 1074446fda4fSPaul Moore } 1075446fda4fSPaul Moore secattr->mls_cat = kzalloc(secattr->mls_cat_len, GFP_ATOMIC); 1076446fda4fSPaul Moore if (secattr->mls_cat == NULL) 1077446fda4fSPaul Moore return -ENOMEM; 1078446fda4fSPaul Moore 1079446fda4fSPaul Moore ret_val = cipso_v4_map_cat_rbm_ntoh(doi_def, 1080446fda4fSPaul Moore &tag[4], 1081446fda4fSPaul Moore tag_len - 4, 1082446fda4fSPaul Moore secattr->mls_cat, 1083446fda4fSPaul Moore secattr->mls_cat_len); 1084446fda4fSPaul Moore if (ret_val < 0) { 1085446fda4fSPaul Moore kfree(secattr->mls_cat); 1086446fda4fSPaul Moore return ret_val; 1087701a90baSPaul Moore } else if (ret_val > 0) { 1088446fda4fSPaul Moore secattr->mls_cat_len = ret_val; 1089701a90baSPaul Moore secattr->flags |= NETLBL_SECATTR_MLS_CAT; 1090701a90baSPaul Moore } 1091446fda4fSPaul Moore } 1092446fda4fSPaul Moore 1093446fda4fSPaul Moore return 0; 1094446fda4fSPaul Moore } 1095446fda4fSPaul Moore 1096446fda4fSPaul Moore /** 1097446fda4fSPaul Moore * cipso_v4_validate - Validate a CIPSO option 1098446fda4fSPaul Moore * @option: the start of the option, on error it is set to point to the error 1099446fda4fSPaul Moore * 1100446fda4fSPaul Moore * Description: 1101446fda4fSPaul Moore * This routine is called to validate a CIPSO option, it checks all of the 1102446fda4fSPaul Moore * fields to ensure that they are at least valid, see the draft snippet below 1103446fda4fSPaul Moore * for details. If the option is valid then a zero value is returned and 1104446fda4fSPaul Moore * the value of @option is unchanged. If the option is invalid then a 1105446fda4fSPaul Moore * non-zero value is returned and @option is adjusted to point to the 1106446fda4fSPaul Moore * offending portion of the option. From the IETF draft ... 1107446fda4fSPaul Moore * 1108446fda4fSPaul Moore * "If any field within the CIPSO options, such as the DOI identifier, is not 1109446fda4fSPaul Moore * recognized the IP datagram is discarded and an ICMP 'parameter problem' 1110446fda4fSPaul Moore * (type 12) is generated and returned. The ICMP code field is set to 'bad 1111446fda4fSPaul Moore * parameter' (code 0) and the pointer is set to the start of the CIPSO field 1112446fda4fSPaul Moore * that is unrecognized." 1113446fda4fSPaul Moore * 1114446fda4fSPaul Moore */ 1115446fda4fSPaul Moore int cipso_v4_validate(unsigned char **option) 1116446fda4fSPaul Moore { 1117446fda4fSPaul Moore unsigned char *opt = *option; 1118446fda4fSPaul Moore unsigned char *tag; 1119446fda4fSPaul Moore unsigned char opt_iter; 1120446fda4fSPaul Moore unsigned char err_offset = 0; 1121446fda4fSPaul Moore u8 opt_len; 1122446fda4fSPaul Moore u8 tag_len; 1123446fda4fSPaul Moore struct cipso_v4_doi *doi_def = NULL; 1124446fda4fSPaul Moore u32 tag_iter; 1125446fda4fSPaul Moore 1126446fda4fSPaul Moore /* caller already checks for length values that are too large */ 1127446fda4fSPaul Moore opt_len = opt[1]; 1128446fda4fSPaul Moore if (opt_len < 8) { 1129446fda4fSPaul Moore err_offset = 1; 1130446fda4fSPaul Moore goto validate_return; 1131446fda4fSPaul Moore } 1132446fda4fSPaul Moore 1133446fda4fSPaul Moore rcu_read_lock(); 1134714e85beSAl Viro doi_def = cipso_v4_doi_getdef(ntohl(*((__be32 *)&opt[2]))); 1135446fda4fSPaul Moore if (doi_def == NULL) { 1136446fda4fSPaul Moore err_offset = 2; 1137446fda4fSPaul Moore goto validate_return_locked; 1138446fda4fSPaul Moore } 1139446fda4fSPaul Moore 1140446fda4fSPaul Moore opt_iter = 6; 1141446fda4fSPaul Moore tag = opt + opt_iter; 1142446fda4fSPaul Moore while (opt_iter < opt_len) { 1143446fda4fSPaul Moore for (tag_iter = 0; doi_def->tags[tag_iter] != tag[0];) 1144446fda4fSPaul Moore if (doi_def->tags[tag_iter] == CIPSO_V4_TAG_INVALID || 1145446fda4fSPaul Moore ++tag_iter == CIPSO_V4_TAG_MAXCNT) { 1146446fda4fSPaul Moore err_offset = opt_iter; 1147446fda4fSPaul Moore goto validate_return_locked; 1148446fda4fSPaul Moore } 1149446fda4fSPaul Moore 1150446fda4fSPaul Moore tag_len = tag[1]; 1151446fda4fSPaul Moore if (tag_len > (opt_len - opt_iter)) { 1152446fda4fSPaul Moore err_offset = opt_iter + 1; 1153446fda4fSPaul Moore goto validate_return_locked; 1154446fda4fSPaul Moore } 1155446fda4fSPaul Moore 1156446fda4fSPaul Moore switch (tag[0]) { 1157446fda4fSPaul Moore case CIPSO_V4_TAG_RBITMAP: 1158446fda4fSPaul Moore if (tag_len < 4) { 1159446fda4fSPaul Moore err_offset = opt_iter + 1; 1160446fda4fSPaul Moore goto validate_return_locked; 1161446fda4fSPaul Moore } 1162446fda4fSPaul Moore 1163446fda4fSPaul Moore /* We are already going to do all the verification 1164446fda4fSPaul Moore * necessary at the socket layer so from our point of 1165446fda4fSPaul Moore * view it is safe to turn these checks off (and less 1166446fda4fSPaul Moore * work), however, the CIPSO draft says we should do 1167446fda4fSPaul Moore * all the CIPSO validations here but it doesn't 1168446fda4fSPaul Moore * really specify _exactly_ what we need to validate 1169446fda4fSPaul Moore * ... so, just make it a sysctl tunable. */ 1170446fda4fSPaul Moore if (cipso_v4_rbm_strictvalid) { 1171446fda4fSPaul Moore if (cipso_v4_map_lvl_valid(doi_def, 1172446fda4fSPaul Moore tag[3]) < 0) { 1173446fda4fSPaul Moore err_offset = opt_iter + 3; 1174446fda4fSPaul Moore goto validate_return_locked; 1175446fda4fSPaul Moore } 1176446fda4fSPaul Moore if (tag_len > 4 && 1177446fda4fSPaul Moore cipso_v4_map_cat_rbm_valid(doi_def, 1178446fda4fSPaul Moore &tag[4], 1179446fda4fSPaul Moore tag_len - 4) < 0) { 1180446fda4fSPaul Moore err_offset = opt_iter + 4; 1181446fda4fSPaul Moore goto validate_return_locked; 1182446fda4fSPaul Moore } 1183446fda4fSPaul Moore } 1184446fda4fSPaul Moore break; 1185446fda4fSPaul Moore default: 1186446fda4fSPaul Moore err_offset = opt_iter; 1187446fda4fSPaul Moore goto validate_return_locked; 1188446fda4fSPaul Moore } 1189446fda4fSPaul Moore 1190446fda4fSPaul Moore tag += tag_len; 1191446fda4fSPaul Moore opt_iter += tag_len; 1192446fda4fSPaul Moore } 1193446fda4fSPaul Moore 1194446fda4fSPaul Moore validate_return_locked: 1195446fda4fSPaul Moore rcu_read_unlock(); 1196446fda4fSPaul Moore validate_return: 1197446fda4fSPaul Moore *option = opt + err_offset; 1198446fda4fSPaul Moore return err_offset; 1199446fda4fSPaul Moore } 1200446fda4fSPaul Moore 1201446fda4fSPaul Moore /** 1202446fda4fSPaul Moore * cipso_v4_error - Send the correct reponse for a bad packet 1203446fda4fSPaul Moore * @skb: the packet 1204446fda4fSPaul Moore * @error: the error code 1205446fda4fSPaul Moore * @gateway: CIPSO gateway flag 1206446fda4fSPaul Moore * 1207446fda4fSPaul Moore * Description: 1208446fda4fSPaul Moore * Based on the error code given in @error, send an ICMP error message back to 1209446fda4fSPaul Moore * the originating host. From the IETF draft ... 1210446fda4fSPaul Moore * 1211446fda4fSPaul Moore * "If the contents of the CIPSO [option] are valid but the security label is 1212446fda4fSPaul Moore * outside of the configured host or port label range, the datagram is 1213446fda4fSPaul Moore * discarded and an ICMP 'destination unreachable' (type 3) is generated and 1214446fda4fSPaul Moore * returned. The code field of the ICMP is set to 'communication with 1215446fda4fSPaul Moore * destination network administratively prohibited' (code 9) or to 1216446fda4fSPaul Moore * 'communication with destination host administratively prohibited' 1217446fda4fSPaul Moore * (code 10). The value of the code is dependent on whether the originator 1218446fda4fSPaul Moore * of the ICMP message is acting as a CIPSO host or a CIPSO gateway. The 1219446fda4fSPaul Moore * recipient of the ICMP message MUST be able to handle either value. The 1220446fda4fSPaul Moore * same procedure is performed if a CIPSO [option] can not be added to an 1221446fda4fSPaul Moore * IP packet because it is too large to fit in the IP options area." 1222446fda4fSPaul Moore * 1223446fda4fSPaul Moore * "If the error is triggered by receipt of an ICMP message, the message is 1224446fda4fSPaul Moore * discarded and no response is permitted (consistent with general ICMP 1225446fda4fSPaul Moore * processing rules)." 1226446fda4fSPaul Moore * 1227446fda4fSPaul Moore */ 1228446fda4fSPaul Moore void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway) 1229446fda4fSPaul Moore { 1230446fda4fSPaul Moore if (skb->nh.iph->protocol == IPPROTO_ICMP || error != -EACCES) 1231446fda4fSPaul Moore return; 1232446fda4fSPaul Moore 1233446fda4fSPaul Moore if (gateway) 1234446fda4fSPaul Moore icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0); 1235446fda4fSPaul Moore else 1236446fda4fSPaul Moore icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0); 1237446fda4fSPaul Moore } 1238446fda4fSPaul Moore 1239446fda4fSPaul Moore /** 1240446fda4fSPaul Moore * cipso_v4_socket_setattr - Add a CIPSO option to a socket 1241446fda4fSPaul Moore * @sock: the socket 1242446fda4fSPaul Moore * @doi_def: the CIPSO DOI to use 1243446fda4fSPaul Moore * @secattr: the specific security attributes of the socket 1244446fda4fSPaul Moore * 1245446fda4fSPaul Moore * Description: 1246446fda4fSPaul Moore * Set the CIPSO option on the given socket using the DOI definition and 1247446fda4fSPaul Moore * security attributes passed to the function. This function requires 1248446fda4fSPaul Moore * exclusive access to @sock->sk, which means it either needs to be in the 1249446fda4fSPaul Moore * process of being created or locked via lock_sock(sock->sk). Returns zero on 1250446fda4fSPaul Moore * success and negative values on failure. 1251446fda4fSPaul Moore * 1252446fda4fSPaul Moore */ 1253446fda4fSPaul Moore int cipso_v4_socket_setattr(const struct socket *sock, 1254446fda4fSPaul Moore const struct cipso_v4_doi *doi_def, 1255446fda4fSPaul Moore const struct netlbl_lsm_secattr *secattr) 1256446fda4fSPaul Moore { 1257446fda4fSPaul Moore int ret_val = -EPERM; 1258446fda4fSPaul Moore u32 iter; 1259*91b1ed0aSPaul Moore unsigned char *buf; 1260446fda4fSPaul Moore u32 buf_len = 0; 1261446fda4fSPaul Moore u32 opt_len; 1262446fda4fSPaul Moore struct ip_options *opt = NULL; 1263446fda4fSPaul Moore struct sock *sk; 1264446fda4fSPaul Moore struct inet_sock *sk_inet; 1265446fda4fSPaul Moore struct inet_connection_sock *sk_conn; 1266446fda4fSPaul Moore 1267446fda4fSPaul Moore /* In the case of sock_create_lite(), the sock->sk field is not 1268446fda4fSPaul Moore * defined yet but it is not a problem as the only users of these 1269446fda4fSPaul Moore * "lite" PF_INET sockets are functions which do an accept() call 1270446fda4fSPaul Moore * afterwards so we will label the socket as part of the accept(). */ 1271446fda4fSPaul Moore sk = sock->sk; 1272446fda4fSPaul Moore if (sk == NULL) 1273446fda4fSPaul Moore return 0; 1274446fda4fSPaul Moore 1275*91b1ed0aSPaul Moore /* We allocate the maximum CIPSO option size here so we are probably 1276*91b1ed0aSPaul Moore * being a little wasteful, but it makes our life _much_ easier later 1277*91b1ed0aSPaul Moore * on and after all we are only talking about 40 bytes. */ 1278*91b1ed0aSPaul Moore buf_len = CIPSO_V4_OPT_LEN_MAX; 1279*91b1ed0aSPaul Moore buf = kmalloc(buf_len, GFP_ATOMIC); 1280*91b1ed0aSPaul Moore if (buf == NULL) { 1281*91b1ed0aSPaul Moore ret_val = -ENOMEM; 1282*91b1ed0aSPaul Moore goto socket_setattr_failure; 1283*91b1ed0aSPaul Moore } 1284*91b1ed0aSPaul Moore 1285446fda4fSPaul Moore /* XXX - This code assumes only one tag per CIPSO option which isn't 1286446fda4fSPaul Moore * really a good assumption to make but since we only support the MAC 1287446fda4fSPaul Moore * tags right now it is a safe assumption. */ 1288446fda4fSPaul Moore iter = 0; 1289446fda4fSPaul Moore do { 1290*91b1ed0aSPaul Moore memset(buf, 0, buf_len); 1291446fda4fSPaul Moore switch (doi_def->tags[iter]) { 1292446fda4fSPaul Moore case CIPSO_V4_TAG_RBITMAP: 1293446fda4fSPaul Moore ret_val = cipso_v4_gentag_rbm(doi_def, 1294446fda4fSPaul Moore secattr, 1295*91b1ed0aSPaul Moore &buf[CIPSO_V4_HDR_LEN], 1296*91b1ed0aSPaul Moore buf_len - CIPSO_V4_HDR_LEN); 1297446fda4fSPaul Moore break; 1298446fda4fSPaul Moore default: 1299446fda4fSPaul Moore ret_val = -EPERM; 1300446fda4fSPaul Moore goto socket_setattr_failure; 1301446fda4fSPaul Moore } 1302446fda4fSPaul Moore 1303446fda4fSPaul Moore iter++; 1304*91b1ed0aSPaul Moore } while (ret_val < 0 && 1305446fda4fSPaul Moore iter < CIPSO_V4_TAG_MAXCNT && 1306446fda4fSPaul Moore doi_def->tags[iter] != CIPSO_V4_TAG_INVALID); 1307*91b1ed0aSPaul Moore if (ret_val < 0) 1308446fda4fSPaul Moore goto socket_setattr_failure; 1309*91b1ed0aSPaul Moore cipso_v4_gentag_hdr(doi_def, buf, ret_val); 1310*91b1ed0aSPaul Moore buf_len = CIPSO_V4_HDR_LEN + ret_val; 1311446fda4fSPaul Moore 1312446fda4fSPaul Moore /* We can't use ip_options_get() directly because it makes a call to 1313446fda4fSPaul Moore * ip_options_get_alloc() which allocates memory with GFP_KERNEL and 1314f8687afeSPaul Moore * we won't always have CAP_NET_RAW even though we _always_ want to 1315f8687afeSPaul Moore * set the IPOPT_CIPSO option. */ 1316446fda4fSPaul Moore opt_len = (buf_len + 3) & ~3; 1317446fda4fSPaul Moore opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC); 1318446fda4fSPaul Moore if (opt == NULL) { 1319446fda4fSPaul Moore ret_val = -ENOMEM; 1320446fda4fSPaul Moore goto socket_setattr_failure; 1321446fda4fSPaul Moore } 1322446fda4fSPaul Moore memcpy(opt->__data, buf, buf_len); 1323446fda4fSPaul Moore opt->optlen = opt_len; 1324446fda4fSPaul Moore opt->is_data = 1; 1325f8687afeSPaul Moore opt->cipso = sizeof(struct iphdr); 1326446fda4fSPaul Moore kfree(buf); 1327446fda4fSPaul Moore buf = NULL; 1328446fda4fSPaul Moore 1329446fda4fSPaul Moore sk_inet = inet_sk(sk); 1330446fda4fSPaul Moore if (sk_inet->is_icsk) { 1331446fda4fSPaul Moore sk_conn = inet_csk(sk); 1332446fda4fSPaul Moore if (sk_inet->opt) 1333446fda4fSPaul Moore sk_conn->icsk_ext_hdr_len -= sk_inet->opt->optlen; 1334446fda4fSPaul Moore sk_conn->icsk_ext_hdr_len += opt->optlen; 1335446fda4fSPaul Moore sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie); 1336446fda4fSPaul Moore } 1337446fda4fSPaul Moore opt = xchg(&sk_inet->opt, opt); 1338446fda4fSPaul Moore kfree(opt); 1339446fda4fSPaul Moore 1340446fda4fSPaul Moore return 0; 1341446fda4fSPaul Moore 1342446fda4fSPaul Moore socket_setattr_failure: 1343446fda4fSPaul Moore kfree(buf); 1344446fda4fSPaul Moore kfree(opt); 1345446fda4fSPaul Moore return ret_val; 1346446fda4fSPaul Moore } 1347446fda4fSPaul Moore 1348446fda4fSPaul Moore /** 134914a72f53SPaul Moore * cipso_v4_sock_getattr - Get the security attributes from a sock 135014a72f53SPaul Moore * @sk: the sock 135114a72f53SPaul Moore * @secattr: the security attributes 135214a72f53SPaul Moore * 135314a72f53SPaul Moore * Description: 135414a72f53SPaul Moore * Query @sk to see if there is a CIPSO option attached to the sock and if 135514a72f53SPaul Moore * there is return the CIPSO security attributes in @secattr. This function 135614a72f53SPaul Moore * requires that @sk be locked, or privately held, but it does not do any 135714a72f53SPaul Moore * locking itself. Returns zero on success and negative values on failure. 135814a72f53SPaul Moore * 135914a72f53SPaul Moore */ 136014a72f53SPaul Moore int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) 136114a72f53SPaul Moore { 136214a72f53SPaul Moore int ret_val = -ENOMSG; 136314a72f53SPaul Moore struct inet_sock *sk_inet; 136414a72f53SPaul Moore unsigned char *cipso_ptr; 136514a72f53SPaul Moore u32 doi; 136614a72f53SPaul Moore struct cipso_v4_doi *doi_def; 136714a72f53SPaul Moore 136814a72f53SPaul Moore sk_inet = inet_sk(sk); 136914a72f53SPaul Moore if (sk_inet->opt == NULL || sk_inet->opt->cipso == 0) 137014a72f53SPaul Moore return -ENOMSG; 137114a72f53SPaul Moore cipso_ptr = sk_inet->opt->__data + sk_inet->opt->cipso - 137214a72f53SPaul Moore sizeof(struct iphdr); 137314a72f53SPaul Moore ret_val = cipso_v4_cache_check(cipso_ptr, cipso_ptr[1], secattr); 137414a72f53SPaul Moore if (ret_val == 0) 137514a72f53SPaul Moore return ret_val; 137614a72f53SPaul Moore 1377714e85beSAl Viro doi = ntohl(*(__be32 *)&cipso_ptr[2]); 137814a72f53SPaul Moore rcu_read_lock(); 137914a72f53SPaul Moore doi_def = cipso_v4_doi_getdef(doi); 138014a72f53SPaul Moore if (doi_def == NULL) { 138114a72f53SPaul Moore rcu_read_unlock(); 138214a72f53SPaul Moore return -ENOMSG; 138314a72f53SPaul Moore } 1384*91b1ed0aSPaul Moore 1385*91b1ed0aSPaul Moore /* XXX - This code assumes only one tag per CIPSO option which isn't 1386*91b1ed0aSPaul Moore * really a good assumption to make but since we only support the MAC 1387*91b1ed0aSPaul Moore * tags right now it is a safe assumption. */ 138814a72f53SPaul Moore switch (cipso_ptr[6]) { 138914a72f53SPaul Moore case CIPSO_V4_TAG_RBITMAP: 139014a72f53SPaul Moore ret_val = cipso_v4_parsetag_rbm(doi_def, 139114a72f53SPaul Moore &cipso_ptr[6], 139214a72f53SPaul Moore secattr); 139314a72f53SPaul Moore break; 139414a72f53SPaul Moore } 139514a72f53SPaul Moore rcu_read_unlock(); 139614a72f53SPaul Moore 139714a72f53SPaul Moore return ret_val; 139814a72f53SPaul Moore } 139914a72f53SPaul Moore 140014a72f53SPaul Moore /** 1401446fda4fSPaul Moore * cipso_v4_socket_getattr - Get the security attributes from a socket 1402446fda4fSPaul Moore * @sock: the socket 1403446fda4fSPaul Moore * @secattr: the security attributes 1404446fda4fSPaul Moore * 1405446fda4fSPaul Moore * Description: 1406446fda4fSPaul Moore * Query @sock to see if there is a CIPSO option attached to the socket and if 1407446fda4fSPaul Moore * there is return the CIPSO security attributes in @secattr. Returns zero on 1408446fda4fSPaul Moore * success and negative values on failure. 1409446fda4fSPaul Moore * 1410446fda4fSPaul Moore */ 1411446fda4fSPaul Moore int cipso_v4_socket_getattr(const struct socket *sock, 1412446fda4fSPaul Moore struct netlbl_lsm_secattr *secattr) 1413446fda4fSPaul Moore { 141414a72f53SPaul Moore int ret_val; 1415446fda4fSPaul Moore 141614a72f53SPaul Moore lock_sock(sock->sk); 141714a72f53SPaul Moore ret_val = cipso_v4_sock_getattr(sock->sk, secattr); 141814a72f53SPaul Moore release_sock(sock->sk); 1419446fda4fSPaul Moore 1420446fda4fSPaul Moore return ret_val; 1421446fda4fSPaul Moore } 1422446fda4fSPaul Moore 1423446fda4fSPaul Moore /** 1424446fda4fSPaul Moore * cipso_v4_skbuff_getattr - Get the security attributes from the CIPSO option 1425446fda4fSPaul Moore * @skb: the packet 1426446fda4fSPaul Moore * @secattr: the security attributes 1427446fda4fSPaul Moore * 1428446fda4fSPaul Moore * Description: 1429446fda4fSPaul Moore * Parse the given packet's CIPSO option and return the security attributes. 1430446fda4fSPaul Moore * Returns zero on success and negative values on failure. 1431446fda4fSPaul Moore * 1432446fda4fSPaul Moore */ 1433446fda4fSPaul Moore int cipso_v4_skbuff_getattr(const struct sk_buff *skb, 1434446fda4fSPaul Moore struct netlbl_lsm_secattr *secattr) 1435446fda4fSPaul Moore { 1436446fda4fSPaul Moore int ret_val = -ENOMSG; 1437446fda4fSPaul Moore unsigned char *cipso_ptr; 1438446fda4fSPaul Moore u32 doi; 1439446fda4fSPaul Moore struct cipso_v4_doi *doi_def; 1440446fda4fSPaul Moore 1441446fda4fSPaul Moore cipso_ptr = CIPSO_V4_OPTPTR(skb); 1442446fda4fSPaul Moore if (cipso_v4_cache_check(cipso_ptr, cipso_ptr[1], secattr) == 0) 1443446fda4fSPaul Moore return 0; 1444446fda4fSPaul Moore 1445714e85beSAl Viro doi = ntohl(*(__be32 *)&cipso_ptr[2]); 1446446fda4fSPaul Moore rcu_read_lock(); 1447446fda4fSPaul Moore doi_def = cipso_v4_doi_getdef(doi); 1448446fda4fSPaul Moore if (doi_def == NULL) 1449446fda4fSPaul Moore goto skbuff_getattr_return; 1450*91b1ed0aSPaul Moore 1451*91b1ed0aSPaul Moore /* XXX - This code assumes only one tag per CIPSO option which isn't 1452*91b1ed0aSPaul Moore * really a good assumption to make but since we only support the MAC 1453*91b1ed0aSPaul Moore * tags right now it is a safe assumption. */ 1454446fda4fSPaul Moore switch (cipso_ptr[6]) { 1455446fda4fSPaul Moore case CIPSO_V4_TAG_RBITMAP: 1456446fda4fSPaul Moore ret_val = cipso_v4_parsetag_rbm(doi_def, 1457446fda4fSPaul Moore &cipso_ptr[6], 1458446fda4fSPaul Moore secattr); 1459446fda4fSPaul Moore break; 1460446fda4fSPaul Moore } 1461446fda4fSPaul Moore 1462446fda4fSPaul Moore skbuff_getattr_return: 1463446fda4fSPaul Moore rcu_read_unlock(); 1464446fda4fSPaul Moore return ret_val; 1465446fda4fSPaul Moore } 1466446fda4fSPaul Moore 1467446fda4fSPaul Moore /* 1468446fda4fSPaul Moore * Setup Functions 1469446fda4fSPaul Moore */ 1470446fda4fSPaul Moore 1471446fda4fSPaul Moore /** 1472446fda4fSPaul Moore * cipso_v4_init - Initialize the CIPSO module 1473446fda4fSPaul Moore * 1474446fda4fSPaul Moore * Description: 1475446fda4fSPaul Moore * Initialize the CIPSO module and prepare it for use. Returns zero on success 1476446fda4fSPaul Moore * and negative values on failure. 1477446fda4fSPaul Moore * 1478446fda4fSPaul Moore */ 1479446fda4fSPaul Moore static int __init cipso_v4_init(void) 1480446fda4fSPaul Moore { 1481446fda4fSPaul Moore int ret_val; 1482446fda4fSPaul Moore 1483446fda4fSPaul Moore ret_val = cipso_v4_cache_init(); 1484446fda4fSPaul Moore if (ret_val != 0) 1485446fda4fSPaul Moore panic("Failed to initialize the CIPSO/IPv4 cache (%d)\n", 1486446fda4fSPaul Moore ret_val); 1487446fda4fSPaul Moore 1488446fda4fSPaul Moore return 0; 1489446fda4fSPaul Moore } 1490446fda4fSPaul Moore 1491446fda4fSPaul Moore subsys_initcall(cipso_v4_init); 1492