diff options
author | James Morris <james.l.morris@oracle.com> | 2016-07-06 20:15:34 -0400 |
---|---|---|
committer | James Morris <james.l.morris@oracle.com> | 2016-07-06 20:15:34 -0400 |
commit | d011a4d861ce583466a8ae72a0c8e7f51c8cba4e (patch) | |
tree | 1ff8dfe7d486f5648e69ee85e54cde1987d8296a /net/ipv6 | |
parent | 544e1cea03e6674e3c12a3b8e8cc507c3dbeaf0c (diff) | |
parent | 3f09354ac84c6904787189d85fb306bf60f714b8 (diff) |
Merge branch 'stable-4.8' of git://git.infradead.org/users/pcmoore/selinux into next
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/Makefile | 1 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 9 | ||||
-rw-r--r-- | net/ipv6/calipso.c | 1473 | ||||
-rw-r--r-- | net/ipv6/exthdrs.c | 76 | ||||
-rw-r--r-- | net/ipv6/exthdrs_core.c | 2 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 1 | ||||
-rw-r--r-- | net/ipv6/sysctl_net_ipv6.c | 19 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 12 |
8 files changed, 1587 insertions, 6 deletions
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 6d8ea099213e..c174ccb340a1 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile | |||
@@ -22,6 +22,7 @@ ipv6-$(CONFIG_NETFILTER) += netfilter.o | |||
22 | ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o | 22 | ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o |
23 | ipv6-$(CONFIG_PROC_FS) += proc.o | 23 | ipv6-$(CONFIG_PROC_FS) += proc.o |
24 | ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o | 24 | ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o |
25 | ipv6-$(CONFIG_NETLABEL) += calipso.o | ||
25 | 26 | ||
26 | ipv6-objs += $(ipv6-y) | 27 | ipv6-objs += $(ipv6-y) |
27 | 28 | ||
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index bfa86f040c16..b0994a49dfca 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c | |||
@@ -60,6 +60,7 @@ | |||
60 | #ifdef CONFIG_IPV6_TUNNEL | 60 | #ifdef CONFIG_IPV6_TUNNEL |
61 | #include <net/ip6_tunnel.h> | 61 | #include <net/ip6_tunnel.h> |
62 | #endif | 62 | #endif |
63 | #include <net/calipso.h> | ||
63 | 64 | ||
64 | #include <asm/uaccess.h> | 65 | #include <asm/uaccess.h> |
65 | #include <linux/mroute6.h> | 66 | #include <linux/mroute6.h> |
@@ -977,6 +978,10 @@ static int __init inet6_init(void) | |||
977 | if (err) | 978 | if (err) |
978 | goto pingv6_fail; | 979 | goto pingv6_fail; |
979 | 980 | ||
981 | err = calipso_init(); | ||
982 | if (err) | ||
983 | goto calipso_fail; | ||
984 | |||
980 | #ifdef CONFIG_SYSCTL | 985 | #ifdef CONFIG_SYSCTL |
981 | err = ipv6_sysctl_register(); | 986 | err = ipv6_sysctl_register(); |
982 | if (err) | 987 | if (err) |
@@ -987,8 +992,10 @@ out: | |||
987 | 992 | ||
988 | #ifdef CONFIG_SYSCTL | 993 | #ifdef CONFIG_SYSCTL |
989 | sysctl_fail: | 994 | sysctl_fail: |
990 | pingv6_exit(); | 995 | calipso_exit(); |
991 | #endif | 996 | #endif |
997 | calipso_fail: | ||
998 | pingv6_exit(); | ||
992 | pingv6_fail: | 999 | pingv6_fail: |
993 | ipv6_packet_cleanup(); | 1000 | ipv6_packet_cleanup(); |
994 | ipv6_packet_fail: | 1001 | ipv6_packet_fail: |
diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c new file mode 100644 index 000000000000..c53b92c617c5 --- /dev/null +++ b/net/ipv6/calipso.c | |||
@@ -0,0 +1,1473 @@ | |||
1 | /* | ||
2 | * CALIPSO - Common Architecture Label IPv6 Security Option | ||
3 | * | ||
4 | * This is an implementation of the CALIPSO protocol as specified in | ||
5 | * RFC 5570. | ||
6 | * | ||
7 | * Authors: Paul Moore <paul.moore@hp.com> | ||
8 | * Huw Davies <huw@codeweavers.com> | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | /* (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008 | ||
13 | * (c) Copyright Huw Davies <huw@codeweavers.com>, 2015 | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or modify | ||
16 | * it under the terms of the GNU General Public License as published by | ||
17 | * the Free Software Foundation; either version 2 of the License, or | ||
18 | * (at your option) any later version. | ||
19 | * | ||
20 | * This program is distributed in the hope that it will be useful, | ||
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See | ||
23 | * the GNU General Public License for more details. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License | ||
26 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/init.h> | ||
31 | #include <linux/types.h> | ||
32 | #include <linux/rcupdate.h> | ||
33 | #include <linux/list.h> | ||
34 | #include <linux/spinlock.h> | ||
35 | #include <linux/string.h> | ||
36 | #include <linux/jhash.h> | ||
37 | #include <linux/audit.h> | ||
38 | #include <linux/slab.h> | ||
39 | #include <net/ip.h> | ||
40 | #include <net/icmp.h> | ||
41 | #include <net/tcp.h> | ||
42 | #include <net/netlabel.h> | ||
43 | #include <net/calipso.h> | ||
44 | #include <linux/atomic.h> | ||
45 | #include <linux/bug.h> | ||
46 | #include <asm/unaligned.h> | ||
47 | #include <linux/crc-ccitt.h> | ||
48 | |||
49 | /* Maximium size of the calipso option including | ||
50 | * the two-byte TLV header. | ||
51 | */ | ||
52 | #define CALIPSO_OPT_LEN_MAX (2 + 252) | ||
53 | |||
54 | /* Size of the minimum calipso option including | ||
55 | * the two-byte TLV header. | ||
56 | */ | ||
57 | #define CALIPSO_HDR_LEN (2 + 8) | ||
58 | |||
59 | /* Maximium size of the calipso option including | ||
60 | * the two-byte TLV header and upto 3 bytes of | ||
61 | * leading pad and 7 bytes of trailing pad. | ||
62 | */ | ||
63 | #define CALIPSO_OPT_LEN_MAX_WITH_PAD (3 + CALIPSO_OPT_LEN_MAX + 7) | ||
64 | |||
65 | /* Maximium size of u32 aligned buffer required to hold calipso | ||
66 | * option. Max of 3 initial pad bytes starting from buffer + 3. | ||
67 | * i.e. the worst case is when the previous tlv finishes on 4n + 3. | ||
68 | */ | ||
69 | #define CALIPSO_MAX_BUFFER (6 + CALIPSO_OPT_LEN_MAX) | ||
70 | |||
71 | /* List of available DOI definitions */ | ||
72 | static DEFINE_SPINLOCK(calipso_doi_list_lock); | ||
73 | static LIST_HEAD(calipso_doi_list); | ||
74 | |||
75 | /* Label mapping cache */ | ||
76 | int calipso_cache_enabled = 1; | ||
77 | int calipso_cache_bucketsize = 10; | ||
78 | #define CALIPSO_CACHE_BUCKETBITS 7 | ||
79 | #define CALIPSO_CACHE_BUCKETS BIT(CALIPSO_CACHE_BUCKETBITS) | ||
80 | #define CALIPSO_CACHE_REORDERLIMIT 10 | ||
81 | struct calipso_map_cache_bkt { | ||
82 | spinlock_t lock; | ||
83 | u32 size; | ||
84 | struct list_head list; | ||
85 | }; | ||
86 | |||
87 | struct calipso_map_cache_entry { | ||
88 | u32 hash; | ||
89 | unsigned char *key; | ||
90 | size_t key_len; | ||
91 | |||
92 | struct netlbl_lsm_cache *lsm_data; | ||
93 | |||
94 | u32 activity; | ||
95 | struct list_head list; | ||
96 | }; | ||
97 | |||
98 | static struct calipso_map_cache_bkt *calipso_cache; | ||
99 | |||
100 | /* Label Mapping Cache Functions | ||
101 | */ | ||
102 | |||
103 | /** | ||
104 | * calipso_cache_entry_free - Frees a cache entry | ||
105 | * @entry: the entry to free | ||
106 | * | ||
107 | * Description: | ||
108 | * This function frees the memory associated with a cache entry including the | ||
109 | * LSM cache data if there are no longer any users, i.e. reference count == 0. | ||
110 | * | ||
111 | */ | ||
112 | static void calipso_cache_entry_free(struct calipso_map_cache_entry *entry) | ||
113 | { | ||
114 | if (entry->lsm_data) | ||
115 | netlbl_secattr_cache_free(entry->lsm_data); | ||
116 | kfree(entry->key); | ||
117 | kfree(entry); | ||
118 | } | ||
119 | |||
120 | /** | ||
121 | * calipso_map_cache_hash - Hashing function for the CALIPSO cache | ||
122 | * @key: the hash key | ||
123 | * @key_len: the length of the key in bytes | ||
124 | * | ||
125 | * Description: | ||
126 | * The CALIPSO tag hashing function. Returns a 32-bit hash value. | ||
127 | * | ||
128 | */ | ||
129 | static u32 calipso_map_cache_hash(const unsigned char *key, u32 key_len) | ||
130 | { | ||
131 | return jhash(key, key_len, 0); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * calipso_cache_init - Initialize the CALIPSO cache | ||
136 | * | ||
137 | * Description: | ||
138 | * Initializes the CALIPSO label mapping cache, this function should be called | ||
139 | * before any of the other functions defined in this file. Returns zero on | ||
140 | * success, negative values on error. | ||
141 | * | ||
142 | */ | ||
143 | static int __init calipso_cache_init(void) | ||
144 | { | ||
145 | u32 iter; | ||
146 | |||
147 | calipso_cache = kcalloc(CALIPSO_CACHE_BUCKETS, | ||
148 | sizeof(struct calipso_map_cache_bkt), | ||
149 | GFP_KERNEL); | ||
150 | if (!calipso_cache) | ||
151 | return -ENOMEM; | ||
152 | |||
153 | for (iter = 0; iter < CALIPSO_CACHE_BUCKETS; iter++) { | ||
154 | spin_lock_init(&calipso_cache[iter].lock); | ||
155 | calipso_cache[iter].size = 0; | ||
156 | INIT_LIST_HEAD(&calipso_cache[iter].list); | ||
157 | } | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | /** | ||
163 | * calipso_cache_invalidate - Invalidates the current CALIPSO cache | ||
164 | * | ||
165 | * Description: | ||
166 | * Invalidates and frees any entries in the CALIPSO cache. Returns zero on | ||
167 | * success and negative values on failure. | ||
168 | * | ||
169 | */ | ||
170 | static void calipso_cache_invalidate(void) | ||
171 | { | ||
172 | struct calipso_map_cache_entry *entry, *tmp_entry; | ||
173 | u32 iter; | ||
174 | |||
175 | for (iter = 0; iter < CALIPSO_CACHE_BUCKETS; iter++) { | ||
176 | spin_lock_bh(&calipso_cache[iter].lock); | ||
177 | list_for_each_entry_safe(entry, | ||
178 | tmp_entry, | ||
179 | &calipso_cache[iter].list, list) { | ||
180 | list_del(&entry->list); | ||
181 | calipso_cache_entry_free(entry); | ||
182 | } | ||
183 | calipso_cache[iter].size = 0; | ||
184 | spin_unlock_bh(&calipso_cache[iter].lock); | ||
185 | } | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * calipso_cache_check - Check the CALIPSO cache for a label mapping | ||
190 | * @key: the buffer to check | ||
191 | * @key_len: buffer length in bytes | ||
192 | * @secattr: the security attribute struct to use | ||
193 | * | ||
194 | * Description: | ||
195 | * This function checks the cache to see if a label mapping already exists for | ||
196 | * the given key. If there is a match then the cache is adjusted and the | ||
197 | * @secattr struct is populated with the correct LSM security attributes. The | ||
198 | * cache is adjusted in the following manner if the entry is not already the | ||
199 | * first in the cache bucket: | ||
200 | * | ||
201 | * 1. The cache entry's activity counter is incremented | ||
202 | * 2. The previous (higher ranking) entry's activity counter is decremented | ||
203 | * 3. If the difference between the two activity counters is geater than | ||
204 | * CALIPSO_CACHE_REORDERLIMIT the two entries are swapped | ||
205 | * | ||
206 | * Returns zero on success, -ENOENT for a cache miss, and other negative values | ||
207 | * on error. | ||
208 | * | ||
209 | */ | ||
210 | static int calipso_cache_check(const unsigned char *key, | ||
211 | u32 key_len, | ||
212 | struct netlbl_lsm_secattr *secattr) | ||
213 | { | ||
214 | u32 bkt; | ||
215 | struct calipso_map_cache_entry *entry; | ||
216 | struct calipso_map_cache_entry *prev_entry = NULL; | ||
217 | u32 hash; | ||
218 | |||
219 | if (!calipso_cache_enabled) | ||
220 | return -ENOENT; | ||
221 | |||
222 | hash = calipso_map_cache_hash(key, key_len); | ||
223 | bkt = hash & (CALIPSO_CACHE_BUCKETS - 1); | ||
224 | spin_lock_bh(&calipso_cache[bkt].lock); | ||
225 | list_for_each_entry(entry, &calipso_cache[bkt].list, list) { | ||
226 | if (entry->hash == hash && | ||
227 | entry->key_len == key_len && | ||
228 | memcmp(entry->key, key, key_len) == 0) { | ||
229 | entry->activity += 1; | ||
230 | atomic_inc(&entry->lsm_data->refcount); | ||
231 | secattr->cache = entry->lsm_data; | ||
232 | secattr->flags |= NETLBL_SECATTR_CACHE; | ||
233 | secattr->type = NETLBL_NLTYPE_CALIPSO; | ||
234 | if (!prev_entry) { | ||
235 | spin_unlock_bh(&calipso_cache[bkt].lock); | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | if (prev_entry->activity > 0) | ||
240 | prev_entry->activity -= 1; | ||
241 | if (entry->activity > prev_entry->activity && | ||
242 | entry->activity - prev_entry->activity > | ||
243 | CALIPSO_CACHE_REORDERLIMIT) { | ||
244 | __list_del(entry->list.prev, entry->list.next); | ||
245 | __list_add(&entry->list, | ||
246 | prev_entry->list.prev, | ||
247 | &prev_entry->list); | ||
248 | } | ||
249 | |||
250 | spin_unlock_bh(&calipso_cache[bkt].lock); | ||
251 | return 0; | ||
252 | } | ||
253 | prev_entry = entry; | ||
254 | } | ||
255 | spin_unlock_bh(&calipso_cache[bkt].lock); | ||
256 | |||
257 | return -ENOENT; | ||
258 | } | ||
259 | |||
260 | /** | ||
261 | * calipso_cache_add - Add an entry to the CALIPSO cache | ||
262 | * @calipso_ptr: the CALIPSO option | ||
263 | * @secattr: the packet's security attributes | ||
264 | * | ||
265 | * Description: | ||
266 | * Add a new entry into the CALIPSO label mapping cache. Add the new entry to | ||
267 | * head of the cache bucket's list, if the cache bucket is out of room remove | ||
268 | * the last entry in the list first. It is important to note that there is | ||
269 | * currently no checking for duplicate keys. Returns zero on success, | ||
270 | * negative values on failure. The key stored starts at calipso_ptr + 2, | ||
271 | * i.e. the type and length bytes are not stored, this corresponds to | ||
272 | * calipso_ptr[1] bytes of data. | ||
273 | * | ||
274 | */ | ||
275 | static int calipso_cache_add(const unsigned char *calipso_ptr, | ||
276 | const struct netlbl_lsm_secattr *secattr) | ||
277 | { | ||
278 | int ret_val = -EPERM; | ||
279 | u32 bkt; | ||
280 | struct calipso_map_cache_entry *entry = NULL; | ||
281 | struct calipso_map_cache_entry *old_entry = NULL; | ||
282 | u32 calipso_ptr_len; | ||
283 | |||
284 | if (!calipso_cache_enabled || calipso_cache_bucketsize <= 0) | ||
285 | return 0; | ||
286 | |||
287 | calipso_ptr_len = calipso_ptr[1]; | ||
288 | |||
289 | entry = kzalloc(sizeof(*entry), GFP_ATOMIC); | ||
290 | if (!entry) | ||
291 | return -ENOMEM; | ||
292 | entry->key = kmemdup(calipso_ptr + 2, calipso_ptr_len, GFP_ATOMIC); | ||
293 | if (!entry->key) { | ||
294 | ret_val = -ENOMEM; | ||
295 | goto cache_add_failure; | ||
296 | } | ||
297 | entry->key_len = calipso_ptr_len; | ||
298 | entry->hash = calipso_map_cache_hash(calipso_ptr, calipso_ptr_len); | ||
299 | atomic_inc(&secattr->cache->refcount); | ||
300 | entry->lsm_data = secattr->cache; | ||
301 | |||
302 | bkt = entry->hash & (CALIPSO_CACHE_BUCKETS - 1); | ||
303 | spin_lock_bh(&calipso_cache[bkt].lock); | ||
304 | if (calipso_cache[bkt].size < calipso_cache_bucketsize) { | ||
305 | list_add(&entry->list, &calipso_cache[bkt].list); | ||
306 | calipso_cache[bkt].size += 1; | ||
307 | } else { | ||
308 | old_entry = list_entry(calipso_cache[bkt].list.prev, | ||
309 | struct calipso_map_cache_entry, list); | ||
310 | list_del(&old_entry->list); | ||
311 | list_add(&entry->list, &calipso_cache[bkt].list); | ||
312 | calipso_cache_entry_free(old_entry); | ||
313 | } | ||
314 | spin_unlock_bh(&calipso_cache[bkt].lock); | ||
315 | |||
316 | return 0; | ||
317 | |||
318 | cache_add_failure: | ||
319 | if (entry) | ||
320 | calipso_cache_entry_free(entry); | ||
321 | return ret_val; | ||
322 | } | ||
323 | |||
324 | /* DOI List Functions | ||
325 | */ | ||
326 | |||
327 | /** | ||
328 | * calipso_doi_search - Searches for a DOI definition | ||
329 | * @doi: the DOI to search for | ||
330 | * | ||
331 | * Description: | ||
332 | * Search the DOI definition list for a DOI definition with a DOI value that | ||
333 | * matches @doi. The caller is responsible for calling rcu_read_[un]lock(). | ||
334 | * Returns a pointer to the DOI definition on success and NULL on failure. | ||
335 | */ | ||
336 | static struct calipso_doi *calipso_doi_search(u32 doi) | ||
337 | { | ||
338 | struct calipso_doi *iter; | ||
339 | |||
340 | list_for_each_entry_rcu(iter, &calipso_doi_list, list) | ||
341 | if (iter->doi == doi && atomic_read(&iter->refcount)) | ||
342 | return iter; | ||
343 | return NULL; | ||
344 | } | ||
345 | |||
346 | /** | ||
347 | * calipso_doi_add - Add a new DOI to the CALIPSO protocol engine | ||
348 | * @doi_def: the DOI structure | ||
349 | * @audit_info: NetLabel audit information | ||
350 | * | ||
351 | * Description: | ||
352 | * The caller defines a new DOI for use by the CALIPSO engine and calls this | ||
353 | * function to add it to the list of acceptable domains. The caller must | ||
354 | * ensure that the mapping table specified in @doi_def->map meets all of the | ||
355 | * requirements of the mapping type (see calipso.h for details). Returns | ||
356 | * zero on success and non-zero on failure. | ||
357 | * | ||
358 | */ | ||
359 | static int calipso_doi_add(struct calipso_doi *doi_def, | ||
360 | struct netlbl_audit *audit_info) | ||
361 | { | ||
362 | int ret_val = -EINVAL; | ||
363 | u32 doi; | ||
364 | u32 doi_type; | ||
365 | struct audit_buffer *audit_buf; | ||
366 | |||
367 | doi = doi_def->doi; | ||
368 | doi_type = doi_def->type; | ||
369 | |||
370 | if (doi_def->doi == CALIPSO_DOI_UNKNOWN) | ||
371 | goto doi_add_return; | ||
372 | |||
373 | atomic_set(&doi_def->refcount, 1); | ||
374 | |||
375 | spin_lock(&calipso_doi_list_lock); | ||
376 | if (calipso_doi_search(doi_def->doi)) { | ||
377 | spin_unlock(&calipso_doi_list_lock); | ||
378 | ret_val = -EEXIST; | ||
379 | goto doi_add_return; | ||
380 | } | ||
381 | list_add_tail_rcu(&doi_def->list, &calipso_doi_list); | ||
382 | spin_unlock(&calipso_doi_list_lock); | ||
383 | ret_val = 0; | ||
384 | |||
385 | doi_add_return: | ||
386 | audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_ADD, audit_info); | ||
387 | if (audit_buf) { | ||
388 | const char *type_str; | ||
389 | |||
390 | switch (doi_type) { | ||
391 | case CALIPSO_MAP_PASS: | ||
392 | type_str = "pass"; | ||
393 | break; | ||
394 | default: | ||
395 | type_str = "(unknown)"; | ||
396 | } | ||
397 | audit_log_format(audit_buf, | ||
398 | " calipso_doi=%u calipso_type=%s res=%u", | ||
399 | doi, type_str, ret_val == 0 ? 1 : 0); | ||
400 | audit_log_end(audit_buf); | ||
401 | } | ||
402 | |||
403 | return ret_val; | ||
404 | } | ||
405 | |||
406 | /** | ||
407 | * calipso_doi_free - Frees a DOI definition | ||
408 | * @doi_def: the DOI definition | ||
409 | * | ||
410 | * Description: | ||
411 | * This function frees all of the memory associated with a DOI definition. | ||
412 | * | ||
413 | */ | ||
414 | static void calipso_doi_free(struct calipso_doi *doi_def) | ||
415 | { | ||
416 | kfree(doi_def); | ||
417 | } | ||
418 | |||
419 | /** | ||
420 | * calipso_doi_free_rcu - Frees a DOI definition via the RCU pointer | ||
421 | * @entry: the entry's RCU field | ||
422 | * | ||
423 | * Description: | ||
424 | * This function is designed to be used as a callback to the call_rcu() | ||
425 | * function so that the memory allocated to the DOI definition can be released | ||
426 | * safely. | ||
427 | * | ||
428 | */ | ||
429 | static void calipso_doi_free_rcu(struct rcu_head *entry) | ||
430 | { | ||
431 | struct calipso_doi *doi_def; | ||
432 | |||
433 | doi_def = container_of(entry, struct calipso_doi, rcu); | ||
434 | calipso_doi_free(doi_def); | ||
435 | } | ||
436 | |||
437 | /** | ||
438 | * calipso_doi_remove - Remove an existing DOI from the CALIPSO protocol engine | ||
439 | * @doi: the DOI value | ||
440 | * @audit_secid: the LSM secid to use in the audit message | ||
441 | * | ||
442 | * Description: | ||
443 | * Removes a DOI definition from the CALIPSO engine. The NetLabel routines will | ||
444 | * be called to release their own LSM domain mappings as well as our own | ||
445 | * domain list. Returns zero on success and negative values on failure. | ||
446 | * | ||
447 | */ | ||
448 | static int calipso_doi_remove(u32 doi, struct netlbl_audit *audit_info) | ||
449 | { | ||
450 | int ret_val; | ||
451 | struct calipso_doi *doi_def; | ||
452 | struct audit_buffer *audit_buf; | ||
453 | |||
454 | spin_lock(&calipso_doi_list_lock); | ||
455 | doi_def = calipso_doi_search(doi); | ||
456 | if (!doi_def) { | ||
457 | spin_unlock(&calipso_doi_list_lock); | ||
458 | ret_val = -ENOENT; | ||
459 | goto doi_remove_return; | ||
460 | } | ||
461 | if (!atomic_dec_and_test(&doi_def->refcount)) { | ||
462 | spin_unlock(&calipso_doi_list_lock); | ||
463 | ret_val = -EBUSY; | ||
464 | goto doi_remove_return; | ||
465 | } | ||
466 | list_del_rcu(&doi_def->list); | ||
467 | spin_unlock(&calipso_doi_list_lock); | ||
468 | |||
469 | call_rcu(&doi_def->rcu, calipso_doi_free_rcu); | ||
470 | ret_val = 0; | ||
471 | |||
472 | doi_remove_return: | ||
473 | audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_DEL, audit_info); | ||
474 | if (audit_buf) { | ||
475 | audit_log_format(audit_buf, | ||
476 | " calipso_doi=%u res=%u", | ||
477 | doi, ret_val == 0 ? 1 : 0); | ||
478 | audit_log_end(audit_buf); | ||
479 | } | ||
480 | |||
481 | return ret_val; | ||
482 | } | ||
483 | |||
484 | /** | ||
485 | * calipso_doi_getdef - Returns a reference to a valid DOI definition | ||
486 | * @doi: the DOI value | ||
487 | * | ||
488 | * Description: | ||
489 | * Searches for a valid DOI definition and if one is found it is returned to | ||
490 | * the caller. Otherwise NULL is returned. The caller must ensure that | ||
491 | * calipso_doi_putdef() is called when the caller is done. | ||
492 | * | ||
493 | */ | ||
494 | static struct calipso_doi *calipso_doi_getdef(u32 doi) | ||
495 | { | ||
496 | struct calipso_doi *doi_def; | ||
497 | |||
498 | rcu_read_lock(); | ||
499 | doi_def = calipso_doi_search(doi); | ||
500 | if (!doi_def) | ||
501 | goto doi_getdef_return; | ||
502 | if (!atomic_inc_not_zero(&doi_def->refcount)) | ||
503 | doi_def = NULL; | ||
504 | |||
505 | doi_getdef_return: | ||
506 | rcu_read_unlock(); | ||
507 | return doi_def; | ||
508 | } | ||
509 | |||
510 | /** | ||
511 | * calipso_doi_putdef - Releases a reference for the given DOI definition | ||
512 | * @doi_def: the DOI definition | ||
513 | * | ||
514 | * Description: | ||
515 | * Releases a DOI definition reference obtained from calipso_doi_getdef(). | ||
516 | * | ||
517 | */ | ||
518 | static void calipso_doi_putdef(struct calipso_doi *doi_def) | ||
519 | { | ||
520 | if (!doi_def) | ||
521 | return; | ||
522 | |||
523 | if (!atomic_dec_and_test(&doi_def->refcount)) | ||
524 | return; | ||
525 | spin_lock(&calipso_doi_list_lock); | ||
526 | list_del_rcu(&doi_def->list); | ||
527 | spin_unlock(&calipso_doi_list_lock); | ||
528 | |||
529 | call_rcu(&doi_def->rcu, calipso_doi_free_rcu); | ||
530 | } | ||
531 | |||
532 | /** | ||
533 | * calipso_doi_walk - Iterate through the DOI definitions | ||
534 | * @skip_cnt: skip past this number of DOI definitions, updated | ||
535 | * @callback: callback for each DOI definition | ||
536 | * @cb_arg: argument for the callback function | ||
537 | * | ||
538 | * Description: | ||
539 | * Iterate over the DOI definition list, skipping the first @skip_cnt entries. | ||
540 | * For each entry call @callback, if @callback returns a negative value stop | ||
541 | * 'walking' through the list and return. Updates the value in @skip_cnt upon | ||
542 | * return. Returns zero on success, negative values on failure. | ||
543 | * | ||
544 | */ | ||
545 | static int calipso_doi_walk(u32 *skip_cnt, | ||
546 | int (*callback)(struct calipso_doi *doi_def, | ||
547 | void *arg), | ||
548 | void *cb_arg) | ||
549 | { | ||
550 | int ret_val = -ENOENT; | ||
551 | u32 doi_cnt = 0; | ||
552 | struct calipso_doi *iter_doi; | ||
553 | |||
554 | rcu_read_lock(); | ||
555 | list_for_each_entry_rcu(iter_doi, &calipso_doi_list, list) | ||
556 | if (atomic_read(&iter_doi->refcount) > 0) { | ||
557 | if (doi_cnt++ < *skip_cnt) | ||
558 | continue; | ||
559 | ret_val = callback(iter_doi, cb_arg); | ||
560 | if (ret_val < 0) { | ||
561 | doi_cnt--; | ||
562 | goto doi_walk_return; | ||
563 | } | ||
564 | } | ||
565 | |||
566 | doi_walk_return: | ||
567 | rcu_read_unlock(); | ||
568 | *skip_cnt = doi_cnt; | ||
569 | return ret_val; | ||
570 | } | ||
571 | |||
572 | /** | ||
573 | * calipso_validate - Validate a CALIPSO option | ||
574 | * @skb: the packet | ||
575 | * @option: the start of the option | ||
576 | * | ||
577 | * Description: | ||
578 | * This routine is called to validate a CALIPSO option. | ||
579 | * If the option is valid then %true is returned, otherwise | ||
580 | * %false is returned. | ||
581 | * | ||
582 | * The caller should have already checked that the length of the | ||
583 | * option (including the TLV header) is >= 10 and that the catmap | ||
584 | * length is consistent with the option length. | ||
585 | * | ||
586 | * We leave checks on the level and categories to the socket layer. | ||
587 | */ | ||
588 | bool calipso_validate(const struct sk_buff *skb, const unsigned char *option) | ||
589 | { | ||
590 | struct calipso_doi *doi_def; | ||
591 | bool ret_val; | ||
592 | u16 crc, len = option[1] + 2; | ||
593 | static const u8 zero[2]; | ||
594 | |||
595 | /* The original CRC runs over the option including the TLV header | ||
596 | * with the CRC-16 field (at offset 8) zeroed out. */ | ||
597 | crc = crc_ccitt(0xffff, option, 8); | ||
598 | crc = crc_ccitt(crc, zero, sizeof(zero)); | ||
599 | if (len > 10) | ||
600 | crc = crc_ccitt(crc, option + 10, len - 10); | ||
601 | crc = ~crc; | ||
602 | if (option[8] != (crc & 0xff) || option[9] != ((crc >> 8) & 0xff)) | ||
603 | return false; | ||
604 | |||
605 | rcu_read_lock(); | ||
606 | doi_def = calipso_doi_search(get_unaligned_be32(option + 2)); | ||
607 | ret_val = !!doi_def; | ||
608 | rcu_read_unlock(); | ||
609 | |||
610 | return ret_val; | ||
611 | } | ||
612 | |||
613 | /** | ||
614 | * calipso_map_cat_hton - Perform a category mapping from host to network | ||
615 | * @doi_def: the DOI definition | ||
616 | * @secattr: the security attributes | ||
617 | * @net_cat: the zero'd out category bitmap in network/CALIPSO format | ||
618 | * @net_cat_len: the length of the CALIPSO bitmap in bytes | ||
619 | * | ||
620 | * Description: | ||
621 | * Perform a label mapping to translate a local MLS category bitmap to the | ||
622 | * correct CALIPSO bitmap using the given DOI definition. Returns the minimum | ||
623 | * size in bytes of the network bitmap on success, negative values otherwise. | ||
624 | * | ||
625 | */ | ||
626 | static int calipso_map_cat_hton(const struct calipso_doi *doi_def, | ||
627 | const struct netlbl_lsm_secattr *secattr, | ||
628 | unsigned char *net_cat, | ||
629 | u32 net_cat_len) | ||
630 | { | ||
631 | int spot = -1; | ||
632 | u32 net_spot_max = 0; | ||
633 | u32 net_clen_bits = net_cat_len * 8; | ||
634 | |||
635 | for (;;) { | ||
636 | spot = netlbl_catmap_walk(secattr->attr.mls.cat, | ||
637 | spot + 1); | ||
638 | if (spot < 0) | ||
639 | break; | ||
640 | if (spot >= net_clen_bits) | ||
641 | return -ENOSPC; | ||
642 | netlbl_bitmap_setbit(net_cat, spot, 1); | ||
643 | |||
644 | if (spot > net_spot_max) | ||
645 | net_spot_max = spot; | ||
646 | } | ||
647 | |||
648 | return (net_spot_max / 32 + 1) * 4; | ||
649 | } | ||
650 | |||
651 | /** | ||
652 | * calipso_map_cat_ntoh - Perform a category mapping from network to host | ||
653 | * @doi_def: the DOI definition | ||
654 | * @net_cat: the category bitmap in network/CALIPSO format | ||
655 | * @net_cat_len: the length of the CALIPSO bitmap in bytes | ||
656 | * @secattr: the security attributes | ||
657 | * | ||
658 | * Description: | ||
659 | * Perform a label mapping to translate a CALIPSO bitmap to the correct local | ||
660 | * MLS category bitmap using the given DOI definition. Returns zero on | ||
661 | * success, negative values on failure. | ||
662 | * | ||
663 | */ | ||
664 | static int calipso_map_cat_ntoh(const struct calipso_doi *doi_def, | ||
665 | const unsigned char *net_cat, | ||
666 | u32 net_cat_len, | ||
667 | struct netlbl_lsm_secattr *secattr) | ||
668 | { | ||
669 | int ret_val; | ||
670 | int spot = -1; | ||
671 | u32 net_clen_bits = net_cat_len * 8; | ||
672 | |||
673 | for (;;) { | ||
674 | spot = netlbl_bitmap_walk(net_cat, | ||
675 | net_clen_bits, | ||
676 | spot + 1, | ||
677 | 1); | ||
678 | if (spot < 0) { | ||
679 | if (spot == -2) | ||
680 | return -EFAULT; | ||
681 | return 0; | ||
682 | } | ||
683 | |||
684 | ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat, | ||
685 | spot, | ||
686 | GFP_ATOMIC); | ||
687 | if (ret_val != 0) | ||
688 | return ret_val; | ||
689 | } | ||
690 | |||
691 | return -EINVAL; | ||
692 | } | ||
693 | |||
694 | /** | ||
695 | * calipso_pad_write - Writes pad bytes in TLV format | ||
696 | * @buf: the buffer | ||
697 | * @offset: offset from start of buffer to write padding | ||
698 | * @count: number of pad bytes to write | ||
699 | * | ||
700 | * Description: | ||
701 | * Write @count bytes of TLV padding into @buffer starting at offset @offset. | ||
702 | * @count should be less than 8 - see RFC 4942. | ||
703 | * | ||
704 | */ | ||
705 | static int calipso_pad_write(unsigned char *buf, unsigned int offset, | ||
706 | unsigned int count) | ||
707 | { | ||
708 | if (WARN_ON_ONCE(count >= 8)) | ||
709 | return -EINVAL; | ||
710 | |||
711 | switch (count) { | ||
712 | case 0: | ||
713 | break; | ||
714 | case 1: | ||
715 | buf[offset] = IPV6_TLV_PAD1; | ||
716 | break; | ||
717 | default: | ||
718 | buf[offset] = IPV6_TLV_PADN; | ||
719 | buf[offset + 1] = count - 2; | ||
720 | if (count > 2) | ||
721 | memset(buf + offset + 2, 0, count - 2); | ||
722 | break; | ||
723 | } | ||
724 | return 0; | ||
725 | } | ||
726 | |||
727 | /** | ||
728 | * calipso_genopt - Generate a CALIPSO option | ||
729 | * @buf: the option buffer | ||
730 | * @start: offset from which to write | ||
731 | * @buf_len: the size of opt_buf | ||
732 | * @doi_def: the CALIPSO DOI to use | ||
733 | * @secattr: the security attributes | ||
734 | * | ||
735 | * Description: | ||
736 | * Generate a CALIPSO option using the DOI definition and security attributes | ||
737 | * passed to the function. This also generates upto three bytes of leading | ||
738 | * padding that ensures that the option is 4n + 2 aligned. It returns the | ||
739 | * number of bytes written (including any initial padding). | ||
740 | */ | ||
741 | static int calipso_genopt(unsigned char *buf, u32 start, u32 buf_len, | ||
742 | const struct calipso_doi *doi_def, | ||
743 | const struct netlbl_lsm_secattr *secattr) | ||
744 | { | ||
745 | int ret_val; | ||
746 | u32 len, pad; | ||
747 | u16 crc; | ||
748 | static const unsigned char padding[4] = {2, 1, 0, 3}; | ||
749 | unsigned char *calipso; | ||
750 | |||
751 | /* CALIPSO has 4n + 2 alignment */ | ||
752 | pad = padding[start & 3]; | ||
753 | if (buf_len <= start + pad + CALIPSO_HDR_LEN) | ||
754 | return -ENOSPC; | ||
755 | |||
756 | if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0) | ||
757 | return -EPERM; | ||
758 | |||
759 | len = CALIPSO_HDR_LEN; | ||
760 | |||
761 | if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { | ||
762 | ret_val = calipso_map_cat_hton(doi_def, | ||
763 | secattr, | ||
764 | buf + start + pad + len, | ||
765 | buf_len - start - pad - len); | ||
766 | if (ret_val < 0) | ||
767 | return ret_val; | ||
768 | len += ret_val; | ||
769 | } | ||
770 | |||
771 | calipso_pad_write(buf, start, pad); | ||
772 | calipso = buf + start + pad; | ||
773 | |||
774 | calipso[0] = IPV6_TLV_CALIPSO; | ||
775 | calipso[1] = len - 2; | ||
776 | *(__be32 *)(calipso + 2) = htonl(doi_def->doi); | ||
777 | calipso[6] = (len - CALIPSO_HDR_LEN) / 4; | ||
778 | calipso[7] = secattr->attr.mls.lvl, | ||
779 | crc = ~crc_ccitt(0xffff, calipso, len); | ||
780 | calipso[8] = crc & 0xff; | ||
781 | calipso[9] = (crc >> 8) & 0xff; | ||
782 | return pad + len; | ||
783 | } | ||
784 | |||
785 | /* Hop-by-hop hdr helper functions | ||
786 | */ | ||
787 | |||
788 | /** | ||
789 | * calipso_opt_update - Replaces socket's hop options with a new set | ||
790 | * @sk: the socket | ||
791 | * @hop: new hop options | ||
792 | * | ||
793 | * Description: | ||
794 | * Replaces @sk's hop options with @hop. @hop may be NULL to leave | ||
795 | * the socket with no hop options. | ||
796 | * | ||
797 | */ | ||
798 | static int calipso_opt_update(struct sock *sk, struct ipv6_opt_hdr *hop) | ||
799 | { | ||
800 | struct ipv6_txoptions *old = txopt_get(inet6_sk(sk)), *txopts; | ||
801 | |||
802 | txopts = ipv6_renew_options_kern(sk, old, IPV6_HOPOPTS, | ||
803 | hop, hop ? ipv6_optlen(hop) : 0); | ||
804 | txopt_put(old); | ||
805 | if (IS_ERR(txopts)) | ||
806 | return PTR_ERR(txopts); | ||
807 | |||
808 | txopts = ipv6_update_options(sk, txopts); | ||
809 | if (txopts) { | ||
810 | atomic_sub(txopts->tot_len, &sk->sk_omem_alloc); | ||
811 | txopt_put(txopts); | ||
812 | } | ||
813 | |||
814 | return 0; | ||
815 | } | ||
816 | |||
817 | /** | ||
818 | * calipso_tlv_len - Returns the length of the TLV | ||
819 | * @opt: the option header | ||
820 | * @offset: offset of the TLV within the header | ||
821 | * | ||
822 | * Description: | ||
823 | * Returns the length of the TLV option at offset @offset within | ||
824 | * the option header @opt. Checks that the entire TLV fits inside | ||
825 | * the option header, returns a negative value if this is not the case. | ||
826 | */ | ||
827 | static int calipso_tlv_len(struct ipv6_opt_hdr *opt, unsigned int offset) | ||
828 | { | ||
829 | unsigned char *tlv = (unsigned char *)opt; | ||
830 | unsigned int opt_len = ipv6_optlen(opt), tlv_len; | ||
831 | |||
832 | if (offset < sizeof(*opt) || offset >= opt_len) | ||
833 | return -EINVAL; | ||
834 | if (tlv[offset] == IPV6_TLV_PAD1) | ||
835 | return 1; | ||
836 | if (offset + 1 >= opt_len) | ||
837 | return -EINVAL; | ||
838 | tlv_len = tlv[offset + 1] + 2; | ||
839 | if (offset + tlv_len > opt_len) | ||
840 | return -EINVAL; | ||
841 | return tlv_len; | ||
842 | } | ||
843 | |||
844 | /** | ||
845 | * calipso_opt_find - Finds the CALIPSO option in an IPv6 hop options header | ||
846 | * @hop: the hop options header | ||
847 | * @start: on return holds the offset of any leading padding | ||
848 | * @end: on return holds the offset of the first non-pad TLV after CALIPSO | ||
849 | * | ||
850 | * Description: | ||
851 | * Finds the space occupied by a CALIPSO option (including any leading and | ||
852 | * trailing padding). | ||
853 | * | ||
854 | * If a CALIPSO option exists set @start and @end to the | ||
855 | * offsets within @hop of the start of padding before the first | ||
856 | * CALIPSO option and the end of padding after the first CALIPSO | ||
857 | * option. In this case the function returns 0. | ||
858 | * | ||
859 | * In the absence of a CALIPSO option, @start and @end will be | ||
860 | * set to the start and end of any trailing padding in the header. | ||
861 | * This is useful when appending a new option, as the caller may want | ||
862 | * to overwrite some of this padding. In this case the function will | ||
863 | * return -ENOENT. | ||
864 | */ | ||
865 | static int calipso_opt_find(struct ipv6_opt_hdr *hop, unsigned int *start, | ||
866 | unsigned int *end) | ||
867 | { | ||
868 | int ret_val = -ENOENT, tlv_len; | ||
869 | unsigned int opt_len, offset, offset_s = 0, offset_e = 0; | ||
870 | unsigned char *opt = (unsigned char *)hop; | ||
871 | |||
872 | opt_len = ipv6_optlen(hop); | ||
873 | offset = sizeof(*hop); | ||
874 | |||
875 | while (offset < opt_len) { | ||
876 | tlv_len = calipso_tlv_len(hop, offset); | ||
877 | if (tlv_len < 0) | ||
878 | return tlv_len; | ||
879 | |||
880 | switch (opt[offset]) { | ||
881 | case IPV6_TLV_PAD1: | ||
882 | case IPV6_TLV_PADN: | ||
883 | if (offset_e) | ||
884 | offset_e = offset; | ||
885 | break; | ||
886 | case IPV6_TLV_CALIPSO: | ||
887 | ret_val = 0; | ||
888 | offset_e = offset; | ||
889 | break; | ||
890 | default: | ||
891 | if (offset_e == 0) | ||
892 | offset_s = offset; | ||
893 | else | ||
894 | goto out; | ||
895 | } | ||
896 | offset += tlv_len; | ||
897 | } | ||
898 | |||
899 | out: | ||
900 | if (offset_s) | ||
901 | *start = offset_s + calipso_tlv_len(hop, offset_s); | ||
902 | else | ||
903 | *start = sizeof(*hop); | ||
904 | if (offset_e) | ||
905 | *end = offset_e + calipso_tlv_len(hop, offset_e); | ||
906 | else | ||
907 | *end = opt_len; | ||
908 | |||
909 | return ret_val; | ||
910 | } | ||
911 | |||
912 | /** | ||
913 | * calipso_opt_insert - Inserts a CALIPSO option into an IPv6 hop opt hdr | ||
914 | * @hop: the original hop options header | ||
915 | * @doi_def: the CALIPSO DOI to use | ||
916 | * @secattr: the specific security attributes of the socket | ||
917 | * | ||
918 | * Description: | ||
919 | * Creates a new hop options header based on @hop with a | ||
920 | * CALIPSO option added to it. If @hop already contains a CALIPSO | ||
921 | * option this is overwritten, otherwise the new option is appended | ||
922 | * after any existing options. If @hop is NULL then the new header | ||
923 | * will contain just the CALIPSO option and any needed padding. | ||
924 | * | ||
925 | */ | ||
926 | static struct ipv6_opt_hdr * | ||
927 | calipso_opt_insert(struct ipv6_opt_hdr *hop, | ||
928 | const struct calipso_doi *doi_def, | ||
929 | const struct netlbl_lsm_secattr *secattr) | ||
930 | { | ||
931 | unsigned int start, end, buf_len, pad, hop_len; | ||
932 | struct ipv6_opt_hdr *new; | ||
933 | int ret_val; | ||
934 | |||
935 | if (hop) { | ||
936 | hop_len = ipv6_optlen(hop); | ||
937 | ret_val = calipso_opt_find(hop, &start, &end); | ||
938 | if (ret_val && ret_val != -ENOENT) | ||
939 | return ERR_PTR(ret_val); | ||
940 | } else { | ||
941 | hop_len = 0; | ||
942 | start = sizeof(*hop); | ||
943 | end = 0; | ||
944 | } | ||
945 | |||
946 | buf_len = hop_len + start - end + CALIPSO_OPT_LEN_MAX_WITH_PAD; | ||
947 | new = kzalloc(buf_len, GFP_ATOMIC); | ||
948 | if (!new) | ||
949 | return ERR_PTR(-ENOMEM); | ||
950 | |||
951 | if (start > sizeof(*hop)) | ||
952 | memcpy(new, hop, start); | ||
953 | ret_val = calipso_genopt((unsigned char *)new, start, buf_len, doi_def, | ||
954 | secattr); | ||
955 | if (ret_val < 0) | ||
956 | return ERR_PTR(ret_val); | ||
957 | |||
958 | buf_len = start + ret_val; | ||
959 | /* At this point buf_len aligns to 4n, so (buf_len & 4) pads to 8n */ | ||
960 | pad = ((buf_len & 4) + (end & 7)) & 7; | ||
961 | calipso_pad_write((unsigned char *)new, buf_len, pad); | ||
962 | buf_len += pad; | ||
963 | |||
964 | if (end != hop_len) { | ||
965 | memcpy((char *)new + buf_len, (char *)hop + end, hop_len - end); | ||
966 | buf_len += hop_len - end; | ||
967 | } | ||
968 | new->nexthdr = 0; | ||
969 | new->hdrlen = buf_len / 8 - 1; | ||
970 | |||
971 | return new; | ||
972 | } | ||
973 | |||
974 | /** | ||
975 | * calipso_opt_del - Removes the CALIPSO option from an option header | ||
976 | * @hop: the original header | ||
977 | * @new: the new header | ||
978 | * | ||
979 | * Description: | ||
980 | * Creates a new header based on @hop without any CALIPSO option. If @hop | ||
981 | * doesn't contain a CALIPSO option it returns -ENOENT. If @hop contains | ||
982 | * no other non-padding options, it returns zero with @new set to NULL. | ||
983 | * Otherwise it returns zero, creates a new header without the CALIPSO | ||
984 | * option (and removing as much padding as possible) and returns with | ||
985 | * @new set to that header. | ||
986 | * | ||
987 | */ | ||
988 | static int calipso_opt_del(struct ipv6_opt_hdr *hop, | ||
989 | struct ipv6_opt_hdr **new) | ||
990 | { | ||
991 | int ret_val; | ||
992 | unsigned int start, end, delta, pad, hop_len; | ||
993 | |||
994 | ret_val = calipso_opt_find(hop, &start, &end); | ||
995 | if (ret_val) | ||
996 | return ret_val; | ||
997 | |||
998 | hop_len = ipv6_optlen(hop); | ||
999 | if (start == sizeof(*hop) && end == hop_len) { | ||
1000 | /* There's no other option in the header so return NULL */ | ||
1001 | *new = NULL; | ||
1002 | return 0; | ||
1003 | } | ||
1004 | |||
1005 | delta = (end - start) & ~7; | ||
1006 | *new = kzalloc(hop_len - delta, GFP_ATOMIC); | ||
1007 | if (!*new) | ||
1008 | return -ENOMEM; | ||
1009 | |||
1010 | memcpy(*new, hop, start); | ||
1011 | (*new)->hdrlen -= delta / 8; | ||
1012 | pad = (end - start) & 7; | ||
1013 | calipso_pad_write((unsigned char *)*new, start, pad); | ||
1014 | if (end != hop_len) | ||
1015 | memcpy((char *)*new + start + pad, (char *)hop + end, | ||
1016 | hop_len - end); | ||
1017 | |||
1018 | return 0; | ||
1019 | } | ||
1020 | |||
1021 | /** | ||
1022 | * calipso_opt_getattr - Get the security attributes from a memory block | ||
1023 | * @calipso: the CALIPSO option | ||
1024 | * @secattr: the security attributes | ||
1025 | * | ||
1026 | * Description: | ||
1027 | * Inspect @calipso and return the security attributes in @secattr. | ||
1028 | * Returns zero on success and negative values on failure. | ||
1029 | * | ||
1030 | */ | ||
1031 | static int calipso_opt_getattr(const unsigned char *calipso, | ||
1032 | struct netlbl_lsm_secattr *secattr) | ||
1033 | { | ||
1034 | int ret_val = -ENOMSG; | ||
1035 | u32 doi, len = calipso[1], cat_len = calipso[6] * 4; | ||
1036 | struct calipso_doi *doi_def; | ||
1037 | |||
1038 | if (cat_len + 8 > len) | ||
1039 | return -EINVAL; | ||
1040 | |||
1041 | if (calipso_cache_check(calipso + 2, calipso[1], secattr) == 0) | ||
1042 | return 0; | ||
1043 | |||
1044 | doi = get_unaligned_be32(calipso + 2); | ||
1045 | rcu_read_lock(); | ||
1046 | doi_def = calipso_doi_search(doi); | ||
1047 | if (!doi_def) | ||
1048 | goto getattr_return; | ||
1049 | |||
1050 | secattr->attr.mls.lvl = calipso[7]; | ||
1051 | secattr->flags |= NETLBL_SECATTR_MLS_LVL; | ||
1052 | |||
1053 | if (cat_len) { | ||
1054 | ret_val = calipso_map_cat_ntoh(doi_def, | ||
1055 | calipso + 10, | ||
1056 | cat_len, | ||
1057 | secattr); | ||
1058 | if (ret_val != 0) { | ||
1059 | netlbl_catmap_free(secattr->attr.mls.cat); | ||
1060 | goto getattr_return; | ||
1061 | } | ||
1062 | |||
1063 | secattr->flags |= NETLBL_SECATTR_MLS_CAT; | ||
1064 | } | ||
1065 | |||
1066 | secattr->type = NETLBL_NLTYPE_CALIPSO; | ||
1067 | |||
1068 | getattr_return: | ||
1069 | rcu_read_unlock(); | ||
1070 | return ret_val; | ||
1071 | } | ||
1072 | |||
1073 | /* sock functions. | ||
1074 | */ | ||
1075 | |||
1076 | /** | ||
1077 | * calipso_sock_getattr - Get the security attributes from a sock | ||
1078 | * @sk: the sock | ||
1079 | * @secattr: the security attributes | ||
1080 | * | ||
1081 | * Description: | ||
1082 | * Query @sk to see if there is a CALIPSO option attached to the sock and if | ||
1083 | * there is return the CALIPSO security attributes in @secattr. This function | ||
1084 | * requires that @sk be locked, or privately held, but it does not do any | ||
1085 | * locking itself. Returns zero on success and negative values on failure. | ||
1086 | * | ||
1087 | */ | ||
1088 | static int calipso_sock_getattr(struct sock *sk, | ||
1089 | struct netlbl_lsm_secattr *secattr) | ||
1090 | { | ||
1091 | struct ipv6_opt_hdr *hop; | ||
1092 | int opt_len, len, ret_val = -ENOMSG, offset; | ||
1093 | unsigned char *opt; | ||
1094 | struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk)); | ||
1095 | |||
1096 | if (!txopts || !txopts->hopopt) | ||
1097 | goto done; | ||
1098 | |||
1099 | hop = txopts->hopopt; | ||
1100 | opt = (unsigned char *)hop; | ||
1101 | opt_len = ipv6_optlen(hop); | ||
1102 | offset = sizeof(*hop); | ||
1103 | while (offset < opt_len) { | ||
1104 | len = calipso_tlv_len(hop, offset); | ||
1105 | if (len < 0) { | ||
1106 | ret_val = len; | ||
1107 | goto done; | ||
1108 | } | ||
1109 | switch (opt[offset]) { | ||
1110 | case IPV6_TLV_CALIPSO: | ||
1111 | if (len < CALIPSO_HDR_LEN) | ||
1112 | ret_val = -EINVAL; | ||
1113 | else | ||
1114 | ret_val = calipso_opt_getattr(&opt[offset], | ||
1115 | secattr); | ||
1116 | goto done; | ||
1117 | default: | ||
1118 | offset += len; | ||
1119 | break; | ||
1120 | } | ||
1121 | } | ||
1122 | done: | ||
1123 | txopt_put(txopts); | ||
1124 | return ret_val; | ||
1125 | } | ||
1126 | |||
1127 | /** | ||
1128 | * calipso_sock_setattr - Add a CALIPSO option to a socket | ||
1129 | * @sk: the socket | ||
1130 | * @doi_def: the CALIPSO DOI to use | ||
1131 | * @secattr: the specific security attributes of the socket | ||
1132 | * | ||
1133 | * Description: | ||
1134 | * Set the CALIPSO option on the given socket using the DOI definition and | ||
1135 | * security attributes passed to the function. This function requires | ||
1136 | * exclusive access to @sk, which means it either needs to be in the | ||
1137 | * process of being created or locked. Returns zero on success and negative | ||
1138 | * values on failure. | ||
1139 | * | ||
1140 | */ | ||
1141 | static int calipso_sock_setattr(struct sock *sk, | ||
1142 | const struct calipso_doi *doi_def, | ||
1143 | const struct netlbl_lsm_secattr *secattr) | ||
1144 | { | ||
1145 | int ret_val; | ||
1146 | struct ipv6_opt_hdr *old, *new; | ||
1147 | struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk)); | ||
1148 | |||
1149 | old = NULL; | ||
1150 | if (txopts) | ||
1151 | old = txopts->hopopt; | ||
1152 | |||
1153 | new = calipso_opt_insert(old, doi_def, secattr); | ||
1154 | txopt_put(txopts); | ||
1155 | if (IS_ERR(new)) | ||
1156 | return PTR_ERR(new); | ||
1157 | |||
1158 | ret_val = calipso_opt_update(sk, new); | ||
1159 | |||
1160 | kfree(new); | ||
1161 | return ret_val; | ||
1162 | } | ||
1163 | |||
1164 | /** | ||
1165 | * calipso_sock_delattr - Delete the CALIPSO option from a socket | ||
1166 | * @sk: the socket | ||
1167 | * | ||
1168 | * Description: | ||
1169 | * Removes the CALIPSO option from a socket, if present. | ||
1170 | * | ||
1171 | */ | ||
1172 | static void calipso_sock_delattr(struct sock *sk) | ||
1173 | { | ||
1174 | struct ipv6_opt_hdr *new_hop; | ||
1175 | struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk)); | ||
1176 | |||
1177 | if (!txopts || !txopts->hopopt) | ||
1178 | goto done; | ||
1179 | |||
1180 | if (calipso_opt_del(txopts->hopopt, &new_hop)) | ||
1181 | goto done; | ||
1182 | |||
1183 | calipso_opt_update(sk, new_hop); | ||
1184 | kfree(new_hop); | ||
1185 | |||
1186 | done: | ||
1187 | txopt_put(txopts); | ||
1188 | } | ||
1189 | |||
1190 | /* request sock functions. | ||
1191 | */ | ||
1192 | |||
1193 | /** | ||
1194 | * calipso_req_setattr - Add a CALIPSO option to a connection request socket | ||
1195 | * @req: the connection request socket | ||
1196 | * @doi_def: the CALIPSO DOI to use | ||
1197 | * @secattr: the specific security attributes of the socket | ||
1198 | * | ||
1199 | * Description: | ||
1200 | * Set the CALIPSO option on the given socket using the DOI definition and | ||
1201 | * security attributes passed to the function. Returns zero on success and | ||
1202 | * negative values on failure. | ||
1203 | * | ||
1204 | */ | ||
1205 | static int calipso_req_setattr(struct request_sock *req, | ||
1206 | const struct calipso_doi *doi_def, | ||
1207 | const struct netlbl_lsm_secattr *secattr) | ||
1208 | { | ||
1209 | struct ipv6_txoptions *txopts; | ||
1210 | struct inet_request_sock *req_inet = inet_rsk(req); | ||
1211 | struct ipv6_opt_hdr *old, *new; | ||
1212 | struct sock *sk = sk_to_full_sk(req_to_sk(req)); | ||
1213 | |||
1214 | if (req_inet->ipv6_opt && req_inet->ipv6_opt->hopopt) | ||
1215 | old = req_inet->ipv6_opt->hopopt; | ||
1216 | else | ||
1217 | old = NULL; | ||
1218 | |||
1219 | new = calipso_opt_insert(old, doi_def, secattr); | ||
1220 | if (IS_ERR(new)) | ||
1221 | return PTR_ERR(new); | ||
1222 | |||
1223 | txopts = ipv6_renew_options_kern(sk, req_inet->ipv6_opt, IPV6_HOPOPTS, | ||
1224 | new, new ? ipv6_optlen(new) : 0); | ||
1225 | |||
1226 | kfree(new); | ||
1227 | |||
1228 | if (IS_ERR(txopts)) | ||
1229 | return PTR_ERR(txopts); | ||
1230 | |||
1231 | txopts = xchg(&req_inet->ipv6_opt, txopts); | ||
1232 | if (txopts) { | ||
1233 | atomic_sub(txopts->tot_len, &sk->sk_omem_alloc); | ||
1234 | txopt_put(txopts); | ||
1235 | } | ||
1236 | |||
1237 | return 0; | ||
1238 | } | ||
1239 | |||
1240 | /** | ||
1241 | * calipso_req_delattr - Delete the CALIPSO option from a request socket | ||
1242 | * @reg: the request socket | ||
1243 | * | ||
1244 | * Description: | ||
1245 | * Removes the CALIPSO option from a request socket, if present. | ||
1246 | * | ||
1247 | */ | ||
1248 | static void calipso_req_delattr(struct request_sock *req) | ||
1249 | { | ||
1250 | struct inet_request_sock *req_inet = inet_rsk(req); | ||
1251 | struct ipv6_opt_hdr *new; | ||
1252 | struct ipv6_txoptions *txopts; | ||
1253 | struct sock *sk = sk_to_full_sk(req_to_sk(req)); | ||
1254 | |||
1255 | if (!req_inet->ipv6_opt || !req_inet->ipv6_opt->hopopt) | ||
1256 | return; | ||
1257 | |||
1258 | if (calipso_opt_del(req_inet->ipv6_opt->hopopt, &new)) | ||
1259 | return; /* Nothing to do */ | ||
1260 | |||
1261 | txopts = ipv6_renew_options_kern(sk, req_inet->ipv6_opt, IPV6_HOPOPTS, | ||
1262 | new, new ? ipv6_optlen(new) : 0); | ||
1263 | |||
1264 | if (!IS_ERR(txopts)) { | ||
1265 | txopts = xchg(&req_inet->ipv6_opt, txopts); | ||
1266 | if (txopts) { | ||
1267 | atomic_sub(txopts->tot_len, &sk->sk_omem_alloc); | ||
1268 | txopt_put(txopts); | ||
1269 | } | ||
1270 | } | ||
1271 | kfree(new); | ||
1272 | } | ||
1273 | |||
1274 | /* skbuff functions. | ||
1275 | */ | ||
1276 | |||
1277 | /** | ||
1278 | * calipso_skbuff_optptr - Find the CALIPSO option in the packet | ||
1279 | * @skb: the packet | ||
1280 | * | ||
1281 | * Description: | ||
1282 | * Parse the packet's IP header looking for a CALIPSO option. Returns a pointer | ||
1283 | * to the start of the CALIPSO option on success, NULL if one if not found. | ||
1284 | * | ||
1285 | */ | ||
1286 | static unsigned char *calipso_skbuff_optptr(const struct sk_buff *skb) | ||
1287 | { | ||
1288 | const struct ipv6hdr *ip6_hdr = ipv6_hdr(skb); | ||
1289 | int offset; | ||
1290 | |||
1291 | if (ip6_hdr->nexthdr != NEXTHDR_HOP) | ||
1292 | return NULL; | ||
1293 | |||
1294 | offset = ipv6_find_tlv(skb, sizeof(*ip6_hdr), IPV6_TLV_CALIPSO); | ||
1295 | if (offset >= 0) | ||
1296 | return (unsigned char *)ip6_hdr + offset; | ||
1297 | |||
1298 | return NULL; | ||
1299 | } | ||
1300 | |||
1301 | /** | ||
1302 | * calipso_skbuff_setattr - Set the CALIPSO option on a packet | ||
1303 | * @skb: the packet | ||
1304 | * @doi_def: the CALIPSO DOI to use | ||
1305 | * @secattr: the security attributes | ||
1306 | * | ||
1307 | * Description: | ||
1308 | * Set the CALIPSO option on the given packet based on the security attributes. | ||
1309 | * Returns a pointer to the IP header on success and NULL on failure. | ||
1310 | * | ||
1311 | */ | ||
1312 | static int calipso_skbuff_setattr(struct sk_buff *skb, | ||
1313 | const struct calipso_doi *doi_def, | ||
1314 | const struct netlbl_lsm_secattr *secattr) | ||
1315 | { | ||
1316 | int ret_val; | ||
1317 | struct ipv6hdr *ip6_hdr; | ||
1318 | struct ipv6_opt_hdr *hop; | ||
1319 | unsigned char buf[CALIPSO_MAX_BUFFER]; | ||
1320 | int len_delta, new_end, pad; | ||
1321 | unsigned int start, end; | ||
1322 | |||
1323 | ip6_hdr = ipv6_hdr(skb); | ||
1324 | if (ip6_hdr->nexthdr == NEXTHDR_HOP) { | ||
1325 | hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1); | ||
1326 | ret_val = calipso_opt_find(hop, &start, &end); | ||
1327 | if (ret_val && ret_val != -ENOENT) | ||
1328 | return ret_val; | ||
1329 | } else { | ||
1330 | start = 0; | ||
1331 | end = 0; | ||
1332 | } | ||
1333 | |||
1334 | memset(buf, 0, sizeof(buf)); | ||
1335 | ret_val = calipso_genopt(buf, start & 3, sizeof(buf), doi_def, secattr); | ||
1336 | if (ret_val < 0) | ||
1337 | return ret_val; | ||
1338 | |||
1339 | new_end = start + ret_val; | ||
1340 | /* At this point new_end aligns to 4n, so (new_end & 4) pads to 8n */ | ||
1341 | pad = ((new_end & 4) + (end & 7)) & 7; | ||
1342 | len_delta = new_end - (int)end + pad; | ||
1343 | ret_val = skb_cow(skb, skb_headroom(skb) + len_delta); | ||
1344 | if (ret_val < 0) | ||
1345 | return ret_val; | ||
1346 | |||
1347 | if (len_delta) { | ||
1348 | if (len_delta > 0) | ||
1349 | skb_push(skb, len_delta); | ||
1350 | else | ||
1351 | skb_pull(skb, -len_delta); | ||
1352 | memmove((char *)ip6_hdr - len_delta, ip6_hdr, | ||
1353 | sizeof(*ip6_hdr) + start); | ||
1354 | skb_reset_network_header(skb); | ||
1355 | ip6_hdr = ipv6_hdr(skb); | ||
1356 | } | ||
1357 | |||
1358 | hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1); | ||
1359 | if (start == 0) { | ||
1360 | struct ipv6_opt_hdr *new_hop = (struct ipv6_opt_hdr *)buf; | ||
1361 | |||
1362 | new_hop->nexthdr = ip6_hdr->nexthdr; | ||
1363 | new_hop->hdrlen = len_delta / 8 - 1; | ||
1364 | ip6_hdr->nexthdr = NEXTHDR_HOP; | ||
1365 | } else { | ||
1366 | hop->hdrlen += len_delta / 8; | ||
1367 | } | ||
1368 | memcpy((char *)hop + start, buf + (start & 3), new_end - start); | ||
1369 | calipso_pad_write((unsigned char *)hop, new_end, pad); | ||
1370 | |||
1371 | return 0; | ||
1372 | } | ||
1373 | |||
1374 | /** | ||
1375 | * calipso_skbuff_delattr - Delete any CALIPSO options from a packet | ||
1376 | * @skb: the packet | ||
1377 | * | ||
1378 | * Description: | ||
1379 | * Removes any and all CALIPSO options from the given packet. Returns zero on | ||
1380 | * success, negative values on failure. | ||
1381 | * | ||
1382 | */ | ||
1383 | static int calipso_skbuff_delattr(struct sk_buff *skb) | ||
1384 | { | ||
1385 | int ret_val; | ||
1386 | struct ipv6hdr *ip6_hdr; | ||
1387 | struct ipv6_opt_hdr *old_hop; | ||
1388 | u32 old_hop_len, start = 0, end = 0, delta, size, pad; | ||
1389 | |||
1390 | if (!calipso_skbuff_optptr(skb)) | ||
1391 | return 0; | ||
1392 | |||
1393 | /* since we are changing the packet we should make a copy */ | ||
1394 | ret_val = skb_cow(skb, skb_headroom(skb)); | ||
1395 | if (ret_val < 0) | ||
1396 | return ret_val; | ||
1397 | |||
1398 | ip6_hdr = ipv6_hdr(skb); | ||
1399 | old_hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1); | ||
1400 | old_hop_len = ipv6_optlen(old_hop); | ||
1401 | |||
1402 | ret_val = calipso_opt_find(old_hop, &start, &end); | ||
1403 | if (ret_val) | ||
1404 | return ret_val; | ||
1405 | |||
1406 | if (start == sizeof(*old_hop) && end == old_hop_len) { | ||
1407 | /* There's no other option in the header so we delete | ||
1408 | * the whole thing. */ | ||
1409 | delta = old_hop_len; | ||
1410 | size = sizeof(*ip6_hdr); | ||
1411 | ip6_hdr->nexthdr = old_hop->nexthdr; | ||
1412 | } else { | ||
1413 | delta = (end - start) & ~7; | ||
1414 | if (delta) | ||
1415 | old_hop->hdrlen -= delta / 8; | ||
1416 | pad = (end - start) & 7; | ||
1417 | size = sizeof(*ip6_hdr) + start + pad; | ||
1418 | calipso_pad_write((unsigned char *)old_hop, start, pad); | ||
1419 | } | ||
1420 | |||
1421 | if (delta) { | ||
1422 | skb_pull(skb, delta); | ||
1423 | memmove((char *)ip6_hdr + delta, ip6_hdr, size); | ||
1424 | skb_reset_network_header(skb); | ||
1425 | } | ||
1426 | |||
1427 | return 0; | ||
1428 | } | ||
1429 | |||
1430 | static const struct netlbl_calipso_ops ops = { | ||
1431 | .doi_add = calipso_doi_add, | ||
1432 | .doi_free = calipso_doi_free, | ||
1433 | .doi_remove = calipso_doi_remove, | ||
1434 | .doi_getdef = calipso_doi_getdef, | ||
1435 | .doi_putdef = calipso_doi_putdef, | ||
1436 | .doi_walk = calipso_doi_walk, | ||
1437 | .sock_getattr = calipso_sock_getattr, | ||
1438 | .sock_setattr = calipso_sock_setattr, | ||
1439 | .sock_delattr = calipso_sock_delattr, | ||
1440 | .req_setattr = calipso_req_setattr, | ||
1441 | .req_delattr = calipso_req_delattr, | ||
1442 | .opt_getattr = calipso_opt_getattr, | ||
1443 | .skbuff_optptr = calipso_skbuff_optptr, | ||
1444 | .skbuff_setattr = calipso_skbuff_setattr, | ||
1445 | .skbuff_delattr = calipso_skbuff_delattr, | ||
1446 | .cache_invalidate = calipso_cache_invalidate, | ||
1447 | .cache_add = calipso_cache_add | ||
1448 | }; | ||
1449 | |||
1450 | /** | ||
1451 | * calipso_init - Initialize the CALIPSO module | ||
1452 | * | ||
1453 | * Description: | ||
1454 | * Initialize the CALIPSO module and prepare it for use. Returns zero on | ||
1455 | * success and negative values on failure. | ||
1456 | * | ||
1457 | */ | ||
1458 | int __init calipso_init(void) | ||
1459 | { | ||
1460 | int ret_val; | ||
1461 | |||
1462 | ret_val = calipso_cache_init(); | ||
1463 | if (!ret_val) | ||
1464 | netlbl_calipso_ops_register(&ops); | ||
1465 | return ret_val; | ||
1466 | } | ||
1467 | |||
1468 | void calipso_exit(void) | ||
1469 | { | ||
1470 | netlbl_calipso_ops_register(NULL); | ||
1471 | calipso_cache_invalidate(); | ||
1472 | kfree(calipso_cache); | ||
1473 | } | ||
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 8de5dd7aaa05..139ceb68bd37 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c | |||
@@ -43,6 +43,7 @@ | |||
43 | #include <net/ndisc.h> | 43 | #include <net/ndisc.h> |
44 | #include <net/ip6_route.h> | 44 | #include <net/ip6_route.h> |
45 | #include <net/addrconf.h> | 45 | #include <net/addrconf.h> |
46 | #include <net/calipso.h> | ||
46 | #if IS_ENABLED(CONFIG_IPV6_MIP6) | 47 | #if IS_ENABLED(CONFIG_IPV6_MIP6) |
47 | #include <net/xfrm.h> | 48 | #include <net/xfrm.h> |
48 | #endif | 49 | #endif |
@@ -603,6 +604,28 @@ drop: | |||
603 | return false; | 604 | return false; |
604 | } | 605 | } |
605 | 606 | ||
607 | /* CALIPSO RFC 5570 */ | ||
608 | |||
609 | static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff) | ||
610 | { | ||
611 | const unsigned char *nh = skb_network_header(skb); | ||
612 | |||
613 | if (nh[optoff + 1] < 8) | ||
614 | goto drop; | ||
615 | |||
616 | if (nh[optoff + 6] * 4 + 8 > nh[optoff + 1]) | ||
617 | goto drop; | ||
618 | |||
619 | if (!calipso_validate(skb, nh + optoff)) | ||
620 | goto drop; | ||
621 | |||
622 | return true; | ||
623 | |||
624 | drop: | ||
625 | kfree_skb(skb); | ||
626 | return false; | ||
627 | } | ||
628 | |||
606 | static const struct tlvtype_proc tlvprochopopt_lst[] = { | 629 | static const struct tlvtype_proc tlvprochopopt_lst[] = { |
607 | { | 630 | { |
608 | .type = IPV6_TLV_ROUTERALERT, | 631 | .type = IPV6_TLV_ROUTERALERT, |
@@ -612,6 +635,10 @@ static const struct tlvtype_proc tlvprochopopt_lst[] = { | |||
612 | .type = IPV6_TLV_JUMBO, | 635 | .type = IPV6_TLV_JUMBO, |
613 | .func = ipv6_hop_jumbo, | 636 | .func = ipv6_hop_jumbo, |
614 | }, | 637 | }, |
638 | { | ||
639 | .type = IPV6_TLV_CALIPSO, | ||
640 | .func = ipv6_hop_calipso, | ||
641 | }, | ||
615 | { -1, } | 642 | { -1, } |
616 | }; | 643 | }; |
617 | 644 | ||
@@ -758,6 +785,27 @@ static int ipv6_renew_option(void *ohdr, | |||
758 | return 0; | 785 | return 0; |
759 | } | 786 | } |
760 | 787 | ||
788 | /** | ||
789 | * ipv6_renew_options - replace a specific ext hdr with a new one. | ||
790 | * | ||
791 | * @sk: sock from which to allocate memory | ||
792 | * @opt: original options | ||
793 | * @newtype: option type to replace in @opt | ||
794 | * @newopt: new option of type @newtype to replace (user-mem) | ||
795 | * @newoptlen: length of @newopt | ||
796 | * | ||
797 | * Returns a new set of options which is a copy of @opt with the | ||
798 | * option type @newtype replaced with @newopt. | ||
799 | * | ||
800 | * @opt may be NULL, in which case a new set of options is returned | ||
801 | * containing just @newopt. | ||
802 | * | ||
803 | * @newopt may be NULL, in which case the specified option type is | ||
804 | * not copied into the new set of options. | ||
805 | * | ||
806 | * The new set of options is allocated from the socket option memory | ||
807 | * buffer of @sk. | ||
808 | */ | ||
761 | struct ipv6_txoptions * | 809 | struct ipv6_txoptions * |
762 | ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, | 810 | ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, |
763 | int newtype, | 811 | int newtype, |
@@ -830,6 +878,34 @@ out: | |||
830 | return ERR_PTR(err); | 878 | return ERR_PTR(err); |
831 | } | 879 | } |
832 | 880 | ||
881 | /** | ||
882 | * ipv6_renew_options_kern - replace a specific ext hdr with a new one. | ||
883 | * | ||
884 | * @sk: sock from which to allocate memory | ||
885 | * @opt: original options | ||
886 | * @newtype: option type to replace in @opt | ||
887 | * @newopt: new option of type @newtype to replace (kernel-mem) | ||
888 | * @newoptlen: length of @newopt | ||
889 | * | ||
890 | * See ipv6_renew_options(). The difference is that @newopt is | ||
891 | * kernel memory, rather than user memory. | ||
892 | */ | ||
893 | struct ipv6_txoptions * | ||
894 | ipv6_renew_options_kern(struct sock *sk, struct ipv6_txoptions *opt, | ||
895 | int newtype, struct ipv6_opt_hdr *newopt, | ||
896 | int newoptlen) | ||
897 | { | ||
898 | struct ipv6_txoptions *ret_val; | ||
899 | const mm_segment_t old_fs = get_fs(); | ||
900 | |||
901 | set_fs(KERNEL_DS); | ||
902 | ret_val = ipv6_renew_options(sk, opt, newtype, | ||
903 | (struct ipv6_opt_hdr __user *)newopt, | ||
904 | newoptlen); | ||
905 | set_fs(old_fs); | ||
906 | return ret_val; | ||
907 | } | ||
908 | |||
833 | struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, | 909 | struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, |
834 | struct ipv6_txoptions *opt) | 910 | struct ipv6_txoptions *opt) |
835 | { | 911 | { |
diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c index 9508a20fbf61..305e2ed730bf 100644 --- a/net/ipv6/exthdrs_core.c +++ b/net/ipv6/exthdrs_core.c | |||
@@ -112,7 +112,7 @@ int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp, | |||
112 | } | 112 | } |
113 | EXPORT_SYMBOL(ipv6_skip_exthdr); | 113 | EXPORT_SYMBOL(ipv6_skip_exthdr); |
114 | 114 | ||
115 | int ipv6_find_tlv(struct sk_buff *skb, int offset, int type) | 115 | int ipv6_find_tlv(const struct sk_buff *skb, int offset, int type) |
116 | { | 116 | { |
117 | const unsigned char *nh = skb_network_header(skb); | 117 | const unsigned char *nh = skb_network_header(skb); |
118 | int packet_len = skb_tail_pointer(skb) - skb_network_header(skb); | 118 | int packet_len = skb_tail_pointer(skb) - skb_network_header(skb); |
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index a9895e15ee9c..5330262ab673 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
@@ -98,7 +98,6 @@ int ip6_ra_control(struct sock *sk, int sel) | |||
98 | return 0; | 98 | return 0; |
99 | } | 99 | } |
100 | 100 | ||
101 | static | ||
102 | struct ipv6_txoptions *ipv6_update_options(struct sock *sk, | 101 | struct ipv6_txoptions *ipv6_update_options(struct sock *sk, |
103 | struct ipv6_txoptions *opt) | 102 | struct ipv6_txoptions *opt) |
104 | { | 103 | { |
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 45243bbe5253..69c50e737c54 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c | |||
@@ -15,6 +15,9 @@ | |||
15 | #include <net/ipv6.h> | 15 | #include <net/ipv6.h> |
16 | #include <net/addrconf.h> | 16 | #include <net/addrconf.h> |
17 | #include <net/inet_frag.h> | 17 | #include <net/inet_frag.h> |
18 | #ifdef CONFIG_NETLABEL | ||
19 | #include <net/calipso.h> | ||
20 | #endif | ||
18 | 21 | ||
19 | static int one = 1; | 22 | static int one = 1; |
20 | static int auto_flowlabels_min; | 23 | static int auto_flowlabels_min; |
@@ -106,6 +109,22 @@ static struct ctl_table ipv6_rotable[] = { | |||
106 | .proc_handler = proc_dointvec_minmax, | 109 | .proc_handler = proc_dointvec_minmax, |
107 | .extra1 = &one | 110 | .extra1 = &one |
108 | }, | 111 | }, |
112 | #ifdef CONFIG_NETLABEL | ||
113 | { | ||
114 | .procname = "calipso_cache_enable", | ||
115 | .data = &calipso_cache_enabled, | ||
116 | .maxlen = sizeof(int), | ||
117 | .mode = 0644, | ||
118 | .proc_handler = proc_dointvec, | ||
119 | }, | ||
120 | { | ||
121 | .procname = "calipso_cache_bucket_size", | ||
122 | .data = &calipso_cache_bucketsize, | ||
123 | .maxlen = sizeof(int), | ||
124 | .mode = 0644, | ||
125 | .proc_handler = proc_dointvec, | ||
126 | }, | ||
127 | #endif /* CONFIG_NETLABEL */ | ||
109 | { } | 128 | { } |
110 | }; | 129 | }; |
111 | 130 | ||
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 79e33e02f11a..818b3b5cd490 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -443,6 +443,7 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst, | |||
443 | { | 443 | { |
444 | struct inet_request_sock *ireq = inet_rsk(req); | 444 | struct inet_request_sock *ireq = inet_rsk(req); |
445 | struct ipv6_pinfo *np = inet6_sk(sk); | 445 | struct ipv6_pinfo *np = inet6_sk(sk); |
446 | struct ipv6_txoptions *opt; | ||
446 | struct flowi6 *fl6 = &fl->u.ip6; | 447 | struct flowi6 *fl6 = &fl->u.ip6; |
447 | struct sk_buff *skb; | 448 | struct sk_buff *skb; |
448 | int err = -ENOMEM; | 449 | int err = -ENOMEM; |
@@ -463,8 +464,10 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst, | |||
463 | fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts)); | 464 | fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts)); |
464 | 465 | ||
465 | rcu_read_lock(); | 466 | rcu_read_lock(); |
466 | err = ip6_xmit(sk, skb, fl6, rcu_dereference(np->opt), | 467 | opt = ireq->ipv6_opt; |
467 | np->tclass); | 468 | if (!opt) |
469 | opt = rcu_dereference(np->opt); | ||
470 | err = ip6_xmit(sk, skb, fl6, opt, np->tclass); | ||
468 | rcu_read_unlock(); | 471 | rcu_read_unlock(); |
469 | err = net_xmit_eval(err); | 472 | err = net_xmit_eval(err); |
470 | } | 473 | } |
@@ -476,6 +479,7 @@ done: | |||
476 | 479 | ||
477 | static void tcp_v6_reqsk_destructor(struct request_sock *req) | 480 | static void tcp_v6_reqsk_destructor(struct request_sock *req) |
478 | { | 481 | { |
482 | kfree(inet_rsk(req)->ipv6_opt); | ||
479 | kfree_skb(inet_rsk(req)->pktopts); | 483 | kfree_skb(inet_rsk(req)->pktopts); |
480 | } | 484 | } |
481 | 485 | ||
@@ -1109,7 +1113,9 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * | |||
1109 | but we make one more one thing there: reattach optmem | 1113 | but we make one more one thing there: reattach optmem |
1110 | to newsk. | 1114 | to newsk. |
1111 | */ | 1115 | */ |
1112 | opt = rcu_dereference(np->opt); | 1116 | opt = ireq->ipv6_opt; |
1117 | if (!opt) | ||
1118 | opt = rcu_dereference(np->opt); | ||
1113 | if (opt) { | 1119 | if (opt) { |
1114 | opt = ipv6_dup_options(newsk, opt); | 1120 | opt = ipv6_dup_options(newsk, opt); |
1115 | RCU_INIT_POINTER(newnp->opt, opt); | 1121 | RCU_INIT_POINTER(newnp->opt, opt); |