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> 4850e5d35cSPaul Moore #include <asm/unaligned.h> 49446fda4fSPaul Moore 50446fda4fSPaul Moore struct cipso_v4_domhsh_entry { 51446fda4fSPaul Moore char *domain; 52446fda4fSPaul Moore u32 valid; 53446fda4fSPaul Moore struct list_head list; 54446fda4fSPaul Moore struct rcu_head rcu; 55446fda4fSPaul Moore }; 56446fda4fSPaul Moore 57446fda4fSPaul Moore /* List of available DOI definitions */ 58446fda4fSPaul Moore /* XXX - Updates should be minimal so having a single lock for the 59446fda4fSPaul Moore * cipso_v4_doi_list and the cipso_v4_doi_list->dom_list should be 60446fda4fSPaul Moore * okay. */ 61446fda4fSPaul Moore /* XXX - This currently assumes a minimal number of different DOIs in use, 62446fda4fSPaul Moore * if in practice there are a lot of different DOIs this list should 63446fda4fSPaul Moore * probably be turned into a hash table or something similar so we 64446fda4fSPaul Moore * can do quick lookups. */ 658ce11e6aSAdrian Bunk static DEFINE_SPINLOCK(cipso_v4_doi_list_lock); 66*1596c97aSDenis Cheng static LIST_HEAD(cipso_v4_doi_list); 67446fda4fSPaul Moore 68446fda4fSPaul Moore /* Label mapping cache */ 69446fda4fSPaul Moore int cipso_v4_cache_enabled = 1; 70446fda4fSPaul Moore int cipso_v4_cache_bucketsize = 10; 71446fda4fSPaul Moore #define CIPSO_V4_CACHE_BUCKETBITS 7 72446fda4fSPaul Moore #define CIPSO_V4_CACHE_BUCKETS (1 << CIPSO_V4_CACHE_BUCKETBITS) 73446fda4fSPaul Moore #define CIPSO_V4_CACHE_REORDERLIMIT 10 74446fda4fSPaul Moore struct cipso_v4_map_cache_bkt { 75446fda4fSPaul Moore spinlock_t lock; 76446fda4fSPaul Moore u32 size; 77446fda4fSPaul Moore struct list_head list; 78446fda4fSPaul Moore }; 79446fda4fSPaul Moore struct cipso_v4_map_cache_entry { 80446fda4fSPaul Moore u32 hash; 81446fda4fSPaul Moore unsigned char *key; 82446fda4fSPaul Moore size_t key_len; 83446fda4fSPaul Moore 84ffb733c6Spaul.moore@hp.com struct netlbl_lsm_cache *lsm_data; 85446fda4fSPaul Moore 86446fda4fSPaul Moore u32 activity; 87446fda4fSPaul Moore struct list_head list; 88446fda4fSPaul Moore }; 89446fda4fSPaul Moore static struct cipso_v4_map_cache_bkt *cipso_v4_cache = NULL; 90446fda4fSPaul Moore 91446fda4fSPaul Moore /* Restricted bitmap (tag #1) flags */ 92446fda4fSPaul Moore int cipso_v4_rbm_optfmt = 0; 93446fda4fSPaul Moore int cipso_v4_rbm_strictvalid = 1; 94446fda4fSPaul Moore 95446fda4fSPaul Moore /* 96f998e8cbSPaul Moore * Protocol Constants 97f998e8cbSPaul Moore */ 98f998e8cbSPaul Moore 99f998e8cbSPaul Moore /* Maximum size of the CIPSO IP option, derived from the fact that the maximum 100f998e8cbSPaul Moore * IPv4 header size is 60 bytes and the base IPv4 header is 20 bytes long. */ 101f998e8cbSPaul Moore #define CIPSO_V4_OPT_LEN_MAX 40 102f998e8cbSPaul Moore 103f998e8cbSPaul Moore /* Length of the base CIPSO option, this includes the option type (1 byte), the 104f998e8cbSPaul Moore * option length (1 byte), and the DOI (4 bytes). */ 105f998e8cbSPaul Moore #define CIPSO_V4_HDR_LEN 6 106f998e8cbSPaul Moore 107f998e8cbSPaul Moore /* Base length of the restrictive category bitmap tag (tag #1). */ 108f998e8cbSPaul Moore #define CIPSO_V4_TAG_RBM_BLEN 4 109f998e8cbSPaul Moore 110f998e8cbSPaul Moore /* Base length of the enumerated category tag (tag #2). */ 111f998e8cbSPaul Moore #define CIPSO_V4_TAG_ENUM_BLEN 4 112f998e8cbSPaul Moore 113f998e8cbSPaul Moore /* Base length of the ranged categories bitmap tag (tag #5). */ 114f998e8cbSPaul Moore #define CIPSO_V4_TAG_RNG_BLEN 4 115f998e8cbSPaul Moore /* The maximum number of category ranges permitted in the ranged category tag 116f998e8cbSPaul Moore * (tag #5). You may note that the IETF draft states that the maximum number 117f998e8cbSPaul Moore * of category ranges is 7, but if the low end of the last category range is 118f998e8cbSPaul Moore * zero then it is possibile to fit 8 category ranges because the zero should 119f998e8cbSPaul Moore * be omitted. */ 120f998e8cbSPaul Moore #define CIPSO_V4_TAG_RNG_CAT_MAX 8 121f998e8cbSPaul Moore 122f998e8cbSPaul Moore /* 123446fda4fSPaul Moore * Helper Functions 124446fda4fSPaul Moore */ 125446fda4fSPaul Moore 126446fda4fSPaul Moore /** 127446fda4fSPaul Moore * cipso_v4_bitmap_walk - Walk a bitmap looking for a bit 128446fda4fSPaul Moore * @bitmap: the bitmap 129446fda4fSPaul Moore * @bitmap_len: length in bits 130446fda4fSPaul Moore * @offset: starting offset 131446fda4fSPaul Moore * @state: if non-zero, look for a set (1) bit else look for a cleared (0) bit 132446fda4fSPaul Moore * 133446fda4fSPaul Moore * Description: 134446fda4fSPaul Moore * Starting at @offset, walk the bitmap from left to right until either the 135446fda4fSPaul Moore * desired bit is found or we reach the end. Return the bit offset, -1 if 136446fda4fSPaul Moore * not found, or -2 if error. 137446fda4fSPaul Moore */ 138446fda4fSPaul Moore static int cipso_v4_bitmap_walk(const unsigned char *bitmap, 139446fda4fSPaul Moore u32 bitmap_len, 140446fda4fSPaul Moore u32 offset, 141446fda4fSPaul Moore u8 state) 142446fda4fSPaul Moore { 143446fda4fSPaul Moore u32 bit_spot; 144446fda4fSPaul Moore u32 byte_offset; 145446fda4fSPaul Moore unsigned char bitmask; 146446fda4fSPaul Moore unsigned char byte; 147446fda4fSPaul Moore 148446fda4fSPaul Moore /* gcc always rounds to zero when doing integer division */ 149446fda4fSPaul Moore byte_offset = offset / 8; 150446fda4fSPaul Moore byte = bitmap[byte_offset]; 151446fda4fSPaul Moore bit_spot = offset; 152446fda4fSPaul Moore bitmask = 0x80 >> (offset % 8); 153446fda4fSPaul Moore 154446fda4fSPaul Moore while (bit_spot < bitmap_len) { 155446fda4fSPaul Moore if ((state && (byte & bitmask) == bitmask) || 156446fda4fSPaul Moore (state == 0 && (byte & bitmask) == 0)) 157446fda4fSPaul Moore return bit_spot; 158446fda4fSPaul Moore 159446fda4fSPaul Moore bit_spot++; 160446fda4fSPaul Moore bitmask >>= 1; 161446fda4fSPaul Moore if (bitmask == 0) { 162446fda4fSPaul Moore byte = bitmap[++byte_offset]; 163446fda4fSPaul Moore bitmask = 0x80; 164446fda4fSPaul Moore } 165446fda4fSPaul Moore } 166446fda4fSPaul Moore 167446fda4fSPaul Moore return -1; 168446fda4fSPaul Moore } 169446fda4fSPaul Moore 170446fda4fSPaul Moore /** 171446fda4fSPaul Moore * cipso_v4_bitmap_setbit - Sets a single bit in a bitmap 172446fda4fSPaul Moore * @bitmap: the bitmap 173446fda4fSPaul Moore * @bit: the bit 174446fda4fSPaul Moore * @state: if non-zero, set the bit (1) else clear the bit (0) 175446fda4fSPaul Moore * 176446fda4fSPaul Moore * Description: 177446fda4fSPaul Moore * Set a single bit in the bitmask. Returns zero on success, negative values 178446fda4fSPaul Moore * on error. 179446fda4fSPaul Moore */ 180446fda4fSPaul Moore static void cipso_v4_bitmap_setbit(unsigned char *bitmap, 181446fda4fSPaul Moore u32 bit, 182446fda4fSPaul Moore u8 state) 183446fda4fSPaul Moore { 184446fda4fSPaul Moore u32 byte_spot; 185446fda4fSPaul Moore u8 bitmask; 186446fda4fSPaul Moore 187446fda4fSPaul Moore /* gcc always rounds to zero when doing integer division */ 188446fda4fSPaul Moore byte_spot = bit / 8; 189446fda4fSPaul Moore bitmask = 0x80 >> (bit % 8); 190446fda4fSPaul Moore if (state) 191446fda4fSPaul Moore bitmap[byte_spot] |= bitmask; 192446fda4fSPaul Moore else 193446fda4fSPaul Moore bitmap[byte_spot] &= ~bitmask; 194446fda4fSPaul Moore } 195446fda4fSPaul Moore 196446fda4fSPaul Moore /** 197446fda4fSPaul Moore * cipso_v4_doi_domhsh_free - Frees a domain list entry 198446fda4fSPaul Moore * @entry: the entry's RCU field 199446fda4fSPaul Moore * 200446fda4fSPaul Moore * Description: 201446fda4fSPaul Moore * This function is designed to be used as a callback to the call_rcu() 202446fda4fSPaul Moore * function so that the memory allocated to a domain list entry can be released 203446fda4fSPaul Moore * safely. 204446fda4fSPaul Moore * 205446fda4fSPaul Moore */ 206446fda4fSPaul Moore static void cipso_v4_doi_domhsh_free(struct rcu_head *entry) 207446fda4fSPaul Moore { 208446fda4fSPaul Moore struct cipso_v4_domhsh_entry *ptr; 209446fda4fSPaul Moore 210446fda4fSPaul Moore ptr = container_of(entry, struct cipso_v4_domhsh_entry, rcu); 211446fda4fSPaul Moore kfree(ptr->domain); 212446fda4fSPaul Moore kfree(ptr); 213446fda4fSPaul Moore } 214446fda4fSPaul Moore 215446fda4fSPaul Moore /** 216446fda4fSPaul Moore * cipso_v4_cache_entry_free - Frees a cache entry 217446fda4fSPaul Moore * @entry: the entry to free 218446fda4fSPaul Moore * 219446fda4fSPaul Moore * Description: 220ffb733c6Spaul.moore@hp.com * This function frees the memory associated with a cache entry including the 221ffb733c6Spaul.moore@hp.com * LSM cache data if there are no longer any users, i.e. reference count == 0. 222446fda4fSPaul Moore * 223446fda4fSPaul Moore */ 224446fda4fSPaul Moore static void cipso_v4_cache_entry_free(struct cipso_v4_map_cache_entry *entry) 225446fda4fSPaul Moore { 226ffb733c6Spaul.moore@hp.com if (entry->lsm_data) 227ffb733c6Spaul.moore@hp.com netlbl_secattr_cache_free(entry->lsm_data); 228446fda4fSPaul Moore kfree(entry->key); 229446fda4fSPaul Moore kfree(entry); 230446fda4fSPaul Moore } 231446fda4fSPaul Moore 232446fda4fSPaul Moore /** 233446fda4fSPaul Moore * cipso_v4_map_cache_hash - Hashing function for the CIPSO cache 234446fda4fSPaul Moore * @key: the hash key 235446fda4fSPaul Moore * @key_len: the length of the key in bytes 236446fda4fSPaul Moore * 237446fda4fSPaul Moore * Description: 238446fda4fSPaul Moore * The CIPSO tag hashing function. Returns a 32-bit hash value. 239446fda4fSPaul Moore * 240446fda4fSPaul Moore */ 241446fda4fSPaul Moore static u32 cipso_v4_map_cache_hash(const unsigned char *key, u32 key_len) 242446fda4fSPaul Moore { 243446fda4fSPaul Moore return jhash(key, key_len, 0); 244446fda4fSPaul Moore } 245446fda4fSPaul Moore 246446fda4fSPaul Moore /* 247446fda4fSPaul Moore * Label Mapping Cache Functions 248446fda4fSPaul Moore */ 249446fda4fSPaul Moore 250446fda4fSPaul Moore /** 251446fda4fSPaul Moore * cipso_v4_cache_init - Initialize the CIPSO cache 252446fda4fSPaul Moore * 253446fda4fSPaul Moore * Description: 254446fda4fSPaul Moore * Initializes the CIPSO label mapping cache, this function should be called 255446fda4fSPaul Moore * before any of the other functions defined in this file. Returns zero on 256446fda4fSPaul Moore * success, negative values on error. 257446fda4fSPaul Moore * 258446fda4fSPaul Moore */ 259446fda4fSPaul Moore static int cipso_v4_cache_init(void) 260446fda4fSPaul Moore { 261446fda4fSPaul Moore u32 iter; 262446fda4fSPaul Moore 263446fda4fSPaul Moore cipso_v4_cache = kcalloc(CIPSO_V4_CACHE_BUCKETS, 264446fda4fSPaul Moore sizeof(struct cipso_v4_map_cache_bkt), 265446fda4fSPaul Moore GFP_KERNEL); 266446fda4fSPaul Moore if (cipso_v4_cache == NULL) 267446fda4fSPaul Moore return -ENOMEM; 268446fda4fSPaul Moore 269446fda4fSPaul Moore for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) { 270446fda4fSPaul Moore spin_lock_init(&cipso_v4_cache[iter].lock); 271446fda4fSPaul Moore cipso_v4_cache[iter].size = 0; 272446fda4fSPaul Moore INIT_LIST_HEAD(&cipso_v4_cache[iter].list); 273446fda4fSPaul Moore } 274446fda4fSPaul Moore 275446fda4fSPaul Moore return 0; 276446fda4fSPaul Moore } 277446fda4fSPaul Moore 278446fda4fSPaul Moore /** 279446fda4fSPaul Moore * cipso_v4_cache_invalidate - Invalidates the current CIPSO cache 280446fda4fSPaul Moore * 281446fda4fSPaul Moore * Description: 282446fda4fSPaul Moore * Invalidates and frees any entries in the CIPSO cache. Returns zero on 283446fda4fSPaul Moore * success and negative values on failure. 284446fda4fSPaul Moore * 285446fda4fSPaul Moore */ 286446fda4fSPaul Moore void cipso_v4_cache_invalidate(void) 287446fda4fSPaul Moore { 288446fda4fSPaul Moore struct cipso_v4_map_cache_entry *entry, *tmp_entry; 289446fda4fSPaul Moore u32 iter; 290446fda4fSPaul Moore 291446fda4fSPaul Moore for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) { 292609c92feSPaul Moore spin_lock_bh(&cipso_v4_cache[iter].lock); 293446fda4fSPaul Moore list_for_each_entry_safe(entry, 294446fda4fSPaul Moore tmp_entry, 295446fda4fSPaul Moore &cipso_v4_cache[iter].list, list) { 296446fda4fSPaul Moore list_del(&entry->list); 297446fda4fSPaul Moore cipso_v4_cache_entry_free(entry); 298446fda4fSPaul Moore } 299446fda4fSPaul Moore cipso_v4_cache[iter].size = 0; 300609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[iter].lock); 301446fda4fSPaul Moore } 302446fda4fSPaul Moore 303446fda4fSPaul Moore return; 304446fda4fSPaul Moore } 305446fda4fSPaul Moore 306446fda4fSPaul Moore /** 307446fda4fSPaul Moore * cipso_v4_cache_check - Check the CIPSO cache for a label mapping 308446fda4fSPaul Moore * @key: the buffer to check 309446fda4fSPaul Moore * @key_len: buffer length in bytes 310446fda4fSPaul Moore * @secattr: the security attribute struct to use 311446fda4fSPaul Moore * 312446fda4fSPaul Moore * Description: 313446fda4fSPaul Moore * This function checks the cache to see if a label mapping already exists for 314446fda4fSPaul Moore * the given key. If there is a match then the cache is adjusted and the 315446fda4fSPaul Moore * @secattr struct is populated with the correct LSM security attributes. The 316446fda4fSPaul Moore * cache is adjusted in the following manner if the entry is not already the 317446fda4fSPaul Moore * first in the cache bucket: 318446fda4fSPaul Moore * 319446fda4fSPaul Moore * 1. The cache entry's activity counter is incremented 320446fda4fSPaul Moore * 2. The previous (higher ranking) entry's activity counter is decremented 321446fda4fSPaul Moore * 3. If the difference between the two activity counters is geater than 322446fda4fSPaul Moore * CIPSO_V4_CACHE_REORDERLIMIT the two entries are swapped 323446fda4fSPaul Moore * 324446fda4fSPaul Moore * Returns zero on success, -ENOENT for a cache miss, and other negative values 325446fda4fSPaul Moore * on error. 326446fda4fSPaul Moore * 327446fda4fSPaul Moore */ 328446fda4fSPaul Moore static int cipso_v4_cache_check(const unsigned char *key, 329446fda4fSPaul Moore u32 key_len, 330446fda4fSPaul Moore struct netlbl_lsm_secattr *secattr) 331446fda4fSPaul Moore { 332446fda4fSPaul Moore u32 bkt; 333446fda4fSPaul Moore struct cipso_v4_map_cache_entry *entry; 334446fda4fSPaul Moore struct cipso_v4_map_cache_entry *prev_entry = NULL; 335446fda4fSPaul Moore u32 hash; 336446fda4fSPaul Moore 337446fda4fSPaul Moore if (!cipso_v4_cache_enabled) 338446fda4fSPaul Moore return -ENOENT; 339446fda4fSPaul Moore 340446fda4fSPaul Moore hash = cipso_v4_map_cache_hash(key, key_len); 341446fda4fSPaul Moore bkt = hash & (CIPSO_V4_CACHE_BUCKETBITS - 1); 342609c92feSPaul Moore spin_lock_bh(&cipso_v4_cache[bkt].lock); 343446fda4fSPaul Moore list_for_each_entry(entry, &cipso_v4_cache[bkt].list, list) { 344446fda4fSPaul Moore if (entry->hash == hash && 345446fda4fSPaul Moore entry->key_len == key_len && 346446fda4fSPaul Moore memcmp(entry->key, key, key_len) == 0) { 347446fda4fSPaul Moore entry->activity += 1; 348ffb733c6Spaul.moore@hp.com atomic_inc(&entry->lsm_data->refcount); 349ffb733c6Spaul.moore@hp.com secattr->cache = entry->lsm_data; 350701a90baSPaul Moore secattr->flags |= NETLBL_SECATTR_CACHE; 351446fda4fSPaul Moore if (prev_entry == NULL) { 352609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[bkt].lock); 353446fda4fSPaul Moore return 0; 354446fda4fSPaul Moore } 355446fda4fSPaul Moore 356446fda4fSPaul Moore if (prev_entry->activity > 0) 357446fda4fSPaul Moore prev_entry->activity -= 1; 358446fda4fSPaul Moore if (entry->activity > prev_entry->activity && 359446fda4fSPaul Moore entry->activity - prev_entry->activity > 360446fda4fSPaul Moore CIPSO_V4_CACHE_REORDERLIMIT) { 361446fda4fSPaul Moore __list_del(entry->list.prev, entry->list.next); 362446fda4fSPaul Moore __list_add(&entry->list, 363446fda4fSPaul Moore prev_entry->list.prev, 364446fda4fSPaul Moore &prev_entry->list); 365446fda4fSPaul Moore } 366446fda4fSPaul Moore 367609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[bkt].lock); 368446fda4fSPaul Moore return 0; 369446fda4fSPaul Moore } 370446fda4fSPaul Moore prev_entry = entry; 371446fda4fSPaul Moore } 372609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[bkt].lock); 373446fda4fSPaul Moore 374446fda4fSPaul Moore return -ENOENT; 375446fda4fSPaul Moore } 376446fda4fSPaul Moore 377446fda4fSPaul Moore /** 378446fda4fSPaul Moore * cipso_v4_cache_add - Add an entry to the CIPSO cache 379446fda4fSPaul Moore * @skb: the packet 380446fda4fSPaul Moore * @secattr: the packet's security attributes 381446fda4fSPaul Moore * 382446fda4fSPaul Moore * Description: 383446fda4fSPaul Moore * Add a new entry into the CIPSO label mapping cache. Add the new entry to 384446fda4fSPaul Moore * head of the cache bucket's list, if the cache bucket is out of room remove 385446fda4fSPaul Moore * the last entry in the list first. It is important to note that there is 386446fda4fSPaul Moore * currently no checking for duplicate keys. Returns zero on success, 387446fda4fSPaul Moore * negative values on failure. 388446fda4fSPaul Moore * 389446fda4fSPaul Moore */ 390446fda4fSPaul Moore int cipso_v4_cache_add(const struct sk_buff *skb, 391446fda4fSPaul Moore const struct netlbl_lsm_secattr *secattr) 392446fda4fSPaul Moore { 393446fda4fSPaul Moore int ret_val = -EPERM; 394446fda4fSPaul Moore u32 bkt; 395446fda4fSPaul Moore struct cipso_v4_map_cache_entry *entry = NULL; 396446fda4fSPaul Moore struct cipso_v4_map_cache_entry *old_entry = NULL; 397446fda4fSPaul Moore unsigned char *cipso_ptr; 398446fda4fSPaul Moore u32 cipso_ptr_len; 399446fda4fSPaul Moore 400446fda4fSPaul Moore if (!cipso_v4_cache_enabled || cipso_v4_cache_bucketsize <= 0) 401446fda4fSPaul Moore return 0; 402446fda4fSPaul Moore 403446fda4fSPaul Moore cipso_ptr = CIPSO_V4_OPTPTR(skb); 404446fda4fSPaul Moore cipso_ptr_len = cipso_ptr[1]; 405446fda4fSPaul Moore 406446fda4fSPaul Moore entry = kzalloc(sizeof(*entry), GFP_ATOMIC); 407446fda4fSPaul Moore if (entry == NULL) 408446fda4fSPaul Moore return -ENOMEM; 409fac5d731SArnaldo Carvalho de Melo entry->key = kmemdup(cipso_ptr, cipso_ptr_len, GFP_ATOMIC); 410446fda4fSPaul Moore if (entry->key == NULL) { 411446fda4fSPaul Moore ret_val = -ENOMEM; 412446fda4fSPaul Moore goto cache_add_failure; 413446fda4fSPaul Moore } 414446fda4fSPaul Moore entry->key_len = cipso_ptr_len; 415446fda4fSPaul Moore entry->hash = cipso_v4_map_cache_hash(cipso_ptr, cipso_ptr_len); 416ffb733c6Spaul.moore@hp.com atomic_inc(&secattr->cache->refcount); 417ffb733c6Spaul.moore@hp.com entry->lsm_data = secattr->cache; 418446fda4fSPaul Moore 419446fda4fSPaul Moore bkt = entry->hash & (CIPSO_V4_CACHE_BUCKETBITS - 1); 420609c92feSPaul Moore spin_lock_bh(&cipso_v4_cache[bkt].lock); 421446fda4fSPaul Moore if (cipso_v4_cache[bkt].size < cipso_v4_cache_bucketsize) { 422446fda4fSPaul Moore list_add(&entry->list, &cipso_v4_cache[bkt].list); 423446fda4fSPaul Moore cipso_v4_cache[bkt].size += 1; 424446fda4fSPaul Moore } else { 425446fda4fSPaul Moore old_entry = list_entry(cipso_v4_cache[bkt].list.prev, 426446fda4fSPaul Moore struct cipso_v4_map_cache_entry, list); 427446fda4fSPaul Moore list_del(&old_entry->list); 428446fda4fSPaul Moore list_add(&entry->list, &cipso_v4_cache[bkt].list); 429446fda4fSPaul Moore cipso_v4_cache_entry_free(old_entry); 430446fda4fSPaul Moore } 431609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[bkt].lock); 432446fda4fSPaul Moore 433446fda4fSPaul Moore return 0; 434446fda4fSPaul Moore 435446fda4fSPaul Moore cache_add_failure: 436446fda4fSPaul Moore if (entry) 437446fda4fSPaul Moore cipso_v4_cache_entry_free(entry); 438446fda4fSPaul Moore return ret_val; 439446fda4fSPaul Moore } 440446fda4fSPaul Moore 441446fda4fSPaul Moore /* 442446fda4fSPaul Moore * DOI List Functions 443446fda4fSPaul Moore */ 444446fda4fSPaul Moore 445446fda4fSPaul Moore /** 446446fda4fSPaul Moore * cipso_v4_doi_search - Searches for a DOI definition 447446fda4fSPaul Moore * @doi: the DOI to search for 448446fda4fSPaul Moore * 449446fda4fSPaul Moore * Description: 450446fda4fSPaul Moore * Search the DOI definition list for a DOI definition with a DOI value that 451446fda4fSPaul Moore * matches @doi. The caller is responsibile for calling rcu_read_[un]lock(). 452446fda4fSPaul Moore * Returns a pointer to the DOI definition on success and NULL on failure. 453446fda4fSPaul Moore */ 454446fda4fSPaul Moore static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi) 455446fda4fSPaul Moore { 456446fda4fSPaul Moore struct cipso_v4_doi *iter; 457446fda4fSPaul Moore 458446fda4fSPaul Moore list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list) 459446fda4fSPaul Moore if (iter->doi == doi && iter->valid) 460446fda4fSPaul Moore return iter; 461446fda4fSPaul Moore return NULL; 462446fda4fSPaul Moore } 463446fda4fSPaul Moore 464446fda4fSPaul Moore /** 465446fda4fSPaul Moore * cipso_v4_doi_add - Add a new DOI to the CIPSO protocol engine 466446fda4fSPaul Moore * @doi_def: the DOI structure 467446fda4fSPaul Moore * 468446fda4fSPaul Moore * Description: 469446fda4fSPaul Moore * The caller defines a new DOI for use by the CIPSO engine and calls this 470446fda4fSPaul Moore * function to add it to the list of acceptable domains. The caller must 471446fda4fSPaul Moore * ensure that the mapping table specified in @doi_def->map meets all of the 472446fda4fSPaul Moore * requirements of the mapping type (see cipso_ipv4.h for details). Returns 473446fda4fSPaul Moore * zero on success and non-zero on failure. 474446fda4fSPaul Moore * 475446fda4fSPaul Moore */ 476446fda4fSPaul Moore int cipso_v4_doi_add(struct cipso_v4_doi *doi_def) 477446fda4fSPaul Moore { 4786ce61a7cSPaul Moore u32 iter; 4796ce61a7cSPaul Moore 480446fda4fSPaul Moore if (doi_def == NULL || doi_def->doi == CIPSO_V4_DOI_UNKNOWN) 481446fda4fSPaul Moore return -EINVAL; 4826ce61a7cSPaul Moore for (iter = 0; iter < CIPSO_V4_TAG_MAXCNT; iter++) { 4836ce61a7cSPaul Moore switch (doi_def->tags[iter]) { 4846ce61a7cSPaul Moore case CIPSO_V4_TAG_RBITMAP: 4856ce61a7cSPaul Moore break; 486484b3669SPaul Moore case CIPSO_V4_TAG_RANGE: 487484b3669SPaul Moore if (doi_def->type != CIPSO_V4_MAP_PASS) 488484b3669SPaul Moore return -EINVAL; 489484b3669SPaul Moore break; 4906ce61a7cSPaul Moore case CIPSO_V4_TAG_INVALID: 4916ce61a7cSPaul Moore if (iter == 0) 4926ce61a7cSPaul Moore return -EINVAL; 4936ce61a7cSPaul Moore break; 494654bbc2aSPaul Moore case CIPSO_V4_TAG_ENUM: 495654bbc2aSPaul Moore if (doi_def->type != CIPSO_V4_MAP_PASS) 496654bbc2aSPaul Moore return -EINVAL; 497654bbc2aSPaul Moore break; 4986ce61a7cSPaul Moore default: 4996ce61a7cSPaul Moore return -EINVAL; 5006ce61a7cSPaul Moore } 5016ce61a7cSPaul Moore } 502446fda4fSPaul Moore 503446fda4fSPaul Moore doi_def->valid = 1; 504446fda4fSPaul Moore INIT_RCU_HEAD(&doi_def->rcu); 505446fda4fSPaul Moore INIT_LIST_HEAD(&doi_def->dom_list); 506446fda4fSPaul Moore 507446fda4fSPaul Moore spin_lock(&cipso_v4_doi_list_lock); 508446fda4fSPaul Moore if (cipso_v4_doi_search(doi_def->doi) != NULL) 5094be2700fSPaul Moore goto doi_add_failure; 510446fda4fSPaul Moore list_add_tail_rcu(&doi_def->list, &cipso_v4_doi_list); 511446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 512446fda4fSPaul Moore 513446fda4fSPaul Moore return 0; 514446fda4fSPaul Moore 5154be2700fSPaul Moore doi_add_failure: 516446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 517446fda4fSPaul Moore return -EEXIST; 518446fda4fSPaul Moore } 519446fda4fSPaul Moore 520446fda4fSPaul Moore /** 521446fda4fSPaul Moore * cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine 522446fda4fSPaul Moore * @doi: the DOI value 52332f50cdeSPaul Moore * @audit_secid: the LSM secid to use in the audit message 524446fda4fSPaul Moore * @callback: the DOI cleanup/free callback 525446fda4fSPaul Moore * 526446fda4fSPaul Moore * Description: 527446fda4fSPaul Moore * Removes a DOI definition from the CIPSO engine, @callback is called to 528446fda4fSPaul Moore * free any memory. The NetLabel routines will be called to release their own 529446fda4fSPaul Moore * LSM domain mappings as well as our own domain list. Returns zero on 530446fda4fSPaul Moore * success and negative values on failure. 531446fda4fSPaul Moore * 532446fda4fSPaul Moore */ 53332f50cdeSPaul Moore int cipso_v4_doi_remove(u32 doi, 53495d4e6beSPaul Moore struct netlbl_audit *audit_info, 53532f50cdeSPaul Moore void (*callback) (struct rcu_head * head)) 536446fda4fSPaul Moore { 537446fda4fSPaul Moore struct cipso_v4_doi *doi_def; 538446fda4fSPaul Moore struct cipso_v4_domhsh_entry *dom_iter; 539446fda4fSPaul Moore 540446fda4fSPaul Moore spin_lock(&cipso_v4_doi_list_lock); 541446fda4fSPaul Moore doi_def = cipso_v4_doi_search(doi); 5424be2700fSPaul Moore if (doi_def != NULL) { 543446fda4fSPaul Moore doi_def->valid = 0; 544446fda4fSPaul Moore list_del_rcu(&doi_def->list); 545446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 5464be2700fSPaul Moore rcu_read_lock(); 547446fda4fSPaul Moore list_for_each_entry_rcu(dom_iter, &doi_def->dom_list, list) 548446fda4fSPaul Moore if (dom_iter->valid) 54932f50cdeSPaul Moore netlbl_domhsh_remove(dom_iter->domain, 55095d4e6beSPaul Moore audit_info); 551446fda4fSPaul Moore rcu_read_unlock(); 5524be2700fSPaul Moore cipso_v4_cache_invalidate(); 553446fda4fSPaul Moore call_rcu(&doi_def->rcu, callback); 554446fda4fSPaul Moore return 0; 555446fda4fSPaul Moore } 5564be2700fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 557446fda4fSPaul Moore 558446fda4fSPaul Moore return -ENOENT; 559446fda4fSPaul Moore } 560446fda4fSPaul Moore 561446fda4fSPaul Moore /** 562446fda4fSPaul Moore * cipso_v4_doi_getdef - Returns a pointer to a valid DOI definition 563446fda4fSPaul Moore * @doi: the DOI value 564446fda4fSPaul Moore * 565446fda4fSPaul Moore * Description: 566446fda4fSPaul Moore * Searches for a valid DOI definition and if one is found it is returned to 567446fda4fSPaul Moore * the caller. Otherwise NULL is returned. The caller must ensure that 568446fda4fSPaul Moore * rcu_read_lock() is held while accessing the returned definition. 569446fda4fSPaul Moore * 570446fda4fSPaul Moore */ 571446fda4fSPaul Moore struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi) 572446fda4fSPaul Moore { 573446fda4fSPaul Moore return cipso_v4_doi_search(doi); 574446fda4fSPaul Moore } 575446fda4fSPaul Moore 576446fda4fSPaul Moore /** 577fcd48280SPaul Moore * cipso_v4_doi_walk - Iterate through the DOI definitions 578fcd48280SPaul Moore * @skip_cnt: skip past this number of DOI definitions, updated 579fcd48280SPaul Moore * @callback: callback for each DOI definition 580fcd48280SPaul Moore * @cb_arg: argument for the callback function 581446fda4fSPaul Moore * 582446fda4fSPaul Moore * Description: 583fcd48280SPaul Moore * Iterate over the DOI definition list, skipping the first @skip_cnt entries. 584fcd48280SPaul Moore * For each entry call @callback, if @callback returns a negative value stop 585fcd48280SPaul Moore * 'walking' through the list and return. Updates the value in @skip_cnt upon 586fcd48280SPaul Moore * return. Returns zero on success, negative values on failure. 587446fda4fSPaul Moore * 588446fda4fSPaul Moore */ 589fcd48280SPaul Moore int cipso_v4_doi_walk(u32 *skip_cnt, 590fcd48280SPaul Moore int (*callback) (struct cipso_v4_doi *doi_def, void *arg), 591fcd48280SPaul Moore void *cb_arg) 592446fda4fSPaul Moore { 593fcd48280SPaul Moore int ret_val = -ENOENT; 594446fda4fSPaul Moore u32 doi_cnt = 0; 595fcd48280SPaul Moore struct cipso_v4_doi *iter_doi; 596446fda4fSPaul Moore 597446fda4fSPaul Moore rcu_read_lock(); 598fcd48280SPaul Moore list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list) 599fcd48280SPaul Moore if (iter_doi->valid) { 600fcd48280SPaul Moore if (doi_cnt++ < *skip_cnt) 601fcd48280SPaul Moore continue; 602fcd48280SPaul Moore ret_val = callback(iter_doi, cb_arg); 603fcd48280SPaul Moore if (ret_val < 0) { 604fcd48280SPaul Moore doi_cnt--; 605fcd48280SPaul Moore goto doi_walk_return; 606446fda4fSPaul Moore } 607446fda4fSPaul Moore } 608446fda4fSPaul Moore 609fcd48280SPaul Moore doi_walk_return: 610446fda4fSPaul Moore rcu_read_unlock(); 611fcd48280SPaul Moore *skip_cnt = doi_cnt; 612fcd48280SPaul Moore return ret_val; 613446fda4fSPaul Moore } 614446fda4fSPaul Moore 615446fda4fSPaul Moore /** 616446fda4fSPaul Moore * cipso_v4_doi_domhsh_add - Adds a domain entry to a DOI definition 617446fda4fSPaul Moore * @doi_def: the DOI definition 618446fda4fSPaul Moore * @domain: the domain to add 619446fda4fSPaul Moore * 620446fda4fSPaul Moore * Description: 62159c51591SMichael Opdenacker * Adds the @domain to the DOI specified by @doi_def, this function 622446fda4fSPaul Moore * should only be called by external functions (i.e. NetLabel). This function 623446fda4fSPaul Moore * does allocate memory. Returns zero on success, negative values on failure. 624446fda4fSPaul Moore * 625446fda4fSPaul Moore */ 626446fda4fSPaul Moore int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain) 627446fda4fSPaul Moore { 628446fda4fSPaul Moore struct cipso_v4_domhsh_entry *iter; 629446fda4fSPaul Moore struct cipso_v4_domhsh_entry *new_dom; 630446fda4fSPaul Moore 631446fda4fSPaul Moore new_dom = kzalloc(sizeof(*new_dom), GFP_KERNEL); 632446fda4fSPaul Moore if (new_dom == NULL) 633446fda4fSPaul Moore return -ENOMEM; 634446fda4fSPaul Moore if (domain) { 635446fda4fSPaul Moore new_dom->domain = kstrdup(domain, GFP_KERNEL); 636446fda4fSPaul Moore if (new_dom->domain == NULL) { 637446fda4fSPaul Moore kfree(new_dom); 638446fda4fSPaul Moore return -ENOMEM; 639446fda4fSPaul Moore } 640446fda4fSPaul Moore } 641446fda4fSPaul Moore new_dom->valid = 1; 642446fda4fSPaul Moore INIT_RCU_HEAD(&new_dom->rcu); 643446fda4fSPaul Moore 644446fda4fSPaul Moore spin_lock(&cipso_v4_doi_list_lock); 6454be2700fSPaul Moore list_for_each_entry(iter, &doi_def->dom_list, list) 646446fda4fSPaul Moore if (iter->valid && 647446fda4fSPaul Moore ((domain != NULL && iter->domain != NULL && 648446fda4fSPaul Moore strcmp(iter->domain, domain) == 0) || 649446fda4fSPaul Moore (domain == NULL && iter->domain == NULL))) { 650446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 651446fda4fSPaul Moore kfree(new_dom->domain); 652446fda4fSPaul Moore kfree(new_dom); 653446fda4fSPaul Moore return -EEXIST; 654446fda4fSPaul Moore } 655446fda4fSPaul Moore list_add_tail_rcu(&new_dom->list, &doi_def->dom_list); 656446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 657446fda4fSPaul Moore 658446fda4fSPaul Moore return 0; 659446fda4fSPaul Moore } 660446fda4fSPaul Moore 661446fda4fSPaul Moore /** 662446fda4fSPaul Moore * cipso_v4_doi_domhsh_remove - Removes a domain entry from a DOI definition 663446fda4fSPaul Moore * @doi_def: the DOI definition 664446fda4fSPaul Moore * @domain: the domain to remove 665446fda4fSPaul Moore * 666446fda4fSPaul Moore * Description: 667446fda4fSPaul Moore * Removes the @domain from the DOI specified by @doi_def, this function 668446fda4fSPaul Moore * should only be called by external functions (i.e. NetLabel). Returns zero 669446fda4fSPaul Moore * on success and negative values on error. 670446fda4fSPaul Moore * 671446fda4fSPaul Moore */ 672446fda4fSPaul Moore int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def, 673446fda4fSPaul Moore const char *domain) 674446fda4fSPaul Moore { 675446fda4fSPaul Moore struct cipso_v4_domhsh_entry *iter; 676446fda4fSPaul Moore 677446fda4fSPaul Moore spin_lock(&cipso_v4_doi_list_lock); 6784be2700fSPaul Moore list_for_each_entry(iter, &doi_def->dom_list, list) 679446fda4fSPaul Moore if (iter->valid && 680446fda4fSPaul Moore ((domain != NULL && iter->domain != NULL && 681446fda4fSPaul Moore strcmp(iter->domain, domain) == 0) || 682446fda4fSPaul Moore (domain == NULL && iter->domain == NULL))) { 683446fda4fSPaul Moore iter->valid = 0; 684446fda4fSPaul Moore list_del_rcu(&iter->list); 685446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 686446fda4fSPaul Moore call_rcu(&iter->rcu, cipso_v4_doi_domhsh_free); 687446fda4fSPaul Moore return 0; 688446fda4fSPaul Moore } 689446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 690446fda4fSPaul Moore 691446fda4fSPaul Moore return -ENOENT; 692446fda4fSPaul Moore } 693446fda4fSPaul Moore 694446fda4fSPaul Moore /* 695446fda4fSPaul Moore * Label Mapping Functions 696446fda4fSPaul Moore */ 697446fda4fSPaul Moore 698446fda4fSPaul Moore /** 699446fda4fSPaul Moore * cipso_v4_map_lvl_valid - Checks to see if the given level is understood 700446fda4fSPaul Moore * @doi_def: the DOI definition 701446fda4fSPaul Moore * @level: the level to check 702446fda4fSPaul Moore * 703446fda4fSPaul Moore * Description: 704446fda4fSPaul Moore * Checks the given level against the given DOI definition and returns a 705446fda4fSPaul Moore * negative value if the level does not have a valid mapping and a zero value 706446fda4fSPaul Moore * if the level is defined by the DOI. 707446fda4fSPaul Moore * 708446fda4fSPaul Moore */ 709446fda4fSPaul Moore static int cipso_v4_map_lvl_valid(const struct cipso_v4_doi *doi_def, u8 level) 710446fda4fSPaul Moore { 711446fda4fSPaul Moore switch (doi_def->type) { 712446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 713446fda4fSPaul Moore return 0; 714446fda4fSPaul Moore case CIPSO_V4_MAP_STD: 715446fda4fSPaul Moore if (doi_def->map.std->lvl.cipso[level] < CIPSO_V4_INV_LVL) 716446fda4fSPaul Moore return 0; 717446fda4fSPaul Moore break; 718446fda4fSPaul Moore } 719446fda4fSPaul Moore 720446fda4fSPaul Moore return -EFAULT; 721446fda4fSPaul Moore } 722446fda4fSPaul Moore 723446fda4fSPaul Moore /** 724446fda4fSPaul Moore * cipso_v4_map_lvl_hton - Perform a level mapping from the host to the network 725446fda4fSPaul Moore * @doi_def: the DOI definition 726446fda4fSPaul Moore * @host_lvl: the host MLS level 727446fda4fSPaul Moore * @net_lvl: the network/CIPSO MLS level 728446fda4fSPaul Moore * 729446fda4fSPaul Moore * Description: 730446fda4fSPaul Moore * Perform a label mapping to translate a local MLS level to the correct 731446fda4fSPaul Moore * CIPSO level using the given DOI definition. Returns zero on success, 732446fda4fSPaul Moore * negative values otherwise. 733446fda4fSPaul Moore * 734446fda4fSPaul Moore */ 735446fda4fSPaul Moore static int cipso_v4_map_lvl_hton(const struct cipso_v4_doi *doi_def, 736446fda4fSPaul Moore u32 host_lvl, 737446fda4fSPaul Moore u32 *net_lvl) 738446fda4fSPaul Moore { 739446fda4fSPaul Moore switch (doi_def->type) { 740446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 741446fda4fSPaul Moore *net_lvl = host_lvl; 742446fda4fSPaul Moore return 0; 743446fda4fSPaul Moore case CIPSO_V4_MAP_STD: 744c6387a86SPaul Moore if (host_lvl < doi_def->map.std->lvl.local_size && 745c6387a86SPaul Moore doi_def->map.std->lvl.local[host_lvl] < CIPSO_V4_INV_LVL) { 746446fda4fSPaul Moore *net_lvl = doi_def->map.std->lvl.local[host_lvl]; 747446fda4fSPaul Moore return 0; 748446fda4fSPaul Moore } 749c6387a86SPaul Moore return -EPERM; 750446fda4fSPaul Moore } 751446fda4fSPaul Moore 752446fda4fSPaul Moore return -EINVAL; 753446fda4fSPaul Moore } 754446fda4fSPaul Moore 755446fda4fSPaul Moore /** 756446fda4fSPaul Moore * cipso_v4_map_lvl_ntoh - Perform a level mapping from the network to the host 757446fda4fSPaul Moore * @doi_def: the DOI definition 758446fda4fSPaul Moore * @net_lvl: the network/CIPSO MLS level 759446fda4fSPaul Moore * @host_lvl: the host MLS level 760446fda4fSPaul Moore * 761446fda4fSPaul Moore * Description: 762446fda4fSPaul Moore * Perform a label mapping to translate a CIPSO level to the correct local MLS 763446fda4fSPaul Moore * level using the given DOI definition. Returns zero on success, negative 764446fda4fSPaul Moore * values otherwise. 765446fda4fSPaul Moore * 766446fda4fSPaul Moore */ 767446fda4fSPaul Moore static int cipso_v4_map_lvl_ntoh(const struct cipso_v4_doi *doi_def, 768446fda4fSPaul Moore u32 net_lvl, 769446fda4fSPaul Moore u32 *host_lvl) 770446fda4fSPaul Moore { 771446fda4fSPaul Moore struct cipso_v4_std_map_tbl *map_tbl; 772446fda4fSPaul Moore 773446fda4fSPaul Moore switch (doi_def->type) { 774446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 775446fda4fSPaul Moore *host_lvl = net_lvl; 776446fda4fSPaul Moore return 0; 777446fda4fSPaul Moore case CIPSO_V4_MAP_STD: 778446fda4fSPaul Moore map_tbl = doi_def->map.std; 779446fda4fSPaul Moore if (net_lvl < map_tbl->lvl.cipso_size && 780446fda4fSPaul Moore map_tbl->lvl.cipso[net_lvl] < CIPSO_V4_INV_LVL) { 781446fda4fSPaul Moore *host_lvl = doi_def->map.std->lvl.cipso[net_lvl]; 782446fda4fSPaul Moore return 0; 783446fda4fSPaul Moore } 784c6387a86SPaul Moore return -EPERM; 785446fda4fSPaul Moore } 786446fda4fSPaul Moore 787446fda4fSPaul Moore return -EINVAL; 788446fda4fSPaul Moore } 789446fda4fSPaul Moore 790446fda4fSPaul Moore /** 791446fda4fSPaul Moore * cipso_v4_map_cat_rbm_valid - Checks to see if the category bitmap is valid 792446fda4fSPaul Moore * @doi_def: the DOI definition 793446fda4fSPaul Moore * @bitmap: category bitmap 794446fda4fSPaul Moore * @bitmap_len: bitmap length in bytes 795446fda4fSPaul Moore * 796446fda4fSPaul Moore * Description: 797446fda4fSPaul Moore * Checks the given category bitmap against the given DOI definition and 798446fda4fSPaul Moore * returns a negative value if any of the categories in the bitmap do not have 799446fda4fSPaul Moore * a valid mapping and a zero value if all of the categories are valid. 800446fda4fSPaul Moore * 801446fda4fSPaul Moore */ 802446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_valid(const struct cipso_v4_doi *doi_def, 803446fda4fSPaul Moore const unsigned char *bitmap, 804446fda4fSPaul Moore u32 bitmap_len) 805446fda4fSPaul Moore { 806446fda4fSPaul Moore int cat = -1; 807446fda4fSPaul Moore u32 bitmap_len_bits = bitmap_len * 8; 808044a68edSPaul Moore u32 cipso_cat_size; 809044a68edSPaul Moore u32 *cipso_array; 810446fda4fSPaul Moore 811446fda4fSPaul Moore switch (doi_def->type) { 812446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 813446fda4fSPaul Moore return 0; 814446fda4fSPaul Moore case CIPSO_V4_MAP_STD: 815044a68edSPaul Moore cipso_cat_size = doi_def->map.std->cat.cipso_size; 816044a68edSPaul Moore cipso_array = doi_def->map.std->cat.cipso; 817446fda4fSPaul Moore for (;;) { 818446fda4fSPaul Moore cat = cipso_v4_bitmap_walk(bitmap, 819446fda4fSPaul Moore bitmap_len_bits, 820446fda4fSPaul Moore cat + 1, 821446fda4fSPaul Moore 1); 822446fda4fSPaul Moore if (cat < 0) 823446fda4fSPaul Moore break; 824446fda4fSPaul Moore if (cat >= cipso_cat_size || 825446fda4fSPaul Moore cipso_array[cat] >= CIPSO_V4_INV_CAT) 826446fda4fSPaul Moore return -EFAULT; 827446fda4fSPaul Moore } 828446fda4fSPaul Moore 829446fda4fSPaul Moore if (cat == -1) 830446fda4fSPaul Moore return 0; 831446fda4fSPaul Moore break; 832446fda4fSPaul Moore } 833446fda4fSPaul Moore 834446fda4fSPaul Moore return -EFAULT; 835446fda4fSPaul Moore } 836446fda4fSPaul Moore 837446fda4fSPaul Moore /** 838446fda4fSPaul Moore * cipso_v4_map_cat_rbm_hton - Perform a category mapping from host to network 839446fda4fSPaul Moore * @doi_def: the DOI definition 84002752760SPaul Moore * @secattr: the security attributes 841446fda4fSPaul Moore * @net_cat: the zero'd out category bitmap in network/CIPSO format 842446fda4fSPaul Moore * @net_cat_len: the length of the CIPSO bitmap in bytes 843446fda4fSPaul Moore * 844446fda4fSPaul Moore * Description: 845446fda4fSPaul Moore * Perform a label mapping to translate a local MLS category bitmap to the 846446fda4fSPaul Moore * correct CIPSO bitmap using the given DOI definition. Returns the minimum 847446fda4fSPaul Moore * size in bytes of the network bitmap on success, negative values otherwise. 848446fda4fSPaul Moore * 849446fda4fSPaul Moore */ 850446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def, 85102752760SPaul Moore const struct netlbl_lsm_secattr *secattr, 852446fda4fSPaul Moore unsigned char *net_cat, 853446fda4fSPaul Moore u32 net_cat_len) 854446fda4fSPaul Moore { 855446fda4fSPaul Moore int host_spot = -1; 85602752760SPaul Moore u32 net_spot = CIPSO_V4_INV_CAT; 857446fda4fSPaul Moore u32 net_spot_max = 0; 858446fda4fSPaul Moore u32 net_clen_bits = net_cat_len * 8; 85902752760SPaul Moore u32 host_cat_size = 0; 86002752760SPaul Moore u32 *host_cat_array = NULL; 86102752760SPaul Moore 86202752760SPaul Moore if (doi_def->type == CIPSO_V4_MAP_STD) { 86302752760SPaul Moore host_cat_size = doi_def->map.std->cat.local_size; 86402752760SPaul Moore host_cat_array = doi_def->map.std->cat.local; 86502752760SPaul Moore } 86602752760SPaul Moore 86702752760SPaul Moore for (;;) { 86802752760SPaul Moore host_spot = netlbl_secattr_catmap_walk(secattr->mls_cat, 86902752760SPaul Moore host_spot + 1); 87002752760SPaul Moore if (host_spot < 0) 87102752760SPaul Moore break; 872446fda4fSPaul Moore 873446fda4fSPaul Moore switch (doi_def->type) { 874446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 87502752760SPaul Moore net_spot = host_spot; 876446fda4fSPaul Moore break; 87702752760SPaul Moore case CIPSO_V4_MAP_STD: 878446fda4fSPaul Moore if (host_spot >= host_cat_size) 879446fda4fSPaul Moore return -EPERM; 880446fda4fSPaul Moore net_spot = host_cat_array[host_spot]; 8819fade4bfSPaul Moore if (net_spot >= CIPSO_V4_INV_CAT) 8829fade4bfSPaul Moore return -EPERM; 88302752760SPaul Moore break; 88402752760SPaul Moore } 885446fda4fSPaul Moore if (net_spot >= net_clen_bits) 886446fda4fSPaul Moore return -ENOSPC; 887446fda4fSPaul Moore cipso_v4_bitmap_setbit(net_cat, net_spot, 1); 888446fda4fSPaul Moore 889446fda4fSPaul Moore if (net_spot > net_spot_max) 890446fda4fSPaul Moore net_spot_max = net_spot; 891446fda4fSPaul Moore } 892446fda4fSPaul Moore 893446fda4fSPaul Moore if (++net_spot_max % 8) 894446fda4fSPaul Moore return net_spot_max / 8 + 1; 895446fda4fSPaul Moore return net_spot_max / 8; 896446fda4fSPaul Moore } 897446fda4fSPaul Moore 898446fda4fSPaul Moore /** 899446fda4fSPaul Moore * cipso_v4_map_cat_rbm_ntoh - Perform a category mapping from network to host 900446fda4fSPaul Moore * @doi_def: the DOI definition 901446fda4fSPaul Moore * @net_cat: the category bitmap in network/CIPSO format 902446fda4fSPaul Moore * @net_cat_len: the length of the CIPSO bitmap in bytes 90302752760SPaul Moore * @secattr: the security attributes 904446fda4fSPaul Moore * 905446fda4fSPaul Moore * Description: 906446fda4fSPaul Moore * Perform a label mapping to translate a CIPSO bitmap to the correct local 90702752760SPaul Moore * MLS category bitmap using the given DOI definition. Returns zero on 90802752760SPaul Moore * success, negative values on failure. 909446fda4fSPaul Moore * 910446fda4fSPaul Moore */ 911446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def, 912446fda4fSPaul Moore const unsigned char *net_cat, 913446fda4fSPaul Moore u32 net_cat_len, 91402752760SPaul Moore struct netlbl_lsm_secattr *secattr) 915446fda4fSPaul Moore { 91602752760SPaul Moore int ret_val; 917446fda4fSPaul Moore int net_spot = -1; 91802752760SPaul Moore u32 host_spot = CIPSO_V4_INV_CAT; 919446fda4fSPaul Moore u32 net_clen_bits = net_cat_len * 8; 92002752760SPaul Moore u32 net_cat_size = 0; 92102752760SPaul Moore u32 *net_cat_array = NULL; 922446fda4fSPaul Moore 92302752760SPaul Moore if (doi_def->type == 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; 92602752760SPaul Moore } 92702752760SPaul Moore 928446fda4fSPaul Moore for (;;) { 929446fda4fSPaul Moore net_spot = cipso_v4_bitmap_walk(net_cat, 930446fda4fSPaul Moore net_clen_bits, 931446fda4fSPaul Moore net_spot + 1, 932446fda4fSPaul Moore 1); 93302752760SPaul Moore if (net_spot < 0) { 93402752760SPaul Moore if (net_spot == -2) 93502752760SPaul Moore return -EFAULT; 93602752760SPaul Moore return 0; 93702752760SPaul Moore } 938446fda4fSPaul Moore 93902752760SPaul Moore switch (doi_def->type) { 94002752760SPaul Moore case CIPSO_V4_MAP_PASS: 94102752760SPaul Moore host_spot = net_spot; 94202752760SPaul Moore break; 94302752760SPaul Moore case CIPSO_V4_MAP_STD: 94402752760SPaul Moore if (net_spot >= net_cat_size) 94502752760SPaul Moore return -EPERM; 946446fda4fSPaul Moore host_spot = net_cat_array[net_spot]; 9479fade4bfSPaul Moore if (host_spot >= CIPSO_V4_INV_CAT) 9489fade4bfSPaul Moore return -EPERM; 94902752760SPaul Moore break; 950446fda4fSPaul Moore } 95102752760SPaul Moore ret_val = netlbl_secattr_catmap_setbit(secattr->mls_cat, 95202752760SPaul Moore host_spot, 95302752760SPaul Moore GFP_ATOMIC); 95402752760SPaul Moore if (ret_val != 0) 95502752760SPaul Moore return ret_val; 956446fda4fSPaul Moore } 957446fda4fSPaul Moore 958446fda4fSPaul Moore return -EINVAL; 959446fda4fSPaul Moore } 960446fda4fSPaul Moore 961654bbc2aSPaul Moore /** 962654bbc2aSPaul Moore * cipso_v4_map_cat_enum_valid - Checks to see if the categories are valid 963654bbc2aSPaul Moore * @doi_def: the DOI definition 964654bbc2aSPaul Moore * @enumcat: category list 965654bbc2aSPaul Moore * @enumcat_len: length of the category list in bytes 966654bbc2aSPaul Moore * 967654bbc2aSPaul Moore * Description: 968654bbc2aSPaul Moore * Checks the given categories against the given DOI definition and returns a 969654bbc2aSPaul Moore * negative value if any of the categories do not have a valid mapping and a 970654bbc2aSPaul Moore * zero value if all of the categories are valid. 971654bbc2aSPaul Moore * 972654bbc2aSPaul Moore */ 973654bbc2aSPaul Moore static int cipso_v4_map_cat_enum_valid(const struct cipso_v4_doi *doi_def, 974654bbc2aSPaul Moore const unsigned char *enumcat, 975654bbc2aSPaul Moore u32 enumcat_len) 976654bbc2aSPaul Moore { 977654bbc2aSPaul Moore u16 cat; 978654bbc2aSPaul Moore int cat_prev = -1; 979654bbc2aSPaul Moore u32 iter; 980654bbc2aSPaul Moore 981654bbc2aSPaul Moore if (doi_def->type != CIPSO_V4_MAP_PASS || enumcat_len & 0x01) 982654bbc2aSPaul Moore return -EFAULT; 983654bbc2aSPaul Moore 984654bbc2aSPaul Moore for (iter = 0; iter < enumcat_len; iter += 2) { 98550e5d35cSPaul Moore cat = ntohs(get_unaligned((__be16 *)&enumcat[iter])); 986654bbc2aSPaul Moore if (cat <= cat_prev) 987654bbc2aSPaul Moore return -EFAULT; 988654bbc2aSPaul Moore cat_prev = cat; 989654bbc2aSPaul Moore } 990654bbc2aSPaul Moore 991654bbc2aSPaul Moore return 0; 992654bbc2aSPaul Moore } 993654bbc2aSPaul Moore 994654bbc2aSPaul Moore /** 995654bbc2aSPaul Moore * cipso_v4_map_cat_enum_hton - Perform a category mapping from host to network 996654bbc2aSPaul Moore * @doi_def: the DOI definition 997654bbc2aSPaul Moore * @secattr: the security attributes 998654bbc2aSPaul Moore * @net_cat: the zero'd out category list in network/CIPSO format 999654bbc2aSPaul Moore * @net_cat_len: the length of the CIPSO category list in bytes 1000654bbc2aSPaul Moore * 1001654bbc2aSPaul Moore * Description: 1002654bbc2aSPaul Moore * Perform a label mapping to translate a local MLS category bitmap to the 1003654bbc2aSPaul Moore * correct CIPSO category list using the given DOI definition. Returns the 1004654bbc2aSPaul Moore * size in bytes of the network category bitmap on success, negative values 1005654bbc2aSPaul Moore * otherwise. 1006654bbc2aSPaul Moore * 1007654bbc2aSPaul Moore */ 1008654bbc2aSPaul Moore static int cipso_v4_map_cat_enum_hton(const struct cipso_v4_doi *doi_def, 1009654bbc2aSPaul Moore const struct netlbl_lsm_secattr *secattr, 1010654bbc2aSPaul Moore unsigned char *net_cat, 1011654bbc2aSPaul Moore u32 net_cat_len) 1012654bbc2aSPaul Moore { 1013654bbc2aSPaul Moore int cat = -1; 1014654bbc2aSPaul Moore u32 cat_iter = 0; 1015654bbc2aSPaul Moore 1016654bbc2aSPaul Moore for (;;) { 1017654bbc2aSPaul Moore cat = netlbl_secattr_catmap_walk(secattr->mls_cat, cat + 1); 1018654bbc2aSPaul Moore if (cat < 0) 1019654bbc2aSPaul Moore break; 1020654bbc2aSPaul Moore if ((cat_iter + 2) > net_cat_len) 1021654bbc2aSPaul Moore return -ENOSPC; 1022654bbc2aSPaul Moore 1023654bbc2aSPaul Moore *((__be16 *)&net_cat[cat_iter]) = htons(cat); 1024654bbc2aSPaul Moore cat_iter += 2; 1025654bbc2aSPaul Moore } 1026654bbc2aSPaul Moore 1027654bbc2aSPaul Moore return cat_iter; 1028654bbc2aSPaul Moore } 1029654bbc2aSPaul Moore 1030654bbc2aSPaul Moore /** 1031654bbc2aSPaul Moore * cipso_v4_map_cat_enum_ntoh - Perform a category mapping from network to host 1032654bbc2aSPaul Moore * @doi_def: the DOI definition 1033654bbc2aSPaul Moore * @net_cat: the category list in network/CIPSO format 1034654bbc2aSPaul Moore * @net_cat_len: the length of the CIPSO bitmap in bytes 1035654bbc2aSPaul Moore * @secattr: the security attributes 1036654bbc2aSPaul Moore * 1037654bbc2aSPaul Moore * Description: 1038654bbc2aSPaul Moore * Perform a label mapping to translate a CIPSO category list to the correct 1039654bbc2aSPaul Moore * local MLS category bitmap using the given DOI definition. Returns zero on 1040654bbc2aSPaul Moore * success, negative values on failure. 1041654bbc2aSPaul Moore * 1042654bbc2aSPaul Moore */ 1043654bbc2aSPaul Moore static int cipso_v4_map_cat_enum_ntoh(const struct cipso_v4_doi *doi_def, 1044654bbc2aSPaul Moore const unsigned char *net_cat, 1045654bbc2aSPaul Moore u32 net_cat_len, 1046654bbc2aSPaul Moore struct netlbl_lsm_secattr *secattr) 1047654bbc2aSPaul Moore { 1048654bbc2aSPaul Moore int ret_val; 1049654bbc2aSPaul Moore u32 iter; 1050654bbc2aSPaul Moore 1051654bbc2aSPaul Moore for (iter = 0; iter < net_cat_len; iter += 2) { 1052654bbc2aSPaul Moore ret_val = netlbl_secattr_catmap_setbit(secattr->mls_cat, 105350e5d35cSPaul Moore ntohs(get_unaligned((__be16 *)&net_cat[iter])), 1054654bbc2aSPaul Moore GFP_ATOMIC); 1055654bbc2aSPaul Moore if (ret_val != 0) 1056654bbc2aSPaul Moore return ret_val; 1057654bbc2aSPaul Moore } 1058654bbc2aSPaul Moore 1059654bbc2aSPaul Moore return 0; 1060654bbc2aSPaul Moore } 1061654bbc2aSPaul Moore 1062484b3669SPaul Moore /** 1063484b3669SPaul Moore * cipso_v4_map_cat_rng_valid - Checks to see if the categories are valid 1064484b3669SPaul Moore * @doi_def: the DOI definition 1065484b3669SPaul Moore * @rngcat: category list 1066484b3669SPaul Moore * @rngcat_len: length of the category list in bytes 1067484b3669SPaul Moore * 1068484b3669SPaul Moore * Description: 1069484b3669SPaul Moore * Checks the given categories against the given DOI definition and returns a 1070484b3669SPaul Moore * negative value if any of the categories do not have a valid mapping and a 1071484b3669SPaul Moore * zero value if all of the categories are valid. 1072484b3669SPaul Moore * 1073484b3669SPaul Moore */ 1074484b3669SPaul Moore static int cipso_v4_map_cat_rng_valid(const struct cipso_v4_doi *doi_def, 1075484b3669SPaul Moore const unsigned char *rngcat, 1076484b3669SPaul Moore u32 rngcat_len) 1077484b3669SPaul Moore { 1078484b3669SPaul Moore u16 cat_high; 1079484b3669SPaul Moore u16 cat_low; 1080484b3669SPaul Moore u32 cat_prev = CIPSO_V4_MAX_REM_CATS + 1; 1081484b3669SPaul Moore u32 iter; 1082484b3669SPaul Moore 1083484b3669SPaul Moore if (doi_def->type != CIPSO_V4_MAP_PASS || rngcat_len & 0x01) 1084484b3669SPaul Moore return -EFAULT; 1085484b3669SPaul Moore 1086484b3669SPaul Moore for (iter = 0; iter < rngcat_len; iter += 4) { 108750e5d35cSPaul Moore cat_high = ntohs(get_unaligned((__be16 *)&rngcat[iter])); 1088484b3669SPaul Moore if ((iter + 4) <= rngcat_len) 108950e5d35cSPaul Moore cat_low = ntohs( 109050e5d35cSPaul Moore get_unaligned((__be16 *)&rngcat[iter + 2])); 1091484b3669SPaul Moore else 1092484b3669SPaul Moore cat_low = 0; 1093484b3669SPaul Moore 1094484b3669SPaul Moore if (cat_high > cat_prev) 1095484b3669SPaul Moore return -EFAULT; 1096484b3669SPaul Moore 1097484b3669SPaul Moore cat_prev = cat_low; 1098484b3669SPaul Moore } 1099484b3669SPaul Moore 1100484b3669SPaul Moore return 0; 1101484b3669SPaul Moore } 1102484b3669SPaul Moore 1103484b3669SPaul Moore /** 1104484b3669SPaul Moore * cipso_v4_map_cat_rng_hton - Perform a category mapping from host to network 1105484b3669SPaul Moore * @doi_def: the DOI definition 1106484b3669SPaul Moore * @secattr: the security attributes 1107484b3669SPaul Moore * @net_cat: the zero'd out category list in network/CIPSO format 1108484b3669SPaul Moore * @net_cat_len: the length of the CIPSO category list in bytes 1109484b3669SPaul Moore * 1110484b3669SPaul Moore * Description: 1111484b3669SPaul Moore * Perform a label mapping to translate a local MLS category bitmap to the 1112484b3669SPaul Moore * correct CIPSO category list using the given DOI definition. Returns the 1113484b3669SPaul Moore * size in bytes of the network category bitmap on success, negative values 1114484b3669SPaul Moore * otherwise. 1115484b3669SPaul Moore * 1116484b3669SPaul Moore */ 1117484b3669SPaul Moore static int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def, 1118484b3669SPaul Moore const struct netlbl_lsm_secattr *secattr, 1119484b3669SPaul Moore unsigned char *net_cat, 1120484b3669SPaul Moore u32 net_cat_len) 1121484b3669SPaul Moore { 1122484b3669SPaul Moore int iter = -1; 1123f998e8cbSPaul Moore u16 array[CIPSO_V4_TAG_RNG_CAT_MAX * 2]; 1124484b3669SPaul Moore u32 array_cnt = 0; 1125484b3669SPaul Moore u32 cat_size = 0; 1126484b3669SPaul Moore 1127f998e8cbSPaul Moore /* make sure we don't overflow the 'array[]' variable */ 1128128c6b6cSPaul Moore if (net_cat_len > 1129128c6b6cSPaul Moore (CIPSO_V4_OPT_LEN_MAX - CIPSO_V4_HDR_LEN - CIPSO_V4_TAG_RNG_BLEN)) 1130128c6b6cSPaul Moore return -ENOSPC; 1131484b3669SPaul Moore 1132484b3669SPaul Moore for (;;) { 1133484b3669SPaul Moore iter = netlbl_secattr_catmap_walk(secattr->mls_cat, iter + 1); 1134484b3669SPaul Moore if (iter < 0) 1135484b3669SPaul Moore break; 1136484b3669SPaul Moore cat_size += (iter == 0 ? 0 : sizeof(u16)); 1137484b3669SPaul Moore if (cat_size > net_cat_len) 1138484b3669SPaul Moore return -ENOSPC; 1139484b3669SPaul Moore array[array_cnt++] = iter; 1140484b3669SPaul Moore 1141484b3669SPaul Moore iter = netlbl_secattr_catmap_walk_rng(secattr->mls_cat, iter); 1142484b3669SPaul Moore if (iter < 0) 1143484b3669SPaul Moore return -EFAULT; 1144484b3669SPaul Moore cat_size += sizeof(u16); 1145484b3669SPaul Moore if (cat_size > net_cat_len) 1146484b3669SPaul Moore return -ENOSPC; 1147484b3669SPaul Moore array[array_cnt++] = iter; 1148484b3669SPaul Moore } 1149484b3669SPaul Moore 1150484b3669SPaul Moore for (iter = 0; array_cnt > 0;) { 1151484b3669SPaul Moore *((__be16 *)&net_cat[iter]) = htons(array[--array_cnt]); 1152484b3669SPaul Moore iter += 2; 1153484b3669SPaul Moore array_cnt--; 1154484b3669SPaul Moore if (array[array_cnt] != 0) { 1155484b3669SPaul Moore *((__be16 *)&net_cat[iter]) = htons(array[array_cnt]); 1156484b3669SPaul Moore iter += 2; 1157484b3669SPaul Moore } 1158484b3669SPaul Moore } 1159484b3669SPaul Moore 1160484b3669SPaul Moore return cat_size; 1161484b3669SPaul Moore } 1162484b3669SPaul Moore 1163484b3669SPaul Moore /** 1164484b3669SPaul Moore * cipso_v4_map_cat_rng_ntoh - Perform a category mapping from network to host 1165484b3669SPaul Moore * @doi_def: the DOI definition 1166484b3669SPaul Moore * @net_cat: the category list in network/CIPSO format 1167484b3669SPaul Moore * @net_cat_len: the length of the CIPSO bitmap in bytes 1168484b3669SPaul Moore * @secattr: the security attributes 1169484b3669SPaul Moore * 1170484b3669SPaul Moore * Description: 1171484b3669SPaul Moore * Perform a label mapping to translate a CIPSO category list to the correct 1172484b3669SPaul Moore * local MLS category bitmap using the given DOI definition. Returns zero on 1173484b3669SPaul Moore * success, negative values on failure. 1174484b3669SPaul Moore * 1175484b3669SPaul Moore */ 1176484b3669SPaul Moore static int cipso_v4_map_cat_rng_ntoh(const struct cipso_v4_doi *doi_def, 1177484b3669SPaul Moore const unsigned char *net_cat, 1178484b3669SPaul Moore u32 net_cat_len, 1179484b3669SPaul Moore struct netlbl_lsm_secattr *secattr) 1180484b3669SPaul Moore { 1181484b3669SPaul Moore int ret_val; 1182484b3669SPaul Moore u32 net_iter; 1183484b3669SPaul Moore u16 cat_low; 1184484b3669SPaul Moore u16 cat_high; 1185484b3669SPaul Moore 1186484b3669SPaul Moore for (net_iter = 0; net_iter < net_cat_len; net_iter += 4) { 118750e5d35cSPaul Moore cat_high = ntohs(get_unaligned((__be16 *)&net_cat[net_iter])); 1188484b3669SPaul Moore if ((net_iter + 4) <= net_cat_len) 118950e5d35cSPaul Moore cat_low = ntohs( 119050e5d35cSPaul Moore get_unaligned((__be16 *)&net_cat[net_iter + 2])); 1191484b3669SPaul Moore else 1192484b3669SPaul Moore cat_low = 0; 1193484b3669SPaul Moore 1194484b3669SPaul Moore ret_val = netlbl_secattr_catmap_setrng(secattr->mls_cat, 1195484b3669SPaul Moore cat_low, 1196484b3669SPaul Moore cat_high, 1197484b3669SPaul Moore GFP_ATOMIC); 1198484b3669SPaul Moore if (ret_val != 0) 1199484b3669SPaul Moore return ret_val; 1200484b3669SPaul Moore } 1201484b3669SPaul Moore 1202484b3669SPaul Moore return 0; 1203484b3669SPaul Moore } 1204484b3669SPaul Moore 1205446fda4fSPaul Moore /* 1206446fda4fSPaul Moore * Protocol Handling Functions 1207446fda4fSPaul Moore */ 1208446fda4fSPaul Moore 1209446fda4fSPaul Moore /** 1210446fda4fSPaul Moore * cipso_v4_gentag_hdr - Generate a CIPSO option header 1211446fda4fSPaul Moore * @doi_def: the DOI definition 121291b1ed0aSPaul Moore * @len: the total tag length in bytes, not including this header 1213446fda4fSPaul Moore * @buf: the CIPSO option buffer 1214446fda4fSPaul Moore * 1215446fda4fSPaul Moore * Description: 121691b1ed0aSPaul Moore * Write a CIPSO header into the beginning of @buffer. 1217446fda4fSPaul Moore * 1218446fda4fSPaul Moore */ 121991b1ed0aSPaul Moore static void cipso_v4_gentag_hdr(const struct cipso_v4_doi *doi_def, 122091b1ed0aSPaul Moore unsigned char *buf, 122191b1ed0aSPaul Moore u32 len) 1222446fda4fSPaul Moore { 1223446fda4fSPaul Moore buf[0] = IPOPT_CIPSO; 1224446fda4fSPaul Moore buf[1] = CIPSO_V4_HDR_LEN + len; 1225714e85beSAl Viro *(__be32 *)&buf[2] = htonl(doi_def->doi); 1226446fda4fSPaul Moore } 1227446fda4fSPaul Moore 1228446fda4fSPaul Moore /** 1229446fda4fSPaul Moore * cipso_v4_gentag_rbm - Generate a CIPSO restricted bitmap tag (type #1) 1230446fda4fSPaul Moore * @doi_def: the DOI definition 1231446fda4fSPaul Moore * @secattr: the security attributes 1232446fda4fSPaul Moore * @buffer: the option buffer 1233446fda4fSPaul Moore * @buffer_len: length of buffer in bytes 1234446fda4fSPaul Moore * 1235446fda4fSPaul Moore * Description: 1236446fda4fSPaul Moore * Generate a CIPSO option using the restricted bitmap tag, tag type #1. The 1237446fda4fSPaul Moore * actual buffer length may be larger than the indicated size due to 123891b1ed0aSPaul Moore * translation between host and network category bitmaps. Returns the size of 123991b1ed0aSPaul Moore * the tag on success, negative values on failure. 1240446fda4fSPaul Moore * 1241446fda4fSPaul Moore */ 1242446fda4fSPaul Moore static int cipso_v4_gentag_rbm(const struct cipso_v4_doi *doi_def, 1243446fda4fSPaul Moore const struct netlbl_lsm_secattr *secattr, 124491b1ed0aSPaul Moore unsigned char *buffer, 124591b1ed0aSPaul Moore u32 buffer_len) 1246446fda4fSPaul Moore { 1247701a90baSPaul Moore int ret_val; 124891b1ed0aSPaul Moore u32 tag_len; 1249446fda4fSPaul Moore u32 level; 1250446fda4fSPaul Moore 1251701a90baSPaul Moore if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0) 1252701a90baSPaul Moore return -EPERM; 1253701a90baSPaul Moore 125491b1ed0aSPaul Moore ret_val = cipso_v4_map_lvl_hton(doi_def, secattr->mls_lvl, &level); 125591b1ed0aSPaul Moore if (ret_val != 0) 125691b1ed0aSPaul Moore return ret_val; 1257446fda4fSPaul Moore 125891b1ed0aSPaul Moore if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { 1259446fda4fSPaul Moore ret_val = cipso_v4_map_cat_rbm_hton(doi_def, 126002752760SPaul Moore secattr, 126191b1ed0aSPaul Moore &buffer[4], 126291b1ed0aSPaul Moore buffer_len - 4); 1263446fda4fSPaul Moore if (ret_val < 0) 126491b1ed0aSPaul Moore return ret_val; 1265446fda4fSPaul Moore 1266446fda4fSPaul Moore /* This will send packets using the "optimized" format when 1267446fda4fSPaul Moore * possibile as specified in section 3.4.2.6 of the 1268446fda4fSPaul Moore * CIPSO draft. */ 1269701a90baSPaul Moore if (cipso_v4_rbm_optfmt && ret_val > 0 && ret_val <= 10) 127091b1ed0aSPaul Moore tag_len = 14; 1271701a90baSPaul Moore else 127291b1ed0aSPaul Moore tag_len = 4 + ret_val; 127391b1ed0aSPaul Moore } else 127491b1ed0aSPaul Moore tag_len = 4; 1275446fda4fSPaul Moore 127691b1ed0aSPaul Moore buffer[0] = 0x01; 127791b1ed0aSPaul Moore buffer[1] = tag_len; 127891b1ed0aSPaul Moore buffer[3] = level; 1279446fda4fSPaul Moore 128091b1ed0aSPaul Moore return tag_len; 1281446fda4fSPaul Moore } 1282446fda4fSPaul Moore 1283446fda4fSPaul Moore /** 1284446fda4fSPaul Moore * cipso_v4_parsetag_rbm - Parse a CIPSO restricted bitmap tag 1285446fda4fSPaul Moore * @doi_def: the DOI definition 1286446fda4fSPaul Moore * @tag: the CIPSO tag 1287446fda4fSPaul Moore * @secattr: the security attributes 1288446fda4fSPaul Moore * 1289446fda4fSPaul Moore * Description: 1290446fda4fSPaul Moore * Parse a CIPSO restricted bitmap tag (tag type #1) and return the security 1291446fda4fSPaul Moore * attributes in @secattr. Return zero on success, negatives values on 1292446fda4fSPaul Moore * failure. 1293446fda4fSPaul Moore * 1294446fda4fSPaul Moore */ 1295446fda4fSPaul Moore static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def, 1296446fda4fSPaul Moore const unsigned char *tag, 1297446fda4fSPaul Moore struct netlbl_lsm_secattr *secattr) 1298446fda4fSPaul Moore { 1299446fda4fSPaul Moore int ret_val; 1300446fda4fSPaul Moore u8 tag_len = tag[1]; 1301446fda4fSPaul Moore u32 level; 1302446fda4fSPaul Moore 1303446fda4fSPaul Moore ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level); 1304446fda4fSPaul Moore if (ret_val != 0) 1305446fda4fSPaul Moore return ret_val; 1306446fda4fSPaul Moore secattr->mls_lvl = level; 1307701a90baSPaul Moore secattr->flags |= NETLBL_SECATTR_MLS_LVL; 1308446fda4fSPaul Moore 1309446fda4fSPaul Moore if (tag_len > 4) { 131002752760SPaul Moore secattr->mls_cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); 1311446fda4fSPaul Moore if (secattr->mls_cat == NULL) 1312446fda4fSPaul Moore return -ENOMEM; 1313446fda4fSPaul Moore 1314446fda4fSPaul Moore ret_val = cipso_v4_map_cat_rbm_ntoh(doi_def, 1315446fda4fSPaul Moore &tag[4], 1316446fda4fSPaul Moore tag_len - 4, 131702752760SPaul Moore secattr); 131802752760SPaul Moore if (ret_val != 0) { 131902752760SPaul Moore netlbl_secattr_catmap_free(secattr->mls_cat); 1320446fda4fSPaul Moore return ret_val; 1321701a90baSPaul Moore } 132202752760SPaul Moore 132302752760SPaul Moore secattr->flags |= NETLBL_SECATTR_MLS_CAT; 1324446fda4fSPaul Moore } 1325446fda4fSPaul Moore 1326446fda4fSPaul Moore return 0; 1327446fda4fSPaul Moore } 1328446fda4fSPaul Moore 1329446fda4fSPaul Moore /** 1330654bbc2aSPaul Moore * cipso_v4_gentag_enum - Generate a CIPSO enumerated tag (type #2) 1331654bbc2aSPaul Moore * @doi_def: the DOI definition 1332654bbc2aSPaul Moore * @secattr: the security attributes 1333654bbc2aSPaul Moore * @buffer: the option buffer 1334654bbc2aSPaul Moore * @buffer_len: length of buffer in bytes 1335654bbc2aSPaul Moore * 1336654bbc2aSPaul Moore * Description: 1337654bbc2aSPaul Moore * Generate a CIPSO option using the enumerated tag, tag type #2. Returns the 1338654bbc2aSPaul Moore * size of the tag on success, negative values on failure. 1339654bbc2aSPaul Moore * 1340654bbc2aSPaul Moore */ 1341654bbc2aSPaul Moore static int cipso_v4_gentag_enum(const struct cipso_v4_doi *doi_def, 1342654bbc2aSPaul Moore const struct netlbl_lsm_secattr *secattr, 1343654bbc2aSPaul Moore unsigned char *buffer, 1344654bbc2aSPaul Moore u32 buffer_len) 1345654bbc2aSPaul Moore { 1346654bbc2aSPaul Moore int ret_val; 1347654bbc2aSPaul Moore u32 tag_len; 1348654bbc2aSPaul Moore u32 level; 1349654bbc2aSPaul Moore 1350654bbc2aSPaul Moore if (!(secattr->flags & NETLBL_SECATTR_MLS_LVL)) 1351654bbc2aSPaul Moore return -EPERM; 1352654bbc2aSPaul Moore 1353654bbc2aSPaul Moore ret_val = cipso_v4_map_lvl_hton(doi_def, secattr->mls_lvl, &level); 1354654bbc2aSPaul Moore if (ret_val != 0) 1355654bbc2aSPaul Moore return ret_val; 1356654bbc2aSPaul Moore 1357654bbc2aSPaul Moore if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { 1358654bbc2aSPaul Moore ret_val = cipso_v4_map_cat_enum_hton(doi_def, 1359654bbc2aSPaul Moore secattr, 1360654bbc2aSPaul Moore &buffer[4], 1361654bbc2aSPaul Moore buffer_len - 4); 1362654bbc2aSPaul Moore if (ret_val < 0) 1363654bbc2aSPaul Moore return ret_val; 1364654bbc2aSPaul Moore 1365654bbc2aSPaul Moore tag_len = 4 + ret_val; 1366654bbc2aSPaul Moore } else 1367654bbc2aSPaul Moore tag_len = 4; 1368654bbc2aSPaul Moore 1369654bbc2aSPaul Moore buffer[0] = 0x02; 1370654bbc2aSPaul Moore buffer[1] = tag_len; 1371654bbc2aSPaul Moore buffer[3] = level; 1372654bbc2aSPaul Moore 1373654bbc2aSPaul Moore return tag_len; 1374654bbc2aSPaul Moore } 1375654bbc2aSPaul Moore 1376654bbc2aSPaul Moore /** 1377654bbc2aSPaul Moore * cipso_v4_parsetag_enum - Parse a CIPSO enumerated tag 1378654bbc2aSPaul Moore * @doi_def: the DOI definition 1379654bbc2aSPaul Moore * @tag: the CIPSO tag 1380654bbc2aSPaul Moore * @secattr: the security attributes 1381654bbc2aSPaul Moore * 1382654bbc2aSPaul Moore * Description: 1383654bbc2aSPaul Moore * Parse a CIPSO enumerated tag (tag type #2) and return the security 1384654bbc2aSPaul Moore * attributes in @secattr. Return zero on success, negatives values on 1385654bbc2aSPaul Moore * failure. 1386654bbc2aSPaul Moore * 1387654bbc2aSPaul Moore */ 1388654bbc2aSPaul Moore static int cipso_v4_parsetag_enum(const struct cipso_v4_doi *doi_def, 1389654bbc2aSPaul Moore const unsigned char *tag, 1390654bbc2aSPaul Moore struct netlbl_lsm_secattr *secattr) 1391654bbc2aSPaul Moore { 1392654bbc2aSPaul Moore int ret_val; 1393654bbc2aSPaul Moore u8 tag_len = tag[1]; 1394654bbc2aSPaul Moore u32 level; 1395654bbc2aSPaul Moore 1396654bbc2aSPaul Moore ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level); 1397654bbc2aSPaul Moore if (ret_val != 0) 1398654bbc2aSPaul Moore return ret_val; 1399654bbc2aSPaul Moore secattr->mls_lvl = level; 1400654bbc2aSPaul Moore secattr->flags |= NETLBL_SECATTR_MLS_LVL; 1401654bbc2aSPaul Moore 1402654bbc2aSPaul Moore if (tag_len > 4) { 1403654bbc2aSPaul Moore secattr->mls_cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); 1404654bbc2aSPaul Moore if (secattr->mls_cat == NULL) 1405654bbc2aSPaul Moore return -ENOMEM; 1406654bbc2aSPaul Moore 1407654bbc2aSPaul Moore ret_val = cipso_v4_map_cat_enum_ntoh(doi_def, 1408654bbc2aSPaul Moore &tag[4], 1409654bbc2aSPaul Moore tag_len - 4, 1410654bbc2aSPaul Moore secattr); 1411654bbc2aSPaul Moore if (ret_val != 0) { 1412654bbc2aSPaul Moore netlbl_secattr_catmap_free(secattr->mls_cat); 1413654bbc2aSPaul Moore return ret_val; 1414654bbc2aSPaul Moore } 1415654bbc2aSPaul Moore 1416654bbc2aSPaul Moore secattr->flags |= NETLBL_SECATTR_MLS_CAT; 1417654bbc2aSPaul Moore } 1418654bbc2aSPaul Moore 1419654bbc2aSPaul Moore return 0; 1420654bbc2aSPaul Moore } 1421654bbc2aSPaul Moore 1422654bbc2aSPaul Moore /** 1423484b3669SPaul Moore * cipso_v4_gentag_rng - Generate a CIPSO ranged tag (type #5) 1424484b3669SPaul Moore * @doi_def: the DOI definition 1425484b3669SPaul Moore * @secattr: the security attributes 1426484b3669SPaul Moore * @buffer: the option buffer 1427484b3669SPaul Moore * @buffer_len: length of buffer in bytes 1428484b3669SPaul Moore * 1429484b3669SPaul Moore * Description: 1430484b3669SPaul Moore * Generate a CIPSO option using the ranged tag, tag type #5. Returns the 1431484b3669SPaul Moore * size of the tag on success, negative values on failure. 1432484b3669SPaul Moore * 1433484b3669SPaul Moore */ 1434484b3669SPaul Moore static int cipso_v4_gentag_rng(const struct cipso_v4_doi *doi_def, 1435484b3669SPaul Moore const struct netlbl_lsm_secattr *secattr, 1436484b3669SPaul Moore unsigned char *buffer, 1437484b3669SPaul Moore u32 buffer_len) 1438484b3669SPaul Moore { 1439484b3669SPaul Moore int ret_val; 1440484b3669SPaul Moore u32 tag_len; 1441484b3669SPaul Moore u32 level; 1442484b3669SPaul Moore 1443484b3669SPaul Moore if (!(secattr->flags & NETLBL_SECATTR_MLS_LVL)) 1444484b3669SPaul Moore return -EPERM; 1445484b3669SPaul Moore 1446484b3669SPaul Moore ret_val = cipso_v4_map_lvl_hton(doi_def, secattr->mls_lvl, &level); 1447484b3669SPaul Moore if (ret_val != 0) 1448484b3669SPaul Moore return ret_val; 1449484b3669SPaul Moore 1450484b3669SPaul Moore if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { 1451484b3669SPaul Moore ret_val = cipso_v4_map_cat_rng_hton(doi_def, 1452484b3669SPaul Moore secattr, 1453484b3669SPaul Moore &buffer[4], 1454484b3669SPaul Moore buffer_len - 4); 1455484b3669SPaul Moore if (ret_val < 0) 1456484b3669SPaul Moore return ret_val; 1457484b3669SPaul Moore 1458484b3669SPaul Moore tag_len = 4 + ret_val; 1459484b3669SPaul Moore } else 1460484b3669SPaul Moore tag_len = 4; 1461484b3669SPaul Moore 1462484b3669SPaul Moore buffer[0] = 0x05; 1463484b3669SPaul Moore buffer[1] = tag_len; 1464484b3669SPaul Moore buffer[3] = level; 1465484b3669SPaul Moore 1466484b3669SPaul Moore return tag_len; 1467484b3669SPaul Moore } 1468484b3669SPaul Moore 1469484b3669SPaul Moore /** 1470484b3669SPaul Moore * cipso_v4_parsetag_rng - Parse a CIPSO ranged tag 1471484b3669SPaul Moore * @doi_def: the DOI definition 1472484b3669SPaul Moore * @tag: the CIPSO tag 1473484b3669SPaul Moore * @secattr: the security attributes 1474484b3669SPaul Moore * 1475484b3669SPaul Moore * Description: 1476484b3669SPaul Moore * Parse a CIPSO ranged tag (tag type #5) and return the security attributes 1477484b3669SPaul Moore * in @secattr. Return zero on success, negatives values on failure. 1478484b3669SPaul Moore * 1479484b3669SPaul Moore */ 1480484b3669SPaul Moore static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def, 1481484b3669SPaul Moore const unsigned char *tag, 1482484b3669SPaul Moore struct netlbl_lsm_secattr *secattr) 1483484b3669SPaul Moore { 1484484b3669SPaul Moore int ret_val; 1485484b3669SPaul Moore u8 tag_len = tag[1]; 1486484b3669SPaul Moore u32 level; 1487484b3669SPaul Moore 1488484b3669SPaul Moore ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level); 1489484b3669SPaul Moore if (ret_val != 0) 1490484b3669SPaul Moore return ret_val; 1491484b3669SPaul Moore secattr->mls_lvl = level; 1492484b3669SPaul Moore secattr->flags |= NETLBL_SECATTR_MLS_LVL; 1493484b3669SPaul Moore 1494484b3669SPaul Moore if (tag_len > 4) { 1495484b3669SPaul Moore secattr->mls_cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); 1496484b3669SPaul Moore if (secattr->mls_cat == NULL) 1497484b3669SPaul Moore return -ENOMEM; 1498484b3669SPaul Moore 1499484b3669SPaul Moore ret_val = cipso_v4_map_cat_rng_ntoh(doi_def, 1500484b3669SPaul Moore &tag[4], 1501484b3669SPaul Moore tag_len - 4, 1502484b3669SPaul Moore secattr); 1503484b3669SPaul Moore if (ret_val != 0) { 1504484b3669SPaul Moore netlbl_secattr_catmap_free(secattr->mls_cat); 1505484b3669SPaul Moore return ret_val; 1506484b3669SPaul Moore } 1507484b3669SPaul Moore 1508484b3669SPaul Moore secattr->flags |= NETLBL_SECATTR_MLS_CAT; 1509484b3669SPaul Moore } 1510484b3669SPaul Moore 1511484b3669SPaul Moore return 0; 1512484b3669SPaul Moore } 1513484b3669SPaul Moore 1514484b3669SPaul Moore /** 1515446fda4fSPaul Moore * cipso_v4_validate - Validate a CIPSO option 1516446fda4fSPaul Moore * @option: the start of the option, on error it is set to point to the error 1517446fda4fSPaul Moore * 1518446fda4fSPaul Moore * Description: 1519446fda4fSPaul Moore * This routine is called to validate a CIPSO option, it checks all of the 1520446fda4fSPaul Moore * fields to ensure that they are at least valid, see the draft snippet below 1521446fda4fSPaul Moore * for details. If the option is valid then a zero value is returned and 1522446fda4fSPaul Moore * the value of @option is unchanged. If the option is invalid then a 1523446fda4fSPaul Moore * non-zero value is returned and @option is adjusted to point to the 1524446fda4fSPaul Moore * offending portion of the option. From the IETF draft ... 1525446fda4fSPaul Moore * 1526446fda4fSPaul Moore * "If any field within the CIPSO options, such as the DOI identifier, is not 1527446fda4fSPaul Moore * recognized the IP datagram is discarded and an ICMP 'parameter problem' 1528446fda4fSPaul Moore * (type 12) is generated and returned. The ICMP code field is set to 'bad 1529446fda4fSPaul Moore * parameter' (code 0) and the pointer is set to the start of the CIPSO field 1530446fda4fSPaul Moore * that is unrecognized." 1531446fda4fSPaul Moore * 1532446fda4fSPaul Moore */ 1533446fda4fSPaul Moore int cipso_v4_validate(unsigned char **option) 1534446fda4fSPaul Moore { 1535446fda4fSPaul Moore unsigned char *opt = *option; 1536446fda4fSPaul Moore unsigned char *tag; 1537446fda4fSPaul Moore unsigned char opt_iter; 1538446fda4fSPaul Moore unsigned char err_offset = 0; 1539446fda4fSPaul Moore u8 opt_len; 1540446fda4fSPaul Moore u8 tag_len; 1541446fda4fSPaul Moore struct cipso_v4_doi *doi_def = NULL; 1542446fda4fSPaul Moore u32 tag_iter; 1543446fda4fSPaul Moore 1544446fda4fSPaul Moore /* caller already checks for length values that are too large */ 1545446fda4fSPaul Moore opt_len = opt[1]; 1546446fda4fSPaul Moore if (opt_len < 8) { 1547446fda4fSPaul Moore err_offset = 1; 1548446fda4fSPaul Moore goto validate_return; 1549446fda4fSPaul Moore } 1550446fda4fSPaul Moore 1551446fda4fSPaul Moore rcu_read_lock(); 155250e5d35cSPaul Moore doi_def = cipso_v4_doi_search(ntohl(get_unaligned((__be32 *)&opt[2]))); 1553446fda4fSPaul Moore if (doi_def == NULL) { 1554446fda4fSPaul Moore err_offset = 2; 1555446fda4fSPaul Moore goto validate_return_locked; 1556446fda4fSPaul Moore } 1557446fda4fSPaul Moore 1558446fda4fSPaul Moore opt_iter = 6; 1559446fda4fSPaul Moore tag = opt + opt_iter; 1560446fda4fSPaul Moore while (opt_iter < opt_len) { 1561446fda4fSPaul Moore for (tag_iter = 0; doi_def->tags[tag_iter] != tag[0];) 1562446fda4fSPaul Moore if (doi_def->tags[tag_iter] == CIPSO_V4_TAG_INVALID || 1563446fda4fSPaul Moore ++tag_iter == CIPSO_V4_TAG_MAXCNT) { 1564446fda4fSPaul Moore err_offset = opt_iter; 1565446fda4fSPaul Moore goto validate_return_locked; 1566446fda4fSPaul Moore } 1567446fda4fSPaul Moore 1568446fda4fSPaul Moore tag_len = tag[1]; 1569446fda4fSPaul Moore if (tag_len > (opt_len - opt_iter)) { 1570446fda4fSPaul Moore err_offset = opt_iter + 1; 1571446fda4fSPaul Moore goto validate_return_locked; 1572446fda4fSPaul Moore } 1573446fda4fSPaul Moore 1574446fda4fSPaul Moore switch (tag[0]) { 1575446fda4fSPaul Moore case CIPSO_V4_TAG_RBITMAP: 1576446fda4fSPaul Moore if (tag_len < 4) { 1577446fda4fSPaul Moore err_offset = opt_iter + 1; 1578446fda4fSPaul Moore goto validate_return_locked; 1579446fda4fSPaul Moore } 1580446fda4fSPaul Moore 1581446fda4fSPaul Moore /* We are already going to do all the verification 1582446fda4fSPaul Moore * necessary at the socket layer so from our point of 1583446fda4fSPaul Moore * view it is safe to turn these checks off (and less 1584446fda4fSPaul Moore * work), however, the CIPSO draft says we should do 1585446fda4fSPaul Moore * all the CIPSO validations here but it doesn't 1586446fda4fSPaul Moore * really specify _exactly_ what we need to validate 1587446fda4fSPaul Moore * ... so, just make it a sysctl tunable. */ 1588446fda4fSPaul Moore if (cipso_v4_rbm_strictvalid) { 1589446fda4fSPaul Moore if (cipso_v4_map_lvl_valid(doi_def, 1590446fda4fSPaul Moore tag[3]) < 0) { 1591446fda4fSPaul Moore err_offset = opt_iter + 3; 1592446fda4fSPaul Moore goto validate_return_locked; 1593446fda4fSPaul Moore } 1594446fda4fSPaul Moore if (tag_len > 4 && 1595446fda4fSPaul Moore cipso_v4_map_cat_rbm_valid(doi_def, 1596446fda4fSPaul Moore &tag[4], 1597446fda4fSPaul Moore tag_len - 4) < 0) { 1598446fda4fSPaul Moore err_offset = opt_iter + 4; 1599446fda4fSPaul Moore goto validate_return_locked; 1600446fda4fSPaul Moore } 1601446fda4fSPaul Moore } 1602446fda4fSPaul Moore break; 1603654bbc2aSPaul Moore case CIPSO_V4_TAG_ENUM: 1604654bbc2aSPaul Moore if (tag_len < 4) { 1605654bbc2aSPaul Moore err_offset = opt_iter + 1; 1606654bbc2aSPaul Moore goto validate_return_locked; 1607654bbc2aSPaul Moore } 1608654bbc2aSPaul Moore 1609654bbc2aSPaul Moore if (cipso_v4_map_lvl_valid(doi_def, 1610654bbc2aSPaul Moore tag[3]) < 0) { 1611654bbc2aSPaul Moore err_offset = opt_iter + 3; 1612654bbc2aSPaul Moore goto validate_return_locked; 1613654bbc2aSPaul Moore } 1614654bbc2aSPaul Moore if (tag_len > 4 && 1615654bbc2aSPaul Moore cipso_v4_map_cat_enum_valid(doi_def, 1616654bbc2aSPaul Moore &tag[4], 1617654bbc2aSPaul Moore tag_len - 4) < 0) { 1618654bbc2aSPaul Moore err_offset = opt_iter + 4; 1619654bbc2aSPaul Moore goto validate_return_locked; 1620654bbc2aSPaul Moore } 1621654bbc2aSPaul Moore break; 1622484b3669SPaul Moore case CIPSO_V4_TAG_RANGE: 1623484b3669SPaul Moore if (tag_len < 4) { 1624484b3669SPaul Moore err_offset = opt_iter + 1; 1625484b3669SPaul Moore goto validate_return_locked; 1626484b3669SPaul Moore } 1627484b3669SPaul Moore 1628484b3669SPaul Moore if (cipso_v4_map_lvl_valid(doi_def, 1629484b3669SPaul Moore tag[3]) < 0) { 1630484b3669SPaul Moore err_offset = opt_iter + 3; 1631484b3669SPaul Moore goto validate_return_locked; 1632484b3669SPaul Moore } 1633484b3669SPaul Moore if (tag_len > 4 && 1634484b3669SPaul Moore cipso_v4_map_cat_rng_valid(doi_def, 1635484b3669SPaul Moore &tag[4], 1636484b3669SPaul Moore tag_len - 4) < 0) { 1637484b3669SPaul Moore err_offset = opt_iter + 4; 1638484b3669SPaul Moore goto validate_return_locked; 1639484b3669SPaul Moore } 1640484b3669SPaul Moore break; 1641446fda4fSPaul Moore default: 1642446fda4fSPaul Moore err_offset = opt_iter; 1643446fda4fSPaul Moore goto validate_return_locked; 1644446fda4fSPaul Moore } 1645446fda4fSPaul Moore 1646446fda4fSPaul Moore tag += tag_len; 1647446fda4fSPaul Moore opt_iter += tag_len; 1648446fda4fSPaul Moore } 1649446fda4fSPaul Moore 1650446fda4fSPaul Moore validate_return_locked: 1651446fda4fSPaul Moore rcu_read_unlock(); 1652446fda4fSPaul Moore validate_return: 1653446fda4fSPaul Moore *option = opt + err_offset; 1654446fda4fSPaul Moore return err_offset; 1655446fda4fSPaul Moore } 1656446fda4fSPaul Moore 1657446fda4fSPaul Moore /** 1658446fda4fSPaul Moore * cipso_v4_error - Send the correct reponse for a bad packet 1659446fda4fSPaul Moore * @skb: the packet 1660446fda4fSPaul Moore * @error: the error code 1661446fda4fSPaul Moore * @gateway: CIPSO gateway flag 1662446fda4fSPaul Moore * 1663446fda4fSPaul Moore * Description: 1664446fda4fSPaul Moore * Based on the error code given in @error, send an ICMP error message back to 1665446fda4fSPaul Moore * the originating host. From the IETF draft ... 1666446fda4fSPaul Moore * 1667446fda4fSPaul Moore * "If the contents of the CIPSO [option] are valid but the security label is 1668446fda4fSPaul Moore * outside of the configured host or port label range, the datagram is 1669446fda4fSPaul Moore * discarded and an ICMP 'destination unreachable' (type 3) is generated and 1670446fda4fSPaul Moore * returned. The code field of the ICMP is set to 'communication with 1671446fda4fSPaul Moore * destination network administratively prohibited' (code 9) or to 1672446fda4fSPaul Moore * 'communication with destination host administratively prohibited' 1673446fda4fSPaul Moore * (code 10). The value of the code is dependent on whether the originator 1674446fda4fSPaul Moore * of the ICMP message is acting as a CIPSO host or a CIPSO gateway. The 1675446fda4fSPaul Moore * recipient of the ICMP message MUST be able to handle either value. The 1676446fda4fSPaul Moore * same procedure is performed if a CIPSO [option] can not be added to an 1677446fda4fSPaul Moore * IP packet because it is too large to fit in the IP options area." 1678446fda4fSPaul Moore * 1679446fda4fSPaul Moore * "If the error is triggered by receipt of an ICMP message, the message is 1680446fda4fSPaul Moore * discarded and no response is permitted (consistent with general ICMP 1681446fda4fSPaul Moore * processing rules)." 1682446fda4fSPaul Moore * 1683446fda4fSPaul Moore */ 1684446fda4fSPaul Moore void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway) 1685446fda4fSPaul Moore { 1686eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->protocol == IPPROTO_ICMP || error != -EACCES) 1687446fda4fSPaul Moore return; 1688446fda4fSPaul Moore 1689446fda4fSPaul Moore if (gateway) 1690446fda4fSPaul Moore icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0); 1691446fda4fSPaul Moore else 1692446fda4fSPaul Moore icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0); 1693446fda4fSPaul Moore } 1694446fda4fSPaul Moore 1695446fda4fSPaul Moore /** 1696ba6ff9f2SPaul Moore * cipso_v4_sock_setattr - Add a CIPSO option to a socket 1697ba6ff9f2SPaul Moore * @sk: the socket 1698446fda4fSPaul Moore * @doi_def: the CIPSO DOI to use 1699446fda4fSPaul Moore * @secattr: the specific security attributes of the socket 1700446fda4fSPaul Moore * 1701446fda4fSPaul Moore * Description: 1702446fda4fSPaul Moore * Set the CIPSO option on the given socket using the DOI definition and 1703446fda4fSPaul Moore * security attributes passed to the function. This function requires 1704ba6ff9f2SPaul Moore * exclusive access to @sk, which means it either needs to be in the 1705ba6ff9f2SPaul Moore * process of being created or locked. Returns zero on success and negative 1706ba6ff9f2SPaul Moore * values on failure. 1707446fda4fSPaul Moore * 1708446fda4fSPaul Moore */ 1709ba6ff9f2SPaul Moore int cipso_v4_sock_setattr(struct sock *sk, 1710446fda4fSPaul Moore const struct cipso_v4_doi *doi_def, 1711446fda4fSPaul Moore const struct netlbl_lsm_secattr *secattr) 1712446fda4fSPaul Moore { 1713446fda4fSPaul Moore int ret_val = -EPERM; 1714446fda4fSPaul Moore u32 iter; 171591b1ed0aSPaul Moore unsigned char *buf; 1716446fda4fSPaul Moore u32 buf_len = 0; 1717446fda4fSPaul Moore u32 opt_len; 1718446fda4fSPaul Moore struct ip_options *opt = NULL; 1719446fda4fSPaul Moore struct inet_sock *sk_inet; 1720446fda4fSPaul Moore struct inet_connection_sock *sk_conn; 1721446fda4fSPaul Moore 1722446fda4fSPaul Moore /* In the case of sock_create_lite(), the sock->sk field is not 1723446fda4fSPaul Moore * defined yet but it is not a problem as the only users of these 1724446fda4fSPaul Moore * "lite" PF_INET sockets are functions which do an accept() call 1725446fda4fSPaul Moore * afterwards so we will label the socket as part of the accept(). */ 1726446fda4fSPaul Moore if (sk == NULL) 1727446fda4fSPaul Moore return 0; 1728446fda4fSPaul Moore 172991b1ed0aSPaul Moore /* We allocate the maximum CIPSO option size here so we are probably 173091b1ed0aSPaul Moore * being a little wasteful, but it makes our life _much_ easier later 173191b1ed0aSPaul Moore * on and after all we are only talking about 40 bytes. */ 173291b1ed0aSPaul Moore buf_len = CIPSO_V4_OPT_LEN_MAX; 173391b1ed0aSPaul Moore buf = kmalloc(buf_len, GFP_ATOMIC); 173491b1ed0aSPaul Moore if (buf == NULL) { 173591b1ed0aSPaul Moore ret_val = -ENOMEM; 173691b1ed0aSPaul Moore goto socket_setattr_failure; 173791b1ed0aSPaul Moore } 173891b1ed0aSPaul Moore 1739446fda4fSPaul Moore /* XXX - This code assumes only one tag per CIPSO option which isn't 1740446fda4fSPaul Moore * really a good assumption to make but since we only support the MAC 1741446fda4fSPaul Moore * tags right now it is a safe assumption. */ 1742446fda4fSPaul Moore iter = 0; 1743446fda4fSPaul Moore do { 174491b1ed0aSPaul Moore memset(buf, 0, buf_len); 1745446fda4fSPaul Moore switch (doi_def->tags[iter]) { 1746446fda4fSPaul Moore case CIPSO_V4_TAG_RBITMAP: 1747446fda4fSPaul Moore ret_val = cipso_v4_gentag_rbm(doi_def, 1748446fda4fSPaul Moore secattr, 174991b1ed0aSPaul Moore &buf[CIPSO_V4_HDR_LEN], 175091b1ed0aSPaul Moore buf_len - CIPSO_V4_HDR_LEN); 1751446fda4fSPaul Moore break; 1752654bbc2aSPaul Moore case CIPSO_V4_TAG_ENUM: 1753654bbc2aSPaul Moore ret_val = cipso_v4_gentag_enum(doi_def, 1754654bbc2aSPaul Moore secattr, 1755654bbc2aSPaul Moore &buf[CIPSO_V4_HDR_LEN], 1756654bbc2aSPaul Moore buf_len - CIPSO_V4_HDR_LEN); 1757654bbc2aSPaul Moore break; 1758484b3669SPaul Moore case CIPSO_V4_TAG_RANGE: 1759484b3669SPaul Moore ret_val = cipso_v4_gentag_rng(doi_def, 1760484b3669SPaul Moore secattr, 1761484b3669SPaul Moore &buf[CIPSO_V4_HDR_LEN], 1762484b3669SPaul Moore buf_len - CIPSO_V4_HDR_LEN); 1763484b3669SPaul Moore break; 1764446fda4fSPaul Moore default: 1765446fda4fSPaul Moore ret_val = -EPERM; 1766446fda4fSPaul Moore goto socket_setattr_failure; 1767446fda4fSPaul Moore } 1768446fda4fSPaul Moore 1769446fda4fSPaul Moore iter++; 177091b1ed0aSPaul Moore } while (ret_val < 0 && 1771446fda4fSPaul Moore iter < CIPSO_V4_TAG_MAXCNT && 1772446fda4fSPaul Moore doi_def->tags[iter] != CIPSO_V4_TAG_INVALID); 177391b1ed0aSPaul Moore if (ret_val < 0) 1774446fda4fSPaul Moore goto socket_setattr_failure; 177591b1ed0aSPaul Moore cipso_v4_gentag_hdr(doi_def, buf, ret_val); 177691b1ed0aSPaul Moore buf_len = CIPSO_V4_HDR_LEN + ret_val; 1777446fda4fSPaul Moore 1778446fda4fSPaul Moore /* We can't use ip_options_get() directly because it makes a call to 1779446fda4fSPaul Moore * ip_options_get_alloc() which allocates memory with GFP_KERNEL and 1780f8687afeSPaul Moore * we won't always have CAP_NET_RAW even though we _always_ want to 1781f8687afeSPaul Moore * set the IPOPT_CIPSO option. */ 1782446fda4fSPaul Moore opt_len = (buf_len + 3) & ~3; 1783446fda4fSPaul Moore opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC); 1784446fda4fSPaul Moore if (opt == NULL) { 1785446fda4fSPaul Moore ret_val = -ENOMEM; 1786446fda4fSPaul Moore goto socket_setattr_failure; 1787446fda4fSPaul Moore } 1788446fda4fSPaul Moore memcpy(opt->__data, buf, buf_len); 1789446fda4fSPaul Moore opt->optlen = opt_len; 1790446fda4fSPaul Moore opt->is_data = 1; 1791f8687afeSPaul Moore opt->cipso = sizeof(struct iphdr); 1792446fda4fSPaul Moore kfree(buf); 1793446fda4fSPaul Moore buf = NULL; 1794446fda4fSPaul Moore 1795446fda4fSPaul Moore sk_inet = inet_sk(sk); 1796446fda4fSPaul Moore if (sk_inet->is_icsk) { 1797446fda4fSPaul Moore sk_conn = inet_csk(sk); 1798446fda4fSPaul Moore if (sk_inet->opt) 1799446fda4fSPaul Moore sk_conn->icsk_ext_hdr_len -= sk_inet->opt->optlen; 1800446fda4fSPaul Moore sk_conn->icsk_ext_hdr_len += opt->optlen; 1801446fda4fSPaul Moore sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie); 1802446fda4fSPaul Moore } 1803446fda4fSPaul Moore opt = xchg(&sk_inet->opt, opt); 1804446fda4fSPaul Moore kfree(opt); 1805446fda4fSPaul Moore 1806446fda4fSPaul Moore return 0; 1807446fda4fSPaul Moore 1808446fda4fSPaul Moore socket_setattr_failure: 1809446fda4fSPaul Moore kfree(buf); 1810446fda4fSPaul Moore kfree(opt); 1811446fda4fSPaul Moore return ret_val; 1812446fda4fSPaul Moore } 1813446fda4fSPaul Moore 1814446fda4fSPaul Moore /** 181563d804eaSPaul Moore * cipso_v4_getattr - Helper function for the cipso_v4_*_getattr functions 181663d804eaSPaul Moore * @cipso: the CIPSO v4 option 181763d804eaSPaul Moore * @secattr: the security attributes 181863d804eaSPaul Moore * 181963d804eaSPaul Moore * Description: 182063d804eaSPaul Moore * Inspect @cipso and return the security attributes in @secattr. Returns zero 182163d804eaSPaul Moore * on success and negative values on failure. 182263d804eaSPaul Moore * 182363d804eaSPaul Moore */ 182463d804eaSPaul Moore static int cipso_v4_getattr(const unsigned char *cipso, 182563d804eaSPaul Moore struct netlbl_lsm_secattr *secattr) 182663d804eaSPaul Moore { 182763d804eaSPaul Moore int ret_val = -ENOMSG; 182863d804eaSPaul Moore u32 doi; 182963d804eaSPaul Moore struct cipso_v4_doi *doi_def; 183063d804eaSPaul Moore 183163d804eaSPaul Moore if (cipso_v4_cache_check(cipso, cipso[1], secattr) == 0) 183263d804eaSPaul Moore return 0; 183363d804eaSPaul Moore 183463d804eaSPaul Moore doi = ntohl(get_unaligned((__be32 *)&cipso[2])); 183563d804eaSPaul Moore rcu_read_lock(); 183663d804eaSPaul Moore doi_def = cipso_v4_doi_search(doi); 183763d804eaSPaul Moore if (doi_def == NULL) 183863d804eaSPaul Moore goto getattr_return; 183963d804eaSPaul Moore /* XXX - This code assumes only one tag per CIPSO option which isn't 184063d804eaSPaul Moore * really a good assumption to make but since we only support the MAC 184163d804eaSPaul Moore * tags right now it is a safe assumption. */ 184263d804eaSPaul Moore switch (cipso[6]) { 184363d804eaSPaul Moore case CIPSO_V4_TAG_RBITMAP: 184463d804eaSPaul Moore ret_val = cipso_v4_parsetag_rbm(doi_def, &cipso[6], secattr); 184563d804eaSPaul Moore break; 184663d804eaSPaul Moore case CIPSO_V4_TAG_ENUM: 184763d804eaSPaul Moore ret_val = cipso_v4_parsetag_enum(doi_def, &cipso[6], secattr); 184863d804eaSPaul Moore break; 184963d804eaSPaul Moore case CIPSO_V4_TAG_RANGE: 185063d804eaSPaul Moore ret_val = cipso_v4_parsetag_rng(doi_def, &cipso[6], secattr); 185163d804eaSPaul Moore break; 185263d804eaSPaul Moore } 185363d804eaSPaul Moore 185463d804eaSPaul Moore getattr_return: 185563d804eaSPaul Moore rcu_read_unlock(); 185663d804eaSPaul Moore return ret_val; 185763d804eaSPaul Moore } 185863d804eaSPaul Moore 185963d804eaSPaul Moore /** 186014a72f53SPaul Moore * cipso_v4_sock_getattr - Get the security attributes from a sock 186114a72f53SPaul Moore * @sk: the sock 186214a72f53SPaul Moore * @secattr: the security attributes 186314a72f53SPaul Moore * 186414a72f53SPaul Moore * Description: 186514a72f53SPaul Moore * Query @sk to see if there is a CIPSO option attached to the sock and if 186614a72f53SPaul Moore * there is return the CIPSO security attributes in @secattr. This function 186714a72f53SPaul Moore * requires that @sk be locked, or privately held, but it does not do any 186814a72f53SPaul Moore * locking itself. Returns zero on success and negative values on failure. 186914a72f53SPaul Moore * 187014a72f53SPaul Moore */ 187114a72f53SPaul Moore int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) 187214a72f53SPaul Moore { 187363d804eaSPaul Moore struct ip_options *opt; 187414a72f53SPaul Moore 187563d804eaSPaul Moore opt = inet_sk(sk)->opt; 187663d804eaSPaul Moore if (opt == NULL || opt->cipso == 0) 187714a72f53SPaul Moore return -ENOMSG; 187814a72f53SPaul Moore 187963d804eaSPaul Moore return cipso_v4_getattr(opt->__data + opt->cipso - sizeof(struct iphdr), 188014a72f53SPaul Moore secattr); 188114a72f53SPaul Moore } 188214a72f53SPaul Moore 188314a72f53SPaul Moore /** 1884446fda4fSPaul Moore * cipso_v4_skbuff_getattr - Get the security attributes from the CIPSO option 1885446fda4fSPaul Moore * @skb: the packet 1886446fda4fSPaul Moore * @secattr: the security attributes 1887446fda4fSPaul Moore * 1888446fda4fSPaul Moore * Description: 1889446fda4fSPaul Moore * Parse the given packet's CIPSO option and return the security attributes. 1890446fda4fSPaul Moore * Returns zero on success and negative values on failure. 1891446fda4fSPaul Moore * 1892446fda4fSPaul Moore */ 1893446fda4fSPaul Moore int cipso_v4_skbuff_getattr(const struct sk_buff *skb, 1894446fda4fSPaul Moore struct netlbl_lsm_secattr *secattr) 1895446fda4fSPaul Moore { 189663d804eaSPaul Moore return cipso_v4_getattr(CIPSO_V4_OPTPTR(skb), secattr); 1897446fda4fSPaul Moore } 1898446fda4fSPaul Moore 1899446fda4fSPaul Moore /* 1900446fda4fSPaul Moore * Setup Functions 1901446fda4fSPaul Moore */ 1902446fda4fSPaul Moore 1903446fda4fSPaul Moore /** 1904446fda4fSPaul Moore * cipso_v4_init - Initialize the CIPSO module 1905446fda4fSPaul Moore * 1906446fda4fSPaul Moore * Description: 1907446fda4fSPaul Moore * Initialize the CIPSO module and prepare it for use. Returns zero on success 1908446fda4fSPaul Moore * and negative values on failure. 1909446fda4fSPaul Moore * 1910446fda4fSPaul Moore */ 1911446fda4fSPaul Moore static int __init cipso_v4_init(void) 1912446fda4fSPaul Moore { 1913446fda4fSPaul Moore int ret_val; 1914446fda4fSPaul Moore 1915446fda4fSPaul Moore ret_val = cipso_v4_cache_init(); 1916446fda4fSPaul Moore if (ret_val != 0) 1917446fda4fSPaul Moore panic("Failed to initialize the CIPSO/IPv4 cache (%d)\n", 1918446fda4fSPaul Moore ret_val); 1919446fda4fSPaul Moore 1920446fda4fSPaul Moore return 0; 1921446fda4fSPaul Moore } 1922446fda4fSPaul Moore 1923446fda4fSPaul Moore subsys_initcall(cipso_v4_init); 1924