162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * CIPSO - Commercial IP Security Option
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This is an implementation of the CIPSO 2.2 protocol as specified in
662306a36Sopenharmony_ci * draft-ietf-cipso-ipsecurity-01.txt with additional tag types as found in
762306a36Sopenharmony_ci * FIPS-188.  While CIPSO never became a full IETF RFC standard many vendors
862306a36Sopenharmony_ci * have chosen to adopt the protocol and over the years it has become a
962306a36Sopenharmony_ci * de-facto standard for labeled networking.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * The CIPSO draft specification can be found in the kernel's Documentation
1262306a36Sopenharmony_ci * directory as well as the following URL:
1362306a36Sopenharmony_ci *   https://tools.ietf.org/id/draft-ietf-cipso-ipsecurity-01.txt
1462306a36Sopenharmony_ci * The FIPS-188 specification can be found at the following URL:
1562306a36Sopenharmony_ci *   https://www.itl.nist.gov/fipspubs/fip188.htm
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * Author: Paul Moore <paul.moore@hp.com>
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/*
2162306a36Sopenharmony_ci * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/init.h>
2562306a36Sopenharmony_ci#include <linux/types.h>
2662306a36Sopenharmony_ci#include <linux/rcupdate.h>
2762306a36Sopenharmony_ci#include <linux/list.h>
2862306a36Sopenharmony_ci#include <linux/spinlock.h>
2962306a36Sopenharmony_ci#include <linux/string.h>
3062306a36Sopenharmony_ci#include <linux/jhash.h>
3162306a36Sopenharmony_ci#include <linux/audit.h>
3262306a36Sopenharmony_ci#include <linux/slab.h>
3362306a36Sopenharmony_ci#include <net/ip.h>
3462306a36Sopenharmony_ci#include <net/icmp.h>
3562306a36Sopenharmony_ci#include <net/tcp.h>
3662306a36Sopenharmony_ci#include <net/netlabel.h>
3762306a36Sopenharmony_ci#include <net/cipso_ipv4.h>
3862306a36Sopenharmony_ci#include <linux/atomic.h>
3962306a36Sopenharmony_ci#include <linux/bug.h>
4062306a36Sopenharmony_ci#include <asm/unaligned.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* List of available DOI definitions */
4362306a36Sopenharmony_ci/* XXX - This currently assumes a minimal number of different DOIs in use,
4462306a36Sopenharmony_ci * if in practice there are a lot of different DOIs this list should
4562306a36Sopenharmony_ci * probably be turned into a hash table or something similar so we
4662306a36Sopenharmony_ci * can do quick lookups. */
4762306a36Sopenharmony_cistatic DEFINE_SPINLOCK(cipso_v4_doi_list_lock);
4862306a36Sopenharmony_cistatic LIST_HEAD(cipso_v4_doi_list);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* Label mapping cache */
5162306a36Sopenharmony_ciint cipso_v4_cache_enabled = 1;
5262306a36Sopenharmony_ciint cipso_v4_cache_bucketsize = 10;
5362306a36Sopenharmony_ci#define CIPSO_V4_CACHE_BUCKETBITS     7
5462306a36Sopenharmony_ci#define CIPSO_V4_CACHE_BUCKETS        (1 << CIPSO_V4_CACHE_BUCKETBITS)
5562306a36Sopenharmony_ci#define CIPSO_V4_CACHE_REORDERLIMIT   10
5662306a36Sopenharmony_cistruct cipso_v4_map_cache_bkt {
5762306a36Sopenharmony_ci	spinlock_t lock;
5862306a36Sopenharmony_ci	u32 size;
5962306a36Sopenharmony_ci	struct list_head list;
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistruct cipso_v4_map_cache_entry {
6362306a36Sopenharmony_ci	u32 hash;
6462306a36Sopenharmony_ci	unsigned char *key;
6562306a36Sopenharmony_ci	size_t key_len;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	struct netlbl_lsm_cache *lsm_data;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	u32 activity;
7062306a36Sopenharmony_ci	struct list_head list;
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic struct cipso_v4_map_cache_bkt *cipso_v4_cache;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/* Restricted bitmap (tag #1) flags */
7662306a36Sopenharmony_ciint cipso_v4_rbm_optfmt;
7762306a36Sopenharmony_ciint cipso_v4_rbm_strictvalid = 1;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/*
8062306a36Sopenharmony_ci * Protocol Constants
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/* Maximum size of the CIPSO IP option, derived from the fact that the maximum
8462306a36Sopenharmony_ci * IPv4 header size is 60 bytes and the base IPv4 header is 20 bytes long. */
8562306a36Sopenharmony_ci#define CIPSO_V4_OPT_LEN_MAX          40
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* Length of the base CIPSO option, this includes the option type (1 byte), the
8862306a36Sopenharmony_ci * option length (1 byte), and the DOI (4 bytes). */
8962306a36Sopenharmony_ci#define CIPSO_V4_HDR_LEN              6
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* Base length of the restrictive category bitmap tag (tag #1). */
9262306a36Sopenharmony_ci#define CIPSO_V4_TAG_RBM_BLEN         4
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/* Base length of the enumerated category tag (tag #2). */
9562306a36Sopenharmony_ci#define CIPSO_V4_TAG_ENUM_BLEN        4
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/* Base length of the ranged categories bitmap tag (tag #5). */
9862306a36Sopenharmony_ci#define CIPSO_V4_TAG_RNG_BLEN         4
9962306a36Sopenharmony_ci/* The maximum number of category ranges permitted in the ranged category tag
10062306a36Sopenharmony_ci * (tag #5).  You may note that the IETF draft states that the maximum number
10162306a36Sopenharmony_ci * of category ranges is 7, but if the low end of the last category range is
10262306a36Sopenharmony_ci * zero then it is possible to fit 8 category ranges because the zero should
10362306a36Sopenharmony_ci * be omitted. */
10462306a36Sopenharmony_ci#define CIPSO_V4_TAG_RNG_CAT_MAX      8
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/* Base length of the local tag (non-standard tag).
10762306a36Sopenharmony_ci *  Tag definition (may change between kernel versions)
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci * 0          8          16         24         32
11062306a36Sopenharmony_ci * +----------+----------+----------+----------+
11162306a36Sopenharmony_ci * | 10000000 | 00000110 | 32-bit secid value  |
11262306a36Sopenharmony_ci * +----------+----------+----------+----------+
11362306a36Sopenharmony_ci * | in (host byte order)|
11462306a36Sopenharmony_ci * +----------+----------+
11562306a36Sopenharmony_ci *
11662306a36Sopenharmony_ci */
11762306a36Sopenharmony_ci#define CIPSO_V4_TAG_LOC_BLEN         6
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/*
12062306a36Sopenharmony_ci * Helper Functions
12162306a36Sopenharmony_ci */
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/**
12462306a36Sopenharmony_ci * cipso_v4_cache_entry_free - Frees a cache entry
12562306a36Sopenharmony_ci * @entry: the entry to free
12662306a36Sopenharmony_ci *
12762306a36Sopenharmony_ci * Description:
12862306a36Sopenharmony_ci * This function frees the memory associated with a cache entry including the
12962306a36Sopenharmony_ci * LSM cache data if there are no longer any users, i.e. reference count == 0.
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_cistatic void cipso_v4_cache_entry_free(struct cipso_v4_map_cache_entry *entry)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	if (entry->lsm_data)
13562306a36Sopenharmony_ci		netlbl_secattr_cache_free(entry->lsm_data);
13662306a36Sopenharmony_ci	kfree(entry->key);
13762306a36Sopenharmony_ci	kfree(entry);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/**
14162306a36Sopenharmony_ci * cipso_v4_map_cache_hash - Hashing function for the CIPSO cache
14262306a36Sopenharmony_ci * @key: the hash key
14362306a36Sopenharmony_ci * @key_len: the length of the key in bytes
14462306a36Sopenharmony_ci *
14562306a36Sopenharmony_ci * Description:
14662306a36Sopenharmony_ci * The CIPSO tag hashing function.  Returns a 32-bit hash value.
14762306a36Sopenharmony_ci *
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_cistatic u32 cipso_v4_map_cache_hash(const unsigned char *key, u32 key_len)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	return jhash(key, key_len, 0);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/*
15562306a36Sopenharmony_ci * Label Mapping Cache Functions
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/**
15962306a36Sopenharmony_ci * cipso_v4_cache_init - Initialize the CIPSO cache
16062306a36Sopenharmony_ci *
16162306a36Sopenharmony_ci * Description:
16262306a36Sopenharmony_ci * Initializes the CIPSO label mapping cache, this function should be called
16362306a36Sopenharmony_ci * before any of the other functions defined in this file.  Returns zero on
16462306a36Sopenharmony_ci * success, negative values on error.
16562306a36Sopenharmony_ci *
16662306a36Sopenharmony_ci */
16762306a36Sopenharmony_cistatic int __init cipso_v4_cache_init(void)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	u32 iter;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	cipso_v4_cache = kcalloc(CIPSO_V4_CACHE_BUCKETS,
17262306a36Sopenharmony_ci				 sizeof(struct cipso_v4_map_cache_bkt),
17362306a36Sopenharmony_ci				 GFP_KERNEL);
17462306a36Sopenharmony_ci	if (!cipso_v4_cache)
17562306a36Sopenharmony_ci		return -ENOMEM;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) {
17862306a36Sopenharmony_ci		spin_lock_init(&cipso_v4_cache[iter].lock);
17962306a36Sopenharmony_ci		cipso_v4_cache[iter].size = 0;
18062306a36Sopenharmony_ci		INIT_LIST_HEAD(&cipso_v4_cache[iter].list);
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return 0;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/**
18762306a36Sopenharmony_ci * cipso_v4_cache_invalidate - Invalidates the current CIPSO cache
18862306a36Sopenharmony_ci *
18962306a36Sopenharmony_ci * Description:
19062306a36Sopenharmony_ci * Invalidates and frees any entries in the CIPSO cache.
19162306a36Sopenharmony_ci *
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_civoid cipso_v4_cache_invalidate(void)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct cipso_v4_map_cache_entry *entry, *tmp_entry;
19662306a36Sopenharmony_ci	u32 iter;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) {
19962306a36Sopenharmony_ci		spin_lock_bh(&cipso_v4_cache[iter].lock);
20062306a36Sopenharmony_ci		list_for_each_entry_safe(entry,
20162306a36Sopenharmony_ci					 tmp_entry,
20262306a36Sopenharmony_ci					 &cipso_v4_cache[iter].list, list) {
20362306a36Sopenharmony_ci			list_del(&entry->list);
20462306a36Sopenharmony_ci			cipso_v4_cache_entry_free(entry);
20562306a36Sopenharmony_ci		}
20662306a36Sopenharmony_ci		cipso_v4_cache[iter].size = 0;
20762306a36Sopenharmony_ci		spin_unlock_bh(&cipso_v4_cache[iter].lock);
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci/**
21262306a36Sopenharmony_ci * cipso_v4_cache_check - Check the CIPSO cache for a label mapping
21362306a36Sopenharmony_ci * @key: the buffer to check
21462306a36Sopenharmony_ci * @key_len: buffer length in bytes
21562306a36Sopenharmony_ci * @secattr: the security attribute struct to use
21662306a36Sopenharmony_ci *
21762306a36Sopenharmony_ci * Description:
21862306a36Sopenharmony_ci * This function checks the cache to see if a label mapping already exists for
21962306a36Sopenharmony_ci * the given key.  If there is a match then the cache is adjusted and the
22062306a36Sopenharmony_ci * @secattr struct is populated with the correct LSM security attributes.  The
22162306a36Sopenharmony_ci * cache is adjusted in the following manner if the entry is not already the
22262306a36Sopenharmony_ci * first in the cache bucket:
22362306a36Sopenharmony_ci *
22462306a36Sopenharmony_ci *  1. The cache entry's activity counter is incremented
22562306a36Sopenharmony_ci *  2. The previous (higher ranking) entry's activity counter is decremented
22662306a36Sopenharmony_ci *  3. If the difference between the two activity counters is geater than
22762306a36Sopenharmony_ci *     CIPSO_V4_CACHE_REORDERLIMIT the two entries are swapped
22862306a36Sopenharmony_ci *
22962306a36Sopenharmony_ci * Returns zero on success, -ENOENT for a cache miss, and other negative values
23062306a36Sopenharmony_ci * on error.
23162306a36Sopenharmony_ci *
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_cistatic int cipso_v4_cache_check(const unsigned char *key,
23462306a36Sopenharmony_ci				u32 key_len,
23562306a36Sopenharmony_ci				struct netlbl_lsm_secattr *secattr)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	u32 bkt;
23862306a36Sopenharmony_ci	struct cipso_v4_map_cache_entry *entry;
23962306a36Sopenharmony_ci	struct cipso_v4_map_cache_entry *prev_entry = NULL;
24062306a36Sopenharmony_ci	u32 hash;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (!READ_ONCE(cipso_v4_cache_enabled))
24362306a36Sopenharmony_ci		return -ENOENT;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	hash = cipso_v4_map_cache_hash(key, key_len);
24662306a36Sopenharmony_ci	bkt = hash & (CIPSO_V4_CACHE_BUCKETS - 1);
24762306a36Sopenharmony_ci	spin_lock_bh(&cipso_v4_cache[bkt].lock);
24862306a36Sopenharmony_ci	list_for_each_entry(entry, &cipso_v4_cache[bkt].list, list) {
24962306a36Sopenharmony_ci		if (entry->hash == hash &&
25062306a36Sopenharmony_ci		    entry->key_len == key_len &&
25162306a36Sopenharmony_ci		    memcmp(entry->key, key, key_len) == 0) {
25262306a36Sopenharmony_ci			entry->activity += 1;
25362306a36Sopenharmony_ci			refcount_inc(&entry->lsm_data->refcount);
25462306a36Sopenharmony_ci			secattr->cache = entry->lsm_data;
25562306a36Sopenharmony_ci			secattr->flags |= NETLBL_SECATTR_CACHE;
25662306a36Sopenharmony_ci			secattr->type = NETLBL_NLTYPE_CIPSOV4;
25762306a36Sopenharmony_ci			if (!prev_entry) {
25862306a36Sopenharmony_ci				spin_unlock_bh(&cipso_v4_cache[bkt].lock);
25962306a36Sopenharmony_ci				return 0;
26062306a36Sopenharmony_ci			}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci			if (prev_entry->activity > 0)
26362306a36Sopenharmony_ci				prev_entry->activity -= 1;
26462306a36Sopenharmony_ci			if (entry->activity > prev_entry->activity &&
26562306a36Sopenharmony_ci			    entry->activity - prev_entry->activity >
26662306a36Sopenharmony_ci			    CIPSO_V4_CACHE_REORDERLIMIT) {
26762306a36Sopenharmony_ci				__list_del(entry->list.prev, entry->list.next);
26862306a36Sopenharmony_ci				__list_add(&entry->list,
26962306a36Sopenharmony_ci					   prev_entry->list.prev,
27062306a36Sopenharmony_ci					   &prev_entry->list);
27162306a36Sopenharmony_ci			}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci			spin_unlock_bh(&cipso_v4_cache[bkt].lock);
27462306a36Sopenharmony_ci			return 0;
27562306a36Sopenharmony_ci		}
27662306a36Sopenharmony_ci		prev_entry = entry;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci	spin_unlock_bh(&cipso_v4_cache[bkt].lock);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return -ENOENT;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/**
28462306a36Sopenharmony_ci * cipso_v4_cache_add - Add an entry to the CIPSO cache
28562306a36Sopenharmony_ci * @cipso_ptr: pointer to CIPSO IP option
28662306a36Sopenharmony_ci * @secattr: the packet's security attributes
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci * Description:
28962306a36Sopenharmony_ci * Add a new entry into the CIPSO label mapping cache.  Add the new entry to
29062306a36Sopenharmony_ci * head of the cache bucket's list, if the cache bucket is out of room remove
29162306a36Sopenharmony_ci * the last entry in the list first.  It is important to note that there is
29262306a36Sopenharmony_ci * currently no checking for duplicate keys.  Returns zero on success,
29362306a36Sopenharmony_ci * negative values on failure.
29462306a36Sopenharmony_ci *
29562306a36Sopenharmony_ci */
29662306a36Sopenharmony_ciint cipso_v4_cache_add(const unsigned char *cipso_ptr,
29762306a36Sopenharmony_ci		       const struct netlbl_lsm_secattr *secattr)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	int bkt_size = READ_ONCE(cipso_v4_cache_bucketsize);
30062306a36Sopenharmony_ci	int ret_val = -EPERM;
30162306a36Sopenharmony_ci	u32 bkt;
30262306a36Sopenharmony_ci	struct cipso_v4_map_cache_entry *entry = NULL;
30362306a36Sopenharmony_ci	struct cipso_v4_map_cache_entry *old_entry = NULL;
30462306a36Sopenharmony_ci	u32 cipso_ptr_len;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	if (!READ_ONCE(cipso_v4_cache_enabled) || bkt_size <= 0)
30762306a36Sopenharmony_ci		return 0;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	cipso_ptr_len = cipso_ptr[1];
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
31262306a36Sopenharmony_ci	if (!entry)
31362306a36Sopenharmony_ci		return -ENOMEM;
31462306a36Sopenharmony_ci	entry->key = kmemdup(cipso_ptr, cipso_ptr_len, GFP_ATOMIC);
31562306a36Sopenharmony_ci	if (!entry->key) {
31662306a36Sopenharmony_ci		ret_val = -ENOMEM;
31762306a36Sopenharmony_ci		goto cache_add_failure;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci	entry->key_len = cipso_ptr_len;
32062306a36Sopenharmony_ci	entry->hash = cipso_v4_map_cache_hash(cipso_ptr, cipso_ptr_len);
32162306a36Sopenharmony_ci	refcount_inc(&secattr->cache->refcount);
32262306a36Sopenharmony_ci	entry->lsm_data = secattr->cache;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	bkt = entry->hash & (CIPSO_V4_CACHE_BUCKETS - 1);
32562306a36Sopenharmony_ci	spin_lock_bh(&cipso_v4_cache[bkt].lock);
32662306a36Sopenharmony_ci	if (cipso_v4_cache[bkt].size < bkt_size) {
32762306a36Sopenharmony_ci		list_add(&entry->list, &cipso_v4_cache[bkt].list);
32862306a36Sopenharmony_ci		cipso_v4_cache[bkt].size += 1;
32962306a36Sopenharmony_ci	} else {
33062306a36Sopenharmony_ci		old_entry = list_entry(cipso_v4_cache[bkt].list.prev,
33162306a36Sopenharmony_ci				       struct cipso_v4_map_cache_entry, list);
33262306a36Sopenharmony_ci		list_del(&old_entry->list);
33362306a36Sopenharmony_ci		list_add(&entry->list, &cipso_v4_cache[bkt].list);
33462306a36Sopenharmony_ci		cipso_v4_cache_entry_free(old_entry);
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci	spin_unlock_bh(&cipso_v4_cache[bkt].lock);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return 0;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cicache_add_failure:
34162306a36Sopenharmony_ci	if (entry)
34262306a36Sopenharmony_ci		cipso_v4_cache_entry_free(entry);
34362306a36Sopenharmony_ci	return ret_val;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/*
34762306a36Sopenharmony_ci * DOI List Functions
34862306a36Sopenharmony_ci */
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci/**
35162306a36Sopenharmony_ci * cipso_v4_doi_search - Searches for a DOI definition
35262306a36Sopenharmony_ci * @doi: the DOI to search for
35362306a36Sopenharmony_ci *
35462306a36Sopenharmony_ci * Description:
35562306a36Sopenharmony_ci * Search the DOI definition list for a DOI definition with a DOI value that
35662306a36Sopenharmony_ci * matches @doi.  The caller is responsible for calling rcu_read_[un]lock().
35762306a36Sopenharmony_ci * Returns a pointer to the DOI definition on success and NULL on failure.
35862306a36Sopenharmony_ci */
35962306a36Sopenharmony_cistatic struct cipso_v4_doi *cipso_v4_doi_search(u32 doi)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	struct cipso_v4_doi *iter;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list)
36462306a36Sopenharmony_ci		if (iter->doi == doi && refcount_read(&iter->refcount))
36562306a36Sopenharmony_ci			return iter;
36662306a36Sopenharmony_ci	return NULL;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci/**
37062306a36Sopenharmony_ci * cipso_v4_doi_add - Add a new DOI to the CIPSO protocol engine
37162306a36Sopenharmony_ci * @doi_def: the DOI structure
37262306a36Sopenharmony_ci * @audit_info: NetLabel audit information
37362306a36Sopenharmony_ci *
37462306a36Sopenharmony_ci * Description:
37562306a36Sopenharmony_ci * The caller defines a new DOI for use by the CIPSO engine and calls this
37662306a36Sopenharmony_ci * function to add it to the list of acceptable domains.  The caller must
37762306a36Sopenharmony_ci * ensure that the mapping table specified in @doi_def->map meets all of the
37862306a36Sopenharmony_ci * requirements of the mapping type (see cipso_ipv4.h for details).  Returns
37962306a36Sopenharmony_ci * zero on success and non-zero on failure.
38062306a36Sopenharmony_ci *
38162306a36Sopenharmony_ci */
38262306a36Sopenharmony_ciint cipso_v4_doi_add(struct cipso_v4_doi *doi_def,
38362306a36Sopenharmony_ci		     struct netlbl_audit *audit_info)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	int ret_val = -EINVAL;
38662306a36Sopenharmony_ci	u32 iter;
38762306a36Sopenharmony_ci	u32 doi;
38862306a36Sopenharmony_ci	u32 doi_type;
38962306a36Sopenharmony_ci	struct audit_buffer *audit_buf;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	doi = doi_def->doi;
39262306a36Sopenharmony_ci	doi_type = doi_def->type;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (doi_def->doi == CIPSO_V4_DOI_UNKNOWN)
39562306a36Sopenharmony_ci		goto doi_add_return;
39662306a36Sopenharmony_ci	for (iter = 0; iter < CIPSO_V4_TAG_MAXCNT; iter++) {
39762306a36Sopenharmony_ci		switch (doi_def->tags[iter]) {
39862306a36Sopenharmony_ci		case CIPSO_V4_TAG_RBITMAP:
39962306a36Sopenharmony_ci			break;
40062306a36Sopenharmony_ci		case CIPSO_V4_TAG_RANGE:
40162306a36Sopenharmony_ci		case CIPSO_V4_TAG_ENUM:
40262306a36Sopenharmony_ci			if (doi_def->type != CIPSO_V4_MAP_PASS)
40362306a36Sopenharmony_ci				goto doi_add_return;
40462306a36Sopenharmony_ci			break;
40562306a36Sopenharmony_ci		case CIPSO_V4_TAG_LOCAL:
40662306a36Sopenharmony_ci			if (doi_def->type != CIPSO_V4_MAP_LOCAL)
40762306a36Sopenharmony_ci				goto doi_add_return;
40862306a36Sopenharmony_ci			break;
40962306a36Sopenharmony_ci		case CIPSO_V4_TAG_INVALID:
41062306a36Sopenharmony_ci			if (iter == 0)
41162306a36Sopenharmony_ci				goto doi_add_return;
41262306a36Sopenharmony_ci			break;
41362306a36Sopenharmony_ci		default:
41462306a36Sopenharmony_ci			goto doi_add_return;
41562306a36Sopenharmony_ci		}
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	refcount_set(&doi_def->refcount, 1);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	spin_lock(&cipso_v4_doi_list_lock);
42162306a36Sopenharmony_ci	if (cipso_v4_doi_search(doi_def->doi)) {
42262306a36Sopenharmony_ci		spin_unlock(&cipso_v4_doi_list_lock);
42362306a36Sopenharmony_ci		ret_val = -EEXIST;
42462306a36Sopenharmony_ci		goto doi_add_return;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci	list_add_tail_rcu(&doi_def->list, &cipso_v4_doi_list);
42762306a36Sopenharmony_ci	spin_unlock(&cipso_v4_doi_list_lock);
42862306a36Sopenharmony_ci	ret_val = 0;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cidoi_add_return:
43162306a36Sopenharmony_ci	audit_buf = netlbl_audit_start(AUDIT_MAC_CIPSOV4_ADD, audit_info);
43262306a36Sopenharmony_ci	if (audit_buf) {
43362306a36Sopenharmony_ci		const char *type_str;
43462306a36Sopenharmony_ci		switch (doi_type) {
43562306a36Sopenharmony_ci		case CIPSO_V4_MAP_TRANS:
43662306a36Sopenharmony_ci			type_str = "trans";
43762306a36Sopenharmony_ci			break;
43862306a36Sopenharmony_ci		case CIPSO_V4_MAP_PASS:
43962306a36Sopenharmony_ci			type_str = "pass";
44062306a36Sopenharmony_ci			break;
44162306a36Sopenharmony_ci		case CIPSO_V4_MAP_LOCAL:
44262306a36Sopenharmony_ci			type_str = "local";
44362306a36Sopenharmony_ci			break;
44462306a36Sopenharmony_ci		default:
44562306a36Sopenharmony_ci			type_str = "(unknown)";
44662306a36Sopenharmony_ci		}
44762306a36Sopenharmony_ci		audit_log_format(audit_buf,
44862306a36Sopenharmony_ci				 " cipso_doi=%u cipso_type=%s res=%u",
44962306a36Sopenharmony_ci				 doi, type_str, ret_val == 0 ? 1 : 0);
45062306a36Sopenharmony_ci		audit_log_end(audit_buf);
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	return ret_val;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci/**
45762306a36Sopenharmony_ci * cipso_v4_doi_free - Frees a DOI definition
45862306a36Sopenharmony_ci * @doi_def: the DOI definition
45962306a36Sopenharmony_ci *
46062306a36Sopenharmony_ci * Description:
46162306a36Sopenharmony_ci * This function frees all of the memory associated with a DOI definition.
46262306a36Sopenharmony_ci *
46362306a36Sopenharmony_ci */
46462306a36Sopenharmony_civoid cipso_v4_doi_free(struct cipso_v4_doi *doi_def)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	if (!doi_def)
46762306a36Sopenharmony_ci		return;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	switch (doi_def->type) {
47062306a36Sopenharmony_ci	case CIPSO_V4_MAP_TRANS:
47162306a36Sopenharmony_ci		kfree(doi_def->map.std->lvl.cipso);
47262306a36Sopenharmony_ci		kfree(doi_def->map.std->lvl.local);
47362306a36Sopenharmony_ci		kfree(doi_def->map.std->cat.cipso);
47462306a36Sopenharmony_ci		kfree(doi_def->map.std->cat.local);
47562306a36Sopenharmony_ci		kfree(doi_def->map.std);
47662306a36Sopenharmony_ci		break;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci	kfree(doi_def);
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci/**
48262306a36Sopenharmony_ci * cipso_v4_doi_free_rcu - Frees a DOI definition via the RCU pointer
48362306a36Sopenharmony_ci * @entry: the entry's RCU field
48462306a36Sopenharmony_ci *
48562306a36Sopenharmony_ci * Description:
48662306a36Sopenharmony_ci * This function is designed to be used as a callback to the call_rcu()
48762306a36Sopenharmony_ci * function so that the memory allocated to the DOI definition can be released
48862306a36Sopenharmony_ci * safely.
48962306a36Sopenharmony_ci *
49062306a36Sopenharmony_ci */
49162306a36Sopenharmony_cistatic void cipso_v4_doi_free_rcu(struct rcu_head *entry)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	struct cipso_v4_doi *doi_def;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	doi_def = container_of(entry, struct cipso_v4_doi, rcu);
49662306a36Sopenharmony_ci	cipso_v4_doi_free(doi_def);
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci/**
50062306a36Sopenharmony_ci * cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine
50162306a36Sopenharmony_ci * @doi: the DOI value
50262306a36Sopenharmony_ci * @audit_info: NetLabel audit information
50362306a36Sopenharmony_ci *
50462306a36Sopenharmony_ci * Description:
50562306a36Sopenharmony_ci * Removes a DOI definition from the CIPSO engine.  The NetLabel routines will
50662306a36Sopenharmony_ci * be called to release their own LSM domain mappings as well as our own
50762306a36Sopenharmony_ci * domain list.  Returns zero on success and negative values on failure.
50862306a36Sopenharmony_ci *
50962306a36Sopenharmony_ci */
51062306a36Sopenharmony_ciint cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	int ret_val;
51362306a36Sopenharmony_ci	struct cipso_v4_doi *doi_def;
51462306a36Sopenharmony_ci	struct audit_buffer *audit_buf;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	spin_lock(&cipso_v4_doi_list_lock);
51762306a36Sopenharmony_ci	doi_def = cipso_v4_doi_search(doi);
51862306a36Sopenharmony_ci	if (!doi_def) {
51962306a36Sopenharmony_ci		spin_unlock(&cipso_v4_doi_list_lock);
52062306a36Sopenharmony_ci		ret_val = -ENOENT;
52162306a36Sopenharmony_ci		goto doi_remove_return;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci	list_del_rcu(&doi_def->list);
52462306a36Sopenharmony_ci	spin_unlock(&cipso_v4_doi_list_lock);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	cipso_v4_doi_putdef(doi_def);
52762306a36Sopenharmony_ci	ret_val = 0;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cidoi_remove_return:
53062306a36Sopenharmony_ci	audit_buf = netlbl_audit_start(AUDIT_MAC_CIPSOV4_DEL, audit_info);
53162306a36Sopenharmony_ci	if (audit_buf) {
53262306a36Sopenharmony_ci		audit_log_format(audit_buf,
53362306a36Sopenharmony_ci				 " cipso_doi=%u res=%u",
53462306a36Sopenharmony_ci				 doi, ret_val == 0 ? 1 : 0);
53562306a36Sopenharmony_ci		audit_log_end(audit_buf);
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	return ret_val;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci/**
54262306a36Sopenharmony_ci * cipso_v4_doi_getdef - Returns a reference to a valid DOI definition
54362306a36Sopenharmony_ci * @doi: the DOI value
54462306a36Sopenharmony_ci *
54562306a36Sopenharmony_ci * Description:
54662306a36Sopenharmony_ci * Searches for a valid DOI definition and if one is found it is returned to
54762306a36Sopenharmony_ci * the caller.  Otherwise NULL is returned.  The caller must ensure that
54862306a36Sopenharmony_ci * rcu_read_lock() is held while accessing the returned definition and the DOI
54962306a36Sopenharmony_ci * definition reference count is decremented when the caller is done.
55062306a36Sopenharmony_ci *
55162306a36Sopenharmony_ci */
55262306a36Sopenharmony_cistruct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct cipso_v4_doi *doi_def;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	rcu_read_lock();
55762306a36Sopenharmony_ci	doi_def = cipso_v4_doi_search(doi);
55862306a36Sopenharmony_ci	if (!doi_def)
55962306a36Sopenharmony_ci		goto doi_getdef_return;
56062306a36Sopenharmony_ci	if (!refcount_inc_not_zero(&doi_def->refcount))
56162306a36Sopenharmony_ci		doi_def = NULL;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cidoi_getdef_return:
56462306a36Sopenharmony_ci	rcu_read_unlock();
56562306a36Sopenharmony_ci	return doi_def;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci/**
56962306a36Sopenharmony_ci * cipso_v4_doi_putdef - Releases a reference for the given DOI definition
57062306a36Sopenharmony_ci * @doi_def: the DOI definition
57162306a36Sopenharmony_ci *
57262306a36Sopenharmony_ci * Description:
57362306a36Sopenharmony_ci * Releases a DOI definition reference obtained from cipso_v4_doi_getdef().
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci */
57662306a36Sopenharmony_civoid cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	if (!doi_def)
57962306a36Sopenharmony_ci		return;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (!refcount_dec_and_test(&doi_def->refcount))
58262306a36Sopenharmony_ci		return;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	cipso_v4_cache_invalidate();
58562306a36Sopenharmony_ci	call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci/**
58962306a36Sopenharmony_ci * cipso_v4_doi_walk - Iterate through the DOI definitions
59062306a36Sopenharmony_ci * @skip_cnt: skip past this number of DOI definitions, updated
59162306a36Sopenharmony_ci * @callback: callback for each DOI definition
59262306a36Sopenharmony_ci * @cb_arg: argument for the callback function
59362306a36Sopenharmony_ci *
59462306a36Sopenharmony_ci * Description:
59562306a36Sopenharmony_ci * Iterate over the DOI definition list, skipping the first @skip_cnt entries.
59662306a36Sopenharmony_ci * For each entry call @callback, if @callback returns a negative value stop
59762306a36Sopenharmony_ci * 'walking' through the list and return.  Updates the value in @skip_cnt upon
59862306a36Sopenharmony_ci * return.  Returns zero on success, negative values on failure.
59962306a36Sopenharmony_ci *
60062306a36Sopenharmony_ci */
60162306a36Sopenharmony_ciint cipso_v4_doi_walk(u32 *skip_cnt,
60262306a36Sopenharmony_ci		     int (*callback) (struct cipso_v4_doi *doi_def, void *arg),
60362306a36Sopenharmony_ci		     void *cb_arg)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	int ret_val = -ENOENT;
60662306a36Sopenharmony_ci	u32 doi_cnt = 0;
60762306a36Sopenharmony_ci	struct cipso_v4_doi *iter_doi;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	rcu_read_lock();
61062306a36Sopenharmony_ci	list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list)
61162306a36Sopenharmony_ci		if (refcount_read(&iter_doi->refcount) > 0) {
61262306a36Sopenharmony_ci			if (doi_cnt++ < *skip_cnt)
61362306a36Sopenharmony_ci				continue;
61462306a36Sopenharmony_ci			ret_val = callback(iter_doi, cb_arg);
61562306a36Sopenharmony_ci			if (ret_val < 0) {
61662306a36Sopenharmony_ci				doi_cnt--;
61762306a36Sopenharmony_ci				goto doi_walk_return;
61862306a36Sopenharmony_ci			}
61962306a36Sopenharmony_ci		}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cidoi_walk_return:
62262306a36Sopenharmony_ci	rcu_read_unlock();
62362306a36Sopenharmony_ci	*skip_cnt = doi_cnt;
62462306a36Sopenharmony_ci	return ret_val;
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci/*
62862306a36Sopenharmony_ci * Label Mapping Functions
62962306a36Sopenharmony_ci */
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci/**
63262306a36Sopenharmony_ci * cipso_v4_map_lvl_valid - Checks to see if the given level is understood
63362306a36Sopenharmony_ci * @doi_def: the DOI definition
63462306a36Sopenharmony_ci * @level: the level to check
63562306a36Sopenharmony_ci *
63662306a36Sopenharmony_ci * Description:
63762306a36Sopenharmony_ci * Checks the given level against the given DOI definition and returns a
63862306a36Sopenharmony_ci * negative value if the level does not have a valid mapping and a zero value
63962306a36Sopenharmony_ci * if the level is defined by the DOI.
64062306a36Sopenharmony_ci *
64162306a36Sopenharmony_ci */
64262306a36Sopenharmony_cistatic int cipso_v4_map_lvl_valid(const struct cipso_v4_doi *doi_def, u8 level)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	switch (doi_def->type) {
64562306a36Sopenharmony_ci	case CIPSO_V4_MAP_PASS:
64662306a36Sopenharmony_ci		return 0;
64762306a36Sopenharmony_ci	case CIPSO_V4_MAP_TRANS:
64862306a36Sopenharmony_ci		if ((level < doi_def->map.std->lvl.cipso_size) &&
64962306a36Sopenharmony_ci		    (doi_def->map.std->lvl.cipso[level] < CIPSO_V4_INV_LVL))
65062306a36Sopenharmony_ci			return 0;
65162306a36Sopenharmony_ci		break;
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	return -EFAULT;
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci/**
65862306a36Sopenharmony_ci * cipso_v4_map_lvl_hton - Perform a level mapping from the host to the network
65962306a36Sopenharmony_ci * @doi_def: the DOI definition
66062306a36Sopenharmony_ci * @host_lvl: the host MLS level
66162306a36Sopenharmony_ci * @net_lvl: the network/CIPSO MLS level
66262306a36Sopenharmony_ci *
66362306a36Sopenharmony_ci * Description:
66462306a36Sopenharmony_ci * Perform a label mapping to translate a local MLS level to the correct
66562306a36Sopenharmony_ci * CIPSO level using the given DOI definition.  Returns zero on success,
66662306a36Sopenharmony_ci * negative values otherwise.
66762306a36Sopenharmony_ci *
66862306a36Sopenharmony_ci */
66962306a36Sopenharmony_cistatic int cipso_v4_map_lvl_hton(const struct cipso_v4_doi *doi_def,
67062306a36Sopenharmony_ci				 u32 host_lvl,
67162306a36Sopenharmony_ci				 u32 *net_lvl)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	switch (doi_def->type) {
67462306a36Sopenharmony_ci	case CIPSO_V4_MAP_PASS:
67562306a36Sopenharmony_ci		*net_lvl = host_lvl;
67662306a36Sopenharmony_ci		return 0;
67762306a36Sopenharmony_ci	case CIPSO_V4_MAP_TRANS:
67862306a36Sopenharmony_ci		if (host_lvl < doi_def->map.std->lvl.local_size &&
67962306a36Sopenharmony_ci		    doi_def->map.std->lvl.local[host_lvl] < CIPSO_V4_INV_LVL) {
68062306a36Sopenharmony_ci			*net_lvl = doi_def->map.std->lvl.local[host_lvl];
68162306a36Sopenharmony_ci			return 0;
68262306a36Sopenharmony_ci		}
68362306a36Sopenharmony_ci		return -EPERM;
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	return -EINVAL;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci/**
69062306a36Sopenharmony_ci * cipso_v4_map_lvl_ntoh - Perform a level mapping from the network to the host
69162306a36Sopenharmony_ci * @doi_def: the DOI definition
69262306a36Sopenharmony_ci * @net_lvl: the network/CIPSO MLS level
69362306a36Sopenharmony_ci * @host_lvl: the host MLS level
69462306a36Sopenharmony_ci *
69562306a36Sopenharmony_ci * Description:
69662306a36Sopenharmony_ci * Perform a label mapping to translate a CIPSO level to the correct local MLS
69762306a36Sopenharmony_ci * level using the given DOI definition.  Returns zero on success, negative
69862306a36Sopenharmony_ci * values otherwise.
69962306a36Sopenharmony_ci *
70062306a36Sopenharmony_ci */
70162306a36Sopenharmony_cistatic int cipso_v4_map_lvl_ntoh(const struct cipso_v4_doi *doi_def,
70262306a36Sopenharmony_ci				 u32 net_lvl,
70362306a36Sopenharmony_ci				 u32 *host_lvl)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	struct cipso_v4_std_map_tbl *map_tbl;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	switch (doi_def->type) {
70862306a36Sopenharmony_ci	case CIPSO_V4_MAP_PASS:
70962306a36Sopenharmony_ci		*host_lvl = net_lvl;
71062306a36Sopenharmony_ci		return 0;
71162306a36Sopenharmony_ci	case CIPSO_V4_MAP_TRANS:
71262306a36Sopenharmony_ci		map_tbl = doi_def->map.std;
71362306a36Sopenharmony_ci		if (net_lvl < map_tbl->lvl.cipso_size &&
71462306a36Sopenharmony_ci		    map_tbl->lvl.cipso[net_lvl] < CIPSO_V4_INV_LVL) {
71562306a36Sopenharmony_ci			*host_lvl = doi_def->map.std->lvl.cipso[net_lvl];
71662306a36Sopenharmony_ci			return 0;
71762306a36Sopenharmony_ci		}
71862306a36Sopenharmony_ci		return -EPERM;
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	return -EINVAL;
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci/**
72562306a36Sopenharmony_ci * cipso_v4_map_cat_rbm_valid - Checks to see if the category bitmap is valid
72662306a36Sopenharmony_ci * @doi_def: the DOI definition
72762306a36Sopenharmony_ci * @bitmap: category bitmap
72862306a36Sopenharmony_ci * @bitmap_len: bitmap length in bytes
72962306a36Sopenharmony_ci *
73062306a36Sopenharmony_ci * Description:
73162306a36Sopenharmony_ci * Checks the given category bitmap against the given DOI definition and
73262306a36Sopenharmony_ci * returns a negative value if any of the categories in the bitmap do not have
73362306a36Sopenharmony_ci * a valid mapping and a zero value if all of the categories are valid.
73462306a36Sopenharmony_ci *
73562306a36Sopenharmony_ci */
73662306a36Sopenharmony_cistatic int cipso_v4_map_cat_rbm_valid(const struct cipso_v4_doi *doi_def,
73762306a36Sopenharmony_ci				      const unsigned char *bitmap,
73862306a36Sopenharmony_ci				      u32 bitmap_len)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	int cat = -1;
74162306a36Sopenharmony_ci	u32 bitmap_len_bits = bitmap_len * 8;
74262306a36Sopenharmony_ci	u32 cipso_cat_size;
74362306a36Sopenharmony_ci	u32 *cipso_array;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	switch (doi_def->type) {
74662306a36Sopenharmony_ci	case CIPSO_V4_MAP_PASS:
74762306a36Sopenharmony_ci		return 0;
74862306a36Sopenharmony_ci	case CIPSO_V4_MAP_TRANS:
74962306a36Sopenharmony_ci		cipso_cat_size = doi_def->map.std->cat.cipso_size;
75062306a36Sopenharmony_ci		cipso_array = doi_def->map.std->cat.cipso;
75162306a36Sopenharmony_ci		for (;;) {
75262306a36Sopenharmony_ci			cat = netlbl_bitmap_walk(bitmap,
75362306a36Sopenharmony_ci						 bitmap_len_bits,
75462306a36Sopenharmony_ci						 cat + 1,
75562306a36Sopenharmony_ci						 1);
75662306a36Sopenharmony_ci			if (cat < 0)
75762306a36Sopenharmony_ci				break;
75862306a36Sopenharmony_ci			if (cat >= cipso_cat_size ||
75962306a36Sopenharmony_ci			    cipso_array[cat] >= CIPSO_V4_INV_CAT)
76062306a36Sopenharmony_ci				return -EFAULT;
76162306a36Sopenharmony_ci		}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci		if (cat == -1)
76462306a36Sopenharmony_ci			return 0;
76562306a36Sopenharmony_ci		break;
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	return -EFAULT;
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci/**
77262306a36Sopenharmony_ci * cipso_v4_map_cat_rbm_hton - Perform a category mapping from host to network
77362306a36Sopenharmony_ci * @doi_def: the DOI definition
77462306a36Sopenharmony_ci * @secattr: the security attributes
77562306a36Sopenharmony_ci * @net_cat: the zero'd out category bitmap in network/CIPSO format
77662306a36Sopenharmony_ci * @net_cat_len: the length of the CIPSO bitmap in bytes
77762306a36Sopenharmony_ci *
77862306a36Sopenharmony_ci * Description:
77962306a36Sopenharmony_ci * Perform a label mapping to translate a local MLS category bitmap to the
78062306a36Sopenharmony_ci * correct CIPSO bitmap using the given DOI definition.  Returns the minimum
78162306a36Sopenharmony_ci * size in bytes of the network bitmap on success, negative values otherwise.
78262306a36Sopenharmony_ci *
78362306a36Sopenharmony_ci */
78462306a36Sopenharmony_cistatic int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def,
78562306a36Sopenharmony_ci				     const struct netlbl_lsm_secattr *secattr,
78662306a36Sopenharmony_ci				     unsigned char *net_cat,
78762306a36Sopenharmony_ci				     u32 net_cat_len)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	int host_spot = -1;
79062306a36Sopenharmony_ci	u32 net_spot = CIPSO_V4_INV_CAT;
79162306a36Sopenharmony_ci	u32 net_spot_max = 0;
79262306a36Sopenharmony_ci	u32 net_clen_bits = net_cat_len * 8;
79362306a36Sopenharmony_ci	u32 host_cat_size = 0;
79462306a36Sopenharmony_ci	u32 *host_cat_array = NULL;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (doi_def->type == CIPSO_V4_MAP_TRANS) {
79762306a36Sopenharmony_ci		host_cat_size = doi_def->map.std->cat.local_size;
79862306a36Sopenharmony_ci		host_cat_array = doi_def->map.std->cat.local;
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	for (;;) {
80262306a36Sopenharmony_ci		host_spot = netlbl_catmap_walk(secattr->attr.mls.cat,
80362306a36Sopenharmony_ci					       host_spot + 1);
80462306a36Sopenharmony_ci		if (host_spot < 0)
80562306a36Sopenharmony_ci			break;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci		switch (doi_def->type) {
80862306a36Sopenharmony_ci		case CIPSO_V4_MAP_PASS:
80962306a36Sopenharmony_ci			net_spot = host_spot;
81062306a36Sopenharmony_ci			break;
81162306a36Sopenharmony_ci		case CIPSO_V4_MAP_TRANS:
81262306a36Sopenharmony_ci			if (host_spot >= host_cat_size)
81362306a36Sopenharmony_ci				return -EPERM;
81462306a36Sopenharmony_ci			net_spot = host_cat_array[host_spot];
81562306a36Sopenharmony_ci			if (net_spot >= CIPSO_V4_INV_CAT)
81662306a36Sopenharmony_ci				return -EPERM;
81762306a36Sopenharmony_ci			break;
81862306a36Sopenharmony_ci		}
81962306a36Sopenharmony_ci		if (net_spot >= net_clen_bits)
82062306a36Sopenharmony_ci			return -ENOSPC;
82162306a36Sopenharmony_ci		netlbl_bitmap_setbit(net_cat, net_spot, 1);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci		if (net_spot > net_spot_max)
82462306a36Sopenharmony_ci			net_spot_max = net_spot;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	if (++net_spot_max % 8)
82862306a36Sopenharmony_ci		return net_spot_max / 8 + 1;
82962306a36Sopenharmony_ci	return net_spot_max / 8;
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci/**
83362306a36Sopenharmony_ci * cipso_v4_map_cat_rbm_ntoh - Perform a category mapping from network to host
83462306a36Sopenharmony_ci * @doi_def: the DOI definition
83562306a36Sopenharmony_ci * @net_cat: the category bitmap in network/CIPSO format
83662306a36Sopenharmony_ci * @net_cat_len: the length of the CIPSO bitmap in bytes
83762306a36Sopenharmony_ci * @secattr: the security attributes
83862306a36Sopenharmony_ci *
83962306a36Sopenharmony_ci * Description:
84062306a36Sopenharmony_ci * Perform a label mapping to translate a CIPSO bitmap to the correct local
84162306a36Sopenharmony_ci * MLS category bitmap using the given DOI definition.  Returns zero on
84262306a36Sopenharmony_ci * success, negative values on failure.
84362306a36Sopenharmony_ci *
84462306a36Sopenharmony_ci */
84562306a36Sopenharmony_cistatic int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def,
84662306a36Sopenharmony_ci				     const unsigned char *net_cat,
84762306a36Sopenharmony_ci				     u32 net_cat_len,
84862306a36Sopenharmony_ci				     struct netlbl_lsm_secattr *secattr)
84962306a36Sopenharmony_ci{
85062306a36Sopenharmony_ci	int ret_val;
85162306a36Sopenharmony_ci	int net_spot = -1;
85262306a36Sopenharmony_ci	u32 host_spot = CIPSO_V4_INV_CAT;
85362306a36Sopenharmony_ci	u32 net_clen_bits = net_cat_len * 8;
85462306a36Sopenharmony_ci	u32 net_cat_size = 0;
85562306a36Sopenharmony_ci	u32 *net_cat_array = NULL;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if (doi_def->type == CIPSO_V4_MAP_TRANS) {
85862306a36Sopenharmony_ci		net_cat_size = doi_def->map.std->cat.cipso_size;
85962306a36Sopenharmony_ci		net_cat_array = doi_def->map.std->cat.cipso;
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	for (;;) {
86362306a36Sopenharmony_ci		net_spot = netlbl_bitmap_walk(net_cat,
86462306a36Sopenharmony_ci					      net_clen_bits,
86562306a36Sopenharmony_ci					      net_spot + 1,
86662306a36Sopenharmony_ci					      1);
86762306a36Sopenharmony_ci		if (net_spot < 0) {
86862306a36Sopenharmony_ci			if (net_spot == -2)
86962306a36Sopenharmony_ci				return -EFAULT;
87062306a36Sopenharmony_ci			return 0;
87162306a36Sopenharmony_ci		}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci		switch (doi_def->type) {
87462306a36Sopenharmony_ci		case CIPSO_V4_MAP_PASS:
87562306a36Sopenharmony_ci			host_spot = net_spot;
87662306a36Sopenharmony_ci			break;
87762306a36Sopenharmony_ci		case CIPSO_V4_MAP_TRANS:
87862306a36Sopenharmony_ci			if (net_spot >= net_cat_size)
87962306a36Sopenharmony_ci				return -EPERM;
88062306a36Sopenharmony_ci			host_spot = net_cat_array[net_spot];
88162306a36Sopenharmony_ci			if (host_spot >= CIPSO_V4_INV_CAT)
88262306a36Sopenharmony_ci				return -EPERM;
88362306a36Sopenharmony_ci			break;
88462306a36Sopenharmony_ci		}
88562306a36Sopenharmony_ci		ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat,
88662306a36Sopenharmony_ci						       host_spot,
88762306a36Sopenharmony_ci						       GFP_ATOMIC);
88862306a36Sopenharmony_ci		if (ret_val != 0)
88962306a36Sopenharmony_ci			return ret_val;
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	return -EINVAL;
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci/**
89662306a36Sopenharmony_ci * cipso_v4_map_cat_enum_valid - Checks to see if the categories are valid
89762306a36Sopenharmony_ci * @doi_def: the DOI definition
89862306a36Sopenharmony_ci * @enumcat: category list
89962306a36Sopenharmony_ci * @enumcat_len: length of the category list in bytes
90062306a36Sopenharmony_ci *
90162306a36Sopenharmony_ci * Description:
90262306a36Sopenharmony_ci * Checks the given categories against the given DOI definition and returns a
90362306a36Sopenharmony_ci * negative value if any of the categories do not have a valid mapping and a
90462306a36Sopenharmony_ci * zero value if all of the categories are valid.
90562306a36Sopenharmony_ci *
90662306a36Sopenharmony_ci */
90762306a36Sopenharmony_cistatic int cipso_v4_map_cat_enum_valid(const struct cipso_v4_doi *doi_def,
90862306a36Sopenharmony_ci				       const unsigned char *enumcat,
90962306a36Sopenharmony_ci				       u32 enumcat_len)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	u16 cat;
91262306a36Sopenharmony_ci	int cat_prev = -1;
91362306a36Sopenharmony_ci	u32 iter;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	if (doi_def->type != CIPSO_V4_MAP_PASS || enumcat_len & 0x01)
91662306a36Sopenharmony_ci		return -EFAULT;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	for (iter = 0; iter < enumcat_len; iter += 2) {
91962306a36Sopenharmony_ci		cat = get_unaligned_be16(&enumcat[iter]);
92062306a36Sopenharmony_ci		if (cat <= cat_prev)
92162306a36Sopenharmony_ci			return -EFAULT;
92262306a36Sopenharmony_ci		cat_prev = cat;
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	return 0;
92662306a36Sopenharmony_ci}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci/**
92962306a36Sopenharmony_ci * cipso_v4_map_cat_enum_hton - Perform a category mapping from host to network
93062306a36Sopenharmony_ci * @doi_def: the DOI definition
93162306a36Sopenharmony_ci * @secattr: the security attributes
93262306a36Sopenharmony_ci * @net_cat: the zero'd out category list in network/CIPSO format
93362306a36Sopenharmony_ci * @net_cat_len: the length of the CIPSO category list in bytes
93462306a36Sopenharmony_ci *
93562306a36Sopenharmony_ci * Description:
93662306a36Sopenharmony_ci * Perform a label mapping to translate a local MLS category bitmap to the
93762306a36Sopenharmony_ci * correct CIPSO category list using the given DOI definition.   Returns the
93862306a36Sopenharmony_ci * size in bytes of the network category bitmap on success, negative values
93962306a36Sopenharmony_ci * otherwise.
94062306a36Sopenharmony_ci *
94162306a36Sopenharmony_ci */
94262306a36Sopenharmony_cistatic int cipso_v4_map_cat_enum_hton(const struct cipso_v4_doi *doi_def,
94362306a36Sopenharmony_ci				      const struct netlbl_lsm_secattr *secattr,
94462306a36Sopenharmony_ci				      unsigned char *net_cat,
94562306a36Sopenharmony_ci				      u32 net_cat_len)
94662306a36Sopenharmony_ci{
94762306a36Sopenharmony_ci	int cat = -1;
94862306a36Sopenharmony_ci	u32 cat_iter = 0;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	for (;;) {
95162306a36Sopenharmony_ci		cat = netlbl_catmap_walk(secattr->attr.mls.cat, cat + 1);
95262306a36Sopenharmony_ci		if (cat < 0)
95362306a36Sopenharmony_ci			break;
95462306a36Sopenharmony_ci		if ((cat_iter + 2) > net_cat_len)
95562306a36Sopenharmony_ci			return -ENOSPC;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci		*((__be16 *)&net_cat[cat_iter]) = htons(cat);
95862306a36Sopenharmony_ci		cat_iter += 2;
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	return cat_iter;
96262306a36Sopenharmony_ci}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci/**
96562306a36Sopenharmony_ci * cipso_v4_map_cat_enum_ntoh - Perform a category mapping from network to host
96662306a36Sopenharmony_ci * @doi_def: the DOI definition
96762306a36Sopenharmony_ci * @net_cat: the category list in network/CIPSO format
96862306a36Sopenharmony_ci * @net_cat_len: the length of the CIPSO bitmap in bytes
96962306a36Sopenharmony_ci * @secattr: the security attributes
97062306a36Sopenharmony_ci *
97162306a36Sopenharmony_ci * Description:
97262306a36Sopenharmony_ci * Perform a label mapping to translate a CIPSO category list to the correct
97362306a36Sopenharmony_ci * local MLS category bitmap using the given DOI definition.  Returns zero on
97462306a36Sopenharmony_ci * success, negative values on failure.
97562306a36Sopenharmony_ci *
97662306a36Sopenharmony_ci */
97762306a36Sopenharmony_cistatic int cipso_v4_map_cat_enum_ntoh(const struct cipso_v4_doi *doi_def,
97862306a36Sopenharmony_ci				      const unsigned char *net_cat,
97962306a36Sopenharmony_ci				      u32 net_cat_len,
98062306a36Sopenharmony_ci				      struct netlbl_lsm_secattr *secattr)
98162306a36Sopenharmony_ci{
98262306a36Sopenharmony_ci	int ret_val;
98362306a36Sopenharmony_ci	u32 iter;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	for (iter = 0; iter < net_cat_len; iter += 2) {
98662306a36Sopenharmony_ci		ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat,
98762306a36Sopenharmony_ci					     get_unaligned_be16(&net_cat[iter]),
98862306a36Sopenharmony_ci					     GFP_ATOMIC);
98962306a36Sopenharmony_ci		if (ret_val != 0)
99062306a36Sopenharmony_ci			return ret_val;
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	return 0;
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci/**
99762306a36Sopenharmony_ci * cipso_v4_map_cat_rng_valid - Checks to see if the categories are valid
99862306a36Sopenharmony_ci * @doi_def: the DOI definition
99962306a36Sopenharmony_ci * @rngcat: category list
100062306a36Sopenharmony_ci * @rngcat_len: length of the category list in bytes
100162306a36Sopenharmony_ci *
100262306a36Sopenharmony_ci * Description:
100362306a36Sopenharmony_ci * Checks the given categories against the given DOI definition and returns a
100462306a36Sopenharmony_ci * negative value if any of the categories do not have a valid mapping and a
100562306a36Sopenharmony_ci * zero value if all of the categories are valid.
100662306a36Sopenharmony_ci *
100762306a36Sopenharmony_ci */
100862306a36Sopenharmony_cistatic int cipso_v4_map_cat_rng_valid(const struct cipso_v4_doi *doi_def,
100962306a36Sopenharmony_ci				      const unsigned char *rngcat,
101062306a36Sopenharmony_ci				      u32 rngcat_len)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	u16 cat_high;
101362306a36Sopenharmony_ci	u16 cat_low;
101462306a36Sopenharmony_ci	u32 cat_prev = CIPSO_V4_MAX_REM_CATS + 1;
101562306a36Sopenharmony_ci	u32 iter;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	if (doi_def->type != CIPSO_V4_MAP_PASS || rngcat_len & 0x01)
101862306a36Sopenharmony_ci		return -EFAULT;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	for (iter = 0; iter < rngcat_len; iter += 4) {
102162306a36Sopenharmony_ci		cat_high = get_unaligned_be16(&rngcat[iter]);
102262306a36Sopenharmony_ci		if ((iter + 4) <= rngcat_len)
102362306a36Sopenharmony_ci			cat_low = get_unaligned_be16(&rngcat[iter + 2]);
102462306a36Sopenharmony_ci		else
102562306a36Sopenharmony_ci			cat_low = 0;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci		if (cat_high > cat_prev)
102862306a36Sopenharmony_ci			return -EFAULT;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci		cat_prev = cat_low;
103162306a36Sopenharmony_ci	}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	return 0;
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci/**
103762306a36Sopenharmony_ci * cipso_v4_map_cat_rng_hton - Perform a category mapping from host to network
103862306a36Sopenharmony_ci * @doi_def: the DOI definition
103962306a36Sopenharmony_ci * @secattr: the security attributes
104062306a36Sopenharmony_ci * @net_cat: the zero'd out category list in network/CIPSO format
104162306a36Sopenharmony_ci * @net_cat_len: the length of the CIPSO category list in bytes
104262306a36Sopenharmony_ci *
104362306a36Sopenharmony_ci * Description:
104462306a36Sopenharmony_ci * Perform a label mapping to translate a local MLS category bitmap to the
104562306a36Sopenharmony_ci * correct CIPSO category list using the given DOI definition.   Returns the
104662306a36Sopenharmony_ci * size in bytes of the network category bitmap on success, negative values
104762306a36Sopenharmony_ci * otherwise.
104862306a36Sopenharmony_ci *
104962306a36Sopenharmony_ci */
105062306a36Sopenharmony_cistatic int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def,
105162306a36Sopenharmony_ci				     const struct netlbl_lsm_secattr *secattr,
105262306a36Sopenharmony_ci				     unsigned char *net_cat,
105362306a36Sopenharmony_ci				     u32 net_cat_len)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	int iter = -1;
105662306a36Sopenharmony_ci	u16 array[CIPSO_V4_TAG_RNG_CAT_MAX * 2];
105762306a36Sopenharmony_ci	u32 array_cnt = 0;
105862306a36Sopenharmony_ci	u32 cat_size = 0;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	/* make sure we don't overflow the 'array[]' variable */
106162306a36Sopenharmony_ci	if (net_cat_len >
106262306a36Sopenharmony_ci	    (CIPSO_V4_OPT_LEN_MAX - CIPSO_V4_HDR_LEN - CIPSO_V4_TAG_RNG_BLEN))
106362306a36Sopenharmony_ci		return -ENOSPC;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	for (;;) {
106662306a36Sopenharmony_ci		iter = netlbl_catmap_walk(secattr->attr.mls.cat, iter + 1);
106762306a36Sopenharmony_ci		if (iter < 0)
106862306a36Sopenharmony_ci			break;
106962306a36Sopenharmony_ci		cat_size += (iter == 0 ? 0 : sizeof(u16));
107062306a36Sopenharmony_ci		if (cat_size > net_cat_len)
107162306a36Sopenharmony_ci			return -ENOSPC;
107262306a36Sopenharmony_ci		array[array_cnt++] = iter;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci		iter = netlbl_catmap_walkrng(secattr->attr.mls.cat, iter);
107562306a36Sopenharmony_ci		if (iter < 0)
107662306a36Sopenharmony_ci			return -EFAULT;
107762306a36Sopenharmony_ci		cat_size += sizeof(u16);
107862306a36Sopenharmony_ci		if (cat_size > net_cat_len)
107962306a36Sopenharmony_ci			return -ENOSPC;
108062306a36Sopenharmony_ci		array[array_cnt++] = iter;
108162306a36Sopenharmony_ci	}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	for (iter = 0; array_cnt > 0;) {
108462306a36Sopenharmony_ci		*((__be16 *)&net_cat[iter]) = htons(array[--array_cnt]);
108562306a36Sopenharmony_ci		iter += 2;
108662306a36Sopenharmony_ci		array_cnt--;
108762306a36Sopenharmony_ci		if (array[array_cnt] != 0) {
108862306a36Sopenharmony_ci			*((__be16 *)&net_cat[iter]) = htons(array[array_cnt]);
108962306a36Sopenharmony_ci			iter += 2;
109062306a36Sopenharmony_ci		}
109162306a36Sopenharmony_ci	}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	return cat_size;
109462306a36Sopenharmony_ci}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci/**
109762306a36Sopenharmony_ci * cipso_v4_map_cat_rng_ntoh - Perform a category mapping from network to host
109862306a36Sopenharmony_ci * @doi_def: the DOI definition
109962306a36Sopenharmony_ci * @net_cat: the category list in network/CIPSO format
110062306a36Sopenharmony_ci * @net_cat_len: the length of the CIPSO bitmap in bytes
110162306a36Sopenharmony_ci * @secattr: the security attributes
110262306a36Sopenharmony_ci *
110362306a36Sopenharmony_ci * Description:
110462306a36Sopenharmony_ci * Perform a label mapping to translate a CIPSO category list to the correct
110562306a36Sopenharmony_ci * local MLS category bitmap using the given DOI definition.  Returns zero on
110662306a36Sopenharmony_ci * success, negative values on failure.
110762306a36Sopenharmony_ci *
110862306a36Sopenharmony_ci */
110962306a36Sopenharmony_cistatic int cipso_v4_map_cat_rng_ntoh(const struct cipso_v4_doi *doi_def,
111062306a36Sopenharmony_ci				     const unsigned char *net_cat,
111162306a36Sopenharmony_ci				     u32 net_cat_len,
111262306a36Sopenharmony_ci				     struct netlbl_lsm_secattr *secattr)
111362306a36Sopenharmony_ci{
111462306a36Sopenharmony_ci	int ret_val;
111562306a36Sopenharmony_ci	u32 net_iter;
111662306a36Sopenharmony_ci	u16 cat_low;
111762306a36Sopenharmony_ci	u16 cat_high;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	for (net_iter = 0; net_iter < net_cat_len; net_iter += 4) {
112062306a36Sopenharmony_ci		cat_high = get_unaligned_be16(&net_cat[net_iter]);
112162306a36Sopenharmony_ci		if ((net_iter + 4) <= net_cat_len)
112262306a36Sopenharmony_ci			cat_low = get_unaligned_be16(&net_cat[net_iter + 2]);
112362306a36Sopenharmony_ci		else
112462306a36Sopenharmony_ci			cat_low = 0;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci		ret_val = netlbl_catmap_setrng(&secattr->attr.mls.cat,
112762306a36Sopenharmony_ci					       cat_low,
112862306a36Sopenharmony_ci					       cat_high,
112962306a36Sopenharmony_ci					       GFP_ATOMIC);
113062306a36Sopenharmony_ci		if (ret_val != 0)
113162306a36Sopenharmony_ci			return ret_val;
113262306a36Sopenharmony_ci	}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	return 0;
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci/*
113862306a36Sopenharmony_ci * Protocol Handling Functions
113962306a36Sopenharmony_ci */
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci/**
114262306a36Sopenharmony_ci * cipso_v4_gentag_hdr - Generate a CIPSO option header
114362306a36Sopenharmony_ci * @doi_def: the DOI definition
114462306a36Sopenharmony_ci * @len: the total tag length in bytes, not including this header
114562306a36Sopenharmony_ci * @buf: the CIPSO option buffer
114662306a36Sopenharmony_ci *
114762306a36Sopenharmony_ci * Description:
114862306a36Sopenharmony_ci * Write a CIPSO header into the beginning of @buffer.
114962306a36Sopenharmony_ci *
115062306a36Sopenharmony_ci */
115162306a36Sopenharmony_cistatic void cipso_v4_gentag_hdr(const struct cipso_v4_doi *doi_def,
115262306a36Sopenharmony_ci				unsigned char *buf,
115362306a36Sopenharmony_ci				u32 len)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	buf[0] = IPOPT_CIPSO;
115662306a36Sopenharmony_ci	buf[1] = CIPSO_V4_HDR_LEN + len;
115762306a36Sopenharmony_ci	put_unaligned_be32(doi_def->doi, &buf[2]);
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci/**
116162306a36Sopenharmony_ci * cipso_v4_gentag_rbm - Generate a CIPSO restricted bitmap tag (type #1)
116262306a36Sopenharmony_ci * @doi_def: the DOI definition
116362306a36Sopenharmony_ci * @secattr: the security attributes
116462306a36Sopenharmony_ci * @buffer: the option buffer
116562306a36Sopenharmony_ci * @buffer_len: length of buffer in bytes
116662306a36Sopenharmony_ci *
116762306a36Sopenharmony_ci * Description:
116862306a36Sopenharmony_ci * Generate a CIPSO option using the restricted bitmap tag, tag type #1.  The
116962306a36Sopenharmony_ci * actual buffer length may be larger than the indicated size due to
117062306a36Sopenharmony_ci * translation between host and network category bitmaps.  Returns the size of
117162306a36Sopenharmony_ci * the tag on success, negative values on failure.
117262306a36Sopenharmony_ci *
117362306a36Sopenharmony_ci */
117462306a36Sopenharmony_cistatic int cipso_v4_gentag_rbm(const struct cipso_v4_doi *doi_def,
117562306a36Sopenharmony_ci			       const struct netlbl_lsm_secattr *secattr,
117662306a36Sopenharmony_ci			       unsigned char *buffer,
117762306a36Sopenharmony_ci			       u32 buffer_len)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	int ret_val;
118062306a36Sopenharmony_ci	u32 tag_len;
118162306a36Sopenharmony_ci	u32 level;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0)
118462306a36Sopenharmony_ci		return -EPERM;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	ret_val = cipso_v4_map_lvl_hton(doi_def,
118762306a36Sopenharmony_ci					secattr->attr.mls.lvl,
118862306a36Sopenharmony_ci					&level);
118962306a36Sopenharmony_ci	if (ret_val != 0)
119062306a36Sopenharmony_ci		return ret_val;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
119362306a36Sopenharmony_ci		ret_val = cipso_v4_map_cat_rbm_hton(doi_def,
119462306a36Sopenharmony_ci						    secattr,
119562306a36Sopenharmony_ci						    &buffer[4],
119662306a36Sopenharmony_ci						    buffer_len - 4);
119762306a36Sopenharmony_ci		if (ret_val < 0)
119862306a36Sopenharmony_ci			return ret_val;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci		/* This will send packets using the "optimized" format when
120162306a36Sopenharmony_ci		 * possible as specified in  section 3.4.2.6 of the
120262306a36Sopenharmony_ci		 * CIPSO draft. */
120362306a36Sopenharmony_ci		if (READ_ONCE(cipso_v4_rbm_optfmt) && ret_val > 0 &&
120462306a36Sopenharmony_ci		    ret_val <= 10)
120562306a36Sopenharmony_ci			tag_len = 14;
120662306a36Sopenharmony_ci		else
120762306a36Sopenharmony_ci			tag_len = 4 + ret_val;
120862306a36Sopenharmony_ci	} else
120962306a36Sopenharmony_ci		tag_len = 4;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	buffer[0] = CIPSO_V4_TAG_RBITMAP;
121262306a36Sopenharmony_ci	buffer[1] = tag_len;
121362306a36Sopenharmony_ci	buffer[3] = level;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	return tag_len;
121662306a36Sopenharmony_ci}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci/**
121962306a36Sopenharmony_ci * cipso_v4_parsetag_rbm - Parse a CIPSO restricted bitmap tag
122062306a36Sopenharmony_ci * @doi_def: the DOI definition
122162306a36Sopenharmony_ci * @tag: the CIPSO tag
122262306a36Sopenharmony_ci * @secattr: the security attributes
122362306a36Sopenharmony_ci *
122462306a36Sopenharmony_ci * Description:
122562306a36Sopenharmony_ci * Parse a CIPSO restricted bitmap tag (tag type #1) and return the security
122662306a36Sopenharmony_ci * attributes in @secattr.  Return zero on success, negatives values on
122762306a36Sopenharmony_ci * failure.
122862306a36Sopenharmony_ci *
122962306a36Sopenharmony_ci */
123062306a36Sopenharmony_cistatic int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def,
123162306a36Sopenharmony_ci				 const unsigned char *tag,
123262306a36Sopenharmony_ci				 struct netlbl_lsm_secattr *secattr)
123362306a36Sopenharmony_ci{
123462306a36Sopenharmony_ci	int ret_val;
123562306a36Sopenharmony_ci	u8 tag_len = tag[1];
123662306a36Sopenharmony_ci	u32 level;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level);
123962306a36Sopenharmony_ci	if (ret_val != 0)
124062306a36Sopenharmony_ci		return ret_val;
124162306a36Sopenharmony_ci	secattr->attr.mls.lvl = level;
124262306a36Sopenharmony_ci	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	if (tag_len > 4) {
124562306a36Sopenharmony_ci		ret_val = cipso_v4_map_cat_rbm_ntoh(doi_def,
124662306a36Sopenharmony_ci						    &tag[4],
124762306a36Sopenharmony_ci						    tag_len - 4,
124862306a36Sopenharmony_ci						    secattr);
124962306a36Sopenharmony_ci		if (ret_val != 0) {
125062306a36Sopenharmony_ci			netlbl_catmap_free(secattr->attr.mls.cat);
125162306a36Sopenharmony_ci			return ret_val;
125262306a36Sopenharmony_ci		}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci		if (secattr->attr.mls.cat)
125562306a36Sopenharmony_ci			secattr->flags |= NETLBL_SECATTR_MLS_CAT;
125662306a36Sopenharmony_ci	}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	return 0;
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci/**
126262306a36Sopenharmony_ci * cipso_v4_gentag_enum - Generate a CIPSO enumerated tag (type #2)
126362306a36Sopenharmony_ci * @doi_def: the DOI definition
126462306a36Sopenharmony_ci * @secattr: the security attributes
126562306a36Sopenharmony_ci * @buffer: the option buffer
126662306a36Sopenharmony_ci * @buffer_len: length of buffer in bytes
126762306a36Sopenharmony_ci *
126862306a36Sopenharmony_ci * Description:
126962306a36Sopenharmony_ci * Generate a CIPSO option using the enumerated tag, tag type #2.  Returns the
127062306a36Sopenharmony_ci * size of the tag on success, negative values on failure.
127162306a36Sopenharmony_ci *
127262306a36Sopenharmony_ci */
127362306a36Sopenharmony_cistatic int cipso_v4_gentag_enum(const struct cipso_v4_doi *doi_def,
127462306a36Sopenharmony_ci				const struct netlbl_lsm_secattr *secattr,
127562306a36Sopenharmony_ci				unsigned char *buffer,
127662306a36Sopenharmony_ci				u32 buffer_len)
127762306a36Sopenharmony_ci{
127862306a36Sopenharmony_ci	int ret_val;
127962306a36Sopenharmony_ci	u32 tag_len;
128062306a36Sopenharmony_ci	u32 level;
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	if (!(secattr->flags & NETLBL_SECATTR_MLS_LVL))
128362306a36Sopenharmony_ci		return -EPERM;
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	ret_val = cipso_v4_map_lvl_hton(doi_def,
128662306a36Sopenharmony_ci					secattr->attr.mls.lvl,
128762306a36Sopenharmony_ci					&level);
128862306a36Sopenharmony_ci	if (ret_val != 0)
128962306a36Sopenharmony_ci		return ret_val;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
129262306a36Sopenharmony_ci		ret_val = cipso_v4_map_cat_enum_hton(doi_def,
129362306a36Sopenharmony_ci						     secattr,
129462306a36Sopenharmony_ci						     &buffer[4],
129562306a36Sopenharmony_ci						     buffer_len - 4);
129662306a36Sopenharmony_ci		if (ret_val < 0)
129762306a36Sopenharmony_ci			return ret_val;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci		tag_len = 4 + ret_val;
130062306a36Sopenharmony_ci	} else
130162306a36Sopenharmony_ci		tag_len = 4;
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	buffer[0] = CIPSO_V4_TAG_ENUM;
130462306a36Sopenharmony_ci	buffer[1] = tag_len;
130562306a36Sopenharmony_ci	buffer[3] = level;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	return tag_len;
130862306a36Sopenharmony_ci}
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci/**
131162306a36Sopenharmony_ci * cipso_v4_parsetag_enum - Parse a CIPSO enumerated tag
131262306a36Sopenharmony_ci * @doi_def: the DOI definition
131362306a36Sopenharmony_ci * @tag: the CIPSO tag
131462306a36Sopenharmony_ci * @secattr: the security attributes
131562306a36Sopenharmony_ci *
131662306a36Sopenharmony_ci * Description:
131762306a36Sopenharmony_ci * Parse a CIPSO enumerated tag (tag type #2) and return the security
131862306a36Sopenharmony_ci * attributes in @secattr.  Return zero on success, negatives values on
131962306a36Sopenharmony_ci * failure.
132062306a36Sopenharmony_ci *
132162306a36Sopenharmony_ci */
132262306a36Sopenharmony_cistatic int cipso_v4_parsetag_enum(const struct cipso_v4_doi *doi_def,
132362306a36Sopenharmony_ci				  const unsigned char *tag,
132462306a36Sopenharmony_ci				  struct netlbl_lsm_secattr *secattr)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	int ret_val;
132762306a36Sopenharmony_ci	u8 tag_len = tag[1];
132862306a36Sopenharmony_ci	u32 level;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level);
133162306a36Sopenharmony_ci	if (ret_val != 0)
133262306a36Sopenharmony_ci		return ret_val;
133362306a36Sopenharmony_ci	secattr->attr.mls.lvl = level;
133462306a36Sopenharmony_ci	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	if (tag_len > 4) {
133762306a36Sopenharmony_ci		ret_val = cipso_v4_map_cat_enum_ntoh(doi_def,
133862306a36Sopenharmony_ci						     &tag[4],
133962306a36Sopenharmony_ci						     tag_len - 4,
134062306a36Sopenharmony_ci						     secattr);
134162306a36Sopenharmony_ci		if (ret_val != 0) {
134262306a36Sopenharmony_ci			netlbl_catmap_free(secattr->attr.mls.cat);
134362306a36Sopenharmony_ci			return ret_val;
134462306a36Sopenharmony_ci		}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci		secattr->flags |= NETLBL_SECATTR_MLS_CAT;
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	return 0;
135062306a36Sopenharmony_ci}
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci/**
135362306a36Sopenharmony_ci * cipso_v4_gentag_rng - Generate a CIPSO ranged tag (type #5)
135462306a36Sopenharmony_ci * @doi_def: the DOI definition
135562306a36Sopenharmony_ci * @secattr: the security attributes
135662306a36Sopenharmony_ci * @buffer: the option buffer
135762306a36Sopenharmony_ci * @buffer_len: length of buffer in bytes
135862306a36Sopenharmony_ci *
135962306a36Sopenharmony_ci * Description:
136062306a36Sopenharmony_ci * Generate a CIPSO option using the ranged tag, tag type #5.  Returns the
136162306a36Sopenharmony_ci * size of the tag on success, negative values on failure.
136262306a36Sopenharmony_ci *
136362306a36Sopenharmony_ci */
136462306a36Sopenharmony_cistatic int cipso_v4_gentag_rng(const struct cipso_v4_doi *doi_def,
136562306a36Sopenharmony_ci			       const struct netlbl_lsm_secattr *secattr,
136662306a36Sopenharmony_ci			       unsigned char *buffer,
136762306a36Sopenharmony_ci			       u32 buffer_len)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	int ret_val;
137062306a36Sopenharmony_ci	u32 tag_len;
137162306a36Sopenharmony_ci	u32 level;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	if (!(secattr->flags & NETLBL_SECATTR_MLS_LVL))
137462306a36Sopenharmony_ci		return -EPERM;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	ret_val = cipso_v4_map_lvl_hton(doi_def,
137762306a36Sopenharmony_ci					secattr->attr.mls.lvl,
137862306a36Sopenharmony_ci					&level);
137962306a36Sopenharmony_ci	if (ret_val != 0)
138062306a36Sopenharmony_ci		return ret_val;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
138362306a36Sopenharmony_ci		ret_val = cipso_v4_map_cat_rng_hton(doi_def,
138462306a36Sopenharmony_ci						    secattr,
138562306a36Sopenharmony_ci						    &buffer[4],
138662306a36Sopenharmony_ci						    buffer_len - 4);
138762306a36Sopenharmony_ci		if (ret_val < 0)
138862306a36Sopenharmony_ci			return ret_val;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci		tag_len = 4 + ret_val;
139162306a36Sopenharmony_ci	} else
139262306a36Sopenharmony_ci		tag_len = 4;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	buffer[0] = CIPSO_V4_TAG_RANGE;
139562306a36Sopenharmony_ci	buffer[1] = tag_len;
139662306a36Sopenharmony_ci	buffer[3] = level;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	return tag_len;
139962306a36Sopenharmony_ci}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci/**
140262306a36Sopenharmony_ci * cipso_v4_parsetag_rng - Parse a CIPSO ranged tag
140362306a36Sopenharmony_ci * @doi_def: the DOI definition
140462306a36Sopenharmony_ci * @tag: the CIPSO tag
140562306a36Sopenharmony_ci * @secattr: the security attributes
140662306a36Sopenharmony_ci *
140762306a36Sopenharmony_ci * Description:
140862306a36Sopenharmony_ci * Parse a CIPSO ranged tag (tag type #5) and return the security attributes
140962306a36Sopenharmony_ci * in @secattr.  Return zero on success, negatives values on failure.
141062306a36Sopenharmony_ci *
141162306a36Sopenharmony_ci */
141262306a36Sopenharmony_cistatic int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def,
141362306a36Sopenharmony_ci				 const unsigned char *tag,
141462306a36Sopenharmony_ci				 struct netlbl_lsm_secattr *secattr)
141562306a36Sopenharmony_ci{
141662306a36Sopenharmony_ci	int ret_val;
141762306a36Sopenharmony_ci	u8 tag_len = tag[1];
141862306a36Sopenharmony_ci	u32 level;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level);
142162306a36Sopenharmony_ci	if (ret_val != 0)
142262306a36Sopenharmony_ci		return ret_val;
142362306a36Sopenharmony_ci	secattr->attr.mls.lvl = level;
142462306a36Sopenharmony_ci	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	if (tag_len > 4) {
142762306a36Sopenharmony_ci		ret_val = cipso_v4_map_cat_rng_ntoh(doi_def,
142862306a36Sopenharmony_ci						    &tag[4],
142962306a36Sopenharmony_ci						    tag_len - 4,
143062306a36Sopenharmony_ci						    secattr);
143162306a36Sopenharmony_ci		if (ret_val != 0) {
143262306a36Sopenharmony_ci			netlbl_catmap_free(secattr->attr.mls.cat);
143362306a36Sopenharmony_ci			return ret_val;
143462306a36Sopenharmony_ci		}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci		if (secattr->attr.mls.cat)
143762306a36Sopenharmony_ci			secattr->flags |= NETLBL_SECATTR_MLS_CAT;
143862306a36Sopenharmony_ci	}
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	return 0;
144162306a36Sopenharmony_ci}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci/**
144462306a36Sopenharmony_ci * cipso_v4_gentag_loc - Generate a CIPSO local tag (non-standard)
144562306a36Sopenharmony_ci * @doi_def: the DOI definition
144662306a36Sopenharmony_ci * @secattr: the security attributes
144762306a36Sopenharmony_ci * @buffer: the option buffer
144862306a36Sopenharmony_ci * @buffer_len: length of buffer in bytes
144962306a36Sopenharmony_ci *
145062306a36Sopenharmony_ci * Description:
145162306a36Sopenharmony_ci * Generate a CIPSO option using the local tag.  Returns the size of the tag
145262306a36Sopenharmony_ci * on success, negative values on failure.
145362306a36Sopenharmony_ci *
145462306a36Sopenharmony_ci */
145562306a36Sopenharmony_cistatic int cipso_v4_gentag_loc(const struct cipso_v4_doi *doi_def,
145662306a36Sopenharmony_ci			       const struct netlbl_lsm_secattr *secattr,
145762306a36Sopenharmony_ci			       unsigned char *buffer,
145862306a36Sopenharmony_ci			       u32 buffer_len)
145962306a36Sopenharmony_ci{
146062306a36Sopenharmony_ci	if (!(secattr->flags & NETLBL_SECATTR_SECID))
146162306a36Sopenharmony_ci		return -EPERM;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	buffer[0] = CIPSO_V4_TAG_LOCAL;
146462306a36Sopenharmony_ci	buffer[1] = CIPSO_V4_TAG_LOC_BLEN;
146562306a36Sopenharmony_ci	*(u32 *)&buffer[2] = secattr->attr.secid;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	return CIPSO_V4_TAG_LOC_BLEN;
146862306a36Sopenharmony_ci}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci/**
147162306a36Sopenharmony_ci * cipso_v4_parsetag_loc - Parse a CIPSO local tag
147262306a36Sopenharmony_ci * @doi_def: the DOI definition
147362306a36Sopenharmony_ci * @tag: the CIPSO tag
147462306a36Sopenharmony_ci * @secattr: the security attributes
147562306a36Sopenharmony_ci *
147662306a36Sopenharmony_ci * Description:
147762306a36Sopenharmony_ci * Parse a CIPSO local tag and return the security attributes in @secattr.
147862306a36Sopenharmony_ci * Return zero on success, negatives values on failure.
147962306a36Sopenharmony_ci *
148062306a36Sopenharmony_ci */
148162306a36Sopenharmony_cistatic int cipso_v4_parsetag_loc(const struct cipso_v4_doi *doi_def,
148262306a36Sopenharmony_ci				 const unsigned char *tag,
148362306a36Sopenharmony_ci				 struct netlbl_lsm_secattr *secattr)
148462306a36Sopenharmony_ci{
148562306a36Sopenharmony_ci	secattr->attr.secid = *(u32 *)&tag[2];
148662306a36Sopenharmony_ci	secattr->flags |= NETLBL_SECATTR_SECID;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	return 0;
148962306a36Sopenharmony_ci}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci/**
149262306a36Sopenharmony_ci * cipso_v4_optptr - Find the CIPSO option in the packet
149362306a36Sopenharmony_ci * @skb: the packet
149462306a36Sopenharmony_ci *
149562306a36Sopenharmony_ci * Description:
149662306a36Sopenharmony_ci * Parse the packet's IP header looking for a CIPSO option.  Returns a pointer
149762306a36Sopenharmony_ci * to the start of the CIPSO option on success, NULL if one is not found.
149862306a36Sopenharmony_ci *
149962306a36Sopenharmony_ci */
150062306a36Sopenharmony_ciunsigned char *cipso_v4_optptr(const struct sk_buff *skb)
150162306a36Sopenharmony_ci{
150262306a36Sopenharmony_ci	const struct iphdr *iph = ip_hdr(skb);
150362306a36Sopenharmony_ci	unsigned char *optptr = (unsigned char *)&(ip_hdr(skb)[1]);
150462306a36Sopenharmony_ci	int optlen;
150562306a36Sopenharmony_ci	int taglen;
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	for (optlen = iph->ihl*4 - sizeof(struct iphdr); optlen > 1; ) {
150862306a36Sopenharmony_ci		switch (optptr[0]) {
150962306a36Sopenharmony_ci		case IPOPT_END:
151062306a36Sopenharmony_ci			return NULL;
151162306a36Sopenharmony_ci		case IPOPT_NOOP:
151262306a36Sopenharmony_ci			taglen = 1;
151362306a36Sopenharmony_ci			break;
151462306a36Sopenharmony_ci		default:
151562306a36Sopenharmony_ci			taglen = optptr[1];
151662306a36Sopenharmony_ci		}
151762306a36Sopenharmony_ci		if (!taglen || taglen > optlen)
151862306a36Sopenharmony_ci			return NULL;
151962306a36Sopenharmony_ci		if (optptr[0] == IPOPT_CIPSO)
152062306a36Sopenharmony_ci			return optptr;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci		optlen -= taglen;
152362306a36Sopenharmony_ci		optptr += taglen;
152462306a36Sopenharmony_ci	}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	return NULL;
152762306a36Sopenharmony_ci}
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci/**
153062306a36Sopenharmony_ci * cipso_v4_validate - Validate a CIPSO option
153162306a36Sopenharmony_ci * @skb: the packet
153262306a36Sopenharmony_ci * @option: the start of the option, on error it is set to point to the error
153362306a36Sopenharmony_ci *
153462306a36Sopenharmony_ci * Description:
153562306a36Sopenharmony_ci * This routine is called to validate a CIPSO option, it checks all of the
153662306a36Sopenharmony_ci * fields to ensure that they are at least valid, see the draft snippet below
153762306a36Sopenharmony_ci * for details.  If the option is valid then a zero value is returned and
153862306a36Sopenharmony_ci * the value of @option is unchanged.  If the option is invalid then a
153962306a36Sopenharmony_ci * non-zero value is returned and @option is adjusted to point to the
154062306a36Sopenharmony_ci * offending portion of the option.  From the IETF draft ...
154162306a36Sopenharmony_ci *
154262306a36Sopenharmony_ci *  "If any field within the CIPSO options, such as the DOI identifier, is not
154362306a36Sopenharmony_ci *   recognized the IP datagram is discarded and an ICMP 'parameter problem'
154462306a36Sopenharmony_ci *   (type 12) is generated and returned.  The ICMP code field is set to 'bad
154562306a36Sopenharmony_ci *   parameter' (code 0) and the pointer is set to the start of the CIPSO field
154662306a36Sopenharmony_ci *   that is unrecognized."
154762306a36Sopenharmony_ci *
154862306a36Sopenharmony_ci */
154962306a36Sopenharmony_ciint cipso_v4_validate(const struct sk_buff *skb, unsigned char **option)
155062306a36Sopenharmony_ci{
155162306a36Sopenharmony_ci	unsigned char *opt = *option;
155262306a36Sopenharmony_ci	unsigned char *tag;
155362306a36Sopenharmony_ci	unsigned char opt_iter;
155462306a36Sopenharmony_ci	unsigned char err_offset = 0;
155562306a36Sopenharmony_ci	u8 opt_len;
155662306a36Sopenharmony_ci	u8 tag_len;
155762306a36Sopenharmony_ci	struct cipso_v4_doi *doi_def = NULL;
155862306a36Sopenharmony_ci	u32 tag_iter;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	/* caller already checks for length values that are too large */
156162306a36Sopenharmony_ci	opt_len = opt[1];
156262306a36Sopenharmony_ci	if (opt_len < 8) {
156362306a36Sopenharmony_ci		err_offset = 1;
156462306a36Sopenharmony_ci		goto validate_return;
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	rcu_read_lock();
156862306a36Sopenharmony_ci	doi_def = cipso_v4_doi_search(get_unaligned_be32(&opt[2]));
156962306a36Sopenharmony_ci	if (!doi_def) {
157062306a36Sopenharmony_ci		err_offset = 2;
157162306a36Sopenharmony_ci		goto validate_return_locked;
157262306a36Sopenharmony_ci	}
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	opt_iter = CIPSO_V4_HDR_LEN;
157562306a36Sopenharmony_ci	tag = opt + opt_iter;
157662306a36Sopenharmony_ci	while (opt_iter < opt_len) {
157762306a36Sopenharmony_ci		for (tag_iter = 0; doi_def->tags[tag_iter] != tag[0];)
157862306a36Sopenharmony_ci			if (doi_def->tags[tag_iter] == CIPSO_V4_TAG_INVALID ||
157962306a36Sopenharmony_ci			    ++tag_iter == CIPSO_V4_TAG_MAXCNT) {
158062306a36Sopenharmony_ci				err_offset = opt_iter;
158162306a36Sopenharmony_ci				goto validate_return_locked;
158262306a36Sopenharmony_ci			}
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci		if (opt_iter + 1 == opt_len) {
158562306a36Sopenharmony_ci			err_offset = opt_iter;
158662306a36Sopenharmony_ci			goto validate_return_locked;
158762306a36Sopenharmony_ci		}
158862306a36Sopenharmony_ci		tag_len = tag[1];
158962306a36Sopenharmony_ci		if (tag_len > (opt_len - opt_iter)) {
159062306a36Sopenharmony_ci			err_offset = opt_iter + 1;
159162306a36Sopenharmony_ci			goto validate_return_locked;
159262306a36Sopenharmony_ci		}
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci		switch (tag[0]) {
159562306a36Sopenharmony_ci		case CIPSO_V4_TAG_RBITMAP:
159662306a36Sopenharmony_ci			if (tag_len < CIPSO_V4_TAG_RBM_BLEN) {
159762306a36Sopenharmony_ci				err_offset = opt_iter + 1;
159862306a36Sopenharmony_ci				goto validate_return_locked;
159962306a36Sopenharmony_ci			}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci			/* We are already going to do all the verification
160262306a36Sopenharmony_ci			 * necessary at the socket layer so from our point of
160362306a36Sopenharmony_ci			 * view it is safe to turn these checks off (and less
160462306a36Sopenharmony_ci			 * work), however, the CIPSO draft says we should do
160562306a36Sopenharmony_ci			 * all the CIPSO validations here but it doesn't
160662306a36Sopenharmony_ci			 * really specify _exactly_ what we need to validate
160762306a36Sopenharmony_ci			 * ... so, just make it a sysctl tunable. */
160862306a36Sopenharmony_ci			if (READ_ONCE(cipso_v4_rbm_strictvalid)) {
160962306a36Sopenharmony_ci				if (cipso_v4_map_lvl_valid(doi_def,
161062306a36Sopenharmony_ci							   tag[3]) < 0) {
161162306a36Sopenharmony_ci					err_offset = opt_iter + 3;
161262306a36Sopenharmony_ci					goto validate_return_locked;
161362306a36Sopenharmony_ci				}
161462306a36Sopenharmony_ci				if (tag_len > CIPSO_V4_TAG_RBM_BLEN &&
161562306a36Sopenharmony_ci				    cipso_v4_map_cat_rbm_valid(doi_def,
161662306a36Sopenharmony_ci							    &tag[4],
161762306a36Sopenharmony_ci							    tag_len - 4) < 0) {
161862306a36Sopenharmony_ci					err_offset = opt_iter + 4;
161962306a36Sopenharmony_ci					goto validate_return_locked;
162062306a36Sopenharmony_ci				}
162162306a36Sopenharmony_ci			}
162262306a36Sopenharmony_ci			break;
162362306a36Sopenharmony_ci		case CIPSO_V4_TAG_ENUM:
162462306a36Sopenharmony_ci			if (tag_len < CIPSO_V4_TAG_ENUM_BLEN) {
162562306a36Sopenharmony_ci				err_offset = opt_iter + 1;
162662306a36Sopenharmony_ci				goto validate_return_locked;
162762306a36Sopenharmony_ci			}
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci			if (cipso_v4_map_lvl_valid(doi_def,
163062306a36Sopenharmony_ci						   tag[3]) < 0) {
163162306a36Sopenharmony_ci				err_offset = opt_iter + 3;
163262306a36Sopenharmony_ci				goto validate_return_locked;
163362306a36Sopenharmony_ci			}
163462306a36Sopenharmony_ci			if (tag_len > CIPSO_V4_TAG_ENUM_BLEN &&
163562306a36Sopenharmony_ci			    cipso_v4_map_cat_enum_valid(doi_def,
163662306a36Sopenharmony_ci							&tag[4],
163762306a36Sopenharmony_ci							tag_len - 4) < 0) {
163862306a36Sopenharmony_ci				err_offset = opt_iter + 4;
163962306a36Sopenharmony_ci				goto validate_return_locked;
164062306a36Sopenharmony_ci			}
164162306a36Sopenharmony_ci			break;
164262306a36Sopenharmony_ci		case CIPSO_V4_TAG_RANGE:
164362306a36Sopenharmony_ci			if (tag_len < CIPSO_V4_TAG_RNG_BLEN) {
164462306a36Sopenharmony_ci				err_offset = opt_iter + 1;
164562306a36Sopenharmony_ci				goto validate_return_locked;
164662306a36Sopenharmony_ci			}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci			if (cipso_v4_map_lvl_valid(doi_def,
164962306a36Sopenharmony_ci						   tag[3]) < 0) {
165062306a36Sopenharmony_ci				err_offset = opt_iter + 3;
165162306a36Sopenharmony_ci				goto validate_return_locked;
165262306a36Sopenharmony_ci			}
165362306a36Sopenharmony_ci			if (tag_len > CIPSO_V4_TAG_RNG_BLEN &&
165462306a36Sopenharmony_ci			    cipso_v4_map_cat_rng_valid(doi_def,
165562306a36Sopenharmony_ci						       &tag[4],
165662306a36Sopenharmony_ci						       tag_len - 4) < 0) {
165762306a36Sopenharmony_ci				err_offset = opt_iter + 4;
165862306a36Sopenharmony_ci				goto validate_return_locked;
165962306a36Sopenharmony_ci			}
166062306a36Sopenharmony_ci			break;
166162306a36Sopenharmony_ci		case CIPSO_V4_TAG_LOCAL:
166262306a36Sopenharmony_ci			/* This is a non-standard tag that we only allow for
166362306a36Sopenharmony_ci			 * local connections, so if the incoming interface is
166462306a36Sopenharmony_ci			 * not the loopback device drop the packet. Further,
166562306a36Sopenharmony_ci			 * there is no legitimate reason for setting this from
166662306a36Sopenharmony_ci			 * userspace so reject it if skb is NULL. */
166762306a36Sopenharmony_ci			if (!skb || !(skb->dev->flags & IFF_LOOPBACK)) {
166862306a36Sopenharmony_ci				err_offset = opt_iter;
166962306a36Sopenharmony_ci				goto validate_return_locked;
167062306a36Sopenharmony_ci			}
167162306a36Sopenharmony_ci			if (tag_len != CIPSO_V4_TAG_LOC_BLEN) {
167262306a36Sopenharmony_ci				err_offset = opt_iter + 1;
167362306a36Sopenharmony_ci				goto validate_return_locked;
167462306a36Sopenharmony_ci			}
167562306a36Sopenharmony_ci			break;
167662306a36Sopenharmony_ci		default:
167762306a36Sopenharmony_ci			err_offset = opt_iter;
167862306a36Sopenharmony_ci			goto validate_return_locked;
167962306a36Sopenharmony_ci		}
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci		tag += tag_len;
168262306a36Sopenharmony_ci		opt_iter += tag_len;
168362306a36Sopenharmony_ci	}
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_civalidate_return_locked:
168662306a36Sopenharmony_ci	rcu_read_unlock();
168762306a36Sopenharmony_civalidate_return:
168862306a36Sopenharmony_ci	*option = opt + err_offset;
168962306a36Sopenharmony_ci	return err_offset;
169062306a36Sopenharmony_ci}
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci/**
169362306a36Sopenharmony_ci * cipso_v4_error - Send the correct response for a bad packet
169462306a36Sopenharmony_ci * @skb: the packet
169562306a36Sopenharmony_ci * @error: the error code
169662306a36Sopenharmony_ci * @gateway: CIPSO gateway flag
169762306a36Sopenharmony_ci *
169862306a36Sopenharmony_ci * Description:
169962306a36Sopenharmony_ci * Based on the error code given in @error, send an ICMP error message back to
170062306a36Sopenharmony_ci * the originating host.  From the IETF draft ...
170162306a36Sopenharmony_ci *
170262306a36Sopenharmony_ci *  "If the contents of the CIPSO [option] are valid but the security label is
170362306a36Sopenharmony_ci *   outside of the configured host or port label range, the datagram is
170462306a36Sopenharmony_ci *   discarded and an ICMP 'destination unreachable' (type 3) is generated and
170562306a36Sopenharmony_ci *   returned.  The code field of the ICMP is set to 'communication with
170662306a36Sopenharmony_ci *   destination network administratively prohibited' (code 9) or to
170762306a36Sopenharmony_ci *   'communication with destination host administratively prohibited'
170862306a36Sopenharmony_ci *   (code 10).  The value of the code is dependent on whether the originator
170962306a36Sopenharmony_ci *   of the ICMP message is acting as a CIPSO host or a CIPSO gateway.  The
171062306a36Sopenharmony_ci *   recipient of the ICMP message MUST be able to handle either value.  The
171162306a36Sopenharmony_ci *   same procedure is performed if a CIPSO [option] can not be added to an
171262306a36Sopenharmony_ci *   IP packet because it is too large to fit in the IP options area."
171362306a36Sopenharmony_ci *
171462306a36Sopenharmony_ci *  "If the error is triggered by receipt of an ICMP message, the message is
171562306a36Sopenharmony_ci *   discarded and no response is permitted (consistent with general ICMP
171662306a36Sopenharmony_ci *   processing rules)."
171762306a36Sopenharmony_ci *
171862306a36Sopenharmony_ci */
171962306a36Sopenharmony_civoid cipso_v4_error(struct sk_buff *skb, int error, u32 gateway)
172062306a36Sopenharmony_ci{
172162306a36Sopenharmony_ci	unsigned char optbuf[sizeof(struct ip_options) + 40];
172262306a36Sopenharmony_ci	struct ip_options *opt = (struct ip_options *)optbuf;
172362306a36Sopenharmony_ci	int res;
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	if (ip_hdr(skb)->protocol == IPPROTO_ICMP || error != -EACCES)
172662306a36Sopenharmony_ci		return;
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	/*
172962306a36Sopenharmony_ci	 * We might be called above the IP layer,
173062306a36Sopenharmony_ci	 * so we can not use icmp_send and IPCB here.
173162306a36Sopenharmony_ci	 */
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	memset(opt, 0, sizeof(struct ip_options));
173462306a36Sopenharmony_ci	opt->optlen = ip_hdr(skb)->ihl*4 - sizeof(struct iphdr);
173562306a36Sopenharmony_ci	rcu_read_lock();
173662306a36Sopenharmony_ci	res = __ip_options_compile(dev_net(skb->dev), opt, skb, NULL);
173762306a36Sopenharmony_ci	rcu_read_unlock();
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	if (res)
174062306a36Sopenharmony_ci		return;
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	if (gateway)
174362306a36Sopenharmony_ci		__icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0, opt);
174462306a36Sopenharmony_ci	else
174562306a36Sopenharmony_ci		__icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0, opt);
174662306a36Sopenharmony_ci}
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci/**
174962306a36Sopenharmony_ci * cipso_v4_genopt - Generate a CIPSO option
175062306a36Sopenharmony_ci * @buf: the option buffer
175162306a36Sopenharmony_ci * @buf_len: the size of opt_buf
175262306a36Sopenharmony_ci * @doi_def: the CIPSO DOI to use
175362306a36Sopenharmony_ci * @secattr: the security attributes
175462306a36Sopenharmony_ci *
175562306a36Sopenharmony_ci * Description:
175662306a36Sopenharmony_ci * Generate a CIPSO option using the DOI definition and security attributes
175762306a36Sopenharmony_ci * passed to the function.  Returns the length of the option on success and
175862306a36Sopenharmony_ci * negative values on failure.
175962306a36Sopenharmony_ci *
176062306a36Sopenharmony_ci */
176162306a36Sopenharmony_cistatic int cipso_v4_genopt(unsigned char *buf, u32 buf_len,
176262306a36Sopenharmony_ci			   const struct cipso_v4_doi *doi_def,
176362306a36Sopenharmony_ci			   const struct netlbl_lsm_secattr *secattr)
176462306a36Sopenharmony_ci{
176562306a36Sopenharmony_ci	int ret_val;
176662306a36Sopenharmony_ci	u32 iter;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	if (buf_len <= CIPSO_V4_HDR_LEN)
176962306a36Sopenharmony_ci		return -ENOSPC;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	/* XXX - This code assumes only one tag per CIPSO option which isn't
177262306a36Sopenharmony_ci	 * really a good assumption to make but since we only support the MAC
177362306a36Sopenharmony_ci	 * tags right now it is a safe assumption. */
177462306a36Sopenharmony_ci	iter = 0;
177562306a36Sopenharmony_ci	do {
177662306a36Sopenharmony_ci		memset(buf, 0, buf_len);
177762306a36Sopenharmony_ci		switch (doi_def->tags[iter]) {
177862306a36Sopenharmony_ci		case CIPSO_V4_TAG_RBITMAP:
177962306a36Sopenharmony_ci			ret_val = cipso_v4_gentag_rbm(doi_def,
178062306a36Sopenharmony_ci						   secattr,
178162306a36Sopenharmony_ci						   &buf[CIPSO_V4_HDR_LEN],
178262306a36Sopenharmony_ci						   buf_len - CIPSO_V4_HDR_LEN);
178362306a36Sopenharmony_ci			break;
178462306a36Sopenharmony_ci		case CIPSO_V4_TAG_ENUM:
178562306a36Sopenharmony_ci			ret_val = cipso_v4_gentag_enum(doi_def,
178662306a36Sopenharmony_ci						   secattr,
178762306a36Sopenharmony_ci						   &buf[CIPSO_V4_HDR_LEN],
178862306a36Sopenharmony_ci						   buf_len - CIPSO_V4_HDR_LEN);
178962306a36Sopenharmony_ci			break;
179062306a36Sopenharmony_ci		case CIPSO_V4_TAG_RANGE:
179162306a36Sopenharmony_ci			ret_val = cipso_v4_gentag_rng(doi_def,
179262306a36Sopenharmony_ci						   secattr,
179362306a36Sopenharmony_ci						   &buf[CIPSO_V4_HDR_LEN],
179462306a36Sopenharmony_ci						   buf_len - CIPSO_V4_HDR_LEN);
179562306a36Sopenharmony_ci			break;
179662306a36Sopenharmony_ci		case CIPSO_V4_TAG_LOCAL:
179762306a36Sopenharmony_ci			ret_val = cipso_v4_gentag_loc(doi_def,
179862306a36Sopenharmony_ci						   secattr,
179962306a36Sopenharmony_ci						   &buf[CIPSO_V4_HDR_LEN],
180062306a36Sopenharmony_ci						   buf_len - CIPSO_V4_HDR_LEN);
180162306a36Sopenharmony_ci			break;
180262306a36Sopenharmony_ci		default:
180362306a36Sopenharmony_ci			return -EPERM;
180462306a36Sopenharmony_ci		}
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci		iter++;
180762306a36Sopenharmony_ci	} while (ret_val < 0 &&
180862306a36Sopenharmony_ci		 iter < CIPSO_V4_TAG_MAXCNT &&
180962306a36Sopenharmony_ci		 doi_def->tags[iter] != CIPSO_V4_TAG_INVALID);
181062306a36Sopenharmony_ci	if (ret_val < 0)
181162306a36Sopenharmony_ci		return ret_val;
181262306a36Sopenharmony_ci	cipso_v4_gentag_hdr(doi_def, buf, ret_val);
181362306a36Sopenharmony_ci	return CIPSO_V4_HDR_LEN + ret_val;
181462306a36Sopenharmony_ci}
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci/**
181762306a36Sopenharmony_ci * cipso_v4_sock_setattr - Add a CIPSO option to a socket
181862306a36Sopenharmony_ci * @sk: the socket
181962306a36Sopenharmony_ci * @doi_def: the CIPSO DOI to use
182062306a36Sopenharmony_ci * @secattr: the specific security attributes of the socket
182162306a36Sopenharmony_ci *
182262306a36Sopenharmony_ci * Description:
182362306a36Sopenharmony_ci * Set the CIPSO option on the given socket using the DOI definition and
182462306a36Sopenharmony_ci * security attributes passed to the function.  This function requires
182562306a36Sopenharmony_ci * exclusive access to @sk, which means it either needs to be in the
182662306a36Sopenharmony_ci * process of being created or locked.  Returns zero on success and negative
182762306a36Sopenharmony_ci * values on failure.
182862306a36Sopenharmony_ci *
182962306a36Sopenharmony_ci */
183062306a36Sopenharmony_ciint cipso_v4_sock_setattr(struct sock *sk,
183162306a36Sopenharmony_ci			  const struct cipso_v4_doi *doi_def,
183262306a36Sopenharmony_ci			  const struct netlbl_lsm_secattr *secattr)
183362306a36Sopenharmony_ci{
183462306a36Sopenharmony_ci	int ret_val = -EPERM;
183562306a36Sopenharmony_ci	unsigned char *buf = NULL;
183662306a36Sopenharmony_ci	u32 buf_len;
183762306a36Sopenharmony_ci	u32 opt_len;
183862306a36Sopenharmony_ci	struct ip_options_rcu *old, *opt = NULL;
183962306a36Sopenharmony_ci	struct inet_sock *sk_inet;
184062306a36Sopenharmony_ci	struct inet_connection_sock *sk_conn;
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci	/* In the case of sock_create_lite(), the sock->sk field is not
184362306a36Sopenharmony_ci	 * defined yet but it is not a problem as the only users of these
184462306a36Sopenharmony_ci	 * "lite" PF_INET sockets are functions which do an accept() call
184562306a36Sopenharmony_ci	 * afterwards so we will label the socket as part of the accept(). */
184662306a36Sopenharmony_ci	if (!sk)
184762306a36Sopenharmony_ci		return 0;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	/* We allocate the maximum CIPSO option size here so we are probably
185062306a36Sopenharmony_ci	 * being a little wasteful, but it makes our life _much_ easier later
185162306a36Sopenharmony_ci	 * on and after all we are only talking about 40 bytes. */
185262306a36Sopenharmony_ci	buf_len = CIPSO_V4_OPT_LEN_MAX;
185362306a36Sopenharmony_ci	buf = kmalloc(buf_len, GFP_ATOMIC);
185462306a36Sopenharmony_ci	if (!buf) {
185562306a36Sopenharmony_ci		ret_val = -ENOMEM;
185662306a36Sopenharmony_ci		goto socket_setattr_failure;
185762306a36Sopenharmony_ci	}
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
186062306a36Sopenharmony_ci	if (ret_val < 0)
186162306a36Sopenharmony_ci		goto socket_setattr_failure;
186262306a36Sopenharmony_ci	buf_len = ret_val;
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	/* We can't use ip_options_get() directly because it makes a call to
186562306a36Sopenharmony_ci	 * ip_options_get_alloc() which allocates memory with GFP_KERNEL and
186662306a36Sopenharmony_ci	 * we won't always have CAP_NET_RAW even though we _always_ want to
186762306a36Sopenharmony_ci	 * set the IPOPT_CIPSO option. */
186862306a36Sopenharmony_ci	opt_len = (buf_len + 3) & ~3;
186962306a36Sopenharmony_ci	opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC);
187062306a36Sopenharmony_ci	if (!opt) {
187162306a36Sopenharmony_ci		ret_val = -ENOMEM;
187262306a36Sopenharmony_ci		goto socket_setattr_failure;
187362306a36Sopenharmony_ci	}
187462306a36Sopenharmony_ci	memcpy(opt->opt.__data, buf, buf_len);
187562306a36Sopenharmony_ci	opt->opt.optlen = opt_len;
187662306a36Sopenharmony_ci	opt->opt.cipso = sizeof(struct iphdr);
187762306a36Sopenharmony_ci	kfree(buf);
187862306a36Sopenharmony_ci	buf = NULL;
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	sk_inet = inet_sk(sk);
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	old = rcu_dereference_protected(sk_inet->inet_opt,
188362306a36Sopenharmony_ci					lockdep_sock_is_held(sk));
188462306a36Sopenharmony_ci	if (inet_test_bit(IS_ICSK, sk)) {
188562306a36Sopenharmony_ci		sk_conn = inet_csk(sk);
188662306a36Sopenharmony_ci		if (old)
188762306a36Sopenharmony_ci			sk_conn->icsk_ext_hdr_len -= old->opt.optlen;
188862306a36Sopenharmony_ci		sk_conn->icsk_ext_hdr_len += opt->opt.optlen;
188962306a36Sopenharmony_ci		sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
189062306a36Sopenharmony_ci	}
189162306a36Sopenharmony_ci	rcu_assign_pointer(sk_inet->inet_opt, opt);
189262306a36Sopenharmony_ci	if (old)
189362306a36Sopenharmony_ci		kfree_rcu(old, rcu);
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	return 0;
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_cisocket_setattr_failure:
189862306a36Sopenharmony_ci	kfree(buf);
189962306a36Sopenharmony_ci	kfree(opt);
190062306a36Sopenharmony_ci	return ret_val;
190162306a36Sopenharmony_ci}
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci/**
190462306a36Sopenharmony_ci * cipso_v4_req_setattr - Add a CIPSO option to a connection request socket
190562306a36Sopenharmony_ci * @req: the connection request socket
190662306a36Sopenharmony_ci * @doi_def: the CIPSO DOI to use
190762306a36Sopenharmony_ci * @secattr: the specific security attributes of the socket
190862306a36Sopenharmony_ci *
190962306a36Sopenharmony_ci * Description:
191062306a36Sopenharmony_ci * Set the CIPSO option on the given socket using the DOI definition and
191162306a36Sopenharmony_ci * security attributes passed to the function.  Returns zero on success and
191262306a36Sopenharmony_ci * negative values on failure.
191362306a36Sopenharmony_ci *
191462306a36Sopenharmony_ci */
191562306a36Sopenharmony_ciint cipso_v4_req_setattr(struct request_sock *req,
191662306a36Sopenharmony_ci			 const struct cipso_v4_doi *doi_def,
191762306a36Sopenharmony_ci			 const struct netlbl_lsm_secattr *secattr)
191862306a36Sopenharmony_ci{
191962306a36Sopenharmony_ci	int ret_val = -EPERM;
192062306a36Sopenharmony_ci	unsigned char *buf = NULL;
192162306a36Sopenharmony_ci	u32 buf_len;
192262306a36Sopenharmony_ci	u32 opt_len;
192362306a36Sopenharmony_ci	struct ip_options_rcu *opt = NULL;
192462306a36Sopenharmony_ci	struct inet_request_sock *req_inet;
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	/* We allocate the maximum CIPSO option size here so we are probably
192762306a36Sopenharmony_ci	 * being a little wasteful, but it makes our life _much_ easier later
192862306a36Sopenharmony_ci	 * on and after all we are only talking about 40 bytes. */
192962306a36Sopenharmony_ci	buf_len = CIPSO_V4_OPT_LEN_MAX;
193062306a36Sopenharmony_ci	buf = kmalloc(buf_len, GFP_ATOMIC);
193162306a36Sopenharmony_ci	if (!buf) {
193262306a36Sopenharmony_ci		ret_val = -ENOMEM;
193362306a36Sopenharmony_ci		goto req_setattr_failure;
193462306a36Sopenharmony_ci	}
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
193762306a36Sopenharmony_ci	if (ret_val < 0)
193862306a36Sopenharmony_ci		goto req_setattr_failure;
193962306a36Sopenharmony_ci	buf_len = ret_val;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	/* We can't use ip_options_get() directly because it makes a call to
194262306a36Sopenharmony_ci	 * ip_options_get_alloc() which allocates memory with GFP_KERNEL and
194362306a36Sopenharmony_ci	 * we won't always have CAP_NET_RAW even though we _always_ want to
194462306a36Sopenharmony_ci	 * set the IPOPT_CIPSO option. */
194562306a36Sopenharmony_ci	opt_len = (buf_len + 3) & ~3;
194662306a36Sopenharmony_ci	opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC);
194762306a36Sopenharmony_ci	if (!opt) {
194862306a36Sopenharmony_ci		ret_val = -ENOMEM;
194962306a36Sopenharmony_ci		goto req_setattr_failure;
195062306a36Sopenharmony_ci	}
195162306a36Sopenharmony_ci	memcpy(opt->opt.__data, buf, buf_len);
195262306a36Sopenharmony_ci	opt->opt.optlen = opt_len;
195362306a36Sopenharmony_ci	opt->opt.cipso = sizeof(struct iphdr);
195462306a36Sopenharmony_ci	kfree(buf);
195562306a36Sopenharmony_ci	buf = NULL;
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	req_inet = inet_rsk(req);
195862306a36Sopenharmony_ci	opt = xchg((__force struct ip_options_rcu **)&req_inet->ireq_opt, opt);
195962306a36Sopenharmony_ci	if (opt)
196062306a36Sopenharmony_ci		kfree_rcu(opt, rcu);
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	return 0;
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_cireq_setattr_failure:
196562306a36Sopenharmony_ci	kfree(buf);
196662306a36Sopenharmony_ci	kfree(opt);
196762306a36Sopenharmony_ci	return ret_val;
196862306a36Sopenharmony_ci}
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci/**
197162306a36Sopenharmony_ci * cipso_v4_delopt - Delete the CIPSO option from a set of IP options
197262306a36Sopenharmony_ci * @opt_ptr: IP option pointer
197362306a36Sopenharmony_ci *
197462306a36Sopenharmony_ci * Description:
197562306a36Sopenharmony_ci * Deletes the CIPSO IP option from a set of IP options and makes the necessary
197662306a36Sopenharmony_ci * adjustments to the IP option structure.  Returns zero on success, negative
197762306a36Sopenharmony_ci * values on failure.
197862306a36Sopenharmony_ci *
197962306a36Sopenharmony_ci */
198062306a36Sopenharmony_cistatic int cipso_v4_delopt(struct ip_options_rcu __rcu **opt_ptr)
198162306a36Sopenharmony_ci{
198262306a36Sopenharmony_ci	struct ip_options_rcu *opt = rcu_dereference_protected(*opt_ptr, 1);
198362306a36Sopenharmony_ci	int hdr_delta = 0;
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci	if (!opt || opt->opt.cipso == 0)
198662306a36Sopenharmony_ci		return 0;
198762306a36Sopenharmony_ci	if (opt->opt.srr || opt->opt.rr || opt->opt.ts || opt->opt.router_alert) {
198862306a36Sopenharmony_ci		u8 cipso_len;
198962306a36Sopenharmony_ci		u8 cipso_off;
199062306a36Sopenharmony_ci		unsigned char *cipso_ptr;
199162306a36Sopenharmony_ci		int iter;
199262306a36Sopenharmony_ci		int optlen_new;
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci		cipso_off = opt->opt.cipso - sizeof(struct iphdr);
199562306a36Sopenharmony_ci		cipso_ptr = &opt->opt.__data[cipso_off];
199662306a36Sopenharmony_ci		cipso_len = cipso_ptr[1];
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci		if (opt->opt.srr > opt->opt.cipso)
199962306a36Sopenharmony_ci			opt->opt.srr -= cipso_len;
200062306a36Sopenharmony_ci		if (opt->opt.rr > opt->opt.cipso)
200162306a36Sopenharmony_ci			opt->opt.rr -= cipso_len;
200262306a36Sopenharmony_ci		if (opt->opt.ts > opt->opt.cipso)
200362306a36Sopenharmony_ci			opt->opt.ts -= cipso_len;
200462306a36Sopenharmony_ci		if (opt->opt.router_alert > opt->opt.cipso)
200562306a36Sopenharmony_ci			opt->opt.router_alert -= cipso_len;
200662306a36Sopenharmony_ci		opt->opt.cipso = 0;
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci		memmove(cipso_ptr, cipso_ptr + cipso_len,
200962306a36Sopenharmony_ci			opt->opt.optlen - cipso_off - cipso_len);
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci		/* determining the new total option length is tricky because of
201262306a36Sopenharmony_ci		 * the padding necessary, the only thing i can think to do at
201362306a36Sopenharmony_ci		 * this point is walk the options one-by-one, skipping the
201462306a36Sopenharmony_ci		 * padding at the end to determine the actual option size and
201562306a36Sopenharmony_ci		 * from there we can determine the new total option length */
201662306a36Sopenharmony_ci		iter = 0;
201762306a36Sopenharmony_ci		optlen_new = 0;
201862306a36Sopenharmony_ci		while (iter < opt->opt.optlen)
201962306a36Sopenharmony_ci			if (opt->opt.__data[iter] != IPOPT_NOP) {
202062306a36Sopenharmony_ci				iter += opt->opt.__data[iter + 1];
202162306a36Sopenharmony_ci				optlen_new = iter;
202262306a36Sopenharmony_ci			} else
202362306a36Sopenharmony_ci				iter++;
202462306a36Sopenharmony_ci		hdr_delta = opt->opt.optlen;
202562306a36Sopenharmony_ci		opt->opt.optlen = (optlen_new + 3) & ~3;
202662306a36Sopenharmony_ci		hdr_delta -= opt->opt.optlen;
202762306a36Sopenharmony_ci	} else {
202862306a36Sopenharmony_ci		/* only the cipso option was present on the socket so we can
202962306a36Sopenharmony_ci		 * remove the entire option struct */
203062306a36Sopenharmony_ci		*opt_ptr = NULL;
203162306a36Sopenharmony_ci		hdr_delta = opt->opt.optlen;
203262306a36Sopenharmony_ci		kfree_rcu(opt, rcu);
203362306a36Sopenharmony_ci	}
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	return hdr_delta;
203662306a36Sopenharmony_ci}
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci/**
203962306a36Sopenharmony_ci * cipso_v4_sock_delattr - Delete the CIPSO option from a socket
204062306a36Sopenharmony_ci * @sk: the socket
204162306a36Sopenharmony_ci *
204262306a36Sopenharmony_ci * Description:
204362306a36Sopenharmony_ci * Removes the CIPSO option from a socket, if present.
204462306a36Sopenharmony_ci *
204562306a36Sopenharmony_ci */
204662306a36Sopenharmony_civoid cipso_v4_sock_delattr(struct sock *sk)
204762306a36Sopenharmony_ci{
204862306a36Sopenharmony_ci	struct inet_sock *sk_inet;
204962306a36Sopenharmony_ci	int hdr_delta;
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	sk_inet = inet_sk(sk);
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	hdr_delta = cipso_v4_delopt(&sk_inet->inet_opt);
205462306a36Sopenharmony_ci	if (inet_test_bit(IS_ICSK, sk) && hdr_delta > 0) {
205562306a36Sopenharmony_ci		struct inet_connection_sock *sk_conn = inet_csk(sk);
205662306a36Sopenharmony_ci		sk_conn->icsk_ext_hdr_len -= hdr_delta;
205762306a36Sopenharmony_ci		sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
205862306a36Sopenharmony_ci	}
205962306a36Sopenharmony_ci}
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci/**
206262306a36Sopenharmony_ci * cipso_v4_req_delattr - Delete the CIPSO option from a request socket
206362306a36Sopenharmony_ci * @req: the request socket
206462306a36Sopenharmony_ci *
206562306a36Sopenharmony_ci * Description:
206662306a36Sopenharmony_ci * Removes the CIPSO option from a request socket, if present.
206762306a36Sopenharmony_ci *
206862306a36Sopenharmony_ci */
206962306a36Sopenharmony_civoid cipso_v4_req_delattr(struct request_sock *req)
207062306a36Sopenharmony_ci{
207162306a36Sopenharmony_ci	cipso_v4_delopt(&inet_rsk(req)->ireq_opt);
207262306a36Sopenharmony_ci}
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci/**
207562306a36Sopenharmony_ci * cipso_v4_getattr - Helper function for the cipso_v4_*_getattr functions
207662306a36Sopenharmony_ci * @cipso: the CIPSO v4 option
207762306a36Sopenharmony_ci * @secattr: the security attributes
207862306a36Sopenharmony_ci *
207962306a36Sopenharmony_ci * Description:
208062306a36Sopenharmony_ci * Inspect @cipso and return the security attributes in @secattr.  Returns zero
208162306a36Sopenharmony_ci * on success and negative values on failure.
208262306a36Sopenharmony_ci *
208362306a36Sopenharmony_ci */
208462306a36Sopenharmony_ciint cipso_v4_getattr(const unsigned char *cipso,
208562306a36Sopenharmony_ci		     struct netlbl_lsm_secattr *secattr)
208662306a36Sopenharmony_ci{
208762306a36Sopenharmony_ci	int ret_val = -ENOMSG;
208862306a36Sopenharmony_ci	u32 doi;
208962306a36Sopenharmony_ci	struct cipso_v4_doi *doi_def;
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	if (cipso_v4_cache_check(cipso, cipso[1], secattr) == 0)
209262306a36Sopenharmony_ci		return 0;
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	doi = get_unaligned_be32(&cipso[2]);
209562306a36Sopenharmony_ci	rcu_read_lock();
209662306a36Sopenharmony_ci	doi_def = cipso_v4_doi_search(doi);
209762306a36Sopenharmony_ci	if (!doi_def)
209862306a36Sopenharmony_ci		goto getattr_return;
209962306a36Sopenharmony_ci	/* XXX - This code assumes only one tag per CIPSO option which isn't
210062306a36Sopenharmony_ci	 * really a good assumption to make but since we only support the MAC
210162306a36Sopenharmony_ci	 * tags right now it is a safe assumption. */
210262306a36Sopenharmony_ci	switch (cipso[6]) {
210362306a36Sopenharmony_ci	case CIPSO_V4_TAG_RBITMAP:
210462306a36Sopenharmony_ci		ret_val = cipso_v4_parsetag_rbm(doi_def, &cipso[6], secattr);
210562306a36Sopenharmony_ci		break;
210662306a36Sopenharmony_ci	case CIPSO_V4_TAG_ENUM:
210762306a36Sopenharmony_ci		ret_val = cipso_v4_parsetag_enum(doi_def, &cipso[6], secattr);
210862306a36Sopenharmony_ci		break;
210962306a36Sopenharmony_ci	case CIPSO_V4_TAG_RANGE:
211062306a36Sopenharmony_ci		ret_val = cipso_v4_parsetag_rng(doi_def, &cipso[6], secattr);
211162306a36Sopenharmony_ci		break;
211262306a36Sopenharmony_ci	case CIPSO_V4_TAG_LOCAL:
211362306a36Sopenharmony_ci		ret_val = cipso_v4_parsetag_loc(doi_def, &cipso[6], secattr);
211462306a36Sopenharmony_ci		break;
211562306a36Sopenharmony_ci	}
211662306a36Sopenharmony_ci	if (ret_val == 0)
211762306a36Sopenharmony_ci		secattr->type = NETLBL_NLTYPE_CIPSOV4;
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_cigetattr_return:
212062306a36Sopenharmony_ci	rcu_read_unlock();
212162306a36Sopenharmony_ci	return ret_val;
212262306a36Sopenharmony_ci}
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci/**
212562306a36Sopenharmony_ci * cipso_v4_sock_getattr - Get the security attributes from a sock
212662306a36Sopenharmony_ci * @sk: the sock
212762306a36Sopenharmony_ci * @secattr: the security attributes
212862306a36Sopenharmony_ci *
212962306a36Sopenharmony_ci * Description:
213062306a36Sopenharmony_ci * Query @sk to see if there is a CIPSO option attached to the sock and if
213162306a36Sopenharmony_ci * there is return the CIPSO security attributes in @secattr.  This function
213262306a36Sopenharmony_ci * requires that @sk be locked, or privately held, but it does not do any
213362306a36Sopenharmony_ci * locking itself.  Returns zero on success and negative values on failure.
213462306a36Sopenharmony_ci *
213562306a36Sopenharmony_ci */
213662306a36Sopenharmony_ciint cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr)
213762306a36Sopenharmony_ci{
213862306a36Sopenharmony_ci	struct ip_options_rcu *opt;
213962306a36Sopenharmony_ci	int res = -ENOMSG;
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	rcu_read_lock();
214262306a36Sopenharmony_ci	opt = rcu_dereference(inet_sk(sk)->inet_opt);
214362306a36Sopenharmony_ci	if (opt && opt->opt.cipso)
214462306a36Sopenharmony_ci		res = cipso_v4_getattr(opt->opt.__data +
214562306a36Sopenharmony_ci						opt->opt.cipso -
214662306a36Sopenharmony_ci						sizeof(struct iphdr),
214762306a36Sopenharmony_ci				       secattr);
214862306a36Sopenharmony_ci	rcu_read_unlock();
214962306a36Sopenharmony_ci	return res;
215062306a36Sopenharmony_ci}
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci/**
215362306a36Sopenharmony_ci * cipso_v4_skbuff_setattr - Set the CIPSO option on a packet
215462306a36Sopenharmony_ci * @skb: the packet
215562306a36Sopenharmony_ci * @doi_def: the DOI structure
215662306a36Sopenharmony_ci * @secattr: the security attributes
215762306a36Sopenharmony_ci *
215862306a36Sopenharmony_ci * Description:
215962306a36Sopenharmony_ci * Set the CIPSO option on the given packet based on the security attributes.
216062306a36Sopenharmony_ci * Returns a pointer to the IP header on success and NULL on failure.
216162306a36Sopenharmony_ci *
216262306a36Sopenharmony_ci */
216362306a36Sopenharmony_ciint cipso_v4_skbuff_setattr(struct sk_buff *skb,
216462306a36Sopenharmony_ci			    const struct cipso_v4_doi *doi_def,
216562306a36Sopenharmony_ci			    const struct netlbl_lsm_secattr *secattr)
216662306a36Sopenharmony_ci{
216762306a36Sopenharmony_ci	int ret_val;
216862306a36Sopenharmony_ci	struct iphdr *iph;
216962306a36Sopenharmony_ci	struct ip_options *opt = &IPCB(skb)->opt;
217062306a36Sopenharmony_ci	unsigned char buf[CIPSO_V4_OPT_LEN_MAX];
217162306a36Sopenharmony_ci	u32 buf_len = CIPSO_V4_OPT_LEN_MAX;
217262306a36Sopenharmony_ci	u32 opt_len;
217362306a36Sopenharmony_ci	int len_delta;
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
217662306a36Sopenharmony_ci	if (ret_val < 0)
217762306a36Sopenharmony_ci		return ret_val;
217862306a36Sopenharmony_ci	buf_len = ret_val;
217962306a36Sopenharmony_ci	opt_len = (buf_len + 3) & ~3;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	/* we overwrite any existing options to ensure that we have enough
218262306a36Sopenharmony_ci	 * room for the CIPSO option, the reason is that we _need_ to guarantee
218362306a36Sopenharmony_ci	 * that the security label is applied to the packet - we do the same
218462306a36Sopenharmony_ci	 * thing when using the socket options and it hasn't caused a problem,
218562306a36Sopenharmony_ci	 * if we need to we can always revisit this choice later */
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	len_delta = opt_len - opt->optlen;
218862306a36Sopenharmony_ci	/* if we don't ensure enough headroom we could panic on the skb_push()
218962306a36Sopenharmony_ci	 * call below so make sure we have enough, we are also "mangling" the
219062306a36Sopenharmony_ci	 * packet so we should probably do a copy-on-write call anyway */
219162306a36Sopenharmony_ci	ret_val = skb_cow(skb, skb_headroom(skb) + len_delta);
219262306a36Sopenharmony_ci	if (ret_val < 0)
219362306a36Sopenharmony_ci		return ret_val;
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	if (len_delta > 0) {
219662306a36Sopenharmony_ci		/* we assume that the header + opt->optlen have already been
219762306a36Sopenharmony_ci		 * "pushed" in ip_options_build() or similar */
219862306a36Sopenharmony_ci		iph = ip_hdr(skb);
219962306a36Sopenharmony_ci		skb_push(skb, len_delta);
220062306a36Sopenharmony_ci		memmove((char *)iph - len_delta, iph, iph->ihl << 2);
220162306a36Sopenharmony_ci		skb_reset_network_header(skb);
220262306a36Sopenharmony_ci		iph = ip_hdr(skb);
220362306a36Sopenharmony_ci	} else if (len_delta < 0) {
220462306a36Sopenharmony_ci		iph = ip_hdr(skb);
220562306a36Sopenharmony_ci		memset(iph + 1, IPOPT_NOP, opt->optlen);
220662306a36Sopenharmony_ci	} else
220762306a36Sopenharmony_ci		iph = ip_hdr(skb);
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	if (opt->optlen > 0)
221062306a36Sopenharmony_ci		memset(opt, 0, sizeof(*opt));
221162306a36Sopenharmony_ci	opt->optlen = opt_len;
221262306a36Sopenharmony_ci	opt->cipso = sizeof(struct iphdr);
221362306a36Sopenharmony_ci	opt->is_changed = 1;
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci	/* we have to do the following because we are being called from a
221662306a36Sopenharmony_ci	 * netfilter hook which means the packet already has had the header
221762306a36Sopenharmony_ci	 * fields populated and the checksum calculated - yes this means we
221862306a36Sopenharmony_ci	 * are doing more work than needed but we do it to keep the core
221962306a36Sopenharmony_ci	 * stack clean and tidy */
222062306a36Sopenharmony_ci	memcpy(iph + 1, buf, buf_len);
222162306a36Sopenharmony_ci	if (opt_len > buf_len)
222262306a36Sopenharmony_ci		memset((char *)(iph + 1) + buf_len, 0, opt_len - buf_len);
222362306a36Sopenharmony_ci	if (len_delta != 0) {
222462306a36Sopenharmony_ci		iph->ihl = 5 + (opt_len >> 2);
222562306a36Sopenharmony_ci		iph_set_totlen(iph, skb->len);
222662306a36Sopenharmony_ci	}
222762306a36Sopenharmony_ci	ip_send_check(iph);
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci	return 0;
223062306a36Sopenharmony_ci}
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci/**
223362306a36Sopenharmony_ci * cipso_v4_skbuff_delattr - Delete any CIPSO options from a packet
223462306a36Sopenharmony_ci * @skb: the packet
223562306a36Sopenharmony_ci *
223662306a36Sopenharmony_ci * Description:
223762306a36Sopenharmony_ci * Removes any and all CIPSO options from the given packet.  Returns zero on
223862306a36Sopenharmony_ci * success, negative values on failure.
223962306a36Sopenharmony_ci *
224062306a36Sopenharmony_ci */
224162306a36Sopenharmony_ciint cipso_v4_skbuff_delattr(struct sk_buff *skb)
224262306a36Sopenharmony_ci{
224362306a36Sopenharmony_ci	int ret_val;
224462306a36Sopenharmony_ci	struct iphdr *iph;
224562306a36Sopenharmony_ci	struct ip_options *opt = &IPCB(skb)->opt;
224662306a36Sopenharmony_ci	unsigned char *cipso_ptr;
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	if (opt->cipso == 0)
224962306a36Sopenharmony_ci		return 0;
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	/* since we are changing the packet we should make a copy */
225262306a36Sopenharmony_ci	ret_val = skb_cow(skb, skb_headroom(skb));
225362306a36Sopenharmony_ci	if (ret_val < 0)
225462306a36Sopenharmony_ci		return ret_val;
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	/* the easiest thing to do is just replace the cipso option with noop
225762306a36Sopenharmony_ci	 * options since we don't change the size of the packet, although we
225862306a36Sopenharmony_ci	 * still need to recalculate the checksum */
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci	iph = ip_hdr(skb);
226162306a36Sopenharmony_ci	cipso_ptr = (unsigned char *)iph + opt->cipso;
226262306a36Sopenharmony_ci	memset(cipso_ptr, IPOPT_NOOP, cipso_ptr[1]);
226362306a36Sopenharmony_ci	opt->cipso = 0;
226462306a36Sopenharmony_ci	opt->is_changed = 1;
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	ip_send_check(iph);
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	return 0;
226962306a36Sopenharmony_ci}
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci/*
227262306a36Sopenharmony_ci * Setup Functions
227362306a36Sopenharmony_ci */
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci/**
227662306a36Sopenharmony_ci * cipso_v4_init - Initialize the CIPSO module
227762306a36Sopenharmony_ci *
227862306a36Sopenharmony_ci * Description:
227962306a36Sopenharmony_ci * Initialize the CIPSO module and prepare it for use.  Returns zero on success
228062306a36Sopenharmony_ci * and negative values on failure.
228162306a36Sopenharmony_ci *
228262306a36Sopenharmony_ci */
228362306a36Sopenharmony_cistatic int __init cipso_v4_init(void)
228462306a36Sopenharmony_ci{
228562306a36Sopenharmony_ci	int ret_val;
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci	ret_val = cipso_v4_cache_init();
228862306a36Sopenharmony_ci	if (ret_val != 0)
228962306a36Sopenharmony_ci		panic("Failed to initialize the CIPSO/IPv4 cache (%d)\n",
229062306a36Sopenharmony_ci		      ret_val);
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	return 0;
229362306a36Sopenharmony_ci}
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_cisubsys_initcall(cipso_v4_init);
2296