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 6586c2500SPaul Moore * FIPS-188. While CIPSO never became a full IETF RFC standard many vendors 7446fda4fSPaul Moore * have chosen to adopt the protocol and over the years it has become a 8446fda4fSPaul Moore * de-facto standard for labeled networking. 9446fda4fSPaul Moore * 10586c2500SPaul Moore * The CIPSO draft specification can be found in the kernel's Documentation 11586c2500SPaul Moore * directory as well as the following URL: 12631dd1a8SJustin P. Mattock * http://tools.ietf.org/id/draft-ietf-cipso-ipsecurity-01.txt 13586c2500SPaul Moore * The FIPS-188 specification can be found at the following URL: 14586c2500SPaul Moore * http://www.itl.nist.gov/fipspubs/fip188.htm 15586c2500SPaul Moore * 16446fda4fSPaul Moore * Author: Paul Moore <paul.moore@hp.com> 17446fda4fSPaul Moore * 18446fda4fSPaul Moore */ 19446fda4fSPaul Moore 20446fda4fSPaul Moore /* 21948bf85cSPaul Moore * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008 22446fda4fSPaul Moore * 23446fda4fSPaul Moore * This program is free software; you can redistribute it and/or modify 24446fda4fSPaul Moore * it under the terms of the GNU General Public License as published by 25446fda4fSPaul Moore * the Free Software Foundation; either version 2 of the License, or 26446fda4fSPaul Moore * (at your option) any later version. 27446fda4fSPaul Moore * 28446fda4fSPaul Moore * This program is distributed in the hope that it will be useful, 29446fda4fSPaul Moore * but WITHOUT ANY WARRANTY; without even the implied warranty of 30446fda4fSPaul Moore * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 31446fda4fSPaul Moore * the GNU General Public License for more details. 32446fda4fSPaul Moore * 33446fda4fSPaul Moore * You should have received a copy of the GNU General Public License 34a99421d9SJeff Kirsher * along with this program; if not, see <http://www.gnu.org/licenses/>. 35446fda4fSPaul Moore * 36446fda4fSPaul Moore */ 37446fda4fSPaul Moore 38446fda4fSPaul Moore #include <linux/init.h> 39446fda4fSPaul Moore #include <linux/types.h> 40446fda4fSPaul Moore #include <linux/rcupdate.h> 41446fda4fSPaul Moore #include <linux/list.h> 42446fda4fSPaul Moore #include <linux/spinlock.h> 43446fda4fSPaul Moore #include <linux/string.h> 44446fda4fSPaul Moore #include <linux/jhash.h> 456c2e8ac0SPaul Moore #include <linux/audit.h> 465a0e3ad6STejun Heo #include <linux/slab.h> 47446fda4fSPaul Moore #include <net/ip.h> 48446fda4fSPaul Moore #include <net/icmp.h> 49446fda4fSPaul Moore #include <net/tcp.h> 50446fda4fSPaul Moore #include <net/netlabel.h> 51446fda4fSPaul Moore #include <net/cipso_ipv4.h> 5260063497SArun Sharma #include <linux/atomic.h> 534c787b16SFabian Frederick #include <linux/bug.h> 5450e5d35cSPaul Moore #include <asm/unaligned.h> 55446fda4fSPaul Moore 56446fda4fSPaul Moore /* List of available DOI definitions */ 57446fda4fSPaul Moore /* XXX - This currently assumes a minimal number of different DOIs in use, 58446fda4fSPaul Moore * if in practice there are a lot of different DOIs this list should 59446fda4fSPaul Moore * probably be turned into a hash table or something similar so we 60446fda4fSPaul Moore * can do quick lookups. */ 618ce11e6aSAdrian Bunk static DEFINE_SPINLOCK(cipso_v4_doi_list_lock); 621596c97aSDenis Cheng static LIST_HEAD(cipso_v4_doi_list); 63446fda4fSPaul Moore 64446fda4fSPaul Moore /* Label mapping cache */ 65446fda4fSPaul Moore int cipso_v4_cache_enabled = 1; 66446fda4fSPaul Moore int cipso_v4_cache_bucketsize = 10; 67446fda4fSPaul Moore #define CIPSO_V4_CACHE_BUCKETBITS 7 68446fda4fSPaul Moore #define CIPSO_V4_CACHE_BUCKETS (1 << CIPSO_V4_CACHE_BUCKETBITS) 69446fda4fSPaul Moore #define CIPSO_V4_CACHE_REORDERLIMIT 10 70446fda4fSPaul Moore struct cipso_v4_map_cache_bkt { 71446fda4fSPaul Moore spinlock_t lock; 72446fda4fSPaul Moore u32 size; 73446fda4fSPaul Moore struct list_head list; 74446fda4fSPaul Moore }; 75988b1343SFabian Frederick 76446fda4fSPaul Moore struct cipso_v4_map_cache_entry { 77446fda4fSPaul Moore u32 hash; 78446fda4fSPaul Moore unsigned char *key; 79446fda4fSPaul Moore size_t key_len; 80446fda4fSPaul Moore 81ffb733c6Spaul.moore@hp.com struct netlbl_lsm_cache *lsm_data; 82446fda4fSPaul Moore 83446fda4fSPaul Moore u32 activity; 84446fda4fSPaul Moore struct list_head list; 85446fda4fSPaul Moore }; 86988b1343SFabian Frederick 87988b1343SFabian Frederick static struct cipso_v4_map_cache_bkt *cipso_v4_cache; 88446fda4fSPaul Moore 89446fda4fSPaul Moore /* Restricted bitmap (tag #1) flags */ 90446fda4fSPaul Moore int cipso_v4_rbm_optfmt = 0; 91446fda4fSPaul Moore int cipso_v4_rbm_strictvalid = 1; 92446fda4fSPaul Moore 93446fda4fSPaul Moore /* 94f998e8cbSPaul Moore * Protocol Constants 95f998e8cbSPaul Moore */ 96f998e8cbSPaul Moore 97f998e8cbSPaul Moore /* Maximum size of the CIPSO IP option, derived from the fact that the maximum 98f998e8cbSPaul Moore * IPv4 header size is 60 bytes and the base IPv4 header is 20 bytes long. */ 99f998e8cbSPaul Moore #define CIPSO_V4_OPT_LEN_MAX 40 100f998e8cbSPaul Moore 101f998e8cbSPaul Moore /* Length of the base CIPSO option, this includes the option type (1 byte), the 102f998e8cbSPaul Moore * option length (1 byte), and the DOI (4 bytes). */ 103f998e8cbSPaul Moore #define CIPSO_V4_HDR_LEN 6 104f998e8cbSPaul Moore 105f998e8cbSPaul Moore /* Base length of the restrictive category bitmap tag (tag #1). */ 106f998e8cbSPaul Moore #define CIPSO_V4_TAG_RBM_BLEN 4 107f998e8cbSPaul Moore 108f998e8cbSPaul Moore /* Base length of the enumerated category tag (tag #2). */ 109f998e8cbSPaul Moore #define CIPSO_V4_TAG_ENUM_BLEN 4 110f998e8cbSPaul Moore 111f998e8cbSPaul Moore /* Base length of the ranged categories bitmap tag (tag #5). */ 112f998e8cbSPaul Moore #define CIPSO_V4_TAG_RNG_BLEN 4 113f998e8cbSPaul Moore /* The maximum number of category ranges permitted in the ranged category tag 114f998e8cbSPaul Moore * (tag #5). You may note that the IETF draft states that the maximum number 115f998e8cbSPaul Moore * of category ranges is 7, but if the low end of the last category range is 11625985edcSLucas De Marchi * zero then it is possible to fit 8 category ranges because the zero should 117f998e8cbSPaul Moore * be omitted. */ 118f998e8cbSPaul Moore #define CIPSO_V4_TAG_RNG_CAT_MAX 8 119f998e8cbSPaul Moore 12015c45f7bSPaul Moore /* Base length of the local tag (non-standard tag). 12115c45f7bSPaul Moore * Tag definition (may change between kernel versions) 12215c45f7bSPaul Moore * 12315c45f7bSPaul Moore * 0 8 16 24 32 12415c45f7bSPaul Moore * +----------+----------+----------+----------+ 12515c45f7bSPaul Moore * | 10000000 | 00000110 | 32-bit secid value | 12615c45f7bSPaul Moore * +----------+----------+----------+----------+ 12715c45f7bSPaul Moore * | in (host byte order)| 12815c45f7bSPaul Moore * +----------+----------+ 12915c45f7bSPaul Moore * 13015c45f7bSPaul Moore */ 13115c45f7bSPaul Moore #define CIPSO_V4_TAG_LOC_BLEN 6 13215c45f7bSPaul Moore 133f998e8cbSPaul Moore /* 134446fda4fSPaul Moore * Helper Functions 135446fda4fSPaul Moore */ 136446fda4fSPaul Moore 137446fda4fSPaul Moore /** 138446fda4fSPaul Moore * cipso_v4_cache_entry_free - Frees a cache entry 139446fda4fSPaul Moore * @entry: the entry to free 140446fda4fSPaul Moore * 141446fda4fSPaul Moore * Description: 142ffb733c6Spaul.moore@hp.com * This function frees the memory associated with a cache entry including the 143ffb733c6Spaul.moore@hp.com * LSM cache data if there are no longer any users, i.e. reference count == 0. 144446fda4fSPaul Moore * 145446fda4fSPaul Moore */ 146446fda4fSPaul Moore static void cipso_v4_cache_entry_free(struct cipso_v4_map_cache_entry *entry) 147446fda4fSPaul Moore { 148ffb733c6Spaul.moore@hp.com if (entry->lsm_data) 149ffb733c6Spaul.moore@hp.com netlbl_secattr_cache_free(entry->lsm_data); 150446fda4fSPaul Moore kfree(entry->key); 151446fda4fSPaul Moore kfree(entry); 152446fda4fSPaul Moore } 153446fda4fSPaul Moore 154446fda4fSPaul Moore /** 155446fda4fSPaul Moore * cipso_v4_map_cache_hash - Hashing function for the CIPSO cache 156446fda4fSPaul Moore * @key: the hash key 157446fda4fSPaul Moore * @key_len: the length of the key in bytes 158446fda4fSPaul Moore * 159446fda4fSPaul Moore * Description: 160446fda4fSPaul Moore * The CIPSO tag hashing function. Returns a 32-bit hash value. 161446fda4fSPaul Moore * 162446fda4fSPaul Moore */ 163446fda4fSPaul Moore static u32 cipso_v4_map_cache_hash(const unsigned char *key, u32 key_len) 164446fda4fSPaul Moore { 165446fda4fSPaul Moore return jhash(key, key_len, 0); 166446fda4fSPaul Moore } 167446fda4fSPaul Moore 168446fda4fSPaul Moore /* 169446fda4fSPaul Moore * Label Mapping Cache Functions 170446fda4fSPaul Moore */ 171446fda4fSPaul Moore 172446fda4fSPaul Moore /** 173446fda4fSPaul Moore * cipso_v4_cache_init - Initialize the CIPSO cache 174446fda4fSPaul Moore * 175446fda4fSPaul Moore * Description: 176446fda4fSPaul Moore * Initializes the CIPSO label mapping cache, this function should be called 177446fda4fSPaul Moore * before any of the other functions defined in this file. Returns zero on 178446fda4fSPaul Moore * success, negative values on error. 179446fda4fSPaul Moore * 180446fda4fSPaul Moore */ 181cb57659aSFabian Frederick static int __init cipso_v4_cache_init(void) 182446fda4fSPaul Moore { 183446fda4fSPaul Moore u32 iter; 184446fda4fSPaul Moore 185446fda4fSPaul Moore cipso_v4_cache = kcalloc(CIPSO_V4_CACHE_BUCKETS, 186446fda4fSPaul Moore sizeof(struct cipso_v4_map_cache_bkt), 187446fda4fSPaul Moore GFP_KERNEL); 18851456b29SIan Morris if (!cipso_v4_cache) 189446fda4fSPaul Moore return -ENOMEM; 190446fda4fSPaul Moore 191446fda4fSPaul Moore for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) { 192446fda4fSPaul Moore spin_lock_init(&cipso_v4_cache[iter].lock); 193446fda4fSPaul Moore cipso_v4_cache[iter].size = 0; 194446fda4fSPaul Moore INIT_LIST_HEAD(&cipso_v4_cache[iter].list); 195446fda4fSPaul Moore } 196446fda4fSPaul Moore 197446fda4fSPaul Moore return 0; 198446fda4fSPaul Moore } 199446fda4fSPaul Moore 200446fda4fSPaul Moore /** 201446fda4fSPaul Moore * cipso_v4_cache_invalidate - Invalidates the current CIPSO cache 202446fda4fSPaul Moore * 203446fda4fSPaul Moore * Description: 204446fda4fSPaul Moore * Invalidates and frees any entries in the CIPSO cache. Returns zero on 205446fda4fSPaul Moore * success and negative values on failure. 206446fda4fSPaul Moore * 207446fda4fSPaul Moore */ 208446fda4fSPaul Moore void cipso_v4_cache_invalidate(void) 209446fda4fSPaul Moore { 210446fda4fSPaul Moore struct cipso_v4_map_cache_entry *entry, *tmp_entry; 211446fda4fSPaul Moore u32 iter; 212446fda4fSPaul Moore 213446fda4fSPaul Moore for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) { 214609c92feSPaul Moore spin_lock_bh(&cipso_v4_cache[iter].lock); 215446fda4fSPaul Moore list_for_each_entry_safe(entry, 216446fda4fSPaul Moore tmp_entry, 217446fda4fSPaul Moore &cipso_v4_cache[iter].list, list) { 218446fda4fSPaul Moore list_del(&entry->list); 219446fda4fSPaul Moore cipso_v4_cache_entry_free(entry); 220446fda4fSPaul Moore } 221446fda4fSPaul Moore cipso_v4_cache[iter].size = 0; 222609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[iter].lock); 223446fda4fSPaul Moore } 224446fda4fSPaul Moore } 225446fda4fSPaul Moore 226446fda4fSPaul Moore /** 227446fda4fSPaul Moore * cipso_v4_cache_check - Check the CIPSO cache for a label mapping 228446fda4fSPaul Moore * @key: the buffer to check 229446fda4fSPaul Moore * @key_len: buffer length in bytes 230446fda4fSPaul Moore * @secattr: the security attribute struct to use 231446fda4fSPaul Moore * 232446fda4fSPaul Moore * Description: 233446fda4fSPaul Moore * This function checks the cache to see if a label mapping already exists for 234446fda4fSPaul Moore * the given key. If there is a match then the cache is adjusted and the 235446fda4fSPaul Moore * @secattr struct is populated with the correct LSM security attributes. The 236446fda4fSPaul Moore * cache is adjusted in the following manner if the entry is not already the 237446fda4fSPaul Moore * first in the cache bucket: 238446fda4fSPaul Moore * 239446fda4fSPaul Moore * 1. The cache entry's activity counter is incremented 240446fda4fSPaul Moore * 2. The previous (higher ranking) entry's activity counter is decremented 241446fda4fSPaul Moore * 3. If the difference between the two activity counters is geater than 242446fda4fSPaul Moore * CIPSO_V4_CACHE_REORDERLIMIT the two entries are swapped 243446fda4fSPaul Moore * 244446fda4fSPaul Moore * Returns zero on success, -ENOENT for a cache miss, and other negative values 245446fda4fSPaul Moore * on error. 246446fda4fSPaul Moore * 247446fda4fSPaul Moore */ 248446fda4fSPaul Moore static int cipso_v4_cache_check(const unsigned char *key, 249446fda4fSPaul Moore u32 key_len, 250446fda4fSPaul Moore struct netlbl_lsm_secattr *secattr) 251446fda4fSPaul Moore { 252446fda4fSPaul Moore u32 bkt; 253446fda4fSPaul Moore struct cipso_v4_map_cache_entry *entry; 254446fda4fSPaul Moore struct cipso_v4_map_cache_entry *prev_entry = NULL; 255446fda4fSPaul Moore u32 hash; 256446fda4fSPaul Moore 257446fda4fSPaul Moore if (!cipso_v4_cache_enabled) 258446fda4fSPaul Moore return -ENOENT; 259446fda4fSPaul Moore 260446fda4fSPaul Moore hash = cipso_v4_map_cache_hash(key, key_len); 2615e0f8923SPavel Emelyanov bkt = hash & (CIPSO_V4_CACHE_BUCKETS - 1); 262609c92feSPaul Moore spin_lock_bh(&cipso_v4_cache[bkt].lock); 263446fda4fSPaul Moore list_for_each_entry(entry, &cipso_v4_cache[bkt].list, list) { 264446fda4fSPaul Moore if (entry->hash == hash && 265446fda4fSPaul Moore entry->key_len == key_len && 266446fda4fSPaul Moore memcmp(entry->key, key, key_len) == 0) { 267446fda4fSPaul Moore entry->activity += 1; 268ffb733c6Spaul.moore@hp.com atomic_inc(&entry->lsm_data->refcount); 269ffb733c6Spaul.moore@hp.com secattr->cache = entry->lsm_data; 270701a90baSPaul Moore secattr->flags |= NETLBL_SECATTR_CACHE; 27116efd454SPaul Moore secattr->type = NETLBL_NLTYPE_CIPSOV4; 27251456b29SIan Morris if (!prev_entry) { 273609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[bkt].lock); 274446fda4fSPaul Moore return 0; 275446fda4fSPaul Moore } 276446fda4fSPaul Moore 277446fda4fSPaul Moore if (prev_entry->activity > 0) 278446fda4fSPaul Moore prev_entry->activity -= 1; 279446fda4fSPaul Moore if (entry->activity > prev_entry->activity && 280446fda4fSPaul Moore entry->activity - prev_entry->activity > 281446fda4fSPaul Moore CIPSO_V4_CACHE_REORDERLIMIT) { 282446fda4fSPaul Moore __list_del(entry->list.prev, entry->list.next); 283446fda4fSPaul Moore __list_add(&entry->list, 284446fda4fSPaul Moore prev_entry->list.prev, 285446fda4fSPaul Moore &prev_entry->list); 286446fda4fSPaul Moore } 287446fda4fSPaul Moore 288609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[bkt].lock); 289446fda4fSPaul Moore return 0; 290446fda4fSPaul Moore } 291446fda4fSPaul Moore prev_entry = entry; 292446fda4fSPaul Moore } 293609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[bkt].lock); 294446fda4fSPaul Moore 295446fda4fSPaul Moore return -ENOENT; 296446fda4fSPaul Moore } 297446fda4fSPaul Moore 298446fda4fSPaul Moore /** 299446fda4fSPaul Moore * cipso_v4_cache_add - Add an entry to the CIPSO cache 300446fda4fSPaul Moore * @skb: the packet 301446fda4fSPaul Moore * @secattr: the packet's security attributes 302446fda4fSPaul Moore * 303446fda4fSPaul Moore * Description: 304446fda4fSPaul Moore * Add a new entry into the CIPSO label mapping cache. Add the new entry to 305446fda4fSPaul Moore * head of the cache bucket's list, if the cache bucket is out of room remove 306446fda4fSPaul Moore * the last entry in the list first. It is important to note that there is 307446fda4fSPaul Moore * currently no checking for duplicate keys. Returns zero on success, 308446fda4fSPaul Moore * negative values on failure. 309446fda4fSPaul Moore * 310446fda4fSPaul Moore */ 31104f81f01SPaul Moore int cipso_v4_cache_add(const unsigned char *cipso_ptr, 312446fda4fSPaul Moore const struct netlbl_lsm_secattr *secattr) 313446fda4fSPaul Moore { 314446fda4fSPaul Moore int ret_val = -EPERM; 315446fda4fSPaul Moore u32 bkt; 316446fda4fSPaul Moore struct cipso_v4_map_cache_entry *entry = NULL; 317446fda4fSPaul Moore struct cipso_v4_map_cache_entry *old_entry = NULL; 318446fda4fSPaul Moore u32 cipso_ptr_len; 319446fda4fSPaul Moore 320446fda4fSPaul Moore if (!cipso_v4_cache_enabled || cipso_v4_cache_bucketsize <= 0) 321446fda4fSPaul Moore return 0; 322446fda4fSPaul Moore 323446fda4fSPaul Moore cipso_ptr_len = cipso_ptr[1]; 324446fda4fSPaul Moore 325446fda4fSPaul Moore entry = kzalloc(sizeof(*entry), GFP_ATOMIC); 32651456b29SIan Morris if (!entry) 327446fda4fSPaul Moore return -ENOMEM; 328fac5d731SArnaldo Carvalho de Melo entry->key = kmemdup(cipso_ptr, cipso_ptr_len, GFP_ATOMIC); 32951456b29SIan Morris if (!entry->key) { 330446fda4fSPaul Moore ret_val = -ENOMEM; 331446fda4fSPaul Moore goto cache_add_failure; 332446fda4fSPaul Moore } 333446fda4fSPaul Moore entry->key_len = cipso_ptr_len; 334446fda4fSPaul Moore entry->hash = cipso_v4_map_cache_hash(cipso_ptr, cipso_ptr_len); 335ffb733c6Spaul.moore@hp.com atomic_inc(&secattr->cache->refcount); 336ffb733c6Spaul.moore@hp.com entry->lsm_data = secattr->cache; 337446fda4fSPaul Moore 3385e0f8923SPavel Emelyanov bkt = entry->hash & (CIPSO_V4_CACHE_BUCKETS - 1); 339609c92feSPaul Moore spin_lock_bh(&cipso_v4_cache[bkt].lock); 340446fda4fSPaul Moore if (cipso_v4_cache[bkt].size < cipso_v4_cache_bucketsize) { 341446fda4fSPaul Moore list_add(&entry->list, &cipso_v4_cache[bkt].list); 342446fda4fSPaul Moore cipso_v4_cache[bkt].size += 1; 343446fda4fSPaul Moore } else { 344446fda4fSPaul Moore old_entry = list_entry(cipso_v4_cache[bkt].list.prev, 345446fda4fSPaul Moore struct cipso_v4_map_cache_entry, list); 346446fda4fSPaul Moore list_del(&old_entry->list); 347446fda4fSPaul Moore list_add(&entry->list, &cipso_v4_cache[bkt].list); 348446fda4fSPaul Moore cipso_v4_cache_entry_free(old_entry); 349446fda4fSPaul Moore } 350609c92feSPaul Moore spin_unlock_bh(&cipso_v4_cache[bkt].lock); 351446fda4fSPaul Moore 352446fda4fSPaul Moore return 0; 353446fda4fSPaul Moore 354446fda4fSPaul Moore cache_add_failure: 355446fda4fSPaul Moore if (entry) 356446fda4fSPaul Moore cipso_v4_cache_entry_free(entry); 357446fda4fSPaul Moore return ret_val; 358446fda4fSPaul Moore } 359446fda4fSPaul Moore 360446fda4fSPaul Moore /* 361446fda4fSPaul Moore * DOI List Functions 362446fda4fSPaul Moore */ 363446fda4fSPaul Moore 364446fda4fSPaul Moore /** 365446fda4fSPaul Moore * cipso_v4_doi_search - Searches for a DOI definition 366446fda4fSPaul Moore * @doi: the DOI to search for 367446fda4fSPaul Moore * 368446fda4fSPaul Moore * Description: 369446fda4fSPaul Moore * Search the DOI definition list for a DOI definition with a DOI value that 37025985edcSLucas De Marchi * matches @doi. The caller is responsible for calling rcu_read_[un]lock(). 371446fda4fSPaul Moore * Returns a pointer to the DOI definition on success and NULL on failure. 372446fda4fSPaul Moore */ 373446fda4fSPaul Moore static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi) 374446fda4fSPaul Moore { 375446fda4fSPaul Moore struct cipso_v4_doi *iter; 376446fda4fSPaul Moore 377446fda4fSPaul Moore list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list) 378b1edeb10SPaul Moore if (iter->doi == doi && atomic_read(&iter->refcount)) 379446fda4fSPaul Moore return iter; 380446fda4fSPaul Moore return NULL; 381446fda4fSPaul Moore } 382446fda4fSPaul Moore 383446fda4fSPaul Moore /** 384446fda4fSPaul Moore * cipso_v4_doi_add - Add a new DOI to the CIPSO protocol engine 385446fda4fSPaul Moore * @doi_def: the DOI structure 3866c2e8ac0SPaul Moore * @audit_info: NetLabel audit information 387446fda4fSPaul Moore * 388446fda4fSPaul Moore * Description: 389446fda4fSPaul Moore * The caller defines a new DOI for use by the CIPSO engine and calls this 390446fda4fSPaul Moore * function to add it to the list of acceptable domains. The caller must 391446fda4fSPaul Moore * ensure that the mapping table specified in @doi_def->map meets all of the 392446fda4fSPaul Moore * requirements of the mapping type (see cipso_ipv4.h for details). Returns 393446fda4fSPaul Moore * zero on success and non-zero on failure. 394446fda4fSPaul Moore * 395446fda4fSPaul Moore */ 3966c2e8ac0SPaul Moore int cipso_v4_doi_add(struct cipso_v4_doi *doi_def, 3976c2e8ac0SPaul Moore struct netlbl_audit *audit_info) 398446fda4fSPaul Moore { 3996c2e8ac0SPaul Moore int ret_val = -EINVAL; 4006ce61a7cSPaul Moore u32 iter; 4016c2e8ac0SPaul Moore u32 doi; 4026c2e8ac0SPaul Moore u32 doi_type; 4036c2e8ac0SPaul Moore struct audit_buffer *audit_buf; 4046c2e8ac0SPaul Moore 4056c2e8ac0SPaul Moore doi = doi_def->doi; 4066c2e8ac0SPaul Moore doi_type = doi_def->type; 4076ce61a7cSPaul Moore 40856755924SDan Carpenter if (doi_def->doi == CIPSO_V4_DOI_UNKNOWN) 4096c2e8ac0SPaul Moore goto doi_add_return; 4106ce61a7cSPaul Moore for (iter = 0; iter < CIPSO_V4_TAG_MAXCNT; iter++) { 4116ce61a7cSPaul Moore switch (doi_def->tags[iter]) { 4126ce61a7cSPaul Moore case CIPSO_V4_TAG_RBITMAP: 4136ce61a7cSPaul Moore break; 414484b3669SPaul Moore case CIPSO_V4_TAG_RANGE: 415654bbc2aSPaul Moore case CIPSO_V4_TAG_ENUM: 416654bbc2aSPaul Moore if (doi_def->type != CIPSO_V4_MAP_PASS) 4176c2e8ac0SPaul Moore goto doi_add_return; 418654bbc2aSPaul Moore break; 41915c45f7bSPaul Moore case CIPSO_V4_TAG_LOCAL: 42015c45f7bSPaul Moore if (doi_def->type != CIPSO_V4_MAP_LOCAL) 4216c2e8ac0SPaul Moore goto doi_add_return; 4226c2e8ac0SPaul Moore break; 4236c2e8ac0SPaul Moore case CIPSO_V4_TAG_INVALID: 4246c2e8ac0SPaul Moore if (iter == 0) 4256c2e8ac0SPaul Moore goto doi_add_return; 42615c45f7bSPaul Moore break; 4276ce61a7cSPaul Moore default: 4286c2e8ac0SPaul Moore goto doi_add_return; 4296ce61a7cSPaul Moore } 4306ce61a7cSPaul Moore } 431446fda4fSPaul Moore 432b1edeb10SPaul Moore atomic_set(&doi_def->refcount, 1); 433446fda4fSPaul Moore 434446fda4fSPaul Moore spin_lock(&cipso_v4_doi_list_lock); 43500db4124SIan Morris if (cipso_v4_doi_search(doi_def->doi)) { 4366c2e8ac0SPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 4376c2e8ac0SPaul Moore ret_val = -EEXIST; 4386c2e8ac0SPaul Moore goto doi_add_return; 4396c2e8ac0SPaul Moore } 440446fda4fSPaul Moore list_add_tail_rcu(&doi_def->list, &cipso_v4_doi_list); 441446fda4fSPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 4426c2e8ac0SPaul Moore ret_val = 0; 443446fda4fSPaul Moore 4446c2e8ac0SPaul Moore doi_add_return: 4456c2e8ac0SPaul Moore audit_buf = netlbl_audit_start(AUDIT_MAC_CIPSOV4_ADD, audit_info); 44600db4124SIan Morris if (audit_buf) { 4476c2e8ac0SPaul Moore const char *type_str; 4486c2e8ac0SPaul Moore switch (doi_type) { 4496c2e8ac0SPaul Moore case CIPSO_V4_MAP_TRANS: 4506c2e8ac0SPaul Moore type_str = "trans"; 4516c2e8ac0SPaul Moore break; 4526c2e8ac0SPaul Moore case CIPSO_V4_MAP_PASS: 4536c2e8ac0SPaul Moore type_str = "pass"; 4546c2e8ac0SPaul Moore break; 4556c2e8ac0SPaul Moore case CIPSO_V4_MAP_LOCAL: 4566c2e8ac0SPaul Moore type_str = "local"; 4576c2e8ac0SPaul Moore break; 4586c2e8ac0SPaul Moore default: 4596c2e8ac0SPaul Moore type_str = "(unknown)"; 4606c2e8ac0SPaul Moore } 4616c2e8ac0SPaul Moore audit_log_format(audit_buf, 4626c2e8ac0SPaul Moore " cipso_doi=%u cipso_type=%s res=%u", 4636c2e8ac0SPaul Moore doi, type_str, ret_val == 0 ? 1 : 0); 4646c2e8ac0SPaul Moore audit_log_end(audit_buf); 4656c2e8ac0SPaul Moore } 466446fda4fSPaul Moore 4676c2e8ac0SPaul Moore return ret_val; 468446fda4fSPaul Moore } 469446fda4fSPaul Moore 470446fda4fSPaul Moore /** 471b1edeb10SPaul Moore * cipso_v4_doi_free - Frees a DOI definition 4724973404fSFabian Frederick * @doi_def: the DOI definition 473446fda4fSPaul Moore * 474446fda4fSPaul Moore * Description: 475b1edeb10SPaul Moore * This function frees all of the memory associated with a DOI definition. 476446fda4fSPaul Moore * 477446fda4fSPaul Moore */ 478b1edeb10SPaul Moore void cipso_v4_doi_free(struct cipso_v4_doi *doi_def) 479446fda4fSPaul Moore { 48051456b29SIan Morris if (!doi_def) 481b1edeb10SPaul Moore return; 482446fda4fSPaul Moore 483b1edeb10SPaul Moore switch (doi_def->type) { 48415c45f7bSPaul Moore case CIPSO_V4_MAP_TRANS: 485b1edeb10SPaul Moore kfree(doi_def->map.std->lvl.cipso); 486b1edeb10SPaul Moore kfree(doi_def->map.std->lvl.local); 487b1edeb10SPaul Moore kfree(doi_def->map.std->cat.cipso); 488b1edeb10SPaul Moore kfree(doi_def->map.std->cat.local); 489b1edeb10SPaul Moore break; 490446fda4fSPaul Moore } 491b1edeb10SPaul Moore kfree(doi_def); 492446fda4fSPaul Moore } 493446fda4fSPaul Moore 494446fda4fSPaul Moore /** 495b1edeb10SPaul Moore * cipso_v4_doi_free_rcu - Frees a DOI definition via the RCU pointer 496b1edeb10SPaul Moore * @entry: the entry's RCU field 497b1edeb10SPaul Moore * 498b1edeb10SPaul Moore * Description: 499b1edeb10SPaul Moore * This function is designed to be used as a callback to the call_rcu() 500b1edeb10SPaul Moore * function so that the memory allocated to the DOI definition can be released 501b1edeb10SPaul Moore * safely. 502b1edeb10SPaul Moore * 503b1edeb10SPaul Moore */ 504b1edeb10SPaul Moore static void cipso_v4_doi_free_rcu(struct rcu_head *entry) 505b1edeb10SPaul Moore { 506b1edeb10SPaul Moore struct cipso_v4_doi *doi_def; 507b1edeb10SPaul Moore 508b1edeb10SPaul Moore doi_def = container_of(entry, struct cipso_v4_doi, rcu); 509b1edeb10SPaul Moore cipso_v4_doi_free(doi_def); 510b1edeb10SPaul Moore } 511b1edeb10SPaul Moore 512b1edeb10SPaul Moore /** 513b1edeb10SPaul Moore * cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine 514b1edeb10SPaul Moore * @doi: the DOI value 515b1edeb10SPaul Moore * @audit_secid: the LSM secid to use in the audit message 516b1edeb10SPaul Moore * 517b1edeb10SPaul Moore * Description: 518b1edeb10SPaul Moore * Removes a DOI definition from the CIPSO engine. The NetLabel routines will 519b1edeb10SPaul Moore * be called to release their own LSM domain mappings as well as our own 520b1edeb10SPaul Moore * domain list. Returns zero on success and negative values on failure. 521b1edeb10SPaul Moore * 522b1edeb10SPaul Moore */ 523b1edeb10SPaul Moore int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info) 524b1edeb10SPaul Moore { 5256c2e8ac0SPaul Moore int ret_val; 526b1edeb10SPaul Moore struct cipso_v4_doi *doi_def; 5276c2e8ac0SPaul Moore struct audit_buffer *audit_buf; 528b1edeb10SPaul Moore 529b1edeb10SPaul Moore spin_lock(&cipso_v4_doi_list_lock); 530b1edeb10SPaul Moore doi_def = cipso_v4_doi_search(doi); 53151456b29SIan Morris if (!doi_def) { 532b1edeb10SPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 5336c2e8ac0SPaul Moore ret_val = -ENOENT; 5346c2e8ac0SPaul Moore goto doi_remove_return; 535b1edeb10SPaul Moore } 536b1edeb10SPaul Moore if (!atomic_dec_and_test(&doi_def->refcount)) { 537b1edeb10SPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 5386c2e8ac0SPaul Moore ret_val = -EBUSY; 5396c2e8ac0SPaul Moore goto doi_remove_return; 540b1edeb10SPaul Moore } 541b1edeb10SPaul Moore list_del_rcu(&doi_def->list); 542b1edeb10SPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 543b1edeb10SPaul Moore 544b1edeb10SPaul Moore cipso_v4_cache_invalidate(); 545b1edeb10SPaul Moore call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu); 5466c2e8ac0SPaul Moore ret_val = 0; 547b1edeb10SPaul Moore 5486c2e8ac0SPaul Moore doi_remove_return: 5496c2e8ac0SPaul Moore audit_buf = netlbl_audit_start(AUDIT_MAC_CIPSOV4_DEL, audit_info); 55000db4124SIan Morris if (audit_buf) { 5516c2e8ac0SPaul Moore audit_log_format(audit_buf, 5526c2e8ac0SPaul Moore " cipso_doi=%u res=%u", 5536c2e8ac0SPaul Moore doi, ret_val == 0 ? 1 : 0); 5546c2e8ac0SPaul Moore audit_log_end(audit_buf); 5556c2e8ac0SPaul Moore } 5566c2e8ac0SPaul Moore 5576c2e8ac0SPaul Moore return ret_val; 558b1edeb10SPaul Moore } 559b1edeb10SPaul Moore 560b1edeb10SPaul Moore /** 561b1edeb10SPaul Moore * cipso_v4_doi_getdef - Returns a reference to a valid DOI definition 562446fda4fSPaul Moore * @doi: the DOI value 563446fda4fSPaul Moore * 564446fda4fSPaul Moore * Description: 565446fda4fSPaul Moore * Searches for a valid DOI definition and if one is found it is returned to 566446fda4fSPaul Moore * the caller. Otherwise NULL is returned. The caller must ensure that 567b1edeb10SPaul Moore * rcu_read_lock() is held while accessing the returned definition and the DOI 568b1edeb10SPaul Moore * definition reference count is decremented when the caller is done. 569446fda4fSPaul Moore * 570446fda4fSPaul Moore */ 571446fda4fSPaul Moore struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi) 572446fda4fSPaul Moore { 573b1edeb10SPaul Moore struct cipso_v4_doi *doi_def; 574b1edeb10SPaul Moore 575b1edeb10SPaul Moore rcu_read_lock(); 576b1edeb10SPaul Moore doi_def = cipso_v4_doi_search(doi); 57751456b29SIan Morris if (!doi_def) 578b1edeb10SPaul Moore goto doi_getdef_return; 579b1edeb10SPaul Moore if (!atomic_inc_not_zero(&doi_def->refcount)) 580b1edeb10SPaul Moore doi_def = NULL; 581b1edeb10SPaul Moore 582b1edeb10SPaul Moore doi_getdef_return: 583b1edeb10SPaul Moore rcu_read_unlock(); 584b1edeb10SPaul Moore return doi_def; 585b1edeb10SPaul Moore } 586b1edeb10SPaul Moore 587b1edeb10SPaul Moore /** 588b1edeb10SPaul Moore * cipso_v4_doi_putdef - Releases a reference for the given DOI definition 589b1edeb10SPaul Moore * @doi_def: the DOI definition 590b1edeb10SPaul Moore * 591b1edeb10SPaul Moore * Description: 592b1edeb10SPaul Moore * Releases a DOI definition reference obtained from cipso_v4_doi_getdef(). 593b1edeb10SPaul Moore * 594b1edeb10SPaul Moore */ 595b1edeb10SPaul Moore void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def) 596b1edeb10SPaul Moore { 59751456b29SIan Morris if (!doi_def) 598b1edeb10SPaul Moore return; 599b1edeb10SPaul Moore 600b1edeb10SPaul Moore if (!atomic_dec_and_test(&doi_def->refcount)) 601b1edeb10SPaul Moore return; 602b1edeb10SPaul Moore spin_lock(&cipso_v4_doi_list_lock); 603b1edeb10SPaul Moore list_del_rcu(&doi_def->list); 604b1edeb10SPaul Moore spin_unlock(&cipso_v4_doi_list_lock); 605b1edeb10SPaul Moore 606b1edeb10SPaul Moore cipso_v4_cache_invalidate(); 607b1edeb10SPaul Moore call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu); 608446fda4fSPaul Moore } 609446fda4fSPaul Moore 610446fda4fSPaul Moore /** 611fcd48280SPaul Moore * cipso_v4_doi_walk - Iterate through the DOI definitions 612fcd48280SPaul Moore * @skip_cnt: skip past this number of DOI definitions, updated 613fcd48280SPaul Moore * @callback: callback for each DOI definition 614fcd48280SPaul Moore * @cb_arg: argument for the callback function 615446fda4fSPaul Moore * 616446fda4fSPaul Moore * Description: 617fcd48280SPaul Moore * Iterate over the DOI definition list, skipping the first @skip_cnt entries. 618fcd48280SPaul Moore * For each entry call @callback, if @callback returns a negative value stop 619fcd48280SPaul Moore * 'walking' through the list and return. Updates the value in @skip_cnt upon 620fcd48280SPaul Moore * return. Returns zero on success, negative values on failure. 621446fda4fSPaul Moore * 622446fda4fSPaul Moore */ 623fcd48280SPaul Moore int cipso_v4_doi_walk(u32 *skip_cnt, 624fcd48280SPaul Moore int (*callback) (struct cipso_v4_doi *doi_def, void *arg), 625fcd48280SPaul Moore void *cb_arg) 626446fda4fSPaul Moore { 627fcd48280SPaul Moore int ret_val = -ENOENT; 628446fda4fSPaul Moore u32 doi_cnt = 0; 629fcd48280SPaul Moore struct cipso_v4_doi *iter_doi; 630446fda4fSPaul Moore 631446fda4fSPaul Moore rcu_read_lock(); 632fcd48280SPaul Moore list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list) 633b1edeb10SPaul Moore if (atomic_read(&iter_doi->refcount) > 0) { 634fcd48280SPaul Moore if (doi_cnt++ < *skip_cnt) 635fcd48280SPaul Moore continue; 636fcd48280SPaul Moore ret_val = callback(iter_doi, cb_arg); 637fcd48280SPaul Moore if (ret_val < 0) { 638fcd48280SPaul Moore doi_cnt--; 639fcd48280SPaul Moore goto doi_walk_return; 640446fda4fSPaul Moore } 641446fda4fSPaul Moore } 642446fda4fSPaul Moore 643fcd48280SPaul Moore doi_walk_return: 644446fda4fSPaul Moore rcu_read_unlock(); 645fcd48280SPaul Moore *skip_cnt = doi_cnt; 646fcd48280SPaul Moore return ret_val; 647446fda4fSPaul Moore } 648446fda4fSPaul Moore 649446fda4fSPaul Moore /* 650446fda4fSPaul Moore * Label Mapping Functions 651446fda4fSPaul Moore */ 652446fda4fSPaul Moore 653446fda4fSPaul Moore /** 654446fda4fSPaul Moore * cipso_v4_map_lvl_valid - Checks to see if the given level is understood 655446fda4fSPaul Moore * @doi_def: the DOI definition 656446fda4fSPaul Moore * @level: the level to check 657446fda4fSPaul Moore * 658446fda4fSPaul Moore * Description: 659446fda4fSPaul Moore * Checks the given level against the given DOI definition and returns a 660446fda4fSPaul Moore * negative value if the level does not have a valid mapping and a zero value 661446fda4fSPaul Moore * if the level is defined by the DOI. 662446fda4fSPaul Moore * 663446fda4fSPaul Moore */ 664446fda4fSPaul Moore static int cipso_v4_map_lvl_valid(const struct cipso_v4_doi *doi_def, u8 level) 665446fda4fSPaul Moore { 666446fda4fSPaul Moore switch (doi_def->type) { 667446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 668446fda4fSPaul Moore return 0; 66915c45f7bSPaul Moore case CIPSO_V4_MAP_TRANS: 670446fda4fSPaul Moore if (doi_def->map.std->lvl.cipso[level] < CIPSO_V4_INV_LVL) 671446fda4fSPaul Moore return 0; 672446fda4fSPaul Moore break; 673446fda4fSPaul Moore } 674446fda4fSPaul Moore 675446fda4fSPaul Moore return -EFAULT; 676446fda4fSPaul Moore } 677446fda4fSPaul Moore 678446fda4fSPaul Moore /** 679446fda4fSPaul Moore * cipso_v4_map_lvl_hton - Perform a level mapping from the host to the network 680446fda4fSPaul Moore * @doi_def: the DOI definition 681446fda4fSPaul Moore * @host_lvl: the host MLS level 682446fda4fSPaul Moore * @net_lvl: the network/CIPSO MLS level 683446fda4fSPaul Moore * 684446fda4fSPaul Moore * Description: 685446fda4fSPaul Moore * Perform a label mapping to translate a local MLS level to the correct 686446fda4fSPaul Moore * CIPSO level using the given DOI definition. Returns zero on success, 687446fda4fSPaul Moore * negative values otherwise. 688446fda4fSPaul Moore * 689446fda4fSPaul Moore */ 690446fda4fSPaul Moore static int cipso_v4_map_lvl_hton(const struct cipso_v4_doi *doi_def, 691446fda4fSPaul Moore u32 host_lvl, 692446fda4fSPaul Moore u32 *net_lvl) 693446fda4fSPaul Moore { 694446fda4fSPaul Moore switch (doi_def->type) { 695446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 696446fda4fSPaul Moore *net_lvl = host_lvl; 697446fda4fSPaul Moore return 0; 69815c45f7bSPaul Moore case CIPSO_V4_MAP_TRANS: 699c6387a86SPaul Moore if (host_lvl < doi_def->map.std->lvl.local_size && 700c6387a86SPaul Moore doi_def->map.std->lvl.local[host_lvl] < CIPSO_V4_INV_LVL) { 701446fda4fSPaul Moore *net_lvl = doi_def->map.std->lvl.local[host_lvl]; 702446fda4fSPaul Moore return 0; 703446fda4fSPaul Moore } 704c6387a86SPaul Moore return -EPERM; 705446fda4fSPaul Moore } 706446fda4fSPaul Moore 707446fda4fSPaul Moore return -EINVAL; 708446fda4fSPaul Moore } 709446fda4fSPaul Moore 710446fda4fSPaul Moore /** 711446fda4fSPaul Moore * cipso_v4_map_lvl_ntoh - Perform a level mapping from the network to the host 712446fda4fSPaul Moore * @doi_def: the DOI definition 713446fda4fSPaul Moore * @net_lvl: the network/CIPSO MLS level 714446fda4fSPaul Moore * @host_lvl: the host MLS level 715446fda4fSPaul Moore * 716446fda4fSPaul Moore * Description: 717446fda4fSPaul Moore * Perform a label mapping to translate a CIPSO level to the correct local MLS 718446fda4fSPaul Moore * level using the given DOI definition. Returns zero on success, negative 719446fda4fSPaul Moore * values otherwise. 720446fda4fSPaul Moore * 721446fda4fSPaul Moore */ 722446fda4fSPaul Moore static int cipso_v4_map_lvl_ntoh(const struct cipso_v4_doi *doi_def, 723446fda4fSPaul Moore u32 net_lvl, 724446fda4fSPaul Moore u32 *host_lvl) 725446fda4fSPaul Moore { 726446fda4fSPaul Moore struct cipso_v4_std_map_tbl *map_tbl; 727446fda4fSPaul Moore 728446fda4fSPaul Moore switch (doi_def->type) { 729446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 730446fda4fSPaul Moore *host_lvl = net_lvl; 731446fda4fSPaul Moore return 0; 73215c45f7bSPaul Moore case CIPSO_V4_MAP_TRANS: 733446fda4fSPaul Moore map_tbl = doi_def->map.std; 734446fda4fSPaul Moore if (net_lvl < map_tbl->lvl.cipso_size && 735446fda4fSPaul Moore map_tbl->lvl.cipso[net_lvl] < CIPSO_V4_INV_LVL) { 736446fda4fSPaul Moore *host_lvl = doi_def->map.std->lvl.cipso[net_lvl]; 737446fda4fSPaul Moore return 0; 738446fda4fSPaul Moore } 739c6387a86SPaul Moore return -EPERM; 740446fda4fSPaul Moore } 741446fda4fSPaul Moore 742446fda4fSPaul Moore return -EINVAL; 743446fda4fSPaul Moore } 744446fda4fSPaul Moore 745446fda4fSPaul Moore /** 746446fda4fSPaul Moore * cipso_v4_map_cat_rbm_valid - Checks to see if the category bitmap is valid 747446fda4fSPaul Moore * @doi_def: the DOI definition 748446fda4fSPaul Moore * @bitmap: category bitmap 749446fda4fSPaul Moore * @bitmap_len: bitmap length in bytes 750446fda4fSPaul Moore * 751446fda4fSPaul Moore * Description: 752446fda4fSPaul Moore * Checks the given category bitmap against the given DOI definition and 753446fda4fSPaul Moore * returns a negative value if any of the categories in the bitmap do not have 754446fda4fSPaul Moore * a valid mapping and a zero value if all of the categories are valid. 755446fda4fSPaul Moore * 756446fda4fSPaul Moore */ 757446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_valid(const struct cipso_v4_doi *doi_def, 758446fda4fSPaul Moore const unsigned char *bitmap, 759446fda4fSPaul Moore u32 bitmap_len) 760446fda4fSPaul Moore { 761446fda4fSPaul Moore int cat = -1; 762446fda4fSPaul Moore u32 bitmap_len_bits = bitmap_len * 8; 763044a68edSPaul Moore u32 cipso_cat_size; 764044a68edSPaul Moore u32 *cipso_array; 765446fda4fSPaul Moore 766446fda4fSPaul Moore switch (doi_def->type) { 767446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 768446fda4fSPaul Moore return 0; 76915c45f7bSPaul Moore case CIPSO_V4_MAP_TRANS: 770044a68edSPaul Moore cipso_cat_size = doi_def->map.std->cat.cipso_size; 771044a68edSPaul Moore cipso_array = doi_def->map.std->cat.cipso; 772446fda4fSPaul Moore for (;;) { 773*3faa8f98SHuw Davies cat = netlbl_bitmap_walk(bitmap, 774446fda4fSPaul Moore bitmap_len_bits, 775446fda4fSPaul Moore cat + 1, 776446fda4fSPaul Moore 1); 777446fda4fSPaul Moore if (cat < 0) 778446fda4fSPaul Moore break; 779446fda4fSPaul Moore if (cat >= cipso_cat_size || 780446fda4fSPaul Moore cipso_array[cat] >= CIPSO_V4_INV_CAT) 781446fda4fSPaul Moore return -EFAULT; 782446fda4fSPaul Moore } 783446fda4fSPaul Moore 784446fda4fSPaul Moore if (cat == -1) 785446fda4fSPaul Moore return 0; 786446fda4fSPaul Moore break; 787446fda4fSPaul Moore } 788446fda4fSPaul Moore 789446fda4fSPaul Moore return -EFAULT; 790446fda4fSPaul Moore } 791446fda4fSPaul Moore 792446fda4fSPaul Moore /** 793446fda4fSPaul Moore * cipso_v4_map_cat_rbm_hton - Perform a category mapping from host to network 794446fda4fSPaul Moore * @doi_def: the DOI definition 79502752760SPaul Moore * @secattr: the security attributes 796446fda4fSPaul Moore * @net_cat: the zero'd out category bitmap in network/CIPSO format 797446fda4fSPaul Moore * @net_cat_len: the length of the CIPSO bitmap in bytes 798446fda4fSPaul Moore * 799446fda4fSPaul Moore * Description: 800446fda4fSPaul Moore * Perform a label mapping to translate a local MLS category bitmap to the 801446fda4fSPaul Moore * correct CIPSO bitmap using the given DOI definition. Returns the minimum 802446fda4fSPaul Moore * size in bytes of the network bitmap on success, negative values otherwise. 803446fda4fSPaul Moore * 804446fda4fSPaul Moore */ 805446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def, 80602752760SPaul Moore const struct netlbl_lsm_secattr *secattr, 807446fda4fSPaul Moore unsigned char *net_cat, 808446fda4fSPaul Moore u32 net_cat_len) 809446fda4fSPaul Moore { 810446fda4fSPaul Moore int host_spot = -1; 81102752760SPaul Moore u32 net_spot = CIPSO_V4_INV_CAT; 812446fda4fSPaul Moore u32 net_spot_max = 0; 813446fda4fSPaul Moore u32 net_clen_bits = net_cat_len * 8; 81402752760SPaul Moore u32 host_cat_size = 0; 81502752760SPaul Moore u32 *host_cat_array = NULL; 81602752760SPaul Moore 81715c45f7bSPaul Moore if (doi_def->type == CIPSO_V4_MAP_TRANS) { 81802752760SPaul Moore host_cat_size = doi_def->map.std->cat.local_size; 81902752760SPaul Moore host_cat_array = doi_def->map.std->cat.local; 82002752760SPaul Moore } 82102752760SPaul Moore 82202752760SPaul Moore for (;;) { 8234fbe63d1SPaul Moore host_spot = netlbl_catmap_walk(secattr->attr.mls.cat, 82402752760SPaul Moore host_spot + 1); 82502752760SPaul Moore if (host_spot < 0) 82602752760SPaul Moore break; 827446fda4fSPaul Moore 828446fda4fSPaul Moore switch (doi_def->type) { 829446fda4fSPaul Moore case CIPSO_V4_MAP_PASS: 83002752760SPaul Moore net_spot = host_spot; 831446fda4fSPaul Moore break; 83215c45f7bSPaul Moore case CIPSO_V4_MAP_TRANS: 833446fda4fSPaul Moore if (host_spot >= host_cat_size) 834446fda4fSPaul Moore return -EPERM; 835446fda4fSPaul Moore net_spot = host_cat_array[host_spot]; 8369fade4bfSPaul Moore if (net_spot >= CIPSO_V4_INV_CAT) 8379fade4bfSPaul Moore return -EPERM; 83802752760SPaul Moore break; 83902752760SPaul Moore } 840446fda4fSPaul Moore if (net_spot >= net_clen_bits) 841446fda4fSPaul Moore return -ENOSPC; 842*3faa8f98SHuw Davies netlbl_bitmap_setbit(net_cat, net_spot, 1); 843446fda4fSPaul Moore 844446fda4fSPaul Moore if (net_spot > net_spot_max) 845446fda4fSPaul Moore net_spot_max = net_spot; 846446fda4fSPaul Moore } 847446fda4fSPaul Moore 848446fda4fSPaul Moore if (++net_spot_max % 8) 849446fda4fSPaul Moore return net_spot_max / 8 + 1; 850446fda4fSPaul Moore return net_spot_max / 8; 851446fda4fSPaul Moore } 852446fda4fSPaul Moore 853446fda4fSPaul Moore /** 854446fda4fSPaul Moore * cipso_v4_map_cat_rbm_ntoh - Perform a category mapping from network to host 855446fda4fSPaul Moore * @doi_def: the DOI definition 856446fda4fSPaul Moore * @net_cat: the category bitmap in network/CIPSO format 857446fda4fSPaul Moore * @net_cat_len: the length of the CIPSO bitmap in bytes 85802752760SPaul Moore * @secattr: the security attributes 859446fda4fSPaul Moore * 860446fda4fSPaul Moore * Description: 861446fda4fSPaul Moore * Perform a label mapping to translate a CIPSO bitmap to the correct local 86202752760SPaul Moore * MLS category bitmap using the given DOI definition. Returns zero on 86302752760SPaul Moore * success, negative values on failure. 864446fda4fSPaul Moore * 865446fda4fSPaul Moore */ 866446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def, 867446fda4fSPaul Moore const unsigned char *net_cat, 868446fda4fSPaul Moore u32 net_cat_len, 86902752760SPaul Moore struct netlbl_lsm_secattr *secattr) 870446fda4fSPaul Moore { 87102752760SPaul Moore int ret_val; 872446fda4fSPaul Moore int net_spot = -1; 87302752760SPaul Moore u32 host_spot = CIPSO_V4_INV_CAT; 874446fda4fSPaul Moore u32 net_clen_bits = net_cat_len * 8; 87502752760SPaul Moore u32 net_cat_size = 0; 87602752760SPaul Moore u32 *net_cat_array = NULL; 877446fda4fSPaul Moore 87815c45f7bSPaul Moore if (doi_def->type == CIPSO_V4_MAP_TRANS) { 879044a68edSPaul Moore net_cat_size = doi_def->map.std->cat.cipso_size; 880044a68edSPaul Moore net_cat_array = doi_def->map.std->cat.cipso; 88102752760SPaul Moore } 88202752760SPaul Moore 883446fda4fSPaul Moore for (;;) { 884*3faa8f98SHuw Davies net_spot = netlbl_bitmap_walk(net_cat, 885446fda4fSPaul Moore net_clen_bits, 886446fda4fSPaul Moore net_spot + 1, 887446fda4fSPaul Moore 1); 88802752760SPaul Moore if (net_spot < 0) { 88902752760SPaul Moore if (net_spot == -2) 89002752760SPaul Moore return -EFAULT; 89102752760SPaul Moore return 0; 89202752760SPaul Moore } 893446fda4fSPaul Moore 89402752760SPaul Moore switch (doi_def->type) { 89502752760SPaul Moore case CIPSO_V4_MAP_PASS: 89602752760SPaul Moore host_spot = net_spot; 89702752760SPaul Moore break; 89815c45f7bSPaul Moore case CIPSO_V4_MAP_TRANS: 89902752760SPaul Moore if (net_spot >= net_cat_size) 90002752760SPaul Moore return -EPERM; 901446fda4fSPaul Moore host_spot = net_cat_array[net_spot]; 9029fade4bfSPaul Moore if (host_spot >= CIPSO_V4_INV_CAT) 9039fade4bfSPaul Moore return -EPERM; 90402752760SPaul Moore break; 905446fda4fSPaul Moore } 9064fbe63d1SPaul Moore ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat, 90702752760SPaul Moore host_spot, 90802752760SPaul Moore GFP_ATOMIC); 90902752760SPaul Moore if (ret_val != 0) 91002752760SPaul Moore return ret_val; 911446fda4fSPaul Moore } 912446fda4fSPaul Moore 913446fda4fSPaul Moore return -EINVAL; 914446fda4fSPaul Moore } 915446fda4fSPaul Moore 916654bbc2aSPaul Moore /** 917654bbc2aSPaul Moore * cipso_v4_map_cat_enum_valid - Checks to see if the categories are valid 918654bbc2aSPaul Moore * @doi_def: the DOI definition 919654bbc2aSPaul Moore * @enumcat: category list 920654bbc2aSPaul Moore * @enumcat_len: length of the category list in bytes 921654bbc2aSPaul Moore * 922654bbc2aSPaul Moore * Description: 923654bbc2aSPaul Moore * Checks the given categories against the given DOI definition and returns a 924654bbc2aSPaul Moore * negative value if any of the categories do not have a valid mapping and a 925654bbc2aSPaul Moore * zero value if all of the categories are valid. 926654bbc2aSPaul Moore * 927654bbc2aSPaul Moore */ 928654bbc2aSPaul Moore static int cipso_v4_map_cat_enum_valid(const struct cipso_v4_doi *doi_def, 929654bbc2aSPaul Moore const unsigned char *enumcat, 930654bbc2aSPaul Moore u32 enumcat_len) 931654bbc2aSPaul Moore { 932654bbc2aSPaul Moore u16 cat; 933654bbc2aSPaul Moore int cat_prev = -1; 934654bbc2aSPaul Moore u32 iter; 935654bbc2aSPaul Moore 936654bbc2aSPaul Moore if (doi_def->type != CIPSO_V4_MAP_PASS || enumcat_len & 0x01) 937654bbc2aSPaul Moore return -EFAULT; 938654bbc2aSPaul Moore 939654bbc2aSPaul Moore for (iter = 0; iter < enumcat_len; iter += 2) { 940d3e2ce3bSHarvey Harrison cat = get_unaligned_be16(&enumcat[iter]); 941654bbc2aSPaul Moore if (cat <= cat_prev) 942654bbc2aSPaul Moore return -EFAULT; 943654bbc2aSPaul Moore cat_prev = cat; 944654bbc2aSPaul Moore } 945654bbc2aSPaul Moore 946654bbc2aSPaul Moore return 0; 947654bbc2aSPaul Moore } 948654bbc2aSPaul Moore 949654bbc2aSPaul Moore /** 950654bbc2aSPaul Moore * cipso_v4_map_cat_enum_hton - Perform a category mapping from host to network 951654bbc2aSPaul Moore * @doi_def: the DOI definition 952654bbc2aSPaul Moore * @secattr: the security attributes 953654bbc2aSPaul Moore * @net_cat: the zero'd out category list in network/CIPSO format 954654bbc2aSPaul Moore * @net_cat_len: the length of the CIPSO category list in bytes 955654bbc2aSPaul Moore * 956654bbc2aSPaul Moore * Description: 957654bbc2aSPaul Moore * Perform a label mapping to translate a local MLS category bitmap to the 958654bbc2aSPaul Moore * correct CIPSO category list using the given DOI definition. Returns the 959654bbc2aSPaul Moore * size in bytes of the network category bitmap on success, negative values 960654bbc2aSPaul Moore * otherwise. 961654bbc2aSPaul Moore * 962654bbc2aSPaul Moore */ 963654bbc2aSPaul Moore static int cipso_v4_map_cat_enum_hton(const struct cipso_v4_doi *doi_def, 964654bbc2aSPaul Moore const struct netlbl_lsm_secattr *secattr, 965654bbc2aSPaul Moore unsigned char *net_cat, 966654bbc2aSPaul Moore u32 net_cat_len) 967654bbc2aSPaul Moore { 968654bbc2aSPaul Moore int cat = -1; 969654bbc2aSPaul Moore u32 cat_iter = 0; 970654bbc2aSPaul Moore 971654bbc2aSPaul Moore for (;;) { 9724fbe63d1SPaul Moore cat = netlbl_catmap_walk(secattr->attr.mls.cat, cat + 1); 973654bbc2aSPaul Moore if (cat < 0) 974654bbc2aSPaul Moore break; 975654bbc2aSPaul Moore if ((cat_iter + 2) > net_cat_len) 976654bbc2aSPaul Moore return -ENOSPC; 977654bbc2aSPaul Moore 978654bbc2aSPaul Moore *((__be16 *)&net_cat[cat_iter]) = htons(cat); 979654bbc2aSPaul Moore cat_iter += 2; 980654bbc2aSPaul Moore } 981654bbc2aSPaul Moore 982654bbc2aSPaul Moore return cat_iter; 983654bbc2aSPaul Moore } 984654bbc2aSPaul Moore 985654bbc2aSPaul Moore /** 986654bbc2aSPaul Moore * cipso_v4_map_cat_enum_ntoh - Perform a category mapping from network to host 987654bbc2aSPaul Moore * @doi_def: the DOI definition 988654bbc2aSPaul Moore * @net_cat: the category list in network/CIPSO format 989654bbc2aSPaul Moore * @net_cat_len: the length of the CIPSO bitmap in bytes 990654bbc2aSPaul Moore * @secattr: the security attributes 991654bbc2aSPaul Moore * 992654bbc2aSPaul Moore * Description: 993654bbc2aSPaul Moore * Perform a label mapping to translate a CIPSO category list to the correct 994654bbc2aSPaul Moore * local MLS category bitmap using the given DOI definition. Returns zero on 995654bbc2aSPaul Moore * success, negative values on failure. 996654bbc2aSPaul Moore * 997654bbc2aSPaul Moore */ 998654bbc2aSPaul Moore static int cipso_v4_map_cat_enum_ntoh(const struct cipso_v4_doi *doi_def, 999654bbc2aSPaul Moore const unsigned char *net_cat, 1000654bbc2aSPaul Moore u32 net_cat_len, 1001654bbc2aSPaul Moore struct netlbl_lsm_secattr *secattr) 1002654bbc2aSPaul Moore { 1003654bbc2aSPaul Moore int ret_val; 1004654bbc2aSPaul Moore u32 iter; 1005654bbc2aSPaul Moore 1006654bbc2aSPaul Moore for (iter = 0; iter < net_cat_len; iter += 2) { 10074fbe63d1SPaul Moore ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat, 1008d3e2ce3bSHarvey Harrison get_unaligned_be16(&net_cat[iter]), 1009654bbc2aSPaul Moore GFP_ATOMIC); 1010654bbc2aSPaul Moore if (ret_val != 0) 1011654bbc2aSPaul Moore return ret_val; 1012654bbc2aSPaul Moore } 1013654bbc2aSPaul Moore 1014654bbc2aSPaul Moore return 0; 1015654bbc2aSPaul Moore } 1016654bbc2aSPaul Moore 1017484b3669SPaul Moore /** 1018484b3669SPaul Moore * cipso_v4_map_cat_rng_valid - Checks to see if the categories are valid 1019484b3669SPaul Moore * @doi_def: the DOI definition 1020484b3669SPaul Moore * @rngcat: category list 1021484b3669SPaul Moore * @rngcat_len: length of the category list in bytes 1022484b3669SPaul Moore * 1023484b3669SPaul Moore * Description: 1024484b3669SPaul Moore * Checks the given categories against the given DOI definition and returns a 1025484b3669SPaul Moore * negative value if any of the categories do not have a valid mapping and a 1026484b3669SPaul Moore * zero value if all of the categories are valid. 1027484b3669SPaul Moore * 1028484b3669SPaul Moore */ 1029484b3669SPaul Moore static int cipso_v4_map_cat_rng_valid(const struct cipso_v4_doi *doi_def, 1030484b3669SPaul Moore const unsigned char *rngcat, 1031484b3669SPaul Moore u32 rngcat_len) 1032484b3669SPaul Moore { 1033484b3669SPaul Moore u16 cat_high; 1034484b3669SPaul Moore u16 cat_low; 1035484b3669SPaul Moore u32 cat_prev = CIPSO_V4_MAX_REM_CATS + 1; 1036484b3669SPaul Moore u32 iter; 1037484b3669SPaul Moore 1038484b3669SPaul Moore if (doi_def->type != CIPSO_V4_MAP_PASS || rngcat_len & 0x01) 1039484b3669SPaul Moore return -EFAULT; 1040484b3669SPaul Moore 1041484b3669SPaul Moore for (iter = 0; iter < rngcat_len; iter += 4) { 1042d3e2ce3bSHarvey Harrison cat_high = get_unaligned_be16(&rngcat[iter]); 1043484b3669SPaul Moore if ((iter + 4) <= rngcat_len) 1044d3e2ce3bSHarvey Harrison cat_low = get_unaligned_be16(&rngcat[iter + 2]); 1045484b3669SPaul Moore else 1046484b3669SPaul Moore cat_low = 0; 1047484b3669SPaul Moore 1048484b3669SPaul Moore if (cat_high > cat_prev) 1049484b3669SPaul Moore return -EFAULT; 1050484b3669SPaul Moore 1051484b3669SPaul Moore cat_prev = cat_low; 1052484b3669SPaul Moore } 1053484b3669SPaul Moore 1054484b3669SPaul Moore return 0; 1055484b3669SPaul Moore } 1056484b3669SPaul Moore 1057484b3669SPaul Moore /** 1058484b3669SPaul Moore * cipso_v4_map_cat_rng_hton - Perform a category mapping from host to network 1059484b3669SPaul Moore * @doi_def: the DOI definition 1060484b3669SPaul Moore * @secattr: the security attributes 1061484b3669SPaul Moore * @net_cat: the zero'd out category list in network/CIPSO format 1062484b3669SPaul Moore * @net_cat_len: the length of the CIPSO category list in bytes 1063484b3669SPaul Moore * 1064484b3669SPaul Moore * Description: 1065484b3669SPaul Moore * Perform a label mapping to translate a local MLS category bitmap to the 1066484b3669SPaul Moore * correct CIPSO category list using the given DOI definition. Returns the 1067484b3669SPaul Moore * size in bytes of the network category bitmap on success, negative values 1068484b3669SPaul Moore * otherwise. 1069484b3669SPaul Moore * 1070484b3669SPaul Moore */ 1071484b3669SPaul Moore static int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def, 1072484b3669SPaul Moore const struct netlbl_lsm_secattr *secattr, 1073484b3669SPaul Moore unsigned char *net_cat, 1074484b3669SPaul Moore u32 net_cat_len) 1075484b3669SPaul Moore { 1076484b3669SPaul Moore int iter = -1; 1077f998e8cbSPaul Moore u16 array[CIPSO_V4_TAG_RNG_CAT_MAX * 2]; 1078484b3669SPaul Moore u32 array_cnt = 0; 1079484b3669SPaul Moore u32 cat_size = 0; 1080484b3669SPaul Moore 1081f998e8cbSPaul Moore /* make sure we don't overflow the 'array[]' variable */ 1082128c6b6cSPaul Moore if (net_cat_len > 1083128c6b6cSPaul Moore (CIPSO_V4_OPT_LEN_MAX - CIPSO_V4_HDR_LEN - CIPSO_V4_TAG_RNG_BLEN)) 1084128c6b6cSPaul Moore return -ENOSPC; 1085484b3669SPaul Moore 1086484b3669SPaul Moore for (;;) { 10874fbe63d1SPaul Moore iter = netlbl_catmap_walk(secattr->attr.mls.cat, iter + 1); 1088484b3669SPaul Moore if (iter < 0) 1089484b3669SPaul Moore break; 1090484b3669SPaul Moore cat_size += (iter == 0 ? 0 : sizeof(u16)); 1091484b3669SPaul Moore if (cat_size > net_cat_len) 1092484b3669SPaul Moore return -ENOSPC; 1093484b3669SPaul Moore array[array_cnt++] = iter; 1094484b3669SPaul Moore 10954fbe63d1SPaul Moore iter = netlbl_catmap_walkrng(secattr->attr.mls.cat, iter); 1096484b3669SPaul Moore if (iter < 0) 1097484b3669SPaul Moore return -EFAULT; 1098484b3669SPaul Moore cat_size += sizeof(u16); 1099484b3669SPaul Moore if (cat_size > net_cat_len) 1100484b3669SPaul Moore return -ENOSPC; 1101484b3669SPaul Moore array[array_cnt++] = iter; 1102484b3669SPaul Moore } 1103484b3669SPaul Moore 1104484b3669SPaul Moore for (iter = 0; array_cnt > 0;) { 1105484b3669SPaul Moore *((__be16 *)&net_cat[iter]) = htons(array[--array_cnt]); 1106484b3669SPaul Moore iter += 2; 1107484b3669SPaul Moore array_cnt--; 1108484b3669SPaul Moore if (array[array_cnt] != 0) { 1109484b3669SPaul Moore *((__be16 *)&net_cat[iter]) = htons(array[array_cnt]); 1110484b3669SPaul Moore iter += 2; 1111484b3669SPaul Moore } 1112484b3669SPaul Moore } 1113484b3669SPaul Moore 1114484b3669SPaul Moore return cat_size; 1115484b3669SPaul Moore } 1116484b3669SPaul Moore 1117484b3669SPaul Moore /** 1118484b3669SPaul Moore * cipso_v4_map_cat_rng_ntoh - Perform a category mapping from network to host 1119484b3669SPaul Moore * @doi_def: the DOI definition 1120484b3669SPaul Moore * @net_cat: the category list in network/CIPSO format 1121484b3669SPaul Moore * @net_cat_len: the length of the CIPSO bitmap in bytes 1122484b3669SPaul Moore * @secattr: the security attributes 1123484b3669SPaul Moore * 1124484b3669SPaul Moore * Description: 1125484b3669SPaul Moore * Perform a label mapping to translate a CIPSO category list to the correct 1126484b3669SPaul Moore * local MLS category bitmap using the given DOI definition. Returns zero on 1127484b3669SPaul Moore * success, negative values on failure. 1128484b3669SPaul Moore * 1129484b3669SPaul Moore */ 1130484b3669SPaul Moore static int cipso_v4_map_cat_rng_ntoh(const struct cipso_v4_doi *doi_def, 1131484b3669SPaul Moore const unsigned char *net_cat, 1132484b3669SPaul Moore u32 net_cat_len, 1133484b3669SPaul Moore struct netlbl_lsm_secattr *secattr) 1134484b3669SPaul Moore { 1135484b3669SPaul Moore int ret_val; 1136484b3669SPaul Moore u32 net_iter; 1137484b3669SPaul Moore u16 cat_low; 1138484b3669SPaul Moore u16 cat_high; 1139484b3669SPaul Moore 1140484b3669SPaul Moore for (net_iter = 0; net_iter < net_cat_len; net_iter += 4) { 1141d3e2ce3bSHarvey Harrison cat_high = get_unaligned_be16(&net_cat[net_iter]); 1142484b3669SPaul Moore if ((net_iter + 4) <= net_cat_len) 1143d3e2ce3bSHarvey Harrison cat_low = get_unaligned_be16(&net_cat[net_iter + 2]); 1144484b3669SPaul Moore else 1145484b3669SPaul Moore cat_low = 0; 1146484b3669SPaul Moore 11474fbe63d1SPaul Moore ret_val = netlbl_catmap_setrng(&secattr->attr.mls.cat, 1148484b3669SPaul Moore cat_low, 1149484b3669SPaul Moore cat_high, 1150484b3669SPaul Moore GFP_ATOMIC); 1151484b3669SPaul Moore if (ret_val != 0) 1152484b3669SPaul Moore return ret_val; 1153484b3669SPaul Moore } 1154484b3669SPaul Moore 1155484b3669SPaul Moore return 0; 1156484b3669SPaul Moore } 1157484b3669SPaul Moore 1158446fda4fSPaul Moore /* 1159446fda4fSPaul Moore * Protocol Handling Functions 1160446fda4fSPaul Moore */ 1161446fda4fSPaul Moore 1162446fda4fSPaul Moore /** 1163446fda4fSPaul Moore * cipso_v4_gentag_hdr - Generate a CIPSO option header 1164446fda4fSPaul Moore * @doi_def: the DOI definition 116591b1ed0aSPaul Moore * @len: the total tag length in bytes, not including this header 1166446fda4fSPaul Moore * @buf: the CIPSO option buffer 1167446fda4fSPaul Moore * 1168446fda4fSPaul Moore * Description: 116991b1ed0aSPaul Moore * Write a CIPSO header into the beginning of @buffer. 1170446fda4fSPaul Moore * 1171446fda4fSPaul Moore */ 117291b1ed0aSPaul Moore static void cipso_v4_gentag_hdr(const struct cipso_v4_doi *doi_def, 117391b1ed0aSPaul Moore unsigned char *buf, 117491b1ed0aSPaul Moore u32 len) 1175446fda4fSPaul Moore { 1176446fda4fSPaul Moore buf[0] = IPOPT_CIPSO; 1177446fda4fSPaul Moore buf[1] = CIPSO_V4_HDR_LEN + len; 1178714e85beSAl Viro *(__be32 *)&buf[2] = htonl(doi_def->doi); 1179446fda4fSPaul Moore } 1180446fda4fSPaul Moore 1181446fda4fSPaul Moore /** 1182446fda4fSPaul Moore * cipso_v4_gentag_rbm - Generate a CIPSO restricted bitmap tag (type #1) 1183446fda4fSPaul Moore * @doi_def: the DOI definition 1184446fda4fSPaul Moore * @secattr: the security attributes 1185446fda4fSPaul Moore * @buffer: the option buffer 1186446fda4fSPaul Moore * @buffer_len: length of buffer in bytes 1187446fda4fSPaul Moore * 1188446fda4fSPaul Moore * Description: 1189446fda4fSPaul Moore * Generate a CIPSO option using the restricted bitmap tag, tag type #1. The 1190446fda4fSPaul Moore * actual buffer length may be larger than the indicated size due to 119191b1ed0aSPaul Moore * translation between host and network category bitmaps. Returns the size of 119291b1ed0aSPaul Moore * the tag on success, negative values on failure. 1193446fda4fSPaul Moore * 1194446fda4fSPaul Moore */ 1195446fda4fSPaul Moore static int cipso_v4_gentag_rbm(const struct cipso_v4_doi *doi_def, 1196446fda4fSPaul Moore const struct netlbl_lsm_secattr *secattr, 119791b1ed0aSPaul Moore unsigned char *buffer, 119891b1ed0aSPaul Moore u32 buffer_len) 1199446fda4fSPaul Moore { 1200701a90baSPaul Moore int ret_val; 120191b1ed0aSPaul Moore u32 tag_len; 1202446fda4fSPaul Moore u32 level; 1203446fda4fSPaul Moore 1204701a90baSPaul Moore if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0) 1205701a90baSPaul Moore return -EPERM; 1206701a90baSPaul Moore 120716efd454SPaul Moore ret_val = cipso_v4_map_lvl_hton(doi_def, 120816efd454SPaul Moore secattr->attr.mls.lvl, 120916efd454SPaul Moore &level); 121091b1ed0aSPaul Moore if (ret_val != 0) 121191b1ed0aSPaul Moore return ret_val; 1212446fda4fSPaul Moore 121391b1ed0aSPaul Moore if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { 1214446fda4fSPaul Moore ret_val = cipso_v4_map_cat_rbm_hton(doi_def, 121502752760SPaul Moore secattr, 121691b1ed0aSPaul Moore &buffer[4], 121791b1ed0aSPaul Moore buffer_len - 4); 1218446fda4fSPaul Moore if (ret_val < 0) 121991b1ed0aSPaul Moore return ret_val; 1220446fda4fSPaul Moore 1221446fda4fSPaul Moore /* This will send packets using the "optimized" format when 122225985edcSLucas De Marchi * possible as specified in section 3.4.2.6 of the 1223446fda4fSPaul Moore * CIPSO draft. */ 1224701a90baSPaul Moore if (cipso_v4_rbm_optfmt && ret_val > 0 && ret_val <= 10) 122591b1ed0aSPaul Moore tag_len = 14; 1226701a90baSPaul Moore else 122791b1ed0aSPaul Moore tag_len = 4 + ret_val; 122891b1ed0aSPaul Moore } else 122991b1ed0aSPaul Moore tag_len = 4; 1230446fda4fSPaul Moore 123115c45f7bSPaul Moore buffer[0] = CIPSO_V4_TAG_RBITMAP; 123291b1ed0aSPaul Moore buffer[1] = tag_len; 123391b1ed0aSPaul Moore buffer[3] = level; 1234446fda4fSPaul Moore 123591b1ed0aSPaul Moore return tag_len; 1236446fda4fSPaul Moore } 1237446fda4fSPaul Moore 1238446fda4fSPaul Moore /** 1239446fda4fSPaul Moore * cipso_v4_parsetag_rbm - Parse a CIPSO restricted bitmap tag 1240446fda4fSPaul Moore * @doi_def: the DOI definition 1241446fda4fSPaul Moore * @tag: the CIPSO tag 1242446fda4fSPaul Moore * @secattr: the security attributes 1243446fda4fSPaul Moore * 1244446fda4fSPaul Moore * Description: 1245446fda4fSPaul Moore * Parse a CIPSO restricted bitmap tag (tag type #1) and return the security 1246446fda4fSPaul Moore * attributes in @secattr. Return zero on success, negatives values on 1247446fda4fSPaul Moore * failure. 1248446fda4fSPaul Moore * 1249446fda4fSPaul Moore */ 1250446fda4fSPaul Moore static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def, 1251446fda4fSPaul Moore const unsigned char *tag, 1252446fda4fSPaul Moore struct netlbl_lsm_secattr *secattr) 1253446fda4fSPaul Moore { 1254446fda4fSPaul Moore int ret_val; 1255446fda4fSPaul Moore u8 tag_len = tag[1]; 1256446fda4fSPaul Moore u32 level; 1257446fda4fSPaul Moore 1258446fda4fSPaul Moore ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level); 1259446fda4fSPaul Moore if (ret_val != 0) 1260446fda4fSPaul Moore return ret_val; 126116efd454SPaul Moore secattr->attr.mls.lvl = level; 1262701a90baSPaul Moore secattr->flags |= NETLBL_SECATTR_MLS_LVL; 1263446fda4fSPaul Moore 1264446fda4fSPaul Moore if (tag_len > 4) { 1265446fda4fSPaul Moore ret_val = cipso_v4_map_cat_rbm_ntoh(doi_def, 1266446fda4fSPaul Moore &tag[4], 1267446fda4fSPaul Moore tag_len - 4, 126802752760SPaul Moore secattr); 126902752760SPaul Moore if (ret_val != 0) { 12704fbe63d1SPaul Moore netlbl_catmap_free(secattr->attr.mls.cat); 1271446fda4fSPaul Moore return ret_val; 1272701a90baSPaul Moore } 127302752760SPaul Moore 127402752760SPaul Moore secattr->flags |= NETLBL_SECATTR_MLS_CAT; 1275446fda4fSPaul Moore } 1276446fda4fSPaul Moore 1277446fda4fSPaul Moore return 0; 1278446fda4fSPaul Moore } 1279446fda4fSPaul Moore 1280446fda4fSPaul Moore /** 1281654bbc2aSPaul Moore * cipso_v4_gentag_enum - Generate a CIPSO enumerated tag (type #2) 1282654bbc2aSPaul Moore * @doi_def: the DOI definition 1283654bbc2aSPaul Moore * @secattr: the security attributes 1284654bbc2aSPaul Moore * @buffer: the option buffer 1285654bbc2aSPaul Moore * @buffer_len: length of buffer in bytes 1286654bbc2aSPaul Moore * 1287654bbc2aSPaul Moore * Description: 1288654bbc2aSPaul Moore * Generate a CIPSO option using the enumerated tag, tag type #2. Returns the 1289654bbc2aSPaul Moore * size of the tag on success, negative values on failure. 1290654bbc2aSPaul Moore * 1291654bbc2aSPaul Moore */ 1292654bbc2aSPaul Moore static int cipso_v4_gentag_enum(const struct cipso_v4_doi *doi_def, 1293654bbc2aSPaul Moore const struct netlbl_lsm_secattr *secattr, 1294654bbc2aSPaul Moore unsigned char *buffer, 1295654bbc2aSPaul Moore u32 buffer_len) 1296654bbc2aSPaul Moore { 1297654bbc2aSPaul Moore int ret_val; 1298654bbc2aSPaul Moore u32 tag_len; 1299654bbc2aSPaul Moore u32 level; 1300654bbc2aSPaul Moore 1301654bbc2aSPaul Moore if (!(secattr->flags & NETLBL_SECATTR_MLS_LVL)) 1302654bbc2aSPaul Moore return -EPERM; 1303654bbc2aSPaul Moore 130416efd454SPaul Moore ret_val = cipso_v4_map_lvl_hton(doi_def, 130516efd454SPaul Moore secattr->attr.mls.lvl, 130616efd454SPaul Moore &level); 1307654bbc2aSPaul Moore if (ret_val != 0) 1308654bbc2aSPaul Moore return ret_val; 1309654bbc2aSPaul Moore 1310654bbc2aSPaul Moore if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { 1311654bbc2aSPaul Moore ret_val = cipso_v4_map_cat_enum_hton(doi_def, 1312654bbc2aSPaul Moore secattr, 1313654bbc2aSPaul Moore &buffer[4], 1314654bbc2aSPaul Moore buffer_len - 4); 1315654bbc2aSPaul Moore if (ret_val < 0) 1316654bbc2aSPaul Moore return ret_val; 1317654bbc2aSPaul Moore 1318654bbc2aSPaul Moore tag_len = 4 + ret_val; 1319654bbc2aSPaul Moore } else 1320654bbc2aSPaul Moore tag_len = 4; 1321654bbc2aSPaul Moore 132215c45f7bSPaul Moore buffer[0] = CIPSO_V4_TAG_ENUM; 1323654bbc2aSPaul Moore buffer[1] = tag_len; 1324654bbc2aSPaul Moore buffer[3] = level; 1325654bbc2aSPaul Moore 1326654bbc2aSPaul Moore return tag_len; 1327654bbc2aSPaul Moore } 1328654bbc2aSPaul Moore 1329654bbc2aSPaul Moore /** 1330654bbc2aSPaul Moore * cipso_v4_parsetag_enum - Parse a CIPSO enumerated tag 1331654bbc2aSPaul Moore * @doi_def: the DOI definition 1332654bbc2aSPaul Moore * @tag: the CIPSO tag 1333654bbc2aSPaul Moore * @secattr: the security attributes 1334654bbc2aSPaul Moore * 1335654bbc2aSPaul Moore * Description: 1336654bbc2aSPaul Moore * Parse a CIPSO enumerated tag (tag type #2) and return the security 1337654bbc2aSPaul Moore * attributes in @secattr. Return zero on success, negatives values on 1338654bbc2aSPaul Moore * failure. 1339654bbc2aSPaul Moore * 1340654bbc2aSPaul Moore */ 1341654bbc2aSPaul Moore static int cipso_v4_parsetag_enum(const struct cipso_v4_doi *doi_def, 1342654bbc2aSPaul Moore const unsigned char *tag, 1343654bbc2aSPaul Moore struct netlbl_lsm_secattr *secattr) 1344654bbc2aSPaul Moore { 1345654bbc2aSPaul Moore int ret_val; 1346654bbc2aSPaul Moore u8 tag_len = tag[1]; 1347654bbc2aSPaul Moore u32 level; 1348654bbc2aSPaul Moore 1349654bbc2aSPaul Moore ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level); 1350654bbc2aSPaul Moore if (ret_val != 0) 1351654bbc2aSPaul Moore return ret_val; 135216efd454SPaul Moore secattr->attr.mls.lvl = level; 1353654bbc2aSPaul Moore secattr->flags |= NETLBL_SECATTR_MLS_LVL; 1354654bbc2aSPaul Moore 1355654bbc2aSPaul Moore if (tag_len > 4) { 1356654bbc2aSPaul Moore ret_val = cipso_v4_map_cat_enum_ntoh(doi_def, 1357654bbc2aSPaul Moore &tag[4], 1358654bbc2aSPaul Moore tag_len - 4, 1359654bbc2aSPaul Moore secattr); 1360654bbc2aSPaul Moore if (ret_val != 0) { 13614fbe63d1SPaul Moore netlbl_catmap_free(secattr->attr.mls.cat); 1362654bbc2aSPaul Moore return ret_val; 1363654bbc2aSPaul Moore } 1364654bbc2aSPaul Moore 1365654bbc2aSPaul Moore secattr->flags |= NETLBL_SECATTR_MLS_CAT; 1366654bbc2aSPaul Moore } 1367654bbc2aSPaul Moore 1368654bbc2aSPaul Moore return 0; 1369654bbc2aSPaul Moore } 1370654bbc2aSPaul Moore 1371654bbc2aSPaul Moore /** 1372484b3669SPaul Moore * cipso_v4_gentag_rng - Generate a CIPSO ranged tag (type #5) 1373484b3669SPaul Moore * @doi_def: the DOI definition 1374484b3669SPaul Moore * @secattr: the security attributes 1375484b3669SPaul Moore * @buffer: the option buffer 1376484b3669SPaul Moore * @buffer_len: length of buffer in bytes 1377484b3669SPaul Moore * 1378484b3669SPaul Moore * Description: 1379484b3669SPaul Moore * Generate a CIPSO option using the ranged tag, tag type #5. Returns the 1380484b3669SPaul Moore * size of the tag on success, negative values on failure. 1381484b3669SPaul Moore * 1382484b3669SPaul Moore */ 1383484b3669SPaul Moore static int cipso_v4_gentag_rng(const struct cipso_v4_doi *doi_def, 1384484b3669SPaul Moore const struct netlbl_lsm_secattr *secattr, 1385484b3669SPaul Moore unsigned char *buffer, 1386484b3669SPaul Moore u32 buffer_len) 1387484b3669SPaul Moore { 1388484b3669SPaul Moore int ret_val; 1389484b3669SPaul Moore u32 tag_len; 1390484b3669SPaul Moore u32 level; 1391484b3669SPaul Moore 1392484b3669SPaul Moore if (!(secattr->flags & NETLBL_SECATTR_MLS_LVL)) 1393484b3669SPaul Moore return -EPERM; 1394484b3669SPaul Moore 139516efd454SPaul Moore ret_val = cipso_v4_map_lvl_hton(doi_def, 139616efd454SPaul Moore secattr->attr.mls.lvl, 139716efd454SPaul Moore &level); 1398484b3669SPaul Moore if (ret_val != 0) 1399484b3669SPaul Moore return ret_val; 1400484b3669SPaul Moore 1401484b3669SPaul Moore if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { 1402484b3669SPaul Moore ret_val = cipso_v4_map_cat_rng_hton(doi_def, 1403484b3669SPaul Moore secattr, 1404484b3669SPaul Moore &buffer[4], 1405484b3669SPaul Moore buffer_len - 4); 1406484b3669SPaul Moore if (ret_val < 0) 1407484b3669SPaul Moore return ret_val; 1408484b3669SPaul Moore 1409484b3669SPaul Moore tag_len = 4 + ret_val; 1410484b3669SPaul Moore } else 1411484b3669SPaul Moore tag_len = 4; 1412484b3669SPaul Moore 141315c45f7bSPaul Moore buffer[0] = CIPSO_V4_TAG_RANGE; 1414484b3669SPaul Moore buffer[1] = tag_len; 1415484b3669SPaul Moore buffer[3] = level; 1416484b3669SPaul Moore 1417484b3669SPaul Moore return tag_len; 1418484b3669SPaul Moore } 1419484b3669SPaul Moore 1420484b3669SPaul Moore /** 1421484b3669SPaul Moore * cipso_v4_parsetag_rng - Parse a CIPSO ranged tag 1422484b3669SPaul Moore * @doi_def: the DOI definition 1423484b3669SPaul Moore * @tag: the CIPSO tag 1424484b3669SPaul Moore * @secattr: the security attributes 1425484b3669SPaul Moore * 1426484b3669SPaul Moore * Description: 1427484b3669SPaul Moore * Parse a CIPSO ranged tag (tag type #5) and return the security attributes 1428484b3669SPaul Moore * in @secattr. Return zero on success, negatives values on failure. 1429484b3669SPaul Moore * 1430484b3669SPaul Moore */ 1431484b3669SPaul Moore static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def, 1432484b3669SPaul Moore const unsigned char *tag, 1433484b3669SPaul Moore struct netlbl_lsm_secattr *secattr) 1434484b3669SPaul Moore { 1435484b3669SPaul Moore int ret_val; 1436484b3669SPaul Moore u8 tag_len = tag[1]; 1437484b3669SPaul Moore u32 level; 1438484b3669SPaul Moore 1439484b3669SPaul Moore ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level); 1440484b3669SPaul Moore if (ret_val != 0) 1441484b3669SPaul Moore return ret_val; 144216efd454SPaul Moore secattr->attr.mls.lvl = level; 1443484b3669SPaul Moore secattr->flags |= NETLBL_SECATTR_MLS_LVL; 1444484b3669SPaul Moore 1445484b3669SPaul Moore if (tag_len > 4) { 1446484b3669SPaul Moore ret_val = cipso_v4_map_cat_rng_ntoh(doi_def, 1447484b3669SPaul Moore &tag[4], 1448484b3669SPaul Moore tag_len - 4, 1449484b3669SPaul Moore secattr); 1450484b3669SPaul Moore if (ret_val != 0) { 14514fbe63d1SPaul Moore netlbl_catmap_free(secattr->attr.mls.cat); 1452484b3669SPaul Moore return ret_val; 1453484b3669SPaul Moore } 1454484b3669SPaul Moore 1455484b3669SPaul Moore secattr->flags |= NETLBL_SECATTR_MLS_CAT; 1456484b3669SPaul Moore } 1457484b3669SPaul Moore 1458484b3669SPaul Moore return 0; 1459484b3669SPaul Moore } 1460484b3669SPaul Moore 1461484b3669SPaul Moore /** 146215c45f7bSPaul Moore * cipso_v4_gentag_loc - Generate a CIPSO local tag (non-standard) 146315c45f7bSPaul Moore * @doi_def: the DOI definition 146415c45f7bSPaul Moore * @secattr: the security attributes 146515c45f7bSPaul Moore * @buffer: the option buffer 146615c45f7bSPaul Moore * @buffer_len: length of buffer in bytes 146715c45f7bSPaul Moore * 146815c45f7bSPaul Moore * Description: 146915c45f7bSPaul Moore * Generate a CIPSO option using the local tag. Returns the size of the tag 147015c45f7bSPaul Moore * on success, negative values on failure. 147115c45f7bSPaul Moore * 147215c45f7bSPaul Moore */ 147315c45f7bSPaul Moore static int cipso_v4_gentag_loc(const struct cipso_v4_doi *doi_def, 147415c45f7bSPaul Moore const struct netlbl_lsm_secattr *secattr, 147515c45f7bSPaul Moore unsigned char *buffer, 147615c45f7bSPaul Moore u32 buffer_len) 147715c45f7bSPaul Moore { 147815c45f7bSPaul Moore if (!(secattr->flags & NETLBL_SECATTR_SECID)) 147915c45f7bSPaul Moore return -EPERM; 148015c45f7bSPaul Moore 148115c45f7bSPaul Moore buffer[0] = CIPSO_V4_TAG_LOCAL; 148215c45f7bSPaul Moore buffer[1] = CIPSO_V4_TAG_LOC_BLEN; 148315c45f7bSPaul Moore *(u32 *)&buffer[2] = secattr->attr.secid; 148415c45f7bSPaul Moore 148515c45f7bSPaul Moore return CIPSO_V4_TAG_LOC_BLEN; 148615c45f7bSPaul Moore } 148715c45f7bSPaul Moore 148815c45f7bSPaul Moore /** 148915c45f7bSPaul Moore * cipso_v4_parsetag_loc - Parse a CIPSO local tag 149015c45f7bSPaul Moore * @doi_def: the DOI definition 149115c45f7bSPaul Moore * @tag: the CIPSO tag 149215c45f7bSPaul Moore * @secattr: the security attributes 149315c45f7bSPaul Moore * 149415c45f7bSPaul Moore * Description: 149515c45f7bSPaul Moore * Parse a CIPSO local tag and return the security attributes in @secattr. 149615c45f7bSPaul Moore * Return zero on success, negatives values on failure. 149715c45f7bSPaul Moore * 149815c45f7bSPaul Moore */ 149915c45f7bSPaul Moore static int cipso_v4_parsetag_loc(const struct cipso_v4_doi *doi_def, 150015c45f7bSPaul Moore const unsigned char *tag, 150115c45f7bSPaul Moore struct netlbl_lsm_secattr *secattr) 150215c45f7bSPaul Moore { 150315c45f7bSPaul Moore secattr->attr.secid = *(u32 *)&tag[2]; 150415c45f7bSPaul Moore secattr->flags |= NETLBL_SECATTR_SECID; 150515c45f7bSPaul Moore 150615c45f7bSPaul Moore return 0; 150715c45f7bSPaul Moore } 150815c45f7bSPaul Moore 150915c45f7bSPaul Moore /** 151004f81f01SPaul Moore * cipso_v4_optptr - Find the CIPSO option in the packet 151104f81f01SPaul Moore * @skb: the packet 151204f81f01SPaul Moore * 151304f81f01SPaul Moore * Description: 151404f81f01SPaul Moore * Parse the packet's IP header looking for a CIPSO option. Returns a pointer 151504f81f01SPaul Moore * to the start of the CIPSO option on success, NULL if one if not found. 151604f81f01SPaul Moore * 151704f81f01SPaul Moore */ 151804f81f01SPaul Moore unsigned char *cipso_v4_optptr(const struct sk_buff *skb) 151904f81f01SPaul Moore { 152004f81f01SPaul Moore const struct iphdr *iph = ip_hdr(skb); 152104f81f01SPaul Moore unsigned char *optptr = (unsigned char *)&(ip_hdr(skb)[1]); 152204f81f01SPaul Moore int optlen; 152304f81f01SPaul Moore int taglen; 152404f81f01SPaul Moore 152504f81f01SPaul Moore for (optlen = iph->ihl*4 - sizeof(struct iphdr); optlen > 0; ) { 152604f81f01SPaul Moore if (optptr[0] == IPOPT_CIPSO) 152704f81f01SPaul Moore return optptr; 152804f81f01SPaul Moore taglen = optptr[1]; 152904f81f01SPaul Moore optlen -= taglen; 153004f81f01SPaul Moore optptr += taglen; 153104f81f01SPaul Moore } 153204f81f01SPaul Moore 153304f81f01SPaul Moore return NULL; 153404f81f01SPaul Moore } 153504f81f01SPaul Moore 153604f81f01SPaul Moore /** 1537446fda4fSPaul Moore * cipso_v4_validate - Validate a CIPSO option 1538446fda4fSPaul Moore * @option: the start of the option, on error it is set to point to the error 1539446fda4fSPaul Moore * 1540446fda4fSPaul Moore * Description: 1541446fda4fSPaul Moore * This routine is called to validate a CIPSO option, it checks all of the 1542446fda4fSPaul Moore * fields to ensure that they are at least valid, see the draft snippet below 1543446fda4fSPaul Moore * for details. If the option is valid then a zero value is returned and 1544446fda4fSPaul Moore * the value of @option is unchanged. If the option is invalid then a 1545446fda4fSPaul Moore * non-zero value is returned and @option is adjusted to point to the 1546446fda4fSPaul Moore * offending portion of the option. From the IETF draft ... 1547446fda4fSPaul Moore * 1548446fda4fSPaul Moore * "If any field within the CIPSO options, such as the DOI identifier, is not 1549446fda4fSPaul Moore * recognized the IP datagram is discarded and an ICMP 'parameter problem' 1550446fda4fSPaul Moore * (type 12) is generated and returned. The ICMP code field is set to 'bad 1551446fda4fSPaul Moore * parameter' (code 0) and the pointer is set to the start of the CIPSO field 1552446fda4fSPaul Moore * that is unrecognized." 1553446fda4fSPaul Moore * 1554446fda4fSPaul Moore */ 155515c45f7bSPaul Moore int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option) 1556446fda4fSPaul Moore { 1557446fda4fSPaul Moore unsigned char *opt = *option; 1558446fda4fSPaul Moore unsigned char *tag; 1559446fda4fSPaul Moore unsigned char opt_iter; 1560446fda4fSPaul Moore unsigned char err_offset = 0; 1561446fda4fSPaul Moore u8 opt_len; 1562446fda4fSPaul Moore u8 tag_len; 1563446fda4fSPaul Moore struct cipso_v4_doi *doi_def = NULL; 1564446fda4fSPaul Moore u32 tag_iter; 1565446fda4fSPaul Moore 1566446fda4fSPaul Moore /* caller already checks for length values that are too large */ 1567446fda4fSPaul Moore opt_len = opt[1]; 1568446fda4fSPaul Moore if (opt_len < 8) { 1569446fda4fSPaul Moore err_offset = 1; 1570446fda4fSPaul Moore goto validate_return; 1571446fda4fSPaul Moore } 1572446fda4fSPaul Moore 1573446fda4fSPaul Moore rcu_read_lock(); 1574d3e2ce3bSHarvey Harrison doi_def = cipso_v4_doi_search(get_unaligned_be32(&opt[2])); 157551456b29SIan Morris if (!doi_def) { 1576446fda4fSPaul Moore err_offset = 2; 1577446fda4fSPaul Moore goto validate_return_locked; 1578446fda4fSPaul Moore } 1579446fda4fSPaul Moore 158015c45f7bSPaul Moore opt_iter = CIPSO_V4_HDR_LEN; 1581446fda4fSPaul Moore tag = opt + opt_iter; 1582446fda4fSPaul Moore while (opt_iter < opt_len) { 1583446fda4fSPaul Moore for (tag_iter = 0; doi_def->tags[tag_iter] != tag[0];) 1584446fda4fSPaul Moore if (doi_def->tags[tag_iter] == CIPSO_V4_TAG_INVALID || 1585446fda4fSPaul Moore ++tag_iter == CIPSO_V4_TAG_MAXCNT) { 1586446fda4fSPaul Moore err_offset = opt_iter; 1587446fda4fSPaul Moore goto validate_return_locked; 1588446fda4fSPaul Moore } 1589446fda4fSPaul Moore 1590446fda4fSPaul Moore tag_len = tag[1]; 1591446fda4fSPaul Moore if (tag_len > (opt_len - opt_iter)) { 1592446fda4fSPaul Moore err_offset = opt_iter + 1; 1593446fda4fSPaul Moore goto validate_return_locked; 1594446fda4fSPaul Moore } 1595446fda4fSPaul Moore 1596446fda4fSPaul Moore switch (tag[0]) { 1597446fda4fSPaul Moore case CIPSO_V4_TAG_RBITMAP: 159815c45f7bSPaul Moore if (tag_len < CIPSO_V4_TAG_RBM_BLEN) { 1599446fda4fSPaul Moore err_offset = opt_iter + 1; 1600446fda4fSPaul Moore goto validate_return_locked; 1601446fda4fSPaul Moore } 1602446fda4fSPaul Moore 1603446fda4fSPaul Moore /* We are already going to do all the verification 1604446fda4fSPaul Moore * necessary at the socket layer so from our point of 1605446fda4fSPaul Moore * view it is safe to turn these checks off (and less 1606446fda4fSPaul Moore * work), however, the CIPSO draft says we should do 1607446fda4fSPaul Moore * all the CIPSO validations here but it doesn't 1608446fda4fSPaul Moore * really specify _exactly_ what we need to validate 1609446fda4fSPaul Moore * ... so, just make it a sysctl tunable. */ 1610446fda4fSPaul Moore if (cipso_v4_rbm_strictvalid) { 1611446fda4fSPaul Moore if (cipso_v4_map_lvl_valid(doi_def, 1612446fda4fSPaul Moore tag[3]) < 0) { 1613446fda4fSPaul Moore err_offset = opt_iter + 3; 1614446fda4fSPaul Moore goto validate_return_locked; 1615446fda4fSPaul Moore } 161615c45f7bSPaul Moore if (tag_len > CIPSO_V4_TAG_RBM_BLEN && 1617446fda4fSPaul Moore cipso_v4_map_cat_rbm_valid(doi_def, 1618446fda4fSPaul Moore &tag[4], 1619446fda4fSPaul Moore tag_len - 4) < 0) { 1620446fda4fSPaul Moore err_offset = opt_iter + 4; 1621446fda4fSPaul Moore goto validate_return_locked; 1622446fda4fSPaul Moore } 1623446fda4fSPaul Moore } 1624446fda4fSPaul Moore break; 1625654bbc2aSPaul Moore case CIPSO_V4_TAG_ENUM: 162615c45f7bSPaul Moore if (tag_len < CIPSO_V4_TAG_ENUM_BLEN) { 1627654bbc2aSPaul Moore err_offset = opt_iter + 1; 1628654bbc2aSPaul Moore goto validate_return_locked; 1629654bbc2aSPaul Moore } 1630654bbc2aSPaul Moore 1631654bbc2aSPaul Moore if (cipso_v4_map_lvl_valid(doi_def, 1632654bbc2aSPaul Moore tag[3]) < 0) { 1633654bbc2aSPaul Moore err_offset = opt_iter + 3; 1634654bbc2aSPaul Moore goto validate_return_locked; 1635654bbc2aSPaul Moore } 163615c45f7bSPaul Moore if (tag_len > CIPSO_V4_TAG_ENUM_BLEN && 1637654bbc2aSPaul Moore cipso_v4_map_cat_enum_valid(doi_def, 1638654bbc2aSPaul Moore &tag[4], 1639654bbc2aSPaul Moore tag_len - 4) < 0) { 1640654bbc2aSPaul Moore err_offset = opt_iter + 4; 1641654bbc2aSPaul Moore goto validate_return_locked; 1642654bbc2aSPaul Moore } 1643654bbc2aSPaul Moore break; 1644484b3669SPaul Moore case CIPSO_V4_TAG_RANGE: 164515c45f7bSPaul Moore if (tag_len < CIPSO_V4_TAG_RNG_BLEN) { 1646484b3669SPaul Moore err_offset = opt_iter + 1; 1647484b3669SPaul Moore goto validate_return_locked; 1648484b3669SPaul Moore } 1649484b3669SPaul Moore 1650484b3669SPaul Moore if (cipso_v4_map_lvl_valid(doi_def, 1651484b3669SPaul Moore tag[3]) < 0) { 1652484b3669SPaul Moore err_offset = opt_iter + 3; 1653484b3669SPaul Moore goto validate_return_locked; 1654484b3669SPaul Moore } 165515c45f7bSPaul Moore if (tag_len > CIPSO_V4_TAG_RNG_BLEN && 1656484b3669SPaul Moore cipso_v4_map_cat_rng_valid(doi_def, 1657484b3669SPaul Moore &tag[4], 1658484b3669SPaul Moore tag_len - 4) < 0) { 1659484b3669SPaul Moore err_offset = opt_iter + 4; 1660484b3669SPaul Moore goto validate_return_locked; 1661484b3669SPaul Moore } 1662484b3669SPaul Moore break; 166315c45f7bSPaul Moore case CIPSO_V4_TAG_LOCAL: 166415c45f7bSPaul Moore /* This is a non-standard tag that we only allow for 166515c45f7bSPaul Moore * local connections, so if the incoming interface is 166689d7ae34SPaul Moore * not the loopback device drop the packet. Further, 166789d7ae34SPaul Moore * there is no legitimate reason for setting this from 166889d7ae34SPaul Moore * userspace so reject it if skb is NULL. */ 166951456b29SIan Morris if (!skb || !(skb->dev->flags & IFF_LOOPBACK)) { 167015c45f7bSPaul Moore err_offset = opt_iter; 167115c45f7bSPaul Moore goto validate_return_locked; 167215c45f7bSPaul Moore } 167315c45f7bSPaul Moore if (tag_len != CIPSO_V4_TAG_LOC_BLEN) { 167415c45f7bSPaul Moore err_offset = opt_iter + 1; 167515c45f7bSPaul Moore goto validate_return_locked; 167615c45f7bSPaul Moore } 167715c45f7bSPaul Moore break; 1678446fda4fSPaul Moore default: 1679446fda4fSPaul Moore err_offset = opt_iter; 1680446fda4fSPaul Moore goto validate_return_locked; 1681446fda4fSPaul Moore } 1682446fda4fSPaul Moore 1683446fda4fSPaul Moore tag += tag_len; 1684446fda4fSPaul Moore opt_iter += tag_len; 1685446fda4fSPaul Moore } 1686446fda4fSPaul Moore 1687446fda4fSPaul Moore validate_return_locked: 1688446fda4fSPaul Moore rcu_read_unlock(); 1689446fda4fSPaul Moore validate_return: 1690446fda4fSPaul Moore *option = opt + err_offset; 1691446fda4fSPaul Moore return err_offset; 1692446fda4fSPaul Moore } 1693446fda4fSPaul Moore 1694446fda4fSPaul Moore /** 169525985edcSLucas De Marchi * cipso_v4_error - Send the correct response for a bad packet 1696446fda4fSPaul Moore * @skb: the packet 1697446fda4fSPaul Moore * @error: the error code 1698446fda4fSPaul Moore * @gateway: CIPSO gateway flag 1699446fda4fSPaul Moore * 1700446fda4fSPaul Moore * Description: 1701446fda4fSPaul Moore * Based on the error code given in @error, send an ICMP error message back to 1702446fda4fSPaul Moore * the originating host. From the IETF draft ... 1703446fda4fSPaul Moore * 1704446fda4fSPaul Moore * "If the contents of the CIPSO [option] are valid but the security label is 1705446fda4fSPaul Moore * outside of the configured host or port label range, the datagram is 1706446fda4fSPaul Moore * discarded and an ICMP 'destination unreachable' (type 3) is generated and 1707446fda4fSPaul Moore * returned. The code field of the ICMP is set to 'communication with 1708446fda4fSPaul Moore * destination network administratively prohibited' (code 9) or to 1709446fda4fSPaul Moore * 'communication with destination host administratively prohibited' 1710446fda4fSPaul Moore * (code 10). The value of the code is dependent on whether the originator 1711446fda4fSPaul Moore * of the ICMP message is acting as a CIPSO host or a CIPSO gateway. The 1712446fda4fSPaul Moore * recipient of the ICMP message MUST be able to handle either value. The 1713446fda4fSPaul Moore * same procedure is performed if a CIPSO [option] can not be added to an 1714446fda4fSPaul Moore * IP packet because it is too large to fit in the IP options area." 1715446fda4fSPaul Moore * 1716446fda4fSPaul Moore * "If the error is triggered by receipt of an ICMP message, the message is 1717446fda4fSPaul Moore * discarded and no response is permitted (consistent with general ICMP 1718446fda4fSPaul Moore * processing rules)." 1719446fda4fSPaul Moore * 1720446fda4fSPaul Moore */ 1721446fda4fSPaul Moore void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway) 1722446fda4fSPaul Moore { 1723eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->protocol == IPPROTO_ICMP || error != -EACCES) 1724446fda4fSPaul Moore return; 1725446fda4fSPaul Moore 1726446fda4fSPaul Moore if (gateway) 1727446fda4fSPaul Moore icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0); 1728446fda4fSPaul Moore else 1729446fda4fSPaul Moore icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0); 1730446fda4fSPaul Moore } 1731446fda4fSPaul Moore 1732446fda4fSPaul Moore /** 1733948bf85cSPaul Moore * cipso_v4_genopt - Generate a CIPSO option 1734948bf85cSPaul Moore * @buf: the option buffer 1735948bf85cSPaul Moore * @buf_len: the size of opt_buf 1736446fda4fSPaul Moore * @doi_def: the CIPSO DOI to use 1737948bf85cSPaul Moore * @secattr: the security attributes 1738446fda4fSPaul Moore * 1739446fda4fSPaul Moore * Description: 1740948bf85cSPaul Moore * Generate a CIPSO option using the DOI definition and security attributes 1741948bf85cSPaul Moore * passed to the function. Returns the length of the option on success and 1742948bf85cSPaul Moore * negative values on failure. 1743446fda4fSPaul Moore * 1744446fda4fSPaul Moore */ 1745948bf85cSPaul Moore static int cipso_v4_genopt(unsigned char *buf, u32 buf_len, 1746446fda4fSPaul Moore const struct cipso_v4_doi *doi_def, 1747446fda4fSPaul Moore const struct netlbl_lsm_secattr *secattr) 1748446fda4fSPaul Moore { 1749948bf85cSPaul Moore int ret_val; 1750446fda4fSPaul Moore u32 iter; 1751446fda4fSPaul Moore 1752948bf85cSPaul Moore if (buf_len <= CIPSO_V4_HDR_LEN) 1753948bf85cSPaul Moore return -ENOSPC; 175491b1ed0aSPaul Moore 1755446fda4fSPaul Moore /* XXX - This code assumes only one tag per CIPSO option which isn't 1756446fda4fSPaul Moore * really a good assumption to make but since we only support the MAC 1757446fda4fSPaul Moore * tags right now it is a safe assumption. */ 1758446fda4fSPaul Moore iter = 0; 1759446fda4fSPaul Moore do { 176091b1ed0aSPaul Moore memset(buf, 0, buf_len); 1761446fda4fSPaul Moore switch (doi_def->tags[iter]) { 1762446fda4fSPaul Moore case CIPSO_V4_TAG_RBITMAP: 1763446fda4fSPaul Moore ret_val = cipso_v4_gentag_rbm(doi_def, 1764446fda4fSPaul Moore secattr, 176591b1ed0aSPaul Moore &buf[CIPSO_V4_HDR_LEN], 176691b1ed0aSPaul Moore buf_len - CIPSO_V4_HDR_LEN); 1767446fda4fSPaul Moore break; 1768654bbc2aSPaul Moore case CIPSO_V4_TAG_ENUM: 1769654bbc2aSPaul Moore ret_val = cipso_v4_gentag_enum(doi_def, 1770654bbc2aSPaul Moore secattr, 1771654bbc2aSPaul Moore &buf[CIPSO_V4_HDR_LEN], 1772654bbc2aSPaul Moore buf_len - CIPSO_V4_HDR_LEN); 1773654bbc2aSPaul Moore break; 1774484b3669SPaul Moore case CIPSO_V4_TAG_RANGE: 1775484b3669SPaul Moore ret_val = cipso_v4_gentag_rng(doi_def, 1776484b3669SPaul Moore secattr, 1777484b3669SPaul Moore &buf[CIPSO_V4_HDR_LEN], 1778484b3669SPaul Moore buf_len - CIPSO_V4_HDR_LEN); 1779484b3669SPaul Moore break; 178015c45f7bSPaul Moore case CIPSO_V4_TAG_LOCAL: 178115c45f7bSPaul Moore ret_val = cipso_v4_gentag_loc(doi_def, 178215c45f7bSPaul Moore secattr, 178315c45f7bSPaul Moore &buf[CIPSO_V4_HDR_LEN], 178415c45f7bSPaul Moore buf_len - CIPSO_V4_HDR_LEN); 178515c45f7bSPaul Moore break; 1786446fda4fSPaul Moore default: 1787948bf85cSPaul Moore return -EPERM; 1788446fda4fSPaul Moore } 1789446fda4fSPaul Moore 1790446fda4fSPaul Moore iter++; 179191b1ed0aSPaul Moore } while (ret_val < 0 && 1792446fda4fSPaul Moore iter < CIPSO_V4_TAG_MAXCNT && 1793446fda4fSPaul Moore doi_def->tags[iter] != CIPSO_V4_TAG_INVALID); 179491b1ed0aSPaul Moore if (ret_val < 0) 1795948bf85cSPaul Moore return ret_val; 179691b1ed0aSPaul Moore cipso_v4_gentag_hdr(doi_def, buf, ret_val); 1797948bf85cSPaul Moore return CIPSO_V4_HDR_LEN + ret_val; 1798948bf85cSPaul Moore } 1799948bf85cSPaul Moore 1800948bf85cSPaul Moore /** 1801948bf85cSPaul Moore * cipso_v4_sock_setattr - Add a CIPSO option to a socket 1802948bf85cSPaul Moore * @sk: the socket 1803948bf85cSPaul Moore * @doi_def: the CIPSO DOI to use 1804948bf85cSPaul Moore * @secattr: the specific security attributes of the socket 1805948bf85cSPaul Moore * 1806948bf85cSPaul Moore * Description: 1807948bf85cSPaul Moore * Set the CIPSO option on the given socket using the DOI definition and 1808948bf85cSPaul Moore * security attributes passed to the function. This function requires 1809948bf85cSPaul Moore * exclusive access to @sk, which means it either needs to be in the 1810948bf85cSPaul Moore * process of being created or locked. Returns zero on success and negative 1811948bf85cSPaul Moore * values on failure. 1812948bf85cSPaul Moore * 1813948bf85cSPaul Moore */ 1814948bf85cSPaul Moore int cipso_v4_sock_setattr(struct sock *sk, 1815948bf85cSPaul Moore const struct cipso_v4_doi *doi_def, 1816948bf85cSPaul Moore const struct netlbl_lsm_secattr *secattr) 1817948bf85cSPaul Moore { 1818948bf85cSPaul Moore int ret_val = -EPERM; 1819948bf85cSPaul Moore unsigned char *buf = NULL; 1820948bf85cSPaul Moore u32 buf_len; 1821948bf85cSPaul Moore u32 opt_len; 1822f6d8bd05SEric Dumazet struct ip_options_rcu *old, *opt = NULL; 1823948bf85cSPaul Moore struct inet_sock *sk_inet; 1824948bf85cSPaul Moore struct inet_connection_sock *sk_conn; 1825948bf85cSPaul Moore 1826948bf85cSPaul Moore /* In the case of sock_create_lite(), the sock->sk field is not 1827948bf85cSPaul Moore * defined yet but it is not a problem as the only users of these 1828948bf85cSPaul Moore * "lite" PF_INET sockets are functions which do an accept() call 1829948bf85cSPaul Moore * afterwards so we will label the socket as part of the accept(). */ 183051456b29SIan Morris if (!sk) 1831948bf85cSPaul Moore return 0; 1832948bf85cSPaul Moore 1833948bf85cSPaul Moore /* We allocate the maximum CIPSO option size here so we are probably 1834948bf85cSPaul Moore * being a little wasteful, but it makes our life _much_ easier later 1835948bf85cSPaul Moore * on and after all we are only talking about 40 bytes. */ 1836948bf85cSPaul Moore buf_len = CIPSO_V4_OPT_LEN_MAX; 1837948bf85cSPaul Moore buf = kmalloc(buf_len, GFP_ATOMIC); 183851456b29SIan Morris if (!buf) { 1839948bf85cSPaul Moore ret_val = -ENOMEM; 1840948bf85cSPaul Moore goto socket_setattr_failure; 1841948bf85cSPaul Moore } 1842948bf85cSPaul Moore 1843948bf85cSPaul Moore ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr); 1844948bf85cSPaul Moore if (ret_val < 0) 1845948bf85cSPaul Moore goto socket_setattr_failure; 1846948bf85cSPaul Moore buf_len = ret_val; 1847446fda4fSPaul Moore 1848446fda4fSPaul Moore /* We can't use ip_options_get() directly because it makes a call to 1849446fda4fSPaul Moore * ip_options_get_alloc() which allocates memory with GFP_KERNEL and 1850f8687afeSPaul Moore * we won't always have CAP_NET_RAW even though we _always_ want to 1851f8687afeSPaul Moore * set the IPOPT_CIPSO option. */ 1852446fda4fSPaul Moore opt_len = (buf_len + 3) & ~3; 1853446fda4fSPaul Moore opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC); 185451456b29SIan Morris if (!opt) { 1855446fda4fSPaul Moore ret_val = -ENOMEM; 1856446fda4fSPaul Moore goto socket_setattr_failure; 1857446fda4fSPaul Moore } 1858f6d8bd05SEric Dumazet memcpy(opt->opt.__data, buf, buf_len); 1859f6d8bd05SEric Dumazet opt->opt.optlen = opt_len; 1860f6d8bd05SEric Dumazet opt->opt.cipso = sizeof(struct iphdr); 1861446fda4fSPaul Moore kfree(buf); 1862446fda4fSPaul Moore buf = NULL; 1863446fda4fSPaul Moore 1864446fda4fSPaul Moore sk_inet = inet_sk(sk); 1865f6d8bd05SEric Dumazet 1866f6d8bd05SEric Dumazet old = rcu_dereference_protected(sk_inet->inet_opt, sock_owned_by_user(sk)); 1867446fda4fSPaul Moore if (sk_inet->is_icsk) { 1868446fda4fSPaul Moore sk_conn = inet_csk(sk); 1869f6d8bd05SEric Dumazet if (old) 1870f6d8bd05SEric Dumazet sk_conn->icsk_ext_hdr_len -= old->opt.optlen; 1871f6d8bd05SEric Dumazet sk_conn->icsk_ext_hdr_len += opt->opt.optlen; 1872446fda4fSPaul Moore sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie); 1873446fda4fSPaul Moore } 1874f6d8bd05SEric Dumazet rcu_assign_pointer(sk_inet->inet_opt, opt); 1875f6d8bd05SEric Dumazet if (old) 18764f9c8c1bSPaul E. McKenney kfree_rcu(old, rcu); 1877446fda4fSPaul Moore 1878446fda4fSPaul Moore return 0; 1879446fda4fSPaul Moore 1880446fda4fSPaul Moore socket_setattr_failure: 1881446fda4fSPaul Moore kfree(buf); 1882446fda4fSPaul Moore kfree(opt); 1883446fda4fSPaul Moore return ret_val; 1884446fda4fSPaul Moore } 1885446fda4fSPaul Moore 1886446fda4fSPaul Moore /** 1887389fb800SPaul Moore * cipso_v4_req_setattr - Add a CIPSO option to a connection request socket 1888389fb800SPaul Moore * @req: the connection request socket 1889389fb800SPaul Moore * @doi_def: the CIPSO DOI to use 1890389fb800SPaul Moore * @secattr: the specific security attributes of the socket 1891014ab19aSPaul Moore * 1892014ab19aSPaul Moore * Description: 1893389fb800SPaul Moore * Set the CIPSO option on the given socket using the DOI definition and 1894389fb800SPaul Moore * security attributes passed to the function. Returns zero on success and 1895389fb800SPaul Moore * negative values on failure. 1896014ab19aSPaul Moore * 1897014ab19aSPaul Moore */ 1898389fb800SPaul Moore int cipso_v4_req_setattr(struct request_sock *req, 1899389fb800SPaul Moore const struct cipso_v4_doi *doi_def, 1900389fb800SPaul Moore const struct netlbl_lsm_secattr *secattr) 1901014ab19aSPaul Moore { 1902389fb800SPaul Moore int ret_val = -EPERM; 1903389fb800SPaul Moore unsigned char *buf = NULL; 1904389fb800SPaul Moore u32 buf_len; 1905389fb800SPaul Moore u32 opt_len; 1906f6d8bd05SEric Dumazet struct ip_options_rcu *opt = NULL; 1907389fb800SPaul Moore struct inet_request_sock *req_inet; 1908014ab19aSPaul Moore 1909389fb800SPaul Moore /* We allocate the maximum CIPSO option size here so we are probably 1910389fb800SPaul Moore * being a little wasteful, but it makes our life _much_ easier later 1911389fb800SPaul Moore * on and after all we are only talking about 40 bytes. */ 1912389fb800SPaul Moore buf_len = CIPSO_V4_OPT_LEN_MAX; 1913389fb800SPaul Moore buf = kmalloc(buf_len, GFP_ATOMIC); 191451456b29SIan Morris if (!buf) { 1915389fb800SPaul Moore ret_val = -ENOMEM; 1916389fb800SPaul Moore goto req_setattr_failure; 1917389fb800SPaul Moore } 1918389fb800SPaul Moore 1919389fb800SPaul Moore ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr); 1920389fb800SPaul Moore if (ret_val < 0) 1921389fb800SPaul Moore goto req_setattr_failure; 1922389fb800SPaul Moore buf_len = ret_val; 1923389fb800SPaul Moore 1924389fb800SPaul Moore /* We can't use ip_options_get() directly because it makes a call to 1925389fb800SPaul Moore * ip_options_get_alloc() which allocates memory with GFP_KERNEL and 1926389fb800SPaul Moore * we won't always have CAP_NET_RAW even though we _always_ want to 1927389fb800SPaul Moore * set the IPOPT_CIPSO option. */ 1928389fb800SPaul Moore opt_len = (buf_len + 3) & ~3; 1929389fb800SPaul Moore opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC); 193051456b29SIan Morris if (!opt) { 1931389fb800SPaul Moore ret_val = -ENOMEM; 1932389fb800SPaul Moore goto req_setattr_failure; 1933389fb800SPaul Moore } 1934f6d8bd05SEric Dumazet memcpy(opt->opt.__data, buf, buf_len); 1935f6d8bd05SEric Dumazet opt->opt.optlen = opt_len; 1936f6d8bd05SEric Dumazet opt->opt.cipso = sizeof(struct iphdr); 1937389fb800SPaul Moore kfree(buf); 1938389fb800SPaul Moore buf = NULL; 1939389fb800SPaul Moore 1940389fb800SPaul Moore req_inet = inet_rsk(req); 1941389fb800SPaul Moore opt = xchg(&req_inet->opt, opt); 1942f6d8bd05SEric Dumazet if (opt) 19434f9c8c1bSPaul E. McKenney kfree_rcu(opt, rcu); 1944389fb800SPaul Moore 1945389fb800SPaul Moore return 0; 1946389fb800SPaul Moore 1947389fb800SPaul Moore req_setattr_failure: 1948389fb800SPaul Moore kfree(buf); 1949389fb800SPaul Moore kfree(opt); 1950389fb800SPaul Moore return ret_val; 1951389fb800SPaul Moore } 1952389fb800SPaul Moore 1953389fb800SPaul Moore /** 1954389fb800SPaul Moore * cipso_v4_delopt - Delete the CIPSO option from a set of IP options 1955389fb800SPaul Moore * @opt_ptr: IP option pointer 1956389fb800SPaul Moore * 1957389fb800SPaul Moore * Description: 1958389fb800SPaul Moore * Deletes the CIPSO IP option from a set of IP options and makes the necessary 1959389fb800SPaul Moore * adjustments to the IP option structure. Returns zero on success, negative 1960389fb800SPaul Moore * values on failure. 1961389fb800SPaul Moore * 1962389fb800SPaul Moore */ 1963f6d8bd05SEric Dumazet static int cipso_v4_delopt(struct ip_options_rcu **opt_ptr) 1964389fb800SPaul Moore { 1965389fb800SPaul Moore int hdr_delta = 0; 1966f6d8bd05SEric Dumazet struct ip_options_rcu *opt = *opt_ptr; 1967014ab19aSPaul Moore 1968f6d8bd05SEric Dumazet if (opt->opt.srr || opt->opt.rr || opt->opt.ts || opt->opt.router_alert) { 1969014ab19aSPaul Moore u8 cipso_len; 1970014ab19aSPaul Moore u8 cipso_off; 1971014ab19aSPaul Moore unsigned char *cipso_ptr; 1972014ab19aSPaul Moore int iter; 1973014ab19aSPaul Moore int optlen_new; 1974014ab19aSPaul Moore 1975f6d8bd05SEric Dumazet cipso_off = opt->opt.cipso - sizeof(struct iphdr); 1976f6d8bd05SEric Dumazet cipso_ptr = &opt->opt.__data[cipso_off]; 1977014ab19aSPaul Moore cipso_len = cipso_ptr[1]; 1978014ab19aSPaul Moore 1979f6d8bd05SEric Dumazet if (opt->opt.srr > opt->opt.cipso) 1980f6d8bd05SEric Dumazet opt->opt.srr -= cipso_len; 1981f6d8bd05SEric Dumazet if (opt->opt.rr > opt->opt.cipso) 1982f6d8bd05SEric Dumazet opt->opt.rr -= cipso_len; 1983f6d8bd05SEric Dumazet if (opt->opt.ts > opt->opt.cipso) 1984f6d8bd05SEric Dumazet opt->opt.ts -= cipso_len; 1985f6d8bd05SEric Dumazet if (opt->opt.router_alert > opt->opt.cipso) 1986f6d8bd05SEric Dumazet opt->opt.router_alert -= cipso_len; 1987f6d8bd05SEric Dumazet opt->opt.cipso = 0; 1988014ab19aSPaul Moore 1989014ab19aSPaul Moore memmove(cipso_ptr, cipso_ptr + cipso_len, 1990f6d8bd05SEric Dumazet opt->opt.optlen - cipso_off - cipso_len); 1991014ab19aSPaul Moore 1992014ab19aSPaul Moore /* determining the new total option length is tricky because of 1993014ab19aSPaul Moore * the padding necessary, the only thing i can think to do at 1994014ab19aSPaul Moore * this point is walk the options one-by-one, skipping the 1995014ab19aSPaul Moore * padding at the end to determine the actual option size and 1996014ab19aSPaul Moore * from there we can determine the new total option length */ 1997014ab19aSPaul Moore iter = 0; 1998014ab19aSPaul Moore optlen_new = 0; 1999f6d8bd05SEric Dumazet while (iter < opt->opt.optlen) 2000f6d8bd05SEric Dumazet if (opt->opt.__data[iter] != IPOPT_NOP) { 2001f6d8bd05SEric Dumazet iter += opt->opt.__data[iter + 1]; 2002014ab19aSPaul Moore optlen_new = iter; 2003014ab19aSPaul Moore } else 2004014ab19aSPaul Moore iter++; 2005f6d8bd05SEric Dumazet hdr_delta = opt->opt.optlen; 2006f6d8bd05SEric Dumazet opt->opt.optlen = (optlen_new + 3) & ~3; 2007f6d8bd05SEric Dumazet hdr_delta -= opt->opt.optlen; 2008014ab19aSPaul Moore } else { 2009014ab19aSPaul Moore /* only the cipso option was present on the socket so we can 2010014ab19aSPaul Moore * remove the entire option struct */ 2011389fb800SPaul Moore *opt_ptr = NULL; 2012f6d8bd05SEric Dumazet hdr_delta = opt->opt.optlen; 20134f9c8c1bSPaul E. McKenney kfree_rcu(opt, rcu); 2014014ab19aSPaul Moore } 2015014ab19aSPaul Moore 2016389fb800SPaul Moore return hdr_delta; 2017389fb800SPaul Moore } 2018389fb800SPaul Moore 2019389fb800SPaul Moore /** 2020389fb800SPaul Moore * cipso_v4_sock_delattr - Delete the CIPSO option from a socket 2021389fb800SPaul Moore * @sk: the socket 2022389fb800SPaul Moore * 2023389fb800SPaul Moore * Description: 2024389fb800SPaul Moore * Removes the CIPSO option from a socket, if present. 2025389fb800SPaul Moore * 2026389fb800SPaul Moore */ 2027389fb800SPaul Moore void cipso_v4_sock_delattr(struct sock *sk) 2028389fb800SPaul Moore { 2029389fb800SPaul Moore int hdr_delta; 2030f6d8bd05SEric Dumazet struct ip_options_rcu *opt; 2031389fb800SPaul Moore struct inet_sock *sk_inet; 2032389fb800SPaul Moore 2033389fb800SPaul Moore sk_inet = inet_sk(sk); 2034f6d8bd05SEric Dumazet opt = rcu_dereference_protected(sk_inet->inet_opt, 1); 203551456b29SIan Morris if (!opt || opt->opt.cipso == 0) 2036389fb800SPaul Moore return; 2037389fb800SPaul Moore 2038f6d8bd05SEric Dumazet hdr_delta = cipso_v4_delopt(&sk_inet->inet_opt); 2039014ab19aSPaul Moore if (sk_inet->is_icsk && hdr_delta > 0) { 2040014ab19aSPaul Moore struct inet_connection_sock *sk_conn = inet_csk(sk); 2041014ab19aSPaul Moore sk_conn->icsk_ext_hdr_len -= hdr_delta; 2042014ab19aSPaul Moore sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie); 2043014ab19aSPaul Moore } 2044014ab19aSPaul Moore } 2045014ab19aSPaul Moore 2046014ab19aSPaul Moore /** 2047389fb800SPaul Moore * cipso_v4_req_delattr - Delete the CIPSO option from a request socket 2048389fb800SPaul Moore * @reg: the request socket 2049389fb800SPaul Moore * 2050389fb800SPaul Moore * Description: 2051389fb800SPaul Moore * Removes the CIPSO option from a request socket, if present. 2052389fb800SPaul Moore * 2053389fb800SPaul Moore */ 2054389fb800SPaul Moore void cipso_v4_req_delattr(struct request_sock *req) 2055389fb800SPaul Moore { 2056f6d8bd05SEric Dumazet struct ip_options_rcu *opt; 2057389fb800SPaul Moore struct inet_request_sock *req_inet; 2058389fb800SPaul Moore 2059389fb800SPaul Moore req_inet = inet_rsk(req); 2060389fb800SPaul Moore opt = req_inet->opt; 206151456b29SIan Morris if (!opt || opt->opt.cipso == 0) 2062389fb800SPaul Moore return; 2063389fb800SPaul Moore 2064389fb800SPaul Moore cipso_v4_delopt(&req_inet->opt); 2065389fb800SPaul Moore } 2066389fb800SPaul Moore 2067389fb800SPaul Moore /** 206863d804eaSPaul Moore * cipso_v4_getattr - Helper function for the cipso_v4_*_getattr functions 206963d804eaSPaul Moore * @cipso: the CIPSO v4 option 207063d804eaSPaul Moore * @secattr: the security attributes 207163d804eaSPaul Moore * 207263d804eaSPaul Moore * Description: 207363d804eaSPaul Moore * Inspect @cipso and return the security attributes in @secattr. Returns zero 207463d804eaSPaul Moore * on success and negative values on failure. 207563d804eaSPaul Moore * 207663d804eaSPaul Moore */ 207704f81f01SPaul Moore int cipso_v4_getattr(const unsigned char *cipso, 207863d804eaSPaul Moore struct netlbl_lsm_secattr *secattr) 207963d804eaSPaul Moore { 208063d804eaSPaul Moore int ret_val = -ENOMSG; 208163d804eaSPaul Moore u32 doi; 208263d804eaSPaul Moore struct cipso_v4_doi *doi_def; 208363d804eaSPaul Moore 208463d804eaSPaul Moore if (cipso_v4_cache_check(cipso, cipso[1], secattr) == 0) 208563d804eaSPaul Moore return 0; 208663d804eaSPaul Moore 2087d3e2ce3bSHarvey Harrison doi = get_unaligned_be32(&cipso[2]); 208863d804eaSPaul Moore rcu_read_lock(); 208963d804eaSPaul Moore doi_def = cipso_v4_doi_search(doi); 209051456b29SIan Morris if (!doi_def) 209163d804eaSPaul Moore goto getattr_return; 209263d804eaSPaul Moore /* XXX - This code assumes only one tag per CIPSO option which isn't 209363d804eaSPaul Moore * really a good assumption to make but since we only support the MAC 209463d804eaSPaul Moore * tags right now it is a safe assumption. */ 209563d804eaSPaul Moore switch (cipso[6]) { 209663d804eaSPaul Moore case CIPSO_V4_TAG_RBITMAP: 209763d804eaSPaul Moore ret_val = cipso_v4_parsetag_rbm(doi_def, &cipso[6], secattr); 209863d804eaSPaul Moore break; 209963d804eaSPaul Moore case CIPSO_V4_TAG_ENUM: 210063d804eaSPaul Moore ret_val = cipso_v4_parsetag_enum(doi_def, &cipso[6], secattr); 210163d804eaSPaul Moore break; 210263d804eaSPaul Moore case CIPSO_V4_TAG_RANGE: 210363d804eaSPaul Moore ret_val = cipso_v4_parsetag_rng(doi_def, &cipso[6], secattr); 210463d804eaSPaul Moore break; 210515c45f7bSPaul Moore case CIPSO_V4_TAG_LOCAL: 210615c45f7bSPaul Moore ret_val = cipso_v4_parsetag_loc(doi_def, &cipso[6], secattr); 210715c45f7bSPaul Moore break; 210863d804eaSPaul Moore } 210916efd454SPaul Moore if (ret_val == 0) 211016efd454SPaul Moore secattr->type = NETLBL_NLTYPE_CIPSOV4; 211163d804eaSPaul Moore 211263d804eaSPaul Moore getattr_return: 211363d804eaSPaul Moore rcu_read_unlock(); 211463d804eaSPaul Moore return ret_val; 211563d804eaSPaul Moore } 211663d804eaSPaul Moore 211763d804eaSPaul Moore /** 211814a72f53SPaul Moore * cipso_v4_sock_getattr - Get the security attributes from a sock 211914a72f53SPaul Moore * @sk: the sock 212014a72f53SPaul Moore * @secattr: the security attributes 212114a72f53SPaul Moore * 212214a72f53SPaul Moore * Description: 212314a72f53SPaul Moore * Query @sk to see if there is a CIPSO option attached to the sock and if 212414a72f53SPaul Moore * there is return the CIPSO security attributes in @secattr. This function 212514a72f53SPaul Moore * requires that @sk be locked, or privately held, but it does not do any 212614a72f53SPaul Moore * locking itself. Returns zero on success and negative values on failure. 212714a72f53SPaul Moore * 212814a72f53SPaul Moore */ 212914a72f53SPaul Moore int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) 213014a72f53SPaul Moore { 2131f6d8bd05SEric Dumazet struct ip_options_rcu *opt; 2132f6d8bd05SEric Dumazet int res = -ENOMSG; 213314a72f53SPaul Moore 2134f6d8bd05SEric Dumazet rcu_read_lock(); 2135f6d8bd05SEric Dumazet opt = rcu_dereference(inet_sk(sk)->inet_opt); 2136f6d8bd05SEric Dumazet if (opt && opt->opt.cipso) 2137f6d8bd05SEric Dumazet res = cipso_v4_getattr(opt->opt.__data + 2138f6d8bd05SEric Dumazet opt->opt.cipso - 2139f6d8bd05SEric Dumazet sizeof(struct iphdr), 214014a72f53SPaul Moore secattr); 2141f6d8bd05SEric Dumazet rcu_read_unlock(); 2142f6d8bd05SEric Dumazet return res; 214314a72f53SPaul Moore } 214414a72f53SPaul Moore 214514a72f53SPaul Moore /** 2146948bf85cSPaul Moore * cipso_v4_skbuff_setattr - Set the CIPSO option on a packet 2147948bf85cSPaul Moore * @skb: the packet 2148948bf85cSPaul Moore * @secattr: the security attributes 2149948bf85cSPaul Moore * 2150948bf85cSPaul Moore * Description: 2151948bf85cSPaul Moore * Set the CIPSO option on the given packet based on the security attributes. 2152948bf85cSPaul Moore * Returns a pointer to the IP header on success and NULL on failure. 2153948bf85cSPaul Moore * 2154948bf85cSPaul Moore */ 2155948bf85cSPaul Moore int cipso_v4_skbuff_setattr(struct sk_buff *skb, 2156948bf85cSPaul Moore const struct cipso_v4_doi *doi_def, 2157948bf85cSPaul Moore const struct netlbl_lsm_secattr *secattr) 2158948bf85cSPaul Moore { 2159948bf85cSPaul Moore int ret_val; 2160948bf85cSPaul Moore struct iphdr *iph; 2161948bf85cSPaul Moore struct ip_options *opt = &IPCB(skb)->opt; 2162948bf85cSPaul Moore unsigned char buf[CIPSO_V4_OPT_LEN_MAX]; 2163948bf85cSPaul Moore u32 buf_len = CIPSO_V4_OPT_LEN_MAX; 2164948bf85cSPaul Moore u32 opt_len; 2165948bf85cSPaul Moore int len_delta; 2166948bf85cSPaul Moore 216700af5c69Sroel kluin ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr); 216800af5c69Sroel kluin if (ret_val < 0) 216900af5c69Sroel kluin return ret_val; 217000af5c69Sroel kluin buf_len = ret_val; 2171948bf85cSPaul Moore opt_len = (buf_len + 3) & ~3; 2172948bf85cSPaul Moore 2173948bf85cSPaul Moore /* we overwrite any existing options to ensure that we have enough 2174948bf85cSPaul Moore * room for the CIPSO option, the reason is that we _need_ to guarantee 2175948bf85cSPaul Moore * that the security label is applied to the packet - we do the same 2176948bf85cSPaul Moore * thing when using the socket options and it hasn't caused a problem, 2177948bf85cSPaul Moore * if we need to we can always revisit this choice later */ 2178948bf85cSPaul Moore 2179948bf85cSPaul Moore len_delta = opt_len - opt->optlen; 2180948bf85cSPaul Moore /* if we don't ensure enough headroom we could panic on the skb_push() 2181948bf85cSPaul Moore * call below so make sure we have enough, we are also "mangling" the 2182948bf85cSPaul Moore * packet so we should probably do a copy-on-write call anyway */ 2183948bf85cSPaul Moore ret_val = skb_cow(skb, skb_headroom(skb) + len_delta); 2184948bf85cSPaul Moore if (ret_val < 0) 2185948bf85cSPaul Moore return ret_val; 2186948bf85cSPaul Moore 2187948bf85cSPaul Moore if (len_delta > 0) { 2188948bf85cSPaul Moore /* we assume that the header + opt->optlen have already been 2189948bf85cSPaul Moore * "pushed" in ip_options_build() or similar */ 2190948bf85cSPaul Moore iph = ip_hdr(skb); 2191948bf85cSPaul Moore skb_push(skb, len_delta); 2192948bf85cSPaul Moore memmove((char *)iph - len_delta, iph, iph->ihl << 2); 2193948bf85cSPaul Moore skb_reset_network_header(skb); 2194948bf85cSPaul Moore iph = ip_hdr(skb); 2195948bf85cSPaul Moore } else if (len_delta < 0) { 2196948bf85cSPaul Moore iph = ip_hdr(skb); 2197948bf85cSPaul Moore memset(iph + 1, IPOPT_NOP, opt->optlen); 2198948bf85cSPaul Moore } else 2199948bf85cSPaul Moore iph = ip_hdr(skb); 2200948bf85cSPaul Moore 2201948bf85cSPaul Moore if (opt->optlen > 0) 2202948bf85cSPaul Moore memset(opt, 0, sizeof(*opt)); 2203948bf85cSPaul Moore opt->optlen = opt_len; 2204948bf85cSPaul Moore opt->cipso = sizeof(struct iphdr); 2205948bf85cSPaul Moore opt->is_changed = 1; 2206948bf85cSPaul Moore 2207948bf85cSPaul Moore /* we have to do the following because we are being called from a 2208948bf85cSPaul Moore * netfilter hook which means the packet already has had the header 2209948bf85cSPaul Moore * fields populated and the checksum calculated - yes this means we 2210948bf85cSPaul Moore * are doing more work than needed but we do it to keep the core 2211948bf85cSPaul Moore * stack clean and tidy */ 2212948bf85cSPaul Moore memcpy(iph + 1, buf, buf_len); 2213948bf85cSPaul Moore if (opt_len > buf_len) 2214948bf85cSPaul Moore memset((char *)(iph + 1) + buf_len, 0, opt_len - buf_len); 2215948bf85cSPaul Moore if (len_delta != 0) { 2216948bf85cSPaul Moore iph->ihl = 5 + (opt_len >> 2); 2217948bf85cSPaul Moore iph->tot_len = htons(skb->len); 2218948bf85cSPaul Moore } 2219948bf85cSPaul Moore ip_send_check(iph); 2220948bf85cSPaul Moore 2221948bf85cSPaul Moore return 0; 2222948bf85cSPaul Moore } 2223948bf85cSPaul Moore 2224948bf85cSPaul Moore /** 2225948bf85cSPaul Moore * cipso_v4_skbuff_delattr - Delete any CIPSO options from a packet 2226948bf85cSPaul Moore * @skb: the packet 2227948bf85cSPaul Moore * 2228948bf85cSPaul Moore * Description: 2229948bf85cSPaul Moore * Removes any and all CIPSO options from the given packet. Returns zero on 2230948bf85cSPaul Moore * success, negative values on failure. 2231948bf85cSPaul Moore * 2232948bf85cSPaul Moore */ 2233948bf85cSPaul Moore int cipso_v4_skbuff_delattr(struct sk_buff *skb) 2234948bf85cSPaul Moore { 2235948bf85cSPaul Moore int ret_val; 2236948bf85cSPaul Moore struct iphdr *iph; 2237948bf85cSPaul Moore struct ip_options *opt = &IPCB(skb)->opt; 2238948bf85cSPaul Moore unsigned char *cipso_ptr; 2239948bf85cSPaul Moore 2240948bf85cSPaul Moore if (opt->cipso == 0) 2241948bf85cSPaul Moore return 0; 2242948bf85cSPaul Moore 2243948bf85cSPaul Moore /* since we are changing the packet we should make a copy */ 2244948bf85cSPaul Moore ret_val = skb_cow(skb, skb_headroom(skb)); 2245948bf85cSPaul Moore if (ret_val < 0) 2246948bf85cSPaul Moore return ret_val; 2247948bf85cSPaul Moore 2248948bf85cSPaul Moore /* the easiest thing to do is just replace the cipso option with noop 2249948bf85cSPaul Moore * options since we don't change the size of the packet, although we 2250948bf85cSPaul Moore * still need to recalculate the checksum */ 2251948bf85cSPaul Moore 2252948bf85cSPaul Moore iph = ip_hdr(skb); 2253948bf85cSPaul Moore cipso_ptr = (unsigned char *)iph + opt->cipso; 2254948bf85cSPaul Moore memset(cipso_ptr, IPOPT_NOOP, cipso_ptr[1]); 2255948bf85cSPaul Moore opt->cipso = 0; 2256948bf85cSPaul Moore opt->is_changed = 1; 2257948bf85cSPaul Moore 2258948bf85cSPaul Moore ip_send_check(iph); 2259948bf85cSPaul Moore 2260948bf85cSPaul Moore return 0; 2261948bf85cSPaul Moore } 2262948bf85cSPaul Moore 2263446fda4fSPaul Moore /* 2264446fda4fSPaul Moore * Setup Functions 2265446fda4fSPaul Moore */ 2266446fda4fSPaul Moore 2267446fda4fSPaul Moore /** 2268446fda4fSPaul Moore * cipso_v4_init - Initialize the CIPSO module 2269446fda4fSPaul Moore * 2270446fda4fSPaul Moore * Description: 2271446fda4fSPaul Moore * Initialize the CIPSO module and prepare it for use. Returns zero on success 2272446fda4fSPaul Moore * and negative values on failure. 2273446fda4fSPaul Moore * 2274446fda4fSPaul Moore */ 2275446fda4fSPaul Moore static int __init cipso_v4_init(void) 2276446fda4fSPaul Moore { 2277446fda4fSPaul Moore int ret_val; 2278446fda4fSPaul Moore 2279446fda4fSPaul Moore ret_val = cipso_v4_cache_init(); 2280446fda4fSPaul Moore if (ret_val != 0) 2281446fda4fSPaul Moore panic("Failed to initialize the CIPSO/IPv4 cache (%d)\n", 2282446fda4fSPaul Moore ret_val); 2283446fda4fSPaul Moore 2284446fda4fSPaul Moore return 0; 2285446fda4fSPaul Moore } 2286446fda4fSPaul Moore 2287446fda4fSPaul Moore subsys_initcall(cipso_v4_init); 2288