xref: /linux/net/ipv4/cipso_ipv4.c (revision 50e5d35ce2c4190cead13a091ea1ceab47d29cc2)
1446fda4fSPaul Moore /*
2446fda4fSPaul Moore  * CIPSO - Commercial IP Security Option
3446fda4fSPaul Moore  *
4446fda4fSPaul Moore  * This is an implementation of the CIPSO 2.2 protocol as specified in
5446fda4fSPaul Moore  * draft-ietf-cipso-ipsecurity-01.txt with additional tag types as found in
6446fda4fSPaul Moore  * FIPS-188, copies of both documents can be found in the Documentation
7446fda4fSPaul Moore  * directory.  While CIPSO never became a full IETF RFC standard many vendors
8446fda4fSPaul Moore  * have chosen to adopt the protocol and over the years it has become a
9446fda4fSPaul Moore  * de-facto standard for labeled networking.
10446fda4fSPaul Moore  *
11446fda4fSPaul Moore  * Author: Paul Moore <paul.moore@hp.com>
12446fda4fSPaul Moore  *
13446fda4fSPaul Moore  */
14446fda4fSPaul Moore 
15446fda4fSPaul Moore /*
16446fda4fSPaul Moore  * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
17446fda4fSPaul Moore  *
18446fda4fSPaul Moore  * This program is free software;  you can redistribute it and/or modify
19446fda4fSPaul Moore  * it under the terms of the GNU General Public License as published by
20446fda4fSPaul Moore  * the Free Software Foundation; either version 2 of the License, or
21446fda4fSPaul Moore  * (at your option) any later version.
22446fda4fSPaul Moore  *
23446fda4fSPaul Moore  * This program is distributed in the hope that it will be useful,
24446fda4fSPaul Moore  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
25446fda4fSPaul Moore  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
26446fda4fSPaul Moore  * the GNU General Public License for more details.
27446fda4fSPaul Moore  *
28446fda4fSPaul Moore  * You should have received a copy of the GNU General Public License
29446fda4fSPaul Moore  * along with this program;  if not, write to the Free Software
30446fda4fSPaul Moore  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31446fda4fSPaul Moore  *
32446fda4fSPaul Moore  */
33446fda4fSPaul Moore 
34446fda4fSPaul Moore #include <linux/init.h>
35446fda4fSPaul Moore #include <linux/types.h>
36446fda4fSPaul Moore #include <linux/rcupdate.h>
37446fda4fSPaul Moore #include <linux/list.h>
38446fda4fSPaul Moore #include <linux/spinlock.h>
39446fda4fSPaul Moore #include <linux/string.h>
40446fda4fSPaul Moore #include <linux/jhash.h>
41446fda4fSPaul Moore #include <net/ip.h>
42446fda4fSPaul Moore #include <net/icmp.h>
43446fda4fSPaul Moore #include <net/tcp.h>
44446fda4fSPaul Moore #include <net/netlabel.h>
45446fda4fSPaul Moore #include <net/cipso_ipv4.h>
46ffb733c6Spaul.moore@hp.com #include <asm/atomic.h>
47446fda4fSPaul Moore #include <asm/bug.h>
48*50e5d35cSPaul Moore #include <asm/unaligned.h>
49446fda4fSPaul Moore 
50446fda4fSPaul Moore struct cipso_v4_domhsh_entry {
51446fda4fSPaul Moore 	char *domain;
52446fda4fSPaul Moore 	u32 valid;
53446fda4fSPaul Moore 	struct list_head list;
54446fda4fSPaul Moore 	struct rcu_head rcu;
55446fda4fSPaul Moore };
56446fda4fSPaul Moore 
57446fda4fSPaul Moore /* List of available DOI definitions */
58446fda4fSPaul Moore /* XXX - Updates should be minimal so having a single lock for the
59446fda4fSPaul Moore  * cipso_v4_doi_list and the cipso_v4_doi_list->dom_list should be
60446fda4fSPaul Moore  * okay. */
61446fda4fSPaul Moore /* XXX - This currently assumes a minimal number of different DOIs in use,
62446fda4fSPaul Moore  * if in practice there are a lot of different DOIs this list should
63446fda4fSPaul Moore  * probably be turned into a hash table or something similar so we
64446fda4fSPaul Moore  * can do quick lookups. */
658ce11e6aSAdrian Bunk static DEFINE_SPINLOCK(cipso_v4_doi_list_lock);
66446fda4fSPaul Moore static struct list_head cipso_v4_doi_list = LIST_HEAD_INIT(cipso_v4_doi_list);
67446fda4fSPaul Moore 
68446fda4fSPaul Moore /* Label mapping cache */
69446fda4fSPaul Moore int cipso_v4_cache_enabled = 1;
70446fda4fSPaul Moore int cipso_v4_cache_bucketsize = 10;
71446fda4fSPaul Moore #define CIPSO_V4_CACHE_BUCKETBITS     7
72446fda4fSPaul Moore #define CIPSO_V4_CACHE_BUCKETS        (1 << CIPSO_V4_CACHE_BUCKETBITS)
73446fda4fSPaul Moore #define CIPSO_V4_CACHE_REORDERLIMIT   10
74446fda4fSPaul Moore struct cipso_v4_map_cache_bkt {
75446fda4fSPaul Moore 	spinlock_t lock;
76446fda4fSPaul Moore 	u32 size;
77446fda4fSPaul Moore 	struct list_head list;
78446fda4fSPaul Moore };
79446fda4fSPaul Moore struct cipso_v4_map_cache_entry {
80446fda4fSPaul Moore 	u32 hash;
81446fda4fSPaul Moore 	unsigned char *key;
82446fda4fSPaul Moore 	size_t key_len;
83446fda4fSPaul Moore 
84ffb733c6Spaul.moore@hp.com 	struct netlbl_lsm_cache *lsm_data;
85446fda4fSPaul Moore 
86446fda4fSPaul Moore 	u32 activity;
87446fda4fSPaul Moore 	struct list_head list;
88446fda4fSPaul Moore };
89446fda4fSPaul Moore static struct cipso_v4_map_cache_bkt *cipso_v4_cache = NULL;
90446fda4fSPaul Moore 
91446fda4fSPaul Moore /* Restricted bitmap (tag #1) flags */
92446fda4fSPaul Moore int cipso_v4_rbm_optfmt = 0;
93446fda4fSPaul Moore int cipso_v4_rbm_strictvalid = 1;
94446fda4fSPaul Moore 
95446fda4fSPaul Moore /*
96f998e8cbSPaul Moore  * Protocol Constants
97f998e8cbSPaul Moore  */
98f998e8cbSPaul Moore 
99f998e8cbSPaul Moore /* Maximum size of the CIPSO IP option, derived from the fact that the maximum
100f998e8cbSPaul Moore  * IPv4 header size is 60 bytes and the base IPv4 header is 20 bytes long. */
101f998e8cbSPaul Moore #define CIPSO_V4_OPT_LEN_MAX          40
102f998e8cbSPaul Moore 
103f998e8cbSPaul Moore /* Length of the base CIPSO option, this includes the option type (1 byte), the
104f998e8cbSPaul Moore  * option length (1 byte), and the DOI (4 bytes). */
105f998e8cbSPaul Moore #define CIPSO_V4_HDR_LEN              6
106f998e8cbSPaul Moore 
107f998e8cbSPaul Moore /* Base length of the restrictive category bitmap tag (tag #1). */
108f998e8cbSPaul Moore #define CIPSO_V4_TAG_RBM_BLEN         4
109f998e8cbSPaul Moore 
110f998e8cbSPaul Moore /* Base length of the enumerated category tag (tag #2). */
111f998e8cbSPaul Moore #define CIPSO_V4_TAG_ENUM_BLEN        4
112f998e8cbSPaul Moore 
113f998e8cbSPaul Moore /* Base length of the ranged categories bitmap tag (tag #5). */
114f998e8cbSPaul Moore #define CIPSO_V4_TAG_RNG_BLEN         4
115f998e8cbSPaul Moore /* The maximum number of category ranges permitted in the ranged category tag
116f998e8cbSPaul Moore  * (tag #5).  You may note that the IETF draft states that the maximum number
117f998e8cbSPaul Moore  * of category ranges is 7, but if the low end of the last category range is
118f998e8cbSPaul Moore  * zero then it is possibile to fit 8 category ranges because the zero should
119f998e8cbSPaul Moore  * be omitted. */
120f998e8cbSPaul Moore #define CIPSO_V4_TAG_RNG_CAT_MAX      8
121f998e8cbSPaul Moore 
122f998e8cbSPaul Moore /*
123446fda4fSPaul Moore  * Helper Functions
124446fda4fSPaul Moore  */
125446fda4fSPaul Moore 
126446fda4fSPaul Moore /**
127446fda4fSPaul Moore  * cipso_v4_bitmap_walk - Walk a bitmap looking for a bit
128446fda4fSPaul Moore  * @bitmap: the bitmap
129446fda4fSPaul Moore  * @bitmap_len: length in bits
130446fda4fSPaul Moore  * @offset: starting offset
131446fda4fSPaul Moore  * @state: if non-zero, look for a set (1) bit else look for a cleared (0) bit
132446fda4fSPaul Moore  *
133446fda4fSPaul Moore  * Description:
134446fda4fSPaul Moore  * Starting at @offset, walk the bitmap from left to right until either the
135446fda4fSPaul Moore  * desired bit is found or we reach the end.  Return the bit offset, -1 if
136446fda4fSPaul Moore  * not found, or -2 if error.
137446fda4fSPaul Moore  */
138446fda4fSPaul Moore static int cipso_v4_bitmap_walk(const unsigned char *bitmap,
139446fda4fSPaul Moore 				u32 bitmap_len,
140446fda4fSPaul Moore 				u32 offset,
141446fda4fSPaul Moore 				u8 state)
142446fda4fSPaul Moore {
143446fda4fSPaul Moore 	u32 bit_spot;
144446fda4fSPaul Moore 	u32 byte_offset;
145446fda4fSPaul Moore 	unsigned char bitmask;
146446fda4fSPaul Moore 	unsigned char byte;
147446fda4fSPaul Moore 
148446fda4fSPaul Moore 	/* gcc always rounds to zero when doing integer division */
149446fda4fSPaul Moore 	byte_offset = offset / 8;
150446fda4fSPaul Moore 	byte = bitmap[byte_offset];
151446fda4fSPaul Moore 	bit_spot = offset;
152446fda4fSPaul Moore 	bitmask = 0x80 >> (offset % 8);
153446fda4fSPaul Moore 
154446fda4fSPaul Moore 	while (bit_spot < bitmap_len) {
155446fda4fSPaul Moore 		if ((state && (byte & bitmask) == bitmask) ||
156446fda4fSPaul Moore 		    (state == 0 && (byte & bitmask) == 0))
157446fda4fSPaul Moore 			return bit_spot;
158446fda4fSPaul Moore 
159446fda4fSPaul Moore 		bit_spot++;
160446fda4fSPaul Moore 		bitmask >>= 1;
161446fda4fSPaul Moore 		if (bitmask == 0) {
162446fda4fSPaul Moore 			byte = bitmap[++byte_offset];
163446fda4fSPaul Moore 			bitmask = 0x80;
164446fda4fSPaul Moore 		}
165446fda4fSPaul Moore 	}
166446fda4fSPaul Moore 
167446fda4fSPaul Moore 	return -1;
168446fda4fSPaul Moore }
169446fda4fSPaul Moore 
170446fda4fSPaul Moore /**
171446fda4fSPaul Moore  * cipso_v4_bitmap_setbit - Sets a single bit in a bitmap
172446fda4fSPaul Moore  * @bitmap: the bitmap
173446fda4fSPaul Moore  * @bit: the bit
174446fda4fSPaul Moore  * @state: if non-zero, set the bit (1) else clear the bit (0)
175446fda4fSPaul Moore  *
176446fda4fSPaul Moore  * Description:
177446fda4fSPaul Moore  * Set a single bit in the bitmask.  Returns zero on success, negative values
178446fda4fSPaul Moore  * on error.
179446fda4fSPaul Moore  */
180446fda4fSPaul Moore static void cipso_v4_bitmap_setbit(unsigned char *bitmap,
181446fda4fSPaul Moore 				   u32 bit,
182446fda4fSPaul Moore 				   u8 state)
183446fda4fSPaul Moore {
184446fda4fSPaul Moore 	u32 byte_spot;
185446fda4fSPaul Moore 	u8 bitmask;
186446fda4fSPaul Moore 
187446fda4fSPaul Moore 	/* gcc always rounds to zero when doing integer division */
188446fda4fSPaul Moore 	byte_spot = bit / 8;
189446fda4fSPaul Moore 	bitmask = 0x80 >> (bit % 8);
190446fda4fSPaul Moore 	if (state)
191446fda4fSPaul Moore 		bitmap[byte_spot] |= bitmask;
192446fda4fSPaul Moore 	else
193446fda4fSPaul Moore 		bitmap[byte_spot] &= ~bitmask;
194446fda4fSPaul Moore }
195446fda4fSPaul Moore 
196446fda4fSPaul Moore /**
197446fda4fSPaul Moore  * cipso_v4_doi_domhsh_free - Frees a domain list entry
198446fda4fSPaul Moore  * @entry: the entry's RCU field
199446fda4fSPaul Moore  *
200446fda4fSPaul Moore  * Description:
201446fda4fSPaul Moore  * This function is designed to be used as a callback to the call_rcu()
202446fda4fSPaul Moore  * function so that the memory allocated to a domain list entry can be released
203446fda4fSPaul Moore  * safely.
204446fda4fSPaul Moore  *
205446fda4fSPaul Moore  */
206446fda4fSPaul Moore static void cipso_v4_doi_domhsh_free(struct rcu_head *entry)
207446fda4fSPaul Moore {
208446fda4fSPaul Moore 	struct cipso_v4_domhsh_entry *ptr;
209446fda4fSPaul Moore 
210446fda4fSPaul Moore 	ptr = container_of(entry, struct cipso_v4_domhsh_entry, rcu);
211446fda4fSPaul Moore 	kfree(ptr->domain);
212446fda4fSPaul Moore 	kfree(ptr);
213446fda4fSPaul Moore }
214446fda4fSPaul Moore 
215446fda4fSPaul Moore /**
216446fda4fSPaul Moore  * cipso_v4_cache_entry_free - Frees a cache entry
217446fda4fSPaul Moore  * @entry: the entry to free
218446fda4fSPaul Moore  *
219446fda4fSPaul Moore  * Description:
220ffb733c6Spaul.moore@hp.com  * This function frees the memory associated with a cache entry including the
221ffb733c6Spaul.moore@hp.com  * LSM cache data if there are no longer any users, i.e. reference count == 0.
222446fda4fSPaul Moore  *
223446fda4fSPaul Moore  */
224446fda4fSPaul Moore static void cipso_v4_cache_entry_free(struct cipso_v4_map_cache_entry *entry)
225446fda4fSPaul Moore {
226ffb733c6Spaul.moore@hp.com 	if (entry->lsm_data)
227ffb733c6Spaul.moore@hp.com 		netlbl_secattr_cache_free(entry->lsm_data);
228446fda4fSPaul Moore 	kfree(entry->key);
229446fda4fSPaul Moore 	kfree(entry);
230446fda4fSPaul Moore }
231446fda4fSPaul Moore 
232446fda4fSPaul Moore /**
233446fda4fSPaul Moore  * cipso_v4_map_cache_hash - Hashing function for the CIPSO cache
234446fda4fSPaul Moore  * @key: the hash key
235446fda4fSPaul Moore  * @key_len: the length of the key in bytes
236446fda4fSPaul Moore  *
237446fda4fSPaul Moore  * Description:
238446fda4fSPaul Moore  * The CIPSO tag hashing function.  Returns a 32-bit hash value.
239446fda4fSPaul Moore  *
240446fda4fSPaul Moore  */
241446fda4fSPaul Moore static u32 cipso_v4_map_cache_hash(const unsigned char *key, u32 key_len)
242446fda4fSPaul Moore {
243446fda4fSPaul Moore 	return jhash(key, key_len, 0);
244446fda4fSPaul Moore }
245446fda4fSPaul Moore 
246446fda4fSPaul Moore /*
247446fda4fSPaul Moore  * Label Mapping Cache Functions
248446fda4fSPaul Moore  */
249446fda4fSPaul Moore 
250446fda4fSPaul Moore /**
251446fda4fSPaul Moore  * cipso_v4_cache_init - Initialize the CIPSO cache
252446fda4fSPaul Moore  *
253446fda4fSPaul Moore  * Description:
254446fda4fSPaul Moore  * Initializes the CIPSO label mapping cache, this function should be called
255446fda4fSPaul Moore  * before any of the other functions defined in this file.  Returns zero on
256446fda4fSPaul Moore  * success, negative values on error.
257446fda4fSPaul Moore  *
258446fda4fSPaul Moore  */
259446fda4fSPaul Moore static int cipso_v4_cache_init(void)
260446fda4fSPaul Moore {
261446fda4fSPaul Moore 	u32 iter;
262446fda4fSPaul Moore 
263446fda4fSPaul Moore 	cipso_v4_cache = kcalloc(CIPSO_V4_CACHE_BUCKETS,
264446fda4fSPaul Moore 				 sizeof(struct cipso_v4_map_cache_bkt),
265446fda4fSPaul Moore 				 GFP_KERNEL);
266446fda4fSPaul Moore 	if (cipso_v4_cache == NULL)
267446fda4fSPaul Moore 		return -ENOMEM;
268446fda4fSPaul Moore 
269446fda4fSPaul Moore 	for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) {
270446fda4fSPaul Moore 		spin_lock_init(&cipso_v4_cache[iter].lock);
271446fda4fSPaul Moore 		cipso_v4_cache[iter].size = 0;
272446fda4fSPaul Moore 		INIT_LIST_HEAD(&cipso_v4_cache[iter].list);
273446fda4fSPaul Moore 	}
274446fda4fSPaul Moore 
275446fda4fSPaul Moore 	return 0;
276446fda4fSPaul Moore }
277446fda4fSPaul Moore 
278446fda4fSPaul Moore /**
279446fda4fSPaul Moore  * cipso_v4_cache_invalidate - Invalidates the current CIPSO cache
280446fda4fSPaul Moore  *
281446fda4fSPaul Moore  * Description:
282446fda4fSPaul Moore  * Invalidates and frees any entries in the CIPSO cache.  Returns zero on
283446fda4fSPaul Moore  * success and negative values on failure.
284446fda4fSPaul Moore  *
285446fda4fSPaul Moore  */
286446fda4fSPaul Moore void cipso_v4_cache_invalidate(void)
287446fda4fSPaul Moore {
288446fda4fSPaul Moore 	struct cipso_v4_map_cache_entry *entry, *tmp_entry;
289446fda4fSPaul Moore 	u32 iter;
290446fda4fSPaul Moore 
291446fda4fSPaul Moore 	for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) {
292609c92feSPaul Moore 		spin_lock_bh(&cipso_v4_cache[iter].lock);
293446fda4fSPaul Moore 		list_for_each_entry_safe(entry,
294446fda4fSPaul Moore 					 tmp_entry,
295446fda4fSPaul Moore 					 &cipso_v4_cache[iter].list, list) {
296446fda4fSPaul Moore 			list_del(&entry->list);
297446fda4fSPaul Moore 			cipso_v4_cache_entry_free(entry);
298446fda4fSPaul Moore 		}
299446fda4fSPaul Moore 		cipso_v4_cache[iter].size = 0;
300609c92feSPaul Moore 		spin_unlock_bh(&cipso_v4_cache[iter].lock);
301446fda4fSPaul Moore 	}
302446fda4fSPaul Moore 
303446fda4fSPaul Moore 	return;
304446fda4fSPaul Moore }
305446fda4fSPaul Moore 
306446fda4fSPaul Moore /**
307446fda4fSPaul Moore  * cipso_v4_cache_check - Check the CIPSO cache for a label mapping
308446fda4fSPaul Moore  * @key: the buffer to check
309446fda4fSPaul Moore  * @key_len: buffer length in bytes
310446fda4fSPaul Moore  * @secattr: the security attribute struct to use
311446fda4fSPaul Moore  *
312446fda4fSPaul Moore  * Description:
313446fda4fSPaul Moore  * This function checks the cache to see if a label mapping already exists for
314446fda4fSPaul Moore  * the given key.  If there is a match then the cache is adjusted and the
315446fda4fSPaul Moore  * @secattr struct is populated with the correct LSM security attributes.  The
316446fda4fSPaul Moore  * cache is adjusted in the following manner if the entry is not already the
317446fda4fSPaul Moore  * first in the cache bucket:
318446fda4fSPaul Moore  *
319446fda4fSPaul Moore  *  1. The cache entry's activity counter is incremented
320446fda4fSPaul Moore  *  2. The previous (higher ranking) entry's activity counter is decremented
321446fda4fSPaul Moore  *  3. If the difference between the two activity counters is geater than
322446fda4fSPaul Moore  *     CIPSO_V4_CACHE_REORDERLIMIT the two entries are swapped
323446fda4fSPaul Moore  *
324446fda4fSPaul Moore  * Returns zero on success, -ENOENT for a cache miss, and other negative values
325446fda4fSPaul Moore  * on error.
326446fda4fSPaul Moore  *
327446fda4fSPaul Moore  */
328446fda4fSPaul Moore static int cipso_v4_cache_check(const unsigned char *key,
329446fda4fSPaul Moore 				u32 key_len,
330446fda4fSPaul Moore 				struct netlbl_lsm_secattr *secattr)
331446fda4fSPaul Moore {
332446fda4fSPaul Moore 	u32 bkt;
333446fda4fSPaul Moore 	struct cipso_v4_map_cache_entry *entry;
334446fda4fSPaul Moore 	struct cipso_v4_map_cache_entry *prev_entry = NULL;
335446fda4fSPaul Moore 	u32 hash;
336446fda4fSPaul Moore 
337446fda4fSPaul Moore 	if (!cipso_v4_cache_enabled)
338446fda4fSPaul Moore 		return -ENOENT;
339446fda4fSPaul Moore 
340446fda4fSPaul Moore 	hash = cipso_v4_map_cache_hash(key, key_len);
341446fda4fSPaul Moore 	bkt = hash & (CIPSO_V4_CACHE_BUCKETBITS - 1);
342609c92feSPaul Moore 	spin_lock_bh(&cipso_v4_cache[bkt].lock);
343446fda4fSPaul Moore 	list_for_each_entry(entry, &cipso_v4_cache[bkt].list, list) {
344446fda4fSPaul Moore 		if (entry->hash == hash &&
345446fda4fSPaul Moore 		    entry->key_len == key_len &&
346446fda4fSPaul Moore 		    memcmp(entry->key, key, key_len) == 0) {
347446fda4fSPaul Moore 			entry->activity += 1;
348ffb733c6Spaul.moore@hp.com 			atomic_inc(&entry->lsm_data->refcount);
349ffb733c6Spaul.moore@hp.com 			secattr->cache = entry->lsm_data;
350701a90baSPaul Moore 			secattr->flags |= NETLBL_SECATTR_CACHE;
351446fda4fSPaul Moore 			if (prev_entry == NULL) {
352609c92feSPaul Moore 				spin_unlock_bh(&cipso_v4_cache[bkt].lock);
353446fda4fSPaul Moore 				return 0;
354446fda4fSPaul Moore 			}
355446fda4fSPaul Moore 
356446fda4fSPaul Moore 			if (prev_entry->activity > 0)
357446fda4fSPaul Moore 				prev_entry->activity -= 1;
358446fda4fSPaul Moore 			if (entry->activity > prev_entry->activity &&
359446fda4fSPaul Moore 			    entry->activity - prev_entry->activity >
360446fda4fSPaul Moore 			    CIPSO_V4_CACHE_REORDERLIMIT) {
361446fda4fSPaul Moore 				__list_del(entry->list.prev, entry->list.next);
362446fda4fSPaul Moore 				__list_add(&entry->list,
363446fda4fSPaul Moore 					   prev_entry->list.prev,
364446fda4fSPaul Moore 					   &prev_entry->list);
365446fda4fSPaul Moore 			}
366446fda4fSPaul Moore 
367609c92feSPaul Moore 			spin_unlock_bh(&cipso_v4_cache[bkt].lock);
368446fda4fSPaul Moore 			return 0;
369446fda4fSPaul Moore 		}
370446fda4fSPaul Moore 		prev_entry = entry;
371446fda4fSPaul Moore 	}
372609c92feSPaul Moore 	spin_unlock_bh(&cipso_v4_cache[bkt].lock);
373446fda4fSPaul Moore 
374446fda4fSPaul Moore 	return -ENOENT;
375446fda4fSPaul Moore }
376446fda4fSPaul Moore 
377446fda4fSPaul Moore /**
378446fda4fSPaul Moore  * cipso_v4_cache_add - Add an entry to the CIPSO cache
379446fda4fSPaul Moore  * @skb: the packet
380446fda4fSPaul Moore  * @secattr: the packet's security attributes
381446fda4fSPaul Moore  *
382446fda4fSPaul Moore  * Description:
383446fda4fSPaul Moore  * Add a new entry into the CIPSO label mapping cache.  Add the new entry to
384446fda4fSPaul Moore  * head of the cache bucket's list, if the cache bucket is out of room remove
385446fda4fSPaul Moore  * the last entry in the list first.  It is important to note that there is
386446fda4fSPaul Moore  * currently no checking for duplicate keys.  Returns zero on success,
387446fda4fSPaul Moore  * negative values on failure.
388446fda4fSPaul Moore  *
389446fda4fSPaul Moore  */
390446fda4fSPaul Moore int cipso_v4_cache_add(const struct sk_buff *skb,
391446fda4fSPaul Moore 		       const struct netlbl_lsm_secattr *secattr)
392446fda4fSPaul Moore {
393446fda4fSPaul Moore 	int ret_val = -EPERM;
394446fda4fSPaul Moore 	u32 bkt;
395446fda4fSPaul Moore 	struct cipso_v4_map_cache_entry *entry = NULL;
396446fda4fSPaul Moore 	struct cipso_v4_map_cache_entry *old_entry = NULL;
397446fda4fSPaul Moore 	unsigned char *cipso_ptr;
398446fda4fSPaul Moore 	u32 cipso_ptr_len;
399446fda4fSPaul Moore 
400446fda4fSPaul Moore 	if (!cipso_v4_cache_enabled || cipso_v4_cache_bucketsize <= 0)
401446fda4fSPaul Moore 		return 0;
402446fda4fSPaul Moore 
403446fda4fSPaul Moore 	cipso_ptr = CIPSO_V4_OPTPTR(skb);
404446fda4fSPaul Moore 	cipso_ptr_len = cipso_ptr[1];
405446fda4fSPaul Moore 
406446fda4fSPaul Moore 	entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
407446fda4fSPaul Moore 	if (entry == NULL)
408446fda4fSPaul Moore 		return -ENOMEM;
409fac5d731SArnaldo Carvalho de Melo 	entry->key = kmemdup(cipso_ptr, cipso_ptr_len, GFP_ATOMIC);
410446fda4fSPaul Moore 	if (entry->key == NULL) {
411446fda4fSPaul Moore 		ret_val = -ENOMEM;
412446fda4fSPaul Moore 		goto cache_add_failure;
413446fda4fSPaul Moore 	}
414446fda4fSPaul Moore 	entry->key_len = cipso_ptr_len;
415446fda4fSPaul Moore 	entry->hash = cipso_v4_map_cache_hash(cipso_ptr, cipso_ptr_len);
416ffb733c6Spaul.moore@hp.com 	atomic_inc(&secattr->cache->refcount);
417ffb733c6Spaul.moore@hp.com 	entry->lsm_data = secattr->cache;
418446fda4fSPaul Moore 
419446fda4fSPaul Moore 	bkt = entry->hash & (CIPSO_V4_CACHE_BUCKETBITS - 1);
420609c92feSPaul Moore 	spin_lock_bh(&cipso_v4_cache[bkt].lock);
421446fda4fSPaul Moore 	if (cipso_v4_cache[bkt].size < cipso_v4_cache_bucketsize) {
422446fda4fSPaul Moore 		list_add(&entry->list, &cipso_v4_cache[bkt].list);
423446fda4fSPaul Moore 		cipso_v4_cache[bkt].size += 1;
424446fda4fSPaul Moore 	} else {
425446fda4fSPaul Moore 		old_entry = list_entry(cipso_v4_cache[bkt].list.prev,
426446fda4fSPaul Moore 				       struct cipso_v4_map_cache_entry, list);
427446fda4fSPaul Moore 		list_del(&old_entry->list);
428446fda4fSPaul Moore 		list_add(&entry->list, &cipso_v4_cache[bkt].list);
429446fda4fSPaul Moore 		cipso_v4_cache_entry_free(old_entry);
430446fda4fSPaul Moore 	}
431609c92feSPaul Moore 	spin_unlock_bh(&cipso_v4_cache[bkt].lock);
432446fda4fSPaul Moore 
433446fda4fSPaul Moore 	return 0;
434446fda4fSPaul Moore 
435446fda4fSPaul Moore cache_add_failure:
436446fda4fSPaul Moore 	if (entry)
437446fda4fSPaul Moore 		cipso_v4_cache_entry_free(entry);
438446fda4fSPaul Moore 	return ret_val;
439446fda4fSPaul Moore }
440446fda4fSPaul Moore 
441446fda4fSPaul Moore /*
442446fda4fSPaul Moore  * DOI List Functions
443446fda4fSPaul Moore  */
444446fda4fSPaul Moore 
445446fda4fSPaul Moore /**
446446fda4fSPaul Moore  * cipso_v4_doi_search - Searches for a DOI definition
447446fda4fSPaul Moore  * @doi: the DOI to search for
448446fda4fSPaul Moore  *
449446fda4fSPaul Moore  * Description:
450446fda4fSPaul Moore  * Search the DOI definition list for a DOI definition with a DOI value that
451446fda4fSPaul Moore  * matches @doi.  The caller is responsibile for calling rcu_read_[un]lock().
452446fda4fSPaul Moore  * Returns a pointer to the DOI definition on success and NULL on failure.
453446fda4fSPaul Moore  */
454446fda4fSPaul Moore static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi)
455446fda4fSPaul Moore {
456446fda4fSPaul Moore 	struct cipso_v4_doi *iter;
457446fda4fSPaul Moore 
458446fda4fSPaul Moore 	list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list)
459446fda4fSPaul Moore 		if (iter->doi == doi && iter->valid)
460446fda4fSPaul Moore 			return iter;
461446fda4fSPaul Moore 	return NULL;
462446fda4fSPaul Moore }
463446fda4fSPaul Moore 
464446fda4fSPaul Moore /**
465446fda4fSPaul Moore  * cipso_v4_doi_add - Add a new DOI to the CIPSO protocol engine
466446fda4fSPaul Moore  * @doi_def: the DOI structure
467446fda4fSPaul Moore  *
468446fda4fSPaul Moore  * Description:
469446fda4fSPaul Moore  * The caller defines a new DOI for use by the CIPSO engine and calls this
470446fda4fSPaul Moore  * function to add it to the list of acceptable domains.  The caller must
471446fda4fSPaul Moore  * ensure that the mapping table specified in @doi_def->map meets all of the
472446fda4fSPaul Moore  * requirements of the mapping type (see cipso_ipv4.h for details).  Returns
473446fda4fSPaul Moore  * zero on success and non-zero on failure.
474446fda4fSPaul Moore  *
475446fda4fSPaul Moore  */
476446fda4fSPaul Moore int cipso_v4_doi_add(struct cipso_v4_doi *doi_def)
477446fda4fSPaul Moore {
4786ce61a7cSPaul Moore 	u32 iter;
4796ce61a7cSPaul Moore 
480446fda4fSPaul Moore 	if (doi_def == NULL || doi_def->doi == CIPSO_V4_DOI_UNKNOWN)
481446fda4fSPaul Moore 		return -EINVAL;
4826ce61a7cSPaul Moore 	for (iter = 0; iter < CIPSO_V4_TAG_MAXCNT; iter++) {
4836ce61a7cSPaul Moore 		switch (doi_def->tags[iter]) {
4846ce61a7cSPaul Moore 		case CIPSO_V4_TAG_RBITMAP:
4856ce61a7cSPaul Moore 			break;
486484b3669SPaul Moore 		case CIPSO_V4_TAG_RANGE:
487484b3669SPaul Moore 			if (doi_def->type != CIPSO_V4_MAP_PASS)
488484b3669SPaul Moore 				return -EINVAL;
489484b3669SPaul Moore 			break;
4906ce61a7cSPaul Moore 		case CIPSO_V4_TAG_INVALID:
4916ce61a7cSPaul Moore 			if (iter == 0)
4926ce61a7cSPaul Moore 				return -EINVAL;
4936ce61a7cSPaul Moore 			break;
494654bbc2aSPaul Moore 		case CIPSO_V4_TAG_ENUM:
495654bbc2aSPaul Moore 			if (doi_def->type != CIPSO_V4_MAP_PASS)
496654bbc2aSPaul Moore 				return -EINVAL;
497654bbc2aSPaul Moore 			break;
4986ce61a7cSPaul Moore 		default:
4996ce61a7cSPaul Moore 			return -EINVAL;
5006ce61a7cSPaul Moore 		}
5016ce61a7cSPaul Moore 	}
502446fda4fSPaul Moore 
503446fda4fSPaul Moore 	doi_def->valid = 1;
504446fda4fSPaul Moore 	INIT_RCU_HEAD(&doi_def->rcu);
505446fda4fSPaul Moore 	INIT_LIST_HEAD(&doi_def->dom_list);
506446fda4fSPaul Moore 
507446fda4fSPaul Moore 	rcu_read_lock();
508446fda4fSPaul Moore 	if (cipso_v4_doi_search(doi_def->doi) != NULL)
509446fda4fSPaul Moore 		goto doi_add_failure_rlock;
510446fda4fSPaul Moore 	spin_lock(&cipso_v4_doi_list_lock);
511446fda4fSPaul Moore 	if (cipso_v4_doi_search(doi_def->doi) != NULL)
512446fda4fSPaul Moore 		goto doi_add_failure_slock;
513446fda4fSPaul Moore 	list_add_tail_rcu(&doi_def->list, &cipso_v4_doi_list);
514446fda4fSPaul Moore 	spin_unlock(&cipso_v4_doi_list_lock);
515446fda4fSPaul Moore 	rcu_read_unlock();
516446fda4fSPaul Moore 
517446fda4fSPaul Moore 	return 0;
518446fda4fSPaul Moore 
519446fda4fSPaul Moore doi_add_failure_slock:
520446fda4fSPaul Moore 	spin_unlock(&cipso_v4_doi_list_lock);
521446fda4fSPaul Moore doi_add_failure_rlock:
522446fda4fSPaul Moore 	rcu_read_unlock();
523446fda4fSPaul Moore 	return -EEXIST;
524446fda4fSPaul Moore }
525446fda4fSPaul Moore 
526446fda4fSPaul Moore /**
527446fda4fSPaul Moore  * cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine
528446fda4fSPaul Moore  * @doi: the DOI value
52932f50cdeSPaul Moore  * @audit_secid: the LSM secid to use in the audit message
530446fda4fSPaul Moore  * @callback: the DOI cleanup/free callback
531446fda4fSPaul Moore  *
532446fda4fSPaul Moore  * Description:
533446fda4fSPaul Moore  * Removes a DOI definition from the CIPSO engine, @callback is called to
534446fda4fSPaul Moore  * free any memory.  The NetLabel routines will be called to release their own
535446fda4fSPaul Moore  * LSM domain mappings as well as our own domain list.  Returns zero on
536446fda4fSPaul Moore  * success and negative values on failure.
537446fda4fSPaul Moore  *
538446fda4fSPaul Moore  */
53932f50cdeSPaul Moore int cipso_v4_doi_remove(u32 doi,
54095d4e6beSPaul Moore 			struct netlbl_audit *audit_info,
54132f50cdeSPaul Moore 			void (*callback) (struct rcu_head * head))
542446fda4fSPaul Moore {
543446fda4fSPaul Moore 	struct cipso_v4_doi *doi_def;
544446fda4fSPaul Moore 	struct cipso_v4_domhsh_entry *dom_iter;
545446fda4fSPaul Moore 
546446fda4fSPaul Moore 	rcu_read_lock();
547446fda4fSPaul Moore 	if (cipso_v4_doi_search(doi) != NULL) {
548446fda4fSPaul Moore 		spin_lock(&cipso_v4_doi_list_lock);
549446fda4fSPaul Moore 		doi_def = cipso_v4_doi_search(doi);
550446fda4fSPaul Moore 		if (doi_def == NULL) {
551446fda4fSPaul Moore 			spin_unlock(&cipso_v4_doi_list_lock);
552446fda4fSPaul Moore 			rcu_read_unlock();
553446fda4fSPaul Moore 			return -ENOENT;
554446fda4fSPaul Moore 		}
555446fda4fSPaul Moore 		doi_def->valid = 0;
556446fda4fSPaul Moore 		list_del_rcu(&doi_def->list);
557446fda4fSPaul Moore 		spin_unlock(&cipso_v4_doi_list_lock);
558446fda4fSPaul Moore 		list_for_each_entry_rcu(dom_iter, &doi_def->dom_list, list)
559446fda4fSPaul Moore 			if (dom_iter->valid)
56032f50cdeSPaul Moore 				netlbl_domhsh_remove(dom_iter->domain,
56195d4e6beSPaul Moore 						     audit_info);
562446fda4fSPaul Moore 		cipso_v4_cache_invalidate();
563446fda4fSPaul Moore 		rcu_read_unlock();
564446fda4fSPaul Moore 
565446fda4fSPaul Moore 		call_rcu(&doi_def->rcu, callback);
566446fda4fSPaul Moore 		return 0;
567446fda4fSPaul Moore 	}
568446fda4fSPaul Moore 	rcu_read_unlock();
569446fda4fSPaul Moore 
570446fda4fSPaul Moore 	return -ENOENT;
571446fda4fSPaul Moore }
572446fda4fSPaul Moore 
573446fda4fSPaul Moore /**
574446fda4fSPaul Moore  * cipso_v4_doi_getdef - Returns a pointer to a valid DOI definition
575446fda4fSPaul Moore  * @doi: the DOI value
576446fda4fSPaul Moore  *
577446fda4fSPaul Moore  * Description:
578446fda4fSPaul Moore  * Searches for a valid DOI definition and if one is found it is returned to
579446fda4fSPaul Moore  * the caller.  Otherwise NULL is returned.  The caller must ensure that
580446fda4fSPaul Moore  * rcu_read_lock() is held while accessing the returned definition.
581446fda4fSPaul Moore  *
582446fda4fSPaul Moore  */
583446fda4fSPaul Moore struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi)
584446fda4fSPaul Moore {
585446fda4fSPaul Moore 	return cipso_v4_doi_search(doi);
586446fda4fSPaul Moore }
587446fda4fSPaul Moore 
588446fda4fSPaul Moore /**
589fcd48280SPaul Moore  * cipso_v4_doi_walk - Iterate through the DOI definitions
590fcd48280SPaul Moore  * @skip_cnt: skip past this number of DOI definitions, updated
591fcd48280SPaul Moore  * @callback: callback for each DOI definition
592fcd48280SPaul Moore  * @cb_arg: argument for the callback function
593446fda4fSPaul Moore  *
594446fda4fSPaul Moore  * Description:
595fcd48280SPaul Moore  * Iterate over the DOI definition list, skipping the first @skip_cnt entries.
596fcd48280SPaul Moore  * For each entry call @callback, if @callback returns a negative value stop
597fcd48280SPaul Moore  * 'walking' through the list and return.  Updates the value in @skip_cnt upon
598fcd48280SPaul Moore  * return.  Returns zero on success, negative values on failure.
599446fda4fSPaul Moore  *
600446fda4fSPaul Moore  */
601fcd48280SPaul Moore int cipso_v4_doi_walk(u32 *skip_cnt,
602fcd48280SPaul Moore 		     int (*callback) (struct cipso_v4_doi *doi_def, void *arg),
603fcd48280SPaul Moore 		     void *cb_arg)
604446fda4fSPaul Moore {
605fcd48280SPaul Moore 	int ret_val = -ENOENT;
606446fda4fSPaul Moore 	u32 doi_cnt = 0;
607fcd48280SPaul Moore 	struct cipso_v4_doi *iter_doi;
608446fda4fSPaul Moore 
609446fda4fSPaul Moore 	rcu_read_lock();
610fcd48280SPaul Moore 	list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list)
611fcd48280SPaul Moore 		if (iter_doi->valid) {
612fcd48280SPaul Moore 			if (doi_cnt++ < *skip_cnt)
613fcd48280SPaul Moore 				continue;
614fcd48280SPaul Moore 			ret_val = callback(iter_doi, cb_arg);
615fcd48280SPaul Moore 			if (ret_val < 0) {
616fcd48280SPaul Moore 				doi_cnt--;
617fcd48280SPaul Moore 				goto doi_walk_return;
618446fda4fSPaul Moore 			}
619446fda4fSPaul Moore 		}
620446fda4fSPaul Moore 
621fcd48280SPaul Moore doi_walk_return:
622446fda4fSPaul Moore 	rcu_read_unlock();
623fcd48280SPaul Moore 	*skip_cnt = doi_cnt;
624fcd48280SPaul Moore 	return ret_val;
625446fda4fSPaul Moore }
626446fda4fSPaul Moore 
627446fda4fSPaul Moore /**
628446fda4fSPaul Moore  * cipso_v4_doi_domhsh_add - Adds a domain entry to a DOI definition
629446fda4fSPaul Moore  * @doi_def: the DOI definition
630446fda4fSPaul Moore  * @domain: the domain to add
631446fda4fSPaul Moore  *
632446fda4fSPaul Moore  * Description:
63359c51591SMichael Opdenacker  * Adds the @domain to the DOI specified by @doi_def, this function
634446fda4fSPaul Moore  * should only be called by external functions (i.e. NetLabel).  This function
635446fda4fSPaul Moore  * does allocate memory.  Returns zero on success, negative values on failure.
636446fda4fSPaul Moore  *
637446fda4fSPaul Moore  */
638446fda4fSPaul Moore int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain)
639446fda4fSPaul Moore {
640446fda4fSPaul Moore 	struct cipso_v4_domhsh_entry *iter;
641446fda4fSPaul Moore 	struct cipso_v4_domhsh_entry *new_dom;
642446fda4fSPaul Moore 
643446fda4fSPaul Moore 	new_dom = kzalloc(sizeof(*new_dom), GFP_KERNEL);
644446fda4fSPaul Moore 	if (new_dom == NULL)
645446fda4fSPaul Moore 		return -ENOMEM;
646446fda4fSPaul Moore 	if (domain) {
647446fda4fSPaul Moore 		new_dom->domain = kstrdup(domain, GFP_KERNEL);
648446fda4fSPaul Moore 		if (new_dom->domain == NULL) {
649446fda4fSPaul Moore 			kfree(new_dom);
650446fda4fSPaul Moore 			return -ENOMEM;
651446fda4fSPaul Moore 		}
652446fda4fSPaul Moore 	}
653446fda4fSPaul Moore 	new_dom->valid = 1;
654446fda4fSPaul Moore 	INIT_RCU_HEAD(&new_dom->rcu);
655446fda4fSPaul Moore 
656446fda4fSPaul Moore 	rcu_read_lock();
657446fda4fSPaul Moore 	spin_lock(&cipso_v4_doi_list_lock);
658446fda4fSPaul Moore 	list_for_each_entry_rcu(iter, &doi_def->dom_list, list)
659446fda4fSPaul Moore 		if (iter->valid &&
660446fda4fSPaul Moore 		    ((domain != NULL && iter->domain != NULL &&
661446fda4fSPaul Moore 		      strcmp(iter->domain, domain) == 0) ||
662446fda4fSPaul Moore 		     (domain == NULL && iter->domain == NULL))) {
663446fda4fSPaul Moore 			spin_unlock(&cipso_v4_doi_list_lock);
664446fda4fSPaul Moore 			rcu_read_unlock();
665446fda4fSPaul Moore 			kfree(new_dom->domain);
666446fda4fSPaul Moore 			kfree(new_dom);
667446fda4fSPaul Moore 			return -EEXIST;
668446fda4fSPaul Moore 		}
669446fda4fSPaul Moore 	list_add_tail_rcu(&new_dom->list, &doi_def->dom_list);
670446fda4fSPaul Moore 	spin_unlock(&cipso_v4_doi_list_lock);
671446fda4fSPaul Moore 	rcu_read_unlock();
672446fda4fSPaul Moore 
673446fda4fSPaul Moore 	return 0;
674446fda4fSPaul Moore }
675446fda4fSPaul Moore 
676446fda4fSPaul Moore /**
677446fda4fSPaul Moore  * cipso_v4_doi_domhsh_remove - Removes a domain entry from a DOI definition
678446fda4fSPaul Moore  * @doi_def: the DOI definition
679446fda4fSPaul Moore  * @domain: the domain to remove
680446fda4fSPaul Moore  *
681446fda4fSPaul Moore  * Description:
682446fda4fSPaul Moore  * Removes the @domain from the DOI specified by @doi_def, this function
683446fda4fSPaul Moore  * should only be called by external functions (i.e. NetLabel).   Returns zero
684446fda4fSPaul Moore  * on success and negative values on error.
685446fda4fSPaul Moore  *
686446fda4fSPaul Moore  */
687446fda4fSPaul Moore int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def,
688446fda4fSPaul Moore 			       const char *domain)
689446fda4fSPaul Moore {
690446fda4fSPaul Moore 	struct cipso_v4_domhsh_entry *iter;
691446fda4fSPaul Moore 
692446fda4fSPaul Moore 	rcu_read_lock();
693446fda4fSPaul Moore 	spin_lock(&cipso_v4_doi_list_lock);
694446fda4fSPaul Moore 	list_for_each_entry_rcu(iter, &doi_def->dom_list, list)
695446fda4fSPaul Moore 		if (iter->valid &&
696446fda4fSPaul Moore 		    ((domain != NULL && iter->domain != NULL &&
697446fda4fSPaul Moore 		      strcmp(iter->domain, domain) == 0) ||
698446fda4fSPaul Moore 		     (domain == NULL && iter->domain == NULL))) {
699446fda4fSPaul Moore 			iter->valid = 0;
700446fda4fSPaul Moore 			list_del_rcu(&iter->list);
701446fda4fSPaul Moore 			spin_unlock(&cipso_v4_doi_list_lock);
702446fda4fSPaul Moore 			rcu_read_unlock();
703446fda4fSPaul Moore 			call_rcu(&iter->rcu, cipso_v4_doi_domhsh_free);
704446fda4fSPaul Moore 
705446fda4fSPaul Moore 			return 0;
706446fda4fSPaul Moore 		}
707446fda4fSPaul Moore 	spin_unlock(&cipso_v4_doi_list_lock);
708446fda4fSPaul Moore 	rcu_read_unlock();
709446fda4fSPaul Moore 
710446fda4fSPaul Moore 	return -ENOENT;
711446fda4fSPaul Moore }
712446fda4fSPaul Moore 
713446fda4fSPaul Moore /*
714446fda4fSPaul Moore  * Label Mapping Functions
715446fda4fSPaul Moore  */
716446fda4fSPaul Moore 
717446fda4fSPaul Moore /**
718446fda4fSPaul Moore  * cipso_v4_map_lvl_valid - Checks to see if the given level is understood
719446fda4fSPaul Moore  * @doi_def: the DOI definition
720446fda4fSPaul Moore  * @level: the level to check
721446fda4fSPaul Moore  *
722446fda4fSPaul Moore  * Description:
723446fda4fSPaul Moore  * Checks the given level against the given DOI definition and returns a
724446fda4fSPaul Moore  * negative value if the level does not have a valid mapping and a zero value
725446fda4fSPaul Moore  * if the level is defined by the DOI.
726446fda4fSPaul Moore  *
727446fda4fSPaul Moore  */
728446fda4fSPaul Moore static int cipso_v4_map_lvl_valid(const struct cipso_v4_doi *doi_def, u8 level)
729446fda4fSPaul Moore {
730446fda4fSPaul Moore 	switch (doi_def->type) {
731446fda4fSPaul Moore 	case CIPSO_V4_MAP_PASS:
732446fda4fSPaul Moore 		return 0;
733446fda4fSPaul Moore 	case CIPSO_V4_MAP_STD:
734446fda4fSPaul Moore 		if (doi_def->map.std->lvl.cipso[level] < CIPSO_V4_INV_LVL)
735446fda4fSPaul Moore 			return 0;
736446fda4fSPaul Moore 		break;
737446fda4fSPaul Moore 	}
738446fda4fSPaul Moore 
739446fda4fSPaul Moore 	return -EFAULT;
740446fda4fSPaul Moore }
741446fda4fSPaul Moore 
742446fda4fSPaul Moore /**
743446fda4fSPaul Moore  * cipso_v4_map_lvl_hton - Perform a level mapping from the host to the network
744446fda4fSPaul Moore  * @doi_def: the DOI definition
745446fda4fSPaul Moore  * @host_lvl: the host MLS level
746446fda4fSPaul Moore  * @net_lvl: the network/CIPSO MLS level
747446fda4fSPaul Moore  *
748446fda4fSPaul Moore  * Description:
749446fda4fSPaul Moore  * Perform a label mapping to translate a local MLS level to the correct
750446fda4fSPaul Moore  * CIPSO level using the given DOI definition.  Returns zero on success,
751446fda4fSPaul Moore  * negative values otherwise.
752446fda4fSPaul Moore  *
753446fda4fSPaul Moore  */
754446fda4fSPaul Moore static int cipso_v4_map_lvl_hton(const struct cipso_v4_doi *doi_def,
755446fda4fSPaul Moore 				 u32 host_lvl,
756446fda4fSPaul Moore 				 u32 *net_lvl)
757446fda4fSPaul Moore {
758446fda4fSPaul Moore 	switch (doi_def->type) {
759446fda4fSPaul Moore 	case CIPSO_V4_MAP_PASS:
760446fda4fSPaul Moore 		*net_lvl = host_lvl;
761446fda4fSPaul Moore 		return 0;
762446fda4fSPaul Moore 	case CIPSO_V4_MAP_STD:
763c6387a86SPaul Moore 		if (host_lvl < doi_def->map.std->lvl.local_size &&
764c6387a86SPaul Moore 		    doi_def->map.std->lvl.local[host_lvl] < CIPSO_V4_INV_LVL) {
765446fda4fSPaul Moore 			*net_lvl = doi_def->map.std->lvl.local[host_lvl];
766446fda4fSPaul Moore 			return 0;
767446fda4fSPaul Moore 		}
768c6387a86SPaul Moore 		return -EPERM;
769446fda4fSPaul Moore 	}
770446fda4fSPaul Moore 
771446fda4fSPaul Moore 	return -EINVAL;
772446fda4fSPaul Moore }
773446fda4fSPaul Moore 
774446fda4fSPaul Moore /**
775446fda4fSPaul Moore  * cipso_v4_map_lvl_ntoh - Perform a level mapping from the network to the host
776446fda4fSPaul Moore  * @doi_def: the DOI definition
777446fda4fSPaul Moore  * @net_lvl: the network/CIPSO MLS level
778446fda4fSPaul Moore  * @host_lvl: the host MLS level
779446fda4fSPaul Moore  *
780446fda4fSPaul Moore  * Description:
781446fda4fSPaul Moore  * Perform a label mapping to translate a CIPSO level to the correct local MLS
782446fda4fSPaul Moore  * level using the given DOI definition.  Returns zero on success, negative
783446fda4fSPaul Moore  * values otherwise.
784446fda4fSPaul Moore  *
785446fda4fSPaul Moore  */
786446fda4fSPaul Moore static int cipso_v4_map_lvl_ntoh(const struct cipso_v4_doi *doi_def,
787446fda4fSPaul Moore 				 u32 net_lvl,
788446fda4fSPaul Moore 				 u32 *host_lvl)
789446fda4fSPaul Moore {
790446fda4fSPaul Moore 	struct cipso_v4_std_map_tbl *map_tbl;
791446fda4fSPaul Moore 
792446fda4fSPaul Moore 	switch (doi_def->type) {
793446fda4fSPaul Moore 	case CIPSO_V4_MAP_PASS:
794446fda4fSPaul Moore 		*host_lvl = net_lvl;
795446fda4fSPaul Moore 		return 0;
796446fda4fSPaul Moore 	case CIPSO_V4_MAP_STD:
797446fda4fSPaul Moore 		map_tbl = doi_def->map.std;
798446fda4fSPaul Moore 		if (net_lvl < map_tbl->lvl.cipso_size &&
799446fda4fSPaul Moore 		    map_tbl->lvl.cipso[net_lvl] < CIPSO_V4_INV_LVL) {
800446fda4fSPaul Moore 			*host_lvl = doi_def->map.std->lvl.cipso[net_lvl];
801446fda4fSPaul Moore 			return 0;
802446fda4fSPaul Moore 		}
803c6387a86SPaul Moore 		return -EPERM;
804446fda4fSPaul Moore 	}
805446fda4fSPaul Moore 
806446fda4fSPaul Moore 	return -EINVAL;
807446fda4fSPaul Moore }
808446fda4fSPaul Moore 
809446fda4fSPaul Moore /**
810446fda4fSPaul Moore  * cipso_v4_map_cat_rbm_valid - Checks to see if the category bitmap is valid
811446fda4fSPaul Moore  * @doi_def: the DOI definition
812446fda4fSPaul Moore  * @bitmap: category bitmap
813446fda4fSPaul Moore  * @bitmap_len: bitmap length in bytes
814446fda4fSPaul Moore  *
815446fda4fSPaul Moore  * Description:
816446fda4fSPaul Moore  * Checks the given category bitmap against the given DOI definition and
817446fda4fSPaul Moore  * returns a negative value if any of the categories in the bitmap do not have
818446fda4fSPaul Moore  * a valid mapping and a zero value if all of the categories are valid.
819446fda4fSPaul Moore  *
820446fda4fSPaul Moore  */
821446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_valid(const struct cipso_v4_doi *doi_def,
822446fda4fSPaul Moore 				      const unsigned char *bitmap,
823446fda4fSPaul Moore 				      u32 bitmap_len)
824446fda4fSPaul Moore {
825446fda4fSPaul Moore 	int cat = -1;
826446fda4fSPaul Moore 	u32 bitmap_len_bits = bitmap_len * 8;
827044a68edSPaul Moore 	u32 cipso_cat_size;
828044a68edSPaul Moore 	u32 *cipso_array;
829446fda4fSPaul Moore 
830446fda4fSPaul Moore 	switch (doi_def->type) {
831446fda4fSPaul Moore 	case CIPSO_V4_MAP_PASS:
832446fda4fSPaul Moore 		return 0;
833446fda4fSPaul Moore 	case CIPSO_V4_MAP_STD:
834044a68edSPaul Moore 		cipso_cat_size = doi_def->map.std->cat.cipso_size;
835044a68edSPaul Moore 		cipso_array = doi_def->map.std->cat.cipso;
836446fda4fSPaul Moore 		for (;;) {
837446fda4fSPaul Moore 			cat = cipso_v4_bitmap_walk(bitmap,
838446fda4fSPaul Moore 						   bitmap_len_bits,
839446fda4fSPaul Moore 						   cat + 1,
840446fda4fSPaul Moore 						   1);
841446fda4fSPaul Moore 			if (cat < 0)
842446fda4fSPaul Moore 				break;
843446fda4fSPaul Moore 			if (cat >= cipso_cat_size ||
844446fda4fSPaul Moore 			    cipso_array[cat] >= CIPSO_V4_INV_CAT)
845446fda4fSPaul Moore 				return -EFAULT;
846446fda4fSPaul Moore 		}
847446fda4fSPaul Moore 
848446fda4fSPaul Moore 		if (cat == -1)
849446fda4fSPaul Moore 			return 0;
850446fda4fSPaul Moore 		break;
851446fda4fSPaul Moore 	}
852446fda4fSPaul Moore 
853446fda4fSPaul Moore 	return -EFAULT;
854446fda4fSPaul Moore }
855446fda4fSPaul Moore 
856446fda4fSPaul Moore /**
857446fda4fSPaul Moore  * cipso_v4_map_cat_rbm_hton - Perform a category mapping from host to network
858446fda4fSPaul Moore  * @doi_def: the DOI definition
85902752760SPaul Moore  * @secattr: the security attributes
860446fda4fSPaul Moore  * @net_cat: the zero'd out category bitmap in network/CIPSO format
861446fda4fSPaul Moore  * @net_cat_len: the length of the CIPSO bitmap in bytes
862446fda4fSPaul Moore  *
863446fda4fSPaul Moore  * Description:
864446fda4fSPaul Moore  * Perform a label mapping to translate a local MLS category bitmap to the
865446fda4fSPaul Moore  * correct CIPSO bitmap using the given DOI definition.  Returns the minimum
866446fda4fSPaul Moore  * size in bytes of the network bitmap on success, negative values otherwise.
867446fda4fSPaul Moore  *
868446fda4fSPaul Moore  */
869446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def,
87002752760SPaul Moore 				     const struct netlbl_lsm_secattr *secattr,
871446fda4fSPaul Moore 				     unsigned char *net_cat,
872446fda4fSPaul Moore 				     u32 net_cat_len)
873446fda4fSPaul Moore {
874446fda4fSPaul Moore 	int host_spot = -1;
87502752760SPaul Moore 	u32 net_spot = CIPSO_V4_INV_CAT;
876446fda4fSPaul Moore 	u32 net_spot_max = 0;
877446fda4fSPaul Moore 	u32 net_clen_bits = net_cat_len * 8;
87802752760SPaul Moore 	u32 host_cat_size = 0;
87902752760SPaul Moore 	u32 *host_cat_array = NULL;
88002752760SPaul Moore 
88102752760SPaul Moore 	if (doi_def->type == CIPSO_V4_MAP_STD) {
88202752760SPaul Moore 		host_cat_size = doi_def->map.std->cat.local_size;
88302752760SPaul Moore 		host_cat_array = doi_def->map.std->cat.local;
88402752760SPaul Moore 	}
88502752760SPaul Moore 
88602752760SPaul Moore 	for (;;) {
88702752760SPaul Moore 		host_spot = netlbl_secattr_catmap_walk(secattr->mls_cat,
88802752760SPaul Moore 						       host_spot + 1);
88902752760SPaul Moore 		if (host_spot < 0)
89002752760SPaul Moore 			break;
891446fda4fSPaul Moore 
892446fda4fSPaul Moore 		switch (doi_def->type) {
893446fda4fSPaul Moore 		case CIPSO_V4_MAP_PASS:
89402752760SPaul Moore 			net_spot = host_spot;
895446fda4fSPaul Moore 			break;
89602752760SPaul Moore 		case CIPSO_V4_MAP_STD:
897446fda4fSPaul Moore 			if (host_spot >= host_cat_size)
898446fda4fSPaul Moore 				return -EPERM;
899446fda4fSPaul Moore 			net_spot = host_cat_array[host_spot];
9009fade4bfSPaul Moore 			if (net_spot >= CIPSO_V4_INV_CAT)
9019fade4bfSPaul Moore 				return -EPERM;
90202752760SPaul Moore 			break;
90302752760SPaul Moore 		}
904446fda4fSPaul Moore 		if (net_spot >= net_clen_bits)
905446fda4fSPaul Moore 			return -ENOSPC;
906446fda4fSPaul Moore 		cipso_v4_bitmap_setbit(net_cat, net_spot, 1);
907446fda4fSPaul Moore 
908446fda4fSPaul Moore 		if (net_spot > net_spot_max)
909446fda4fSPaul Moore 			net_spot_max = net_spot;
910446fda4fSPaul Moore 	}
911446fda4fSPaul Moore 
912446fda4fSPaul Moore 	if (++net_spot_max % 8)
913446fda4fSPaul Moore 		return net_spot_max / 8 + 1;
914446fda4fSPaul Moore 	return net_spot_max / 8;
915446fda4fSPaul Moore }
916446fda4fSPaul Moore 
917446fda4fSPaul Moore /**
918446fda4fSPaul Moore  * cipso_v4_map_cat_rbm_ntoh - Perform a category mapping from network to host
919446fda4fSPaul Moore  * @doi_def: the DOI definition
920446fda4fSPaul Moore  * @net_cat: the category bitmap in network/CIPSO format
921446fda4fSPaul Moore  * @net_cat_len: the length of the CIPSO bitmap in bytes
92202752760SPaul Moore  * @secattr: the security attributes
923446fda4fSPaul Moore  *
924446fda4fSPaul Moore  * Description:
925446fda4fSPaul Moore  * Perform a label mapping to translate a CIPSO bitmap to the correct local
92602752760SPaul Moore  * MLS category bitmap using the given DOI definition.  Returns zero on
92702752760SPaul Moore  * success, negative values on failure.
928446fda4fSPaul Moore  *
929446fda4fSPaul Moore  */
930446fda4fSPaul Moore static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def,
931446fda4fSPaul Moore 				     const unsigned char *net_cat,
932446fda4fSPaul Moore 				     u32 net_cat_len,
93302752760SPaul Moore 				     struct netlbl_lsm_secattr *secattr)
934446fda4fSPaul Moore {
93502752760SPaul Moore 	int ret_val;
936446fda4fSPaul Moore 	int net_spot = -1;
93702752760SPaul Moore 	u32 host_spot = CIPSO_V4_INV_CAT;
938446fda4fSPaul Moore 	u32 net_clen_bits = net_cat_len * 8;
93902752760SPaul Moore 	u32 net_cat_size = 0;
94002752760SPaul Moore 	u32 *net_cat_array = NULL;
941446fda4fSPaul Moore 
94202752760SPaul Moore 	if (doi_def->type == CIPSO_V4_MAP_STD) {
943044a68edSPaul Moore 		net_cat_size = doi_def->map.std->cat.cipso_size;
944044a68edSPaul Moore 		net_cat_array = doi_def->map.std->cat.cipso;
94502752760SPaul Moore 	}
94602752760SPaul Moore 
947446fda4fSPaul Moore 	for (;;) {
948446fda4fSPaul Moore 		net_spot = cipso_v4_bitmap_walk(net_cat,
949446fda4fSPaul Moore 						net_clen_bits,
950446fda4fSPaul Moore 						net_spot + 1,
951446fda4fSPaul Moore 						1);
95202752760SPaul Moore 		if (net_spot < 0) {
95302752760SPaul Moore 			if (net_spot == -2)
95402752760SPaul Moore 				return -EFAULT;
95502752760SPaul Moore 			return 0;
95602752760SPaul Moore 		}
957446fda4fSPaul Moore 
95802752760SPaul Moore 		switch (doi_def->type) {
95902752760SPaul Moore 		case CIPSO_V4_MAP_PASS:
96002752760SPaul Moore 			host_spot = net_spot;
96102752760SPaul Moore 			break;
96202752760SPaul Moore 		case CIPSO_V4_MAP_STD:
96302752760SPaul Moore 			if (net_spot >= net_cat_size)
96402752760SPaul Moore 				return -EPERM;
965446fda4fSPaul Moore 			host_spot = net_cat_array[net_spot];
9669fade4bfSPaul Moore 			if (host_spot >= CIPSO_V4_INV_CAT)
9679fade4bfSPaul Moore 				return -EPERM;
96802752760SPaul Moore 			break;
969446fda4fSPaul Moore 		}
97002752760SPaul Moore 		ret_val = netlbl_secattr_catmap_setbit(secattr->mls_cat,
97102752760SPaul Moore 						       host_spot,
97202752760SPaul Moore 						       GFP_ATOMIC);
97302752760SPaul Moore 		if (ret_val != 0)
97402752760SPaul Moore 			return ret_val;
975446fda4fSPaul Moore 	}
976446fda4fSPaul Moore 
977446fda4fSPaul Moore 	return -EINVAL;
978446fda4fSPaul Moore }
979446fda4fSPaul Moore 
980654bbc2aSPaul Moore /**
981654bbc2aSPaul Moore  * cipso_v4_map_cat_enum_valid - Checks to see if the categories are valid
982654bbc2aSPaul Moore  * @doi_def: the DOI definition
983654bbc2aSPaul Moore  * @enumcat: category list
984654bbc2aSPaul Moore  * @enumcat_len: length of the category list in bytes
985654bbc2aSPaul Moore  *
986654bbc2aSPaul Moore  * Description:
987654bbc2aSPaul Moore  * Checks the given categories against the given DOI definition and returns a
988654bbc2aSPaul Moore  * negative value if any of the categories do not have a valid mapping and a
989654bbc2aSPaul Moore  * zero value if all of the categories are valid.
990654bbc2aSPaul Moore  *
991654bbc2aSPaul Moore  */
992654bbc2aSPaul Moore static int cipso_v4_map_cat_enum_valid(const struct cipso_v4_doi *doi_def,
993654bbc2aSPaul Moore 				       const unsigned char *enumcat,
994654bbc2aSPaul Moore 				       u32 enumcat_len)
995654bbc2aSPaul Moore {
996654bbc2aSPaul Moore 	u16 cat;
997654bbc2aSPaul Moore 	int cat_prev = -1;
998654bbc2aSPaul Moore 	u32 iter;
999654bbc2aSPaul Moore 
1000654bbc2aSPaul Moore 	if (doi_def->type != CIPSO_V4_MAP_PASS || enumcat_len & 0x01)
1001654bbc2aSPaul Moore 		return -EFAULT;
1002654bbc2aSPaul Moore 
1003654bbc2aSPaul Moore 	for (iter = 0; iter < enumcat_len; iter += 2) {
1004*50e5d35cSPaul Moore 		cat = ntohs(get_unaligned((__be16 *)&enumcat[iter]));
1005654bbc2aSPaul Moore 		if (cat <= cat_prev)
1006654bbc2aSPaul Moore 			return -EFAULT;
1007654bbc2aSPaul Moore 		cat_prev = cat;
1008654bbc2aSPaul Moore 	}
1009654bbc2aSPaul Moore 
1010654bbc2aSPaul Moore 	return 0;
1011654bbc2aSPaul Moore }
1012654bbc2aSPaul Moore 
1013654bbc2aSPaul Moore /**
1014654bbc2aSPaul Moore  * cipso_v4_map_cat_enum_hton - Perform a category mapping from host to network
1015654bbc2aSPaul Moore  * @doi_def: the DOI definition
1016654bbc2aSPaul Moore  * @secattr: the security attributes
1017654bbc2aSPaul Moore  * @net_cat: the zero'd out category list in network/CIPSO format
1018654bbc2aSPaul Moore  * @net_cat_len: the length of the CIPSO category list in bytes
1019654bbc2aSPaul Moore  *
1020654bbc2aSPaul Moore  * Description:
1021654bbc2aSPaul Moore  * Perform a label mapping to translate a local MLS category bitmap to the
1022654bbc2aSPaul Moore  * correct CIPSO category list using the given DOI definition.   Returns the
1023654bbc2aSPaul Moore  * size in bytes of the network category bitmap on success, negative values
1024654bbc2aSPaul Moore  * otherwise.
1025654bbc2aSPaul Moore  *
1026654bbc2aSPaul Moore  */
1027654bbc2aSPaul Moore static int cipso_v4_map_cat_enum_hton(const struct cipso_v4_doi *doi_def,
1028654bbc2aSPaul Moore 				      const struct netlbl_lsm_secattr *secattr,
1029654bbc2aSPaul Moore 				      unsigned char *net_cat,
1030654bbc2aSPaul Moore 				      u32 net_cat_len)
1031654bbc2aSPaul Moore {
1032654bbc2aSPaul Moore 	int cat = -1;
1033654bbc2aSPaul Moore 	u32 cat_iter = 0;
1034654bbc2aSPaul Moore 
1035654bbc2aSPaul Moore 	for (;;) {
1036654bbc2aSPaul Moore 		cat = netlbl_secattr_catmap_walk(secattr->mls_cat, cat + 1);
1037654bbc2aSPaul Moore 		if (cat < 0)
1038654bbc2aSPaul Moore 			break;
1039654bbc2aSPaul Moore 		if ((cat_iter + 2) > net_cat_len)
1040654bbc2aSPaul Moore 			return -ENOSPC;
1041654bbc2aSPaul Moore 
1042654bbc2aSPaul Moore 		*((__be16 *)&net_cat[cat_iter]) = htons(cat);
1043654bbc2aSPaul Moore 		cat_iter += 2;
1044654bbc2aSPaul Moore 	}
1045654bbc2aSPaul Moore 
1046654bbc2aSPaul Moore 	return cat_iter;
1047654bbc2aSPaul Moore }
1048654bbc2aSPaul Moore 
1049654bbc2aSPaul Moore /**
1050654bbc2aSPaul Moore  * cipso_v4_map_cat_enum_ntoh - Perform a category mapping from network to host
1051654bbc2aSPaul Moore  * @doi_def: the DOI definition
1052654bbc2aSPaul Moore  * @net_cat: the category list in network/CIPSO format
1053654bbc2aSPaul Moore  * @net_cat_len: the length of the CIPSO bitmap in bytes
1054654bbc2aSPaul Moore  * @secattr: the security attributes
1055654bbc2aSPaul Moore  *
1056654bbc2aSPaul Moore  * Description:
1057654bbc2aSPaul Moore  * Perform a label mapping to translate a CIPSO category list to the correct
1058654bbc2aSPaul Moore  * local MLS category bitmap using the given DOI definition.  Returns zero on
1059654bbc2aSPaul Moore  * success, negative values on failure.
1060654bbc2aSPaul Moore  *
1061654bbc2aSPaul Moore  */
1062654bbc2aSPaul Moore static int cipso_v4_map_cat_enum_ntoh(const struct cipso_v4_doi *doi_def,
1063654bbc2aSPaul Moore 				      const unsigned char *net_cat,
1064654bbc2aSPaul Moore 				      u32 net_cat_len,
1065654bbc2aSPaul Moore 				      struct netlbl_lsm_secattr *secattr)
1066654bbc2aSPaul Moore {
1067654bbc2aSPaul Moore 	int ret_val;
1068654bbc2aSPaul Moore 	u32 iter;
1069654bbc2aSPaul Moore 
1070654bbc2aSPaul Moore 	for (iter = 0; iter < net_cat_len; iter += 2) {
1071654bbc2aSPaul Moore 		ret_val = netlbl_secattr_catmap_setbit(secattr->mls_cat,
1072*50e5d35cSPaul Moore 				ntohs(get_unaligned((__be16 *)&net_cat[iter])),
1073654bbc2aSPaul Moore 				GFP_ATOMIC);
1074654bbc2aSPaul Moore 		if (ret_val != 0)
1075654bbc2aSPaul Moore 			return ret_val;
1076654bbc2aSPaul Moore 	}
1077654bbc2aSPaul Moore 
1078654bbc2aSPaul Moore 	return 0;
1079654bbc2aSPaul Moore }
1080654bbc2aSPaul Moore 
1081484b3669SPaul Moore /**
1082484b3669SPaul Moore  * cipso_v4_map_cat_rng_valid - Checks to see if the categories are valid
1083484b3669SPaul Moore  * @doi_def: the DOI definition
1084484b3669SPaul Moore  * @rngcat: category list
1085484b3669SPaul Moore  * @rngcat_len: length of the category list in bytes
1086484b3669SPaul Moore  *
1087484b3669SPaul Moore  * Description:
1088484b3669SPaul Moore  * Checks the given categories against the given DOI definition and returns a
1089484b3669SPaul Moore  * negative value if any of the categories do not have a valid mapping and a
1090484b3669SPaul Moore  * zero value if all of the categories are valid.
1091484b3669SPaul Moore  *
1092484b3669SPaul Moore  */
1093484b3669SPaul Moore static int cipso_v4_map_cat_rng_valid(const struct cipso_v4_doi *doi_def,
1094484b3669SPaul Moore 				      const unsigned char *rngcat,
1095484b3669SPaul Moore 				      u32 rngcat_len)
1096484b3669SPaul Moore {
1097484b3669SPaul Moore 	u16 cat_high;
1098484b3669SPaul Moore 	u16 cat_low;
1099484b3669SPaul Moore 	u32 cat_prev = CIPSO_V4_MAX_REM_CATS + 1;
1100484b3669SPaul Moore 	u32 iter;
1101484b3669SPaul Moore 
1102484b3669SPaul Moore 	if (doi_def->type != CIPSO_V4_MAP_PASS || rngcat_len & 0x01)
1103484b3669SPaul Moore 		return -EFAULT;
1104484b3669SPaul Moore 
1105484b3669SPaul Moore 	for (iter = 0; iter < rngcat_len; iter += 4) {
1106*50e5d35cSPaul Moore 		cat_high = ntohs(get_unaligned((__be16 *)&rngcat[iter]));
1107484b3669SPaul Moore 		if ((iter + 4) <= rngcat_len)
1108*50e5d35cSPaul Moore 			cat_low = ntohs(
1109*50e5d35cSPaul Moore 				get_unaligned((__be16 *)&rngcat[iter + 2]));
1110484b3669SPaul Moore 		else
1111484b3669SPaul Moore 			cat_low = 0;
1112484b3669SPaul Moore 
1113484b3669SPaul Moore 		if (cat_high > cat_prev)
1114484b3669SPaul Moore 			return -EFAULT;
1115484b3669SPaul Moore 
1116484b3669SPaul Moore 		cat_prev = cat_low;
1117484b3669SPaul Moore 	}
1118484b3669SPaul Moore 
1119484b3669SPaul Moore 	return 0;
1120484b3669SPaul Moore }
1121484b3669SPaul Moore 
1122484b3669SPaul Moore /**
1123484b3669SPaul Moore  * cipso_v4_map_cat_rng_hton - Perform a category mapping from host to network
1124484b3669SPaul Moore  * @doi_def: the DOI definition
1125484b3669SPaul Moore  * @secattr: the security attributes
1126484b3669SPaul Moore  * @net_cat: the zero'd out category list in network/CIPSO format
1127484b3669SPaul Moore  * @net_cat_len: the length of the CIPSO category list in bytes
1128484b3669SPaul Moore  *
1129484b3669SPaul Moore  * Description:
1130484b3669SPaul Moore  * Perform a label mapping to translate a local MLS category bitmap to the
1131484b3669SPaul Moore  * correct CIPSO category list using the given DOI definition.   Returns the
1132484b3669SPaul Moore  * size in bytes of the network category bitmap on success, negative values
1133484b3669SPaul Moore  * otherwise.
1134484b3669SPaul Moore  *
1135484b3669SPaul Moore  */
1136484b3669SPaul Moore static int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def,
1137484b3669SPaul Moore 				     const struct netlbl_lsm_secattr *secattr,
1138484b3669SPaul Moore 				     unsigned char *net_cat,
1139484b3669SPaul Moore 				     u32 net_cat_len)
1140484b3669SPaul Moore {
1141484b3669SPaul Moore 	int iter = -1;
1142f998e8cbSPaul Moore 	u16 array[CIPSO_V4_TAG_RNG_CAT_MAX * 2];
1143484b3669SPaul Moore 	u32 array_cnt = 0;
1144484b3669SPaul Moore 	u32 cat_size = 0;
1145484b3669SPaul Moore 
1146f998e8cbSPaul Moore 	/* make sure we don't overflow the 'array[]' variable */
1147128c6b6cSPaul Moore 	if (net_cat_len >
1148128c6b6cSPaul Moore 	    (CIPSO_V4_OPT_LEN_MAX - CIPSO_V4_HDR_LEN - CIPSO_V4_TAG_RNG_BLEN))
1149128c6b6cSPaul Moore 		return -ENOSPC;
1150484b3669SPaul Moore 
1151484b3669SPaul Moore 	for (;;) {
1152484b3669SPaul Moore 		iter = netlbl_secattr_catmap_walk(secattr->mls_cat, iter + 1);
1153484b3669SPaul Moore 		if (iter < 0)
1154484b3669SPaul Moore 			break;
1155484b3669SPaul Moore 		cat_size += (iter == 0 ? 0 : sizeof(u16));
1156484b3669SPaul Moore 		if (cat_size > net_cat_len)
1157484b3669SPaul Moore 			return -ENOSPC;
1158484b3669SPaul Moore 		array[array_cnt++] = iter;
1159484b3669SPaul Moore 
1160484b3669SPaul Moore 		iter = netlbl_secattr_catmap_walk_rng(secattr->mls_cat, iter);
1161484b3669SPaul Moore 		if (iter < 0)
1162484b3669SPaul Moore 			return -EFAULT;
1163484b3669SPaul Moore 		cat_size += sizeof(u16);
1164484b3669SPaul Moore 		if (cat_size > net_cat_len)
1165484b3669SPaul Moore 			return -ENOSPC;
1166484b3669SPaul Moore 		array[array_cnt++] = iter;
1167484b3669SPaul Moore 	}
1168484b3669SPaul Moore 
1169484b3669SPaul Moore 	for (iter = 0; array_cnt > 0;) {
1170484b3669SPaul Moore 		*((__be16 *)&net_cat[iter]) = htons(array[--array_cnt]);
1171484b3669SPaul Moore 		iter += 2;
1172484b3669SPaul Moore 		array_cnt--;
1173484b3669SPaul Moore 		if (array[array_cnt] != 0) {
1174484b3669SPaul Moore 			*((__be16 *)&net_cat[iter]) = htons(array[array_cnt]);
1175484b3669SPaul Moore 			iter += 2;
1176484b3669SPaul Moore 		}
1177484b3669SPaul Moore 	}
1178484b3669SPaul Moore 
1179484b3669SPaul Moore 	return cat_size;
1180484b3669SPaul Moore }
1181484b3669SPaul Moore 
1182484b3669SPaul Moore /**
1183484b3669SPaul Moore  * cipso_v4_map_cat_rng_ntoh - Perform a category mapping from network to host
1184484b3669SPaul Moore  * @doi_def: the DOI definition
1185484b3669SPaul Moore  * @net_cat: the category list in network/CIPSO format
1186484b3669SPaul Moore  * @net_cat_len: the length of the CIPSO bitmap in bytes
1187484b3669SPaul Moore  * @secattr: the security attributes
1188484b3669SPaul Moore  *
1189484b3669SPaul Moore  * Description:
1190484b3669SPaul Moore  * Perform a label mapping to translate a CIPSO category list to the correct
1191484b3669SPaul Moore  * local MLS category bitmap using the given DOI definition.  Returns zero on
1192484b3669SPaul Moore  * success, negative values on failure.
1193484b3669SPaul Moore  *
1194484b3669SPaul Moore  */
1195484b3669SPaul Moore static int cipso_v4_map_cat_rng_ntoh(const struct cipso_v4_doi *doi_def,
1196484b3669SPaul Moore 				     const unsigned char *net_cat,
1197484b3669SPaul Moore 				     u32 net_cat_len,
1198484b3669SPaul Moore 				     struct netlbl_lsm_secattr *secattr)
1199484b3669SPaul Moore {
1200484b3669SPaul Moore 	int ret_val;
1201484b3669SPaul Moore 	u32 net_iter;
1202484b3669SPaul Moore 	u16 cat_low;
1203484b3669SPaul Moore 	u16 cat_high;
1204484b3669SPaul Moore 
1205484b3669SPaul Moore 	for (net_iter = 0; net_iter < net_cat_len; net_iter += 4) {
1206*50e5d35cSPaul Moore 		cat_high = ntohs(get_unaligned((__be16 *)&net_cat[net_iter]));
1207484b3669SPaul Moore 		if ((net_iter + 4) <= net_cat_len)
1208*50e5d35cSPaul Moore 			cat_low = ntohs(
1209*50e5d35cSPaul Moore 			      get_unaligned((__be16 *)&net_cat[net_iter + 2]));
1210484b3669SPaul Moore 		else
1211484b3669SPaul Moore 			cat_low = 0;
1212484b3669SPaul Moore 
1213484b3669SPaul Moore 		ret_val = netlbl_secattr_catmap_setrng(secattr->mls_cat,
1214484b3669SPaul Moore 						       cat_low,
1215484b3669SPaul Moore 						       cat_high,
1216484b3669SPaul Moore 						       GFP_ATOMIC);
1217484b3669SPaul Moore 		if (ret_val != 0)
1218484b3669SPaul Moore 			return ret_val;
1219484b3669SPaul Moore 	}
1220484b3669SPaul Moore 
1221484b3669SPaul Moore 	return 0;
1222484b3669SPaul Moore }
1223484b3669SPaul Moore 
1224446fda4fSPaul Moore /*
1225446fda4fSPaul Moore  * Protocol Handling Functions
1226446fda4fSPaul Moore  */
1227446fda4fSPaul Moore 
1228446fda4fSPaul Moore /**
1229446fda4fSPaul Moore  * cipso_v4_gentag_hdr - Generate a CIPSO option header
1230446fda4fSPaul Moore  * @doi_def: the DOI definition
123191b1ed0aSPaul Moore  * @len: the total tag length in bytes, not including this header
1232446fda4fSPaul Moore  * @buf: the CIPSO option buffer
1233446fda4fSPaul Moore  *
1234446fda4fSPaul Moore  * Description:
123591b1ed0aSPaul Moore  * Write a CIPSO header into the beginning of @buffer.
1236446fda4fSPaul Moore  *
1237446fda4fSPaul Moore  */
123891b1ed0aSPaul Moore static void cipso_v4_gentag_hdr(const struct cipso_v4_doi *doi_def,
123991b1ed0aSPaul Moore 				unsigned char *buf,
124091b1ed0aSPaul Moore 				u32 len)
1241446fda4fSPaul Moore {
1242446fda4fSPaul Moore 	buf[0] = IPOPT_CIPSO;
1243446fda4fSPaul Moore 	buf[1] = CIPSO_V4_HDR_LEN + len;
1244714e85beSAl Viro 	*(__be32 *)&buf[2] = htonl(doi_def->doi);
1245446fda4fSPaul Moore }
1246446fda4fSPaul Moore 
1247446fda4fSPaul Moore /**
1248446fda4fSPaul Moore  * cipso_v4_gentag_rbm - Generate a CIPSO restricted bitmap tag (type #1)
1249446fda4fSPaul Moore  * @doi_def: the DOI definition
1250446fda4fSPaul Moore  * @secattr: the security attributes
1251446fda4fSPaul Moore  * @buffer: the option buffer
1252446fda4fSPaul Moore  * @buffer_len: length of buffer in bytes
1253446fda4fSPaul Moore  *
1254446fda4fSPaul Moore  * Description:
1255446fda4fSPaul Moore  * Generate a CIPSO option using the restricted bitmap tag, tag type #1.  The
1256446fda4fSPaul Moore  * actual buffer length may be larger than the indicated size due to
125791b1ed0aSPaul Moore  * translation between host and network category bitmaps.  Returns the size of
125891b1ed0aSPaul Moore  * the tag on success, negative values on failure.
1259446fda4fSPaul Moore  *
1260446fda4fSPaul Moore  */
1261446fda4fSPaul Moore static int cipso_v4_gentag_rbm(const struct cipso_v4_doi *doi_def,
1262446fda4fSPaul Moore 			       const struct netlbl_lsm_secattr *secattr,
126391b1ed0aSPaul Moore 			       unsigned char *buffer,
126491b1ed0aSPaul Moore 			       u32 buffer_len)
1265446fda4fSPaul Moore {
1266701a90baSPaul Moore 	int ret_val;
126791b1ed0aSPaul Moore 	u32 tag_len;
1268446fda4fSPaul Moore 	u32 level;
1269446fda4fSPaul Moore 
1270701a90baSPaul Moore 	if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0)
1271701a90baSPaul Moore 		return -EPERM;
1272701a90baSPaul Moore 
127391b1ed0aSPaul Moore 	ret_val = cipso_v4_map_lvl_hton(doi_def, secattr->mls_lvl, &level);
127491b1ed0aSPaul Moore 	if (ret_val != 0)
127591b1ed0aSPaul Moore 		return ret_val;
1276446fda4fSPaul Moore 
127791b1ed0aSPaul Moore 	if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
1278446fda4fSPaul Moore 		ret_val = cipso_v4_map_cat_rbm_hton(doi_def,
127902752760SPaul Moore 						    secattr,
128091b1ed0aSPaul Moore 						    &buffer[4],
128191b1ed0aSPaul Moore 						    buffer_len - 4);
1282446fda4fSPaul Moore 		if (ret_val < 0)
128391b1ed0aSPaul Moore 			return ret_val;
1284446fda4fSPaul Moore 
1285446fda4fSPaul Moore 		/* This will send packets using the "optimized" format when
1286446fda4fSPaul Moore 		 * possibile as specified in  section 3.4.2.6 of the
1287446fda4fSPaul Moore 		 * CIPSO draft. */
1288701a90baSPaul Moore 		if (cipso_v4_rbm_optfmt && ret_val > 0 && ret_val <= 10)
128991b1ed0aSPaul Moore 			tag_len = 14;
1290701a90baSPaul Moore 		else
129191b1ed0aSPaul Moore 			tag_len = 4 + ret_val;
129291b1ed0aSPaul Moore 	} else
129391b1ed0aSPaul Moore 		tag_len = 4;
1294446fda4fSPaul Moore 
129591b1ed0aSPaul Moore 	buffer[0] = 0x01;
129691b1ed0aSPaul Moore 	buffer[1] = tag_len;
129791b1ed0aSPaul Moore 	buffer[3] = level;
1298446fda4fSPaul Moore 
129991b1ed0aSPaul Moore 	return tag_len;
1300446fda4fSPaul Moore }
1301446fda4fSPaul Moore 
1302446fda4fSPaul Moore /**
1303446fda4fSPaul Moore  * cipso_v4_parsetag_rbm - Parse a CIPSO restricted bitmap tag
1304446fda4fSPaul Moore  * @doi_def: the DOI definition
1305446fda4fSPaul Moore  * @tag: the CIPSO tag
1306446fda4fSPaul Moore  * @secattr: the security attributes
1307446fda4fSPaul Moore  *
1308446fda4fSPaul Moore  * Description:
1309446fda4fSPaul Moore  * Parse a CIPSO restricted bitmap tag (tag type #1) and return the security
1310446fda4fSPaul Moore  * attributes in @secattr.  Return zero on success, negatives values on
1311446fda4fSPaul Moore  * failure.
1312446fda4fSPaul Moore  *
1313446fda4fSPaul Moore  */
1314446fda4fSPaul Moore static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def,
1315446fda4fSPaul Moore 				 const unsigned char *tag,
1316446fda4fSPaul Moore 				 struct netlbl_lsm_secattr *secattr)
1317446fda4fSPaul Moore {
1318446fda4fSPaul Moore 	int ret_val;
1319446fda4fSPaul Moore 	u8 tag_len = tag[1];
1320446fda4fSPaul Moore 	u32 level;
1321446fda4fSPaul Moore 
1322446fda4fSPaul Moore 	ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level);
1323446fda4fSPaul Moore 	if (ret_val != 0)
1324446fda4fSPaul Moore 		return ret_val;
1325446fda4fSPaul Moore 	secattr->mls_lvl = level;
1326701a90baSPaul Moore 	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
1327446fda4fSPaul Moore 
1328446fda4fSPaul Moore 	if (tag_len > 4) {
132902752760SPaul Moore 		secattr->mls_cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
1330446fda4fSPaul Moore 		if (secattr->mls_cat == NULL)
1331446fda4fSPaul Moore 			return -ENOMEM;
1332446fda4fSPaul Moore 
1333446fda4fSPaul Moore 		ret_val = cipso_v4_map_cat_rbm_ntoh(doi_def,
1334446fda4fSPaul Moore 						    &tag[4],
1335446fda4fSPaul Moore 						    tag_len - 4,
133602752760SPaul Moore 						    secattr);
133702752760SPaul Moore 		if (ret_val != 0) {
133802752760SPaul Moore 			netlbl_secattr_catmap_free(secattr->mls_cat);
1339446fda4fSPaul Moore 			return ret_val;
1340701a90baSPaul Moore 		}
134102752760SPaul Moore 
134202752760SPaul Moore 		secattr->flags |= NETLBL_SECATTR_MLS_CAT;
1343446fda4fSPaul Moore 	}
1344446fda4fSPaul Moore 
1345446fda4fSPaul Moore 	return 0;
1346446fda4fSPaul Moore }
1347446fda4fSPaul Moore 
1348446fda4fSPaul Moore /**
1349654bbc2aSPaul Moore  * cipso_v4_gentag_enum - Generate a CIPSO enumerated tag (type #2)
1350654bbc2aSPaul Moore  * @doi_def: the DOI definition
1351654bbc2aSPaul Moore  * @secattr: the security attributes
1352654bbc2aSPaul Moore  * @buffer: the option buffer
1353654bbc2aSPaul Moore  * @buffer_len: length of buffer in bytes
1354654bbc2aSPaul Moore  *
1355654bbc2aSPaul Moore  * Description:
1356654bbc2aSPaul Moore  * Generate a CIPSO option using the enumerated tag, tag type #2.  Returns the
1357654bbc2aSPaul Moore  * size of the tag on success, negative values on failure.
1358654bbc2aSPaul Moore  *
1359654bbc2aSPaul Moore  */
1360654bbc2aSPaul Moore static int cipso_v4_gentag_enum(const struct cipso_v4_doi *doi_def,
1361654bbc2aSPaul Moore 				const struct netlbl_lsm_secattr *secattr,
1362654bbc2aSPaul Moore 				unsigned char *buffer,
1363654bbc2aSPaul Moore 				u32 buffer_len)
1364654bbc2aSPaul Moore {
1365654bbc2aSPaul Moore 	int ret_val;
1366654bbc2aSPaul Moore 	u32 tag_len;
1367654bbc2aSPaul Moore 	u32 level;
1368654bbc2aSPaul Moore 
1369654bbc2aSPaul Moore 	if (!(secattr->flags & NETLBL_SECATTR_MLS_LVL))
1370654bbc2aSPaul Moore 		return -EPERM;
1371654bbc2aSPaul Moore 
1372654bbc2aSPaul Moore 	ret_val = cipso_v4_map_lvl_hton(doi_def, secattr->mls_lvl, &level);
1373654bbc2aSPaul Moore 	if (ret_val != 0)
1374654bbc2aSPaul Moore 		return ret_val;
1375654bbc2aSPaul Moore 
1376654bbc2aSPaul Moore 	if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
1377654bbc2aSPaul Moore 		ret_val = cipso_v4_map_cat_enum_hton(doi_def,
1378654bbc2aSPaul Moore 						     secattr,
1379654bbc2aSPaul Moore 						     &buffer[4],
1380654bbc2aSPaul Moore 						     buffer_len - 4);
1381654bbc2aSPaul Moore 		if (ret_val < 0)
1382654bbc2aSPaul Moore 			return ret_val;
1383654bbc2aSPaul Moore 
1384654bbc2aSPaul Moore 		tag_len = 4 + ret_val;
1385654bbc2aSPaul Moore 	} else
1386654bbc2aSPaul Moore 		tag_len = 4;
1387654bbc2aSPaul Moore 
1388654bbc2aSPaul Moore 	buffer[0] = 0x02;
1389654bbc2aSPaul Moore 	buffer[1] = tag_len;
1390654bbc2aSPaul Moore 	buffer[3] = level;
1391654bbc2aSPaul Moore 
1392654bbc2aSPaul Moore 	return tag_len;
1393654bbc2aSPaul Moore }
1394654bbc2aSPaul Moore 
1395654bbc2aSPaul Moore /**
1396654bbc2aSPaul Moore  * cipso_v4_parsetag_enum - Parse a CIPSO enumerated tag
1397654bbc2aSPaul Moore  * @doi_def: the DOI definition
1398654bbc2aSPaul Moore  * @tag: the CIPSO tag
1399654bbc2aSPaul Moore  * @secattr: the security attributes
1400654bbc2aSPaul Moore  *
1401654bbc2aSPaul Moore  * Description:
1402654bbc2aSPaul Moore  * Parse a CIPSO enumerated tag (tag type #2) and return the security
1403654bbc2aSPaul Moore  * attributes in @secattr.  Return zero on success, negatives values on
1404654bbc2aSPaul Moore  * failure.
1405654bbc2aSPaul Moore  *
1406654bbc2aSPaul Moore  */
1407654bbc2aSPaul Moore static int cipso_v4_parsetag_enum(const struct cipso_v4_doi *doi_def,
1408654bbc2aSPaul Moore 				  const unsigned char *tag,
1409654bbc2aSPaul Moore 				  struct netlbl_lsm_secattr *secattr)
1410654bbc2aSPaul Moore {
1411654bbc2aSPaul Moore 	int ret_val;
1412654bbc2aSPaul Moore 	u8 tag_len = tag[1];
1413654bbc2aSPaul Moore 	u32 level;
1414654bbc2aSPaul Moore 
1415654bbc2aSPaul Moore 	ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level);
1416654bbc2aSPaul Moore 	if (ret_val != 0)
1417654bbc2aSPaul Moore 		return ret_val;
1418654bbc2aSPaul Moore 	secattr->mls_lvl = level;
1419654bbc2aSPaul Moore 	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
1420654bbc2aSPaul Moore 
1421654bbc2aSPaul Moore 	if (tag_len > 4) {
1422654bbc2aSPaul Moore 		secattr->mls_cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
1423654bbc2aSPaul Moore 		if (secattr->mls_cat == NULL)
1424654bbc2aSPaul Moore 			return -ENOMEM;
1425654bbc2aSPaul Moore 
1426654bbc2aSPaul Moore 		ret_val = cipso_v4_map_cat_enum_ntoh(doi_def,
1427654bbc2aSPaul Moore 						     &tag[4],
1428654bbc2aSPaul Moore 						     tag_len - 4,
1429654bbc2aSPaul Moore 						     secattr);
1430654bbc2aSPaul Moore 		if (ret_val != 0) {
1431654bbc2aSPaul Moore 			netlbl_secattr_catmap_free(secattr->mls_cat);
1432654bbc2aSPaul Moore 			return ret_val;
1433654bbc2aSPaul Moore 		}
1434654bbc2aSPaul Moore 
1435654bbc2aSPaul Moore 		secattr->flags |= NETLBL_SECATTR_MLS_CAT;
1436654bbc2aSPaul Moore 	}
1437654bbc2aSPaul Moore 
1438654bbc2aSPaul Moore 	return 0;
1439654bbc2aSPaul Moore }
1440654bbc2aSPaul Moore 
1441654bbc2aSPaul Moore /**
1442484b3669SPaul Moore  * cipso_v4_gentag_rng - Generate a CIPSO ranged tag (type #5)
1443484b3669SPaul Moore  * @doi_def: the DOI definition
1444484b3669SPaul Moore  * @secattr: the security attributes
1445484b3669SPaul Moore  * @buffer: the option buffer
1446484b3669SPaul Moore  * @buffer_len: length of buffer in bytes
1447484b3669SPaul Moore  *
1448484b3669SPaul Moore  * Description:
1449484b3669SPaul Moore  * Generate a CIPSO option using the ranged tag, tag type #5.  Returns the
1450484b3669SPaul Moore  * size of the tag on success, negative values on failure.
1451484b3669SPaul Moore  *
1452484b3669SPaul Moore  */
1453484b3669SPaul Moore static int cipso_v4_gentag_rng(const struct cipso_v4_doi *doi_def,
1454484b3669SPaul Moore 			       const struct netlbl_lsm_secattr *secattr,
1455484b3669SPaul Moore 			       unsigned char *buffer,
1456484b3669SPaul Moore 			       u32 buffer_len)
1457484b3669SPaul Moore {
1458484b3669SPaul Moore 	int ret_val;
1459484b3669SPaul Moore 	u32 tag_len;
1460484b3669SPaul Moore 	u32 level;
1461484b3669SPaul Moore 
1462484b3669SPaul Moore 	if (!(secattr->flags & NETLBL_SECATTR_MLS_LVL))
1463484b3669SPaul Moore 		return -EPERM;
1464484b3669SPaul Moore 
1465484b3669SPaul Moore 	ret_val = cipso_v4_map_lvl_hton(doi_def, secattr->mls_lvl, &level);
1466484b3669SPaul Moore 	if (ret_val != 0)
1467484b3669SPaul Moore 		return ret_val;
1468484b3669SPaul Moore 
1469484b3669SPaul Moore 	if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
1470484b3669SPaul Moore 		ret_val = cipso_v4_map_cat_rng_hton(doi_def,
1471484b3669SPaul Moore 						    secattr,
1472484b3669SPaul Moore 						    &buffer[4],
1473484b3669SPaul Moore 						    buffer_len - 4);
1474484b3669SPaul Moore 		if (ret_val < 0)
1475484b3669SPaul Moore 			return ret_val;
1476484b3669SPaul Moore 
1477484b3669SPaul Moore 		tag_len = 4 + ret_val;
1478484b3669SPaul Moore 	} else
1479484b3669SPaul Moore 		tag_len = 4;
1480484b3669SPaul Moore 
1481484b3669SPaul Moore 	buffer[0] = 0x05;
1482484b3669SPaul Moore 	buffer[1] = tag_len;
1483484b3669SPaul Moore 	buffer[3] = level;
1484484b3669SPaul Moore 
1485484b3669SPaul Moore 	return tag_len;
1486484b3669SPaul Moore }
1487484b3669SPaul Moore 
1488484b3669SPaul Moore /**
1489484b3669SPaul Moore  * cipso_v4_parsetag_rng - Parse a CIPSO ranged tag
1490484b3669SPaul Moore  * @doi_def: the DOI definition
1491484b3669SPaul Moore  * @tag: the CIPSO tag
1492484b3669SPaul Moore  * @secattr: the security attributes
1493484b3669SPaul Moore  *
1494484b3669SPaul Moore  * Description:
1495484b3669SPaul Moore  * Parse a CIPSO ranged tag (tag type #5) and return the security attributes
1496484b3669SPaul Moore  * in @secattr.  Return zero on success, negatives values on failure.
1497484b3669SPaul Moore  *
1498484b3669SPaul Moore  */
1499484b3669SPaul Moore static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def,
1500484b3669SPaul Moore 				 const unsigned char *tag,
1501484b3669SPaul Moore 				 struct netlbl_lsm_secattr *secattr)
1502484b3669SPaul Moore {
1503484b3669SPaul Moore 	int ret_val;
1504484b3669SPaul Moore 	u8 tag_len = tag[1];
1505484b3669SPaul Moore 	u32 level;
1506484b3669SPaul Moore 
1507484b3669SPaul Moore 	ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level);
1508484b3669SPaul Moore 	if (ret_val != 0)
1509484b3669SPaul Moore 		return ret_val;
1510484b3669SPaul Moore 	secattr->mls_lvl = level;
1511484b3669SPaul Moore 	secattr->flags |= NETLBL_SECATTR_MLS_LVL;
1512484b3669SPaul Moore 
1513484b3669SPaul Moore 	if (tag_len > 4) {
1514484b3669SPaul Moore 		secattr->mls_cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
1515484b3669SPaul Moore 		if (secattr->mls_cat == NULL)
1516484b3669SPaul Moore 			return -ENOMEM;
1517484b3669SPaul Moore 
1518484b3669SPaul Moore 		ret_val = cipso_v4_map_cat_rng_ntoh(doi_def,
1519484b3669SPaul Moore 						    &tag[4],
1520484b3669SPaul Moore 						    tag_len - 4,
1521484b3669SPaul Moore 						    secattr);
1522484b3669SPaul Moore 		if (ret_val != 0) {
1523484b3669SPaul Moore 			netlbl_secattr_catmap_free(secattr->mls_cat);
1524484b3669SPaul Moore 			return ret_val;
1525484b3669SPaul Moore 		}
1526484b3669SPaul Moore 
1527484b3669SPaul Moore 		secattr->flags |= NETLBL_SECATTR_MLS_CAT;
1528484b3669SPaul Moore 	}
1529484b3669SPaul Moore 
1530484b3669SPaul Moore 	return 0;
1531484b3669SPaul Moore }
1532484b3669SPaul Moore 
1533484b3669SPaul Moore /**
1534446fda4fSPaul Moore  * cipso_v4_validate - Validate a CIPSO option
1535446fda4fSPaul Moore  * @option: the start of the option, on error it is set to point to the error
1536446fda4fSPaul Moore  *
1537446fda4fSPaul Moore  * Description:
1538446fda4fSPaul Moore  * This routine is called to validate a CIPSO option, it checks all of the
1539446fda4fSPaul Moore  * fields to ensure that they are at least valid, see the draft snippet below
1540446fda4fSPaul Moore  * for details.  If the option is valid then a zero value is returned and
1541446fda4fSPaul Moore  * the value of @option is unchanged.  If the option is invalid then a
1542446fda4fSPaul Moore  * non-zero value is returned and @option is adjusted to point to the
1543446fda4fSPaul Moore  * offending portion of the option.  From the IETF draft ...
1544446fda4fSPaul Moore  *
1545446fda4fSPaul Moore  *  "If any field within the CIPSO options, such as the DOI identifier, is not
1546446fda4fSPaul Moore  *   recognized the IP datagram is discarded and an ICMP 'parameter problem'
1547446fda4fSPaul Moore  *   (type 12) is generated and returned.  The ICMP code field is set to 'bad
1548446fda4fSPaul Moore  *   parameter' (code 0) and the pointer is set to the start of the CIPSO field
1549446fda4fSPaul Moore  *   that is unrecognized."
1550446fda4fSPaul Moore  *
1551446fda4fSPaul Moore  */
1552446fda4fSPaul Moore int cipso_v4_validate(unsigned char **option)
1553446fda4fSPaul Moore {
1554446fda4fSPaul Moore 	unsigned char *opt = *option;
1555446fda4fSPaul Moore 	unsigned char *tag;
1556446fda4fSPaul Moore 	unsigned char opt_iter;
1557446fda4fSPaul Moore 	unsigned char err_offset = 0;
1558446fda4fSPaul Moore 	u8 opt_len;
1559446fda4fSPaul Moore 	u8 tag_len;
1560446fda4fSPaul Moore 	struct cipso_v4_doi *doi_def = NULL;
1561446fda4fSPaul Moore 	u32 tag_iter;
1562446fda4fSPaul Moore 
1563446fda4fSPaul Moore 	/* caller already checks for length values that are too large */
1564446fda4fSPaul Moore 	opt_len = opt[1];
1565446fda4fSPaul Moore 	if (opt_len < 8) {
1566446fda4fSPaul Moore 		err_offset = 1;
1567446fda4fSPaul Moore 		goto validate_return;
1568446fda4fSPaul Moore 	}
1569446fda4fSPaul Moore 
1570446fda4fSPaul Moore 	rcu_read_lock();
1571*50e5d35cSPaul Moore 	doi_def = cipso_v4_doi_search(ntohl(get_unaligned((__be32 *)&opt[2])));
1572446fda4fSPaul Moore 	if (doi_def == NULL) {
1573446fda4fSPaul Moore 		err_offset = 2;
1574446fda4fSPaul Moore 		goto validate_return_locked;
1575446fda4fSPaul Moore 	}
1576446fda4fSPaul Moore 
1577446fda4fSPaul Moore 	opt_iter = 6;
1578446fda4fSPaul Moore 	tag = opt + opt_iter;
1579446fda4fSPaul Moore 	while (opt_iter < opt_len) {
1580446fda4fSPaul Moore 		for (tag_iter = 0; doi_def->tags[tag_iter] != tag[0];)
1581446fda4fSPaul Moore 			if (doi_def->tags[tag_iter] == CIPSO_V4_TAG_INVALID ||
1582446fda4fSPaul Moore 			    ++tag_iter == CIPSO_V4_TAG_MAXCNT) {
1583446fda4fSPaul Moore 				err_offset = opt_iter;
1584446fda4fSPaul Moore 				goto validate_return_locked;
1585446fda4fSPaul Moore 			}
1586446fda4fSPaul Moore 
1587446fda4fSPaul Moore 		tag_len = tag[1];
1588446fda4fSPaul Moore 		if (tag_len > (opt_len - opt_iter)) {
1589446fda4fSPaul Moore 			err_offset = opt_iter + 1;
1590446fda4fSPaul Moore 			goto validate_return_locked;
1591446fda4fSPaul Moore 		}
1592446fda4fSPaul Moore 
1593446fda4fSPaul Moore 		switch (tag[0]) {
1594446fda4fSPaul Moore 		case CIPSO_V4_TAG_RBITMAP:
1595446fda4fSPaul Moore 			if (tag_len < 4) {
1596446fda4fSPaul Moore 				err_offset = opt_iter + 1;
1597446fda4fSPaul Moore 				goto validate_return_locked;
1598446fda4fSPaul Moore 			}
1599446fda4fSPaul Moore 
1600446fda4fSPaul Moore 			/* We are already going to do all the verification
1601446fda4fSPaul Moore 			 * necessary at the socket layer so from our point of
1602446fda4fSPaul Moore 			 * view it is safe to turn these checks off (and less
1603446fda4fSPaul Moore 			 * work), however, the CIPSO draft says we should do
1604446fda4fSPaul Moore 			 * all the CIPSO validations here but it doesn't
1605446fda4fSPaul Moore 			 * really specify _exactly_ what we need to validate
1606446fda4fSPaul Moore 			 * ... so, just make it a sysctl tunable. */
1607446fda4fSPaul Moore 			if (cipso_v4_rbm_strictvalid) {
1608446fda4fSPaul Moore 				if (cipso_v4_map_lvl_valid(doi_def,
1609446fda4fSPaul Moore 							   tag[3]) < 0) {
1610446fda4fSPaul Moore 					err_offset = opt_iter + 3;
1611446fda4fSPaul Moore 					goto validate_return_locked;
1612446fda4fSPaul Moore 				}
1613446fda4fSPaul Moore 				if (tag_len > 4 &&
1614446fda4fSPaul Moore 				    cipso_v4_map_cat_rbm_valid(doi_def,
1615446fda4fSPaul Moore 							    &tag[4],
1616446fda4fSPaul Moore 							    tag_len - 4) < 0) {
1617446fda4fSPaul Moore 					err_offset = opt_iter + 4;
1618446fda4fSPaul Moore 					goto validate_return_locked;
1619446fda4fSPaul Moore 				}
1620446fda4fSPaul Moore 			}
1621446fda4fSPaul Moore 			break;
1622654bbc2aSPaul Moore 		case CIPSO_V4_TAG_ENUM:
1623654bbc2aSPaul Moore 			if (tag_len < 4) {
1624654bbc2aSPaul Moore 				err_offset = opt_iter + 1;
1625654bbc2aSPaul Moore 				goto validate_return_locked;
1626654bbc2aSPaul Moore 			}
1627654bbc2aSPaul Moore 
1628654bbc2aSPaul Moore 			if (cipso_v4_map_lvl_valid(doi_def,
1629654bbc2aSPaul Moore 						   tag[3]) < 0) {
1630654bbc2aSPaul Moore 				err_offset = opt_iter + 3;
1631654bbc2aSPaul Moore 				goto validate_return_locked;
1632654bbc2aSPaul Moore 			}
1633654bbc2aSPaul Moore 			if (tag_len > 4 &&
1634654bbc2aSPaul Moore 			    cipso_v4_map_cat_enum_valid(doi_def,
1635654bbc2aSPaul Moore 							&tag[4],
1636654bbc2aSPaul Moore 							tag_len - 4) < 0) {
1637654bbc2aSPaul Moore 				err_offset = opt_iter + 4;
1638654bbc2aSPaul Moore 				goto validate_return_locked;
1639654bbc2aSPaul Moore 			}
1640654bbc2aSPaul Moore 			break;
1641484b3669SPaul Moore 		case CIPSO_V4_TAG_RANGE:
1642484b3669SPaul Moore 			if (tag_len < 4) {
1643484b3669SPaul Moore 				err_offset = opt_iter + 1;
1644484b3669SPaul Moore 				goto validate_return_locked;
1645484b3669SPaul Moore 			}
1646484b3669SPaul Moore 
1647484b3669SPaul Moore 			if (cipso_v4_map_lvl_valid(doi_def,
1648484b3669SPaul Moore 						   tag[3]) < 0) {
1649484b3669SPaul Moore 				err_offset = opt_iter + 3;
1650484b3669SPaul Moore 				goto validate_return_locked;
1651484b3669SPaul Moore 			}
1652484b3669SPaul Moore 			if (tag_len > 4 &&
1653484b3669SPaul Moore 			    cipso_v4_map_cat_rng_valid(doi_def,
1654484b3669SPaul Moore 						       &tag[4],
1655484b3669SPaul Moore 						       tag_len - 4) < 0) {
1656484b3669SPaul Moore 				err_offset = opt_iter + 4;
1657484b3669SPaul Moore 				goto validate_return_locked;
1658484b3669SPaul Moore 			}
1659484b3669SPaul Moore 			break;
1660446fda4fSPaul Moore 		default:
1661446fda4fSPaul Moore 			err_offset = opt_iter;
1662446fda4fSPaul Moore 			goto validate_return_locked;
1663446fda4fSPaul Moore 		}
1664446fda4fSPaul Moore 
1665446fda4fSPaul Moore 		tag += tag_len;
1666446fda4fSPaul Moore 		opt_iter += tag_len;
1667446fda4fSPaul Moore 	}
1668446fda4fSPaul Moore 
1669446fda4fSPaul Moore validate_return_locked:
1670446fda4fSPaul Moore 	rcu_read_unlock();
1671446fda4fSPaul Moore validate_return:
1672446fda4fSPaul Moore 	*option = opt + err_offset;
1673446fda4fSPaul Moore 	return err_offset;
1674446fda4fSPaul Moore }
1675446fda4fSPaul Moore 
1676446fda4fSPaul Moore /**
1677446fda4fSPaul Moore  * cipso_v4_error - Send the correct reponse for a bad packet
1678446fda4fSPaul Moore  * @skb: the packet
1679446fda4fSPaul Moore  * @error: the error code
1680446fda4fSPaul Moore  * @gateway: CIPSO gateway flag
1681446fda4fSPaul Moore  *
1682446fda4fSPaul Moore  * Description:
1683446fda4fSPaul Moore  * Based on the error code given in @error, send an ICMP error message back to
1684446fda4fSPaul Moore  * the originating host.  From the IETF draft ...
1685446fda4fSPaul Moore  *
1686446fda4fSPaul Moore  *  "If the contents of the CIPSO [option] are valid but the security label is
1687446fda4fSPaul Moore  *   outside of the configured host or port label range, the datagram is
1688446fda4fSPaul Moore  *   discarded and an ICMP 'destination unreachable' (type 3) is generated and
1689446fda4fSPaul Moore  *   returned.  The code field of the ICMP is set to 'communication with
1690446fda4fSPaul Moore  *   destination network administratively prohibited' (code 9) or to
1691446fda4fSPaul Moore  *   'communication with destination host administratively prohibited'
1692446fda4fSPaul Moore  *   (code 10).  The value of the code is dependent on whether the originator
1693446fda4fSPaul Moore  *   of the ICMP message is acting as a CIPSO host or a CIPSO gateway.  The
1694446fda4fSPaul Moore  *   recipient of the ICMP message MUST be able to handle either value.  The
1695446fda4fSPaul Moore  *   same procedure is performed if a CIPSO [option] can not be added to an
1696446fda4fSPaul Moore  *   IP packet because it is too large to fit in the IP options area."
1697446fda4fSPaul Moore  *
1698446fda4fSPaul Moore  *  "If the error is triggered by receipt of an ICMP message, the message is
1699446fda4fSPaul Moore  *   discarded and no response is permitted (consistent with general ICMP
1700446fda4fSPaul Moore  *   processing rules)."
1701446fda4fSPaul Moore  *
1702446fda4fSPaul Moore  */
1703446fda4fSPaul Moore void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway)
1704446fda4fSPaul Moore {
1705eddc9ec5SArnaldo Carvalho de Melo 	if (ip_hdr(skb)->protocol == IPPROTO_ICMP || error != -EACCES)
1706446fda4fSPaul Moore 		return;
1707446fda4fSPaul Moore 
1708446fda4fSPaul Moore 	if (gateway)
1709446fda4fSPaul Moore 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0);
1710446fda4fSPaul Moore 	else
1711446fda4fSPaul Moore 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0);
1712446fda4fSPaul Moore }
1713446fda4fSPaul Moore 
1714446fda4fSPaul Moore /**
1715ba6ff9f2SPaul Moore  * cipso_v4_sock_setattr - Add a CIPSO option to a socket
1716ba6ff9f2SPaul Moore  * @sk: the socket
1717446fda4fSPaul Moore  * @doi_def: the CIPSO DOI to use
1718446fda4fSPaul Moore  * @secattr: the specific security attributes of the socket
1719446fda4fSPaul Moore  *
1720446fda4fSPaul Moore  * Description:
1721446fda4fSPaul Moore  * Set the CIPSO option on the given socket using the DOI definition and
1722446fda4fSPaul Moore  * security attributes passed to the function.  This function requires
1723ba6ff9f2SPaul Moore  * exclusive access to @sk, which means it either needs to be in the
1724ba6ff9f2SPaul Moore  * process of being created or locked.  Returns zero on success and negative
1725ba6ff9f2SPaul Moore  * values on failure.
1726446fda4fSPaul Moore  *
1727446fda4fSPaul Moore  */
1728ba6ff9f2SPaul Moore int cipso_v4_sock_setattr(struct sock *sk,
1729446fda4fSPaul Moore 			  const struct cipso_v4_doi *doi_def,
1730446fda4fSPaul Moore 			  const struct netlbl_lsm_secattr *secattr)
1731446fda4fSPaul Moore {
1732446fda4fSPaul Moore 	int ret_val = -EPERM;
1733446fda4fSPaul Moore 	u32 iter;
173491b1ed0aSPaul Moore 	unsigned char *buf;
1735446fda4fSPaul Moore 	u32 buf_len = 0;
1736446fda4fSPaul Moore 	u32 opt_len;
1737446fda4fSPaul Moore 	struct ip_options *opt = NULL;
1738446fda4fSPaul Moore 	struct inet_sock *sk_inet;
1739446fda4fSPaul Moore 	struct inet_connection_sock *sk_conn;
1740446fda4fSPaul Moore 
1741446fda4fSPaul Moore 	/* In the case of sock_create_lite(), the sock->sk field is not
1742446fda4fSPaul Moore 	 * defined yet but it is not a problem as the only users of these
1743446fda4fSPaul Moore 	 * "lite" PF_INET sockets are functions which do an accept() call
1744446fda4fSPaul Moore 	 * afterwards so we will label the socket as part of the accept(). */
1745446fda4fSPaul Moore 	if (sk == NULL)
1746446fda4fSPaul Moore 		return 0;
1747446fda4fSPaul Moore 
174891b1ed0aSPaul Moore 	/* We allocate the maximum CIPSO option size here so we are probably
174991b1ed0aSPaul Moore 	 * being a little wasteful, but it makes our life _much_ easier later
175091b1ed0aSPaul Moore 	 * on and after all we are only talking about 40 bytes. */
175191b1ed0aSPaul Moore 	buf_len = CIPSO_V4_OPT_LEN_MAX;
175291b1ed0aSPaul Moore 	buf = kmalloc(buf_len, GFP_ATOMIC);
175391b1ed0aSPaul Moore 	if (buf == NULL) {
175491b1ed0aSPaul Moore 		ret_val = -ENOMEM;
175591b1ed0aSPaul Moore 		goto socket_setattr_failure;
175691b1ed0aSPaul Moore 	}
175791b1ed0aSPaul Moore 
1758446fda4fSPaul Moore 	/* XXX - This code assumes only one tag per CIPSO option which isn't
1759446fda4fSPaul Moore 	 * really a good assumption to make but since we only support the MAC
1760446fda4fSPaul Moore 	 * tags right now it is a safe assumption. */
1761446fda4fSPaul Moore 	iter = 0;
1762446fda4fSPaul Moore 	do {
176391b1ed0aSPaul Moore 		memset(buf, 0, buf_len);
1764446fda4fSPaul Moore 		switch (doi_def->tags[iter]) {
1765446fda4fSPaul Moore 		case CIPSO_V4_TAG_RBITMAP:
1766446fda4fSPaul Moore 			ret_val = cipso_v4_gentag_rbm(doi_def,
1767446fda4fSPaul Moore 						   secattr,
176891b1ed0aSPaul Moore 						   &buf[CIPSO_V4_HDR_LEN],
176991b1ed0aSPaul Moore 						   buf_len - CIPSO_V4_HDR_LEN);
1770446fda4fSPaul Moore 			break;
1771654bbc2aSPaul Moore 		case CIPSO_V4_TAG_ENUM:
1772654bbc2aSPaul Moore 			ret_val = cipso_v4_gentag_enum(doi_def,
1773654bbc2aSPaul Moore 						   secattr,
1774654bbc2aSPaul Moore 						   &buf[CIPSO_V4_HDR_LEN],
1775654bbc2aSPaul Moore 						   buf_len - CIPSO_V4_HDR_LEN);
1776654bbc2aSPaul Moore 			break;
1777484b3669SPaul Moore 		case CIPSO_V4_TAG_RANGE:
1778484b3669SPaul Moore 			ret_val = cipso_v4_gentag_rng(doi_def,
1779484b3669SPaul Moore 						   secattr,
1780484b3669SPaul Moore 						   &buf[CIPSO_V4_HDR_LEN],
1781484b3669SPaul Moore 						   buf_len - CIPSO_V4_HDR_LEN);
1782484b3669SPaul Moore 			break;
1783446fda4fSPaul Moore 		default:
1784446fda4fSPaul Moore 			ret_val = -EPERM;
1785446fda4fSPaul Moore 			goto socket_setattr_failure;
1786446fda4fSPaul Moore 		}
1787446fda4fSPaul Moore 
1788446fda4fSPaul Moore 		iter++;
178991b1ed0aSPaul Moore 	} while (ret_val < 0 &&
1790446fda4fSPaul Moore 		 iter < CIPSO_V4_TAG_MAXCNT &&
1791446fda4fSPaul Moore 		 doi_def->tags[iter] != CIPSO_V4_TAG_INVALID);
179291b1ed0aSPaul Moore 	if (ret_val < 0)
1793446fda4fSPaul Moore 		goto socket_setattr_failure;
179491b1ed0aSPaul Moore 	cipso_v4_gentag_hdr(doi_def, buf, ret_val);
179591b1ed0aSPaul Moore 	buf_len = CIPSO_V4_HDR_LEN + ret_val;
1796446fda4fSPaul Moore 
1797446fda4fSPaul Moore 	/* We can't use ip_options_get() directly because it makes a call to
1798446fda4fSPaul Moore 	 * ip_options_get_alloc() which allocates memory with GFP_KERNEL and
1799f8687afeSPaul Moore 	 * we won't always have CAP_NET_RAW even though we _always_ want to
1800f8687afeSPaul Moore 	 * set the IPOPT_CIPSO option. */
1801446fda4fSPaul Moore 	opt_len = (buf_len + 3) & ~3;
1802446fda4fSPaul Moore 	opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC);
1803446fda4fSPaul Moore 	if (opt == NULL) {
1804446fda4fSPaul Moore 		ret_val = -ENOMEM;
1805446fda4fSPaul Moore 		goto socket_setattr_failure;
1806446fda4fSPaul Moore 	}
1807446fda4fSPaul Moore 	memcpy(opt->__data, buf, buf_len);
1808446fda4fSPaul Moore 	opt->optlen = opt_len;
1809446fda4fSPaul Moore 	opt->is_data = 1;
1810f8687afeSPaul Moore 	opt->cipso = sizeof(struct iphdr);
1811446fda4fSPaul Moore 	kfree(buf);
1812446fda4fSPaul Moore 	buf = NULL;
1813446fda4fSPaul Moore 
1814446fda4fSPaul Moore 	sk_inet = inet_sk(sk);
1815446fda4fSPaul Moore 	if (sk_inet->is_icsk) {
1816446fda4fSPaul Moore 		sk_conn = inet_csk(sk);
1817446fda4fSPaul Moore 		if (sk_inet->opt)
1818446fda4fSPaul Moore 			sk_conn->icsk_ext_hdr_len -= sk_inet->opt->optlen;
1819446fda4fSPaul Moore 		sk_conn->icsk_ext_hdr_len += opt->optlen;
1820446fda4fSPaul Moore 		sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
1821446fda4fSPaul Moore 	}
1822446fda4fSPaul Moore 	opt = xchg(&sk_inet->opt, opt);
1823446fda4fSPaul Moore 	kfree(opt);
1824446fda4fSPaul Moore 
1825446fda4fSPaul Moore 	return 0;
1826446fda4fSPaul Moore 
1827446fda4fSPaul Moore socket_setattr_failure:
1828446fda4fSPaul Moore 	kfree(buf);
1829446fda4fSPaul Moore 	kfree(opt);
1830446fda4fSPaul Moore 	return ret_val;
1831446fda4fSPaul Moore }
1832446fda4fSPaul Moore 
1833446fda4fSPaul Moore /**
183414a72f53SPaul Moore  * cipso_v4_sock_getattr - Get the security attributes from a sock
183514a72f53SPaul Moore  * @sk: the sock
183614a72f53SPaul Moore  * @secattr: the security attributes
183714a72f53SPaul Moore  *
183814a72f53SPaul Moore  * Description:
183914a72f53SPaul Moore  * Query @sk to see if there is a CIPSO option attached to the sock and if
184014a72f53SPaul Moore  * there is return the CIPSO security attributes in @secattr.  This function
184114a72f53SPaul Moore  * requires that @sk be locked, or privately held, but it does not do any
184214a72f53SPaul Moore  * locking itself.  Returns zero on success and negative values on failure.
184314a72f53SPaul Moore  *
184414a72f53SPaul Moore  */
184514a72f53SPaul Moore int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr)
184614a72f53SPaul Moore {
184714a72f53SPaul Moore 	int ret_val = -ENOMSG;
184814a72f53SPaul Moore 	struct inet_sock *sk_inet;
184914a72f53SPaul Moore 	unsigned char *cipso_ptr;
185014a72f53SPaul Moore 	u32 doi;
185114a72f53SPaul Moore 	struct cipso_v4_doi *doi_def;
185214a72f53SPaul Moore 
185314a72f53SPaul Moore 	sk_inet = inet_sk(sk);
185414a72f53SPaul Moore 	if (sk_inet->opt == NULL || sk_inet->opt->cipso == 0)
185514a72f53SPaul Moore 		return -ENOMSG;
185614a72f53SPaul Moore 	cipso_ptr = sk_inet->opt->__data + sk_inet->opt->cipso -
185714a72f53SPaul Moore 		sizeof(struct iphdr);
185814a72f53SPaul Moore 	ret_val = cipso_v4_cache_check(cipso_ptr, cipso_ptr[1], secattr);
185914a72f53SPaul Moore 	if (ret_val == 0)
186014a72f53SPaul Moore 		return ret_val;
186114a72f53SPaul Moore 
1862*50e5d35cSPaul Moore 	doi = ntohl(get_unaligned((__be32 *)&cipso_ptr[2]));
186314a72f53SPaul Moore 	rcu_read_lock();
18649bb5fd2bSPaul Moore 	doi_def = cipso_v4_doi_search(doi);
186514a72f53SPaul Moore 	if (doi_def == NULL) {
186614a72f53SPaul Moore 		rcu_read_unlock();
186714a72f53SPaul Moore 		return -ENOMSG;
186814a72f53SPaul Moore 	}
186991b1ed0aSPaul Moore 
187091b1ed0aSPaul Moore 	/* XXX - This code assumes only one tag per CIPSO option which isn't
187191b1ed0aSPaul Moore 	 * really a good assumption to make but since we only support the MAC
187291b1ed0aSPaul Moore 	 * tags right now it is a safe assumption. */
187314a72f53SPaul Moore 	switch (cipso_ptr[6]) {
187414a72f53SPaul Moore 	case CIPSO_V4_TAG_RBITMAP:
187514a72f53SPaul Moore 		ret_val = cipso_v4_parsetag_rbm(doi_def,
187614a72f53SPaul Moore 						&cipso_ptr[6],
187714a72f53SPaul Moore 						secattr);
187814a72f53SPaul Moore 		break;
1879654bbc2aSPaul Moore 	case CIPSO_V4_TAG_ENUM:
1880654bbc2aSPaul Moore 		ret_val = cipso_v4_parsetag_enum(doi_def,
1881654bbc2aSPaul Moore 						 &cipso_ptr[6],
1882654bbc2aSPaul Moore 						 secattr);
1883654bbc2aSPaul Moore 		break;
1884484b3669SPaul Moore 	case CIPSO_V4_TAG_RANGE:
1885484b3669SPaul Moore 		ret_val = cipso_v4_parsetag_rng(doi_def,
1886484b3669SPaul Moore 						&cipso_ptr[6],
1887484b3669SPaul Moore 						secattr);
1888484b3669SPaul Moore 		break;
188914a72f53SPaul Moore 	}
189014a72f53SPaul Moore 	rcu_read_unlock();
189114a72f53SPaul Moore 
189214a72f53SPaul Moore 	return ret_val;
189314a72f53SPaul Moore }
189414a72f53SPaul Moore 
189514a72f53SPaul Moore /**
1896446fda4fSPaul Moore  * cipso_v4_skbuff_getattr - Get the security attributes from the CIPSO option
1897446fda4fSPaul Moore  * @skb: the packet
1898446fda4fSPaul Moore  * @secattr: the security attributes
1899446fda4fSPaul Moore  *
1900446fda4fSPaul Moore  * Description:
1901446fda4fSPaul Moore  * Parse the given packet's CIPSO option and return the security attributes.
1902446fda4fSPaul Moore  * Returns zero on success and negative values on failure.
1903446fda4fSPaul Moore  *
1904446fda4fSPaul Moore  */
1905446fda4fSPaul Moore int cipso_v4_skbuff_getattr(const struct sk_buff *skb,
1906446fda4fSPaul Moore 			    struct netlbl_lsm_secattr *secattr)
1907446fda4fSPaul Moore {
1908446fda4fSPaul Moore 	int ret_val = -ENOMSG;
1909446fda4fSPaul Moore 	unsigned char *cipso_ptr;
1910446fda4fSPaul Moore 	u32 doi;
1911446fda4fSPaul Moore 	struct cipso_v4_doi *doi_def;
1912446fda4fSPaul Moore 
1913446fda4fSPaul Moore 	cipso_ptr = CIPSO_V4_OPTPTR(skb);
1914446fda4fSPaul Moore 	if (cipso_v4_cache_check(cipso_ptr, cipso_ptr[1], secattr) == 0)
1915446fda4fSPaul Moore 		return 0;
1916446fda4fSPaul Moore 
1917*50e5d35cSPaul Moore 	doi = ntohl(get_unaligned((__be32 *)&cipso_ptr[2]));
1918446fda4fSPaul Moore 	rcu_read_lock();
19199bb5fd2bSPaul Moore 	doi_def = cipso_v4_doi_search(doi);
1920446fda4fSPaul Moore 	if (doi_def == NULL)
1921446fda4fSPaul Moore 		goto skbuff_getattr_return;
192291b1ed0aSPaul Moore 
192391b1ed0aSPaul Moore 	/* XXX - This code assumes only one tag per CIPSO option which isn't
192491b1ed0aSPaul Moore 	 * really a good assumption to make but since we only support the MAC
192591b1ed0aSPaul Moore 	 * tags right now it is a safe assumption. */
1926446fda4fSPaul Moore 	switch (cipso_ptr[6]) {
1927446fda4fSPaul Moore 	case CIPSO_V4_TAG_RBITMAP:
1928446fda4fSPaul Moore 		ret_val = cipso_v4_parsetag_rbm(doi_def,
1929446fda4fSPaul Moore 						&cipso_ptr[6],
1930446fda4fSPaul Moore 						secattr);
1931446fda4fSPaul Moore 		break;
1932654bbc2aSPaul Moore 	case CIPSO_V4_TAG_ENUM:
1933654bbc2aSPaul Moore 		ret_val = cipso_v4_parsetag_enum(doi_def,
1934654bbc2aSPaul Moore 						 &cipso_ptr[6],
1935654bbc2aSPaul Moore 						 secattr);
1936654bbc2aSPaul Moore 		break;
193738c8947cSPaul Moore 	case CIPSO_V4_TAG_RANGE:
193838c8947cSPaul Moore 		ret_val = cipso_v4_parsetag_rng(doi_def,
193938c8947cSPaul Moore 						&cipso_ptr[6],
194038c8947cSPaul Moore 						secattr);
194138c8947cSPaul Moore 		break;
1942446fda4fSPaul Moore 	}
1943446fda4fSPaul Moore 
1944446fda4fSPaul Moore skbuff_getattr_return:
1945446fda4fSPaul Moore 	rcu_read_unlock();
1946446fda4fSPaul Moore 	return ret_val;
1947446fda4fSPaul Moore }
1948446fda4fSPaul Moore 
1949446fda4fSPaul Moore /*
1950446fda4fSPaul Moore  * Setup Functions
1951446fda4fSPaul Moore  */
1952446fda4fSPaul Moore 
1953446fda4fSPaul Moore /**
1954446fda4fSPaul Moore  * cipso_v4_init - Initialize the CIPSO module
1955446fda4fSPaul Moore  *
1956446fda4fSPaul Moore  * Description:
1957446fda4fSPaul Moore  * Initialize the CIPSO module and prepare it for use.  Returns zero on success
1958446fda4fSPaul Moore  * and negative values on failure.
1959446fda4fSPaul Moore  *
1960446fda4fSPaul Moore  */
1961446fda4fSPaul Moore static int __init cipso_v4_init(void)
1962446fda4fSPaul Moore {
1963446fda4fSPaul Moore 	int ret_val;
1964446fda4fSPaul Moore 
1965446fda4fSPaul Moore 	ret_val = cipso_v4_cache_init();
1966446fda4fSPaul Moore 	if (ret_val != 0)
1967446fda4fSPaul Moore 		panic("Failed to initialize the CIPSO/IPv4 cache (%d)\n",
1968446fda4fSPaul Moore 		      ret_val);
1969446fda4fSPaul Moore 
1970446fda4fSPaul Moore 	return 0;
1971446fda4fSPaul Moore }
1972446fda4fSPaul Moore 
1973446fda4fSPaul Moore subsys_initcall(cipso_v4_init);
1974