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