calipso: Set the calipso socket label to match the secattr.
[deliverable/linux.git] / net / ipv6 / calipso.c
CommitLineData
cb72d382
HD
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>
ceba1832
HD
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
cb72d382
HD
65
66/* List of available DOI definitions */
67static DEFINE_SPINLOCK(calipso_doi_list_lock);
68static LIST_HEAD(calipso_doi_list);
69
70/* DOI List Functions
71 */
72
73/**
74 * calipso_doi_search - Searches for a DOI definition
75 * @doi: the DOI to search for
76 *
77 * Description:
78 * Search the DOI definition list for a DOI definition with a DOI value that
79 * matches @doi. The caller is responsible for calling rcu_read_[un]lock().
80 * Returns a pointer to the DOI definition on success and NULL on failure.
81 */
82static struct calipso_doi *calipso_doi_search(u32 doi)
83{
84 struct calipso_doi *iter;
85
86 list_for_each_entry_rcu(iter, &calipso_doi_list, list)
87 if (iter->doi == doi && atomic_read(&iter->refcount))
88 return iter;
89 return NULL;
90}
91
92/**
93 * calipso_doi_add - Add a new DOI to the CALIPSO protocol engine
94 * @doi_def: the DOI structure
95 * @audit_info: NetLabel audit information
96 *
97 * Description:
98 * The caller defines a new DOI for use by the CALIPSO engine and calls this
99 * function to add it to the list of acceptable domains. The caller must
100 * ensure that the mapping table specified in @doi_def->map meets all of the
101 * requirements of the mapping type (see calipso.h for details). Returns
102 * zero on success and non-zero on failure.
103 *
104 */
105static int calipso_doi_add(struct calipso_doi *doi_def,
106 struct netlbl_audit *audit_info)
107{
108 int ret_val = -EINVAL;
109 u32 doi;
110 u32 doi_type;
111 struct audit_buffer *audit_buf;
112
113 doi = doi_def->doi;
114 doi_type = doi_def->type;
115
116 if (doi_def->doi == CALIPSO_DOI_UNKNOWN)
117 goto doi_add_return;
118
119 atomic_set(&doi_def->refcount, 1);
120
121 spin_lock(&calipso_doi_list_lock);
122 if (calipso_doi_search(doi_def->doi)) {
123 spin_unlock(&calipso_doi_list_lock);
124 ret_val = -EEXIST;
125 goto doi_add_return;
126 }
127 list_add_tail_rcu(&doi_def->list, &calipso_doi_list);
128 spin_unlock(&calipso_doi_list_lock);
129 ret_val = 0;
130
131doi_add_return:
132 audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_ADD, audit_info);
133 if (audit_buf) {
134 const char *type_str;
135
136 switch (doi_type) {
137 case CALIPSO_MAP_PASS:
138 type_str = "pass";
139 break;
140 default:
141 type_str = "(unknown)";
142 }
143 audit_log_format(audit_buf,
144 " calipso_doi=%u calipso_type=%s res=%u",
145 doi, type_str, ret_val == 0 ? 1 : 0);
146 audit_log_end(audit_buf);
147 }
148
149 return ret_val;
150}
151
152/**
153 * calipso_doi_free - Frees a DOI definition
154 * @doi_def: the DOI definition
155 *
156 * Description:
157 * This function frees all of the memory associated with a DOI definition.
158 *
159 */
160static void calipso_doi_free(struct calipso_doi *doi_def)
161{
162 kfree(doi_def);
163}
164
a5e34490
HD
165/**
166 * calipso_doi_free_rcu - Frees a DOI definition via the RCU pointer
167 * @entry: the entry's RCU field
168 *
169 * Description:
170 * This function is designed to be used as a callback to the call_rcu()
171 * function so that the memory allocated to the DOI definition can be released
172 * safely.
173 *
174 */
175static void calipso_doi_free_rcu(struct rcu_head *entry)
176{
177 struct calipso_doi *doi_def;
178
179 doi_def = container_of(entry, struct calipso_doi, rcu);
180 calipso_doi_free(doi_def);
181}
182
d7cce015
HD
183/**
184 * calipso_doi_remove - Remove an existing DOI from the CALIPSO protocol engine
185 * @doi: the DOI value
186 * @audit_secid: the LSM secid to use in the audit message
187 *
188 * Description:
189 * Removes a DOI definition from the CALIPSO engine. The NetLabel routines will
190 * be called to release their own LSM domain mappings as well as our own
191 * domain list. Returns zero on success and negative values on failure.
192 *
193 */
194static int calipso_doi_remove(u32 doi, struct netlbl_audit *audit_info)
195{
196 int ret_val;
197 struct calipso_doi *doi_def;
198 struct audit_buffer *audit_buf;
199
200 spin_lock(&calipso_doi_list_lock);
201 doi_def = calipso_doi_search(doi);
202 if (!doi_def) {
203 spin_unlock(&calipso_doi_list_lock);
204 ret_val = -ENOENT;
205 goto doi_remove_return;
206 }
207 if (!atomic_dec_and_test(&doi_def->refcount)) {
208 spin_unlock(&calipso_doi_list_lock);
209 ret_val = -EBUSY;
210 goto doi_remove_return;
211 }
212 list_del_rcu(&doi_def->list);
213 spin_unlock(&calipso_doi_list_lock);
214
215 call_rcu(&doi_def->rcu, calipso_doi_free_rcu);
216 ret_val = 0;
217
218doi_remove_return:
219 audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_DEL, audit_info);
220 if (audit_buf) {
221 audit_log_format(audit_buf,
222 " calipso_doi=%u res=%u",
223 doi, ret_val == 0 ? 1 : 0);
224 audit_log_end(audit_buf);
225 }
226
227 return ret_val;
228}
229
a5e34490
HD
230/**
231 * calipso_doi_getdef - Returns a reference to a valid DOI definition
232 * @doi: the DOI value
233 *
234 * Description:
235 * Searches for a valid DOI definition and if one is found it is returned to
236 * the caller. Otherwise NULL is returned. The caller must ensure that
237 * calipso_doi_putdef() is called when the caller is done.
238 *
239 */
240static struct calipso_doi *calipso_doi_getdef(u32 doi)
241{
242 struct calipso_doi *doi_def;
243
244 rcu_read_lock();
245 doi_def = calipso_doi_search(doi);
246 if (!doi_def)
247 goto doi_getdef_return;
248 if (!atomic_inc_not_zero(&doi_def->refcount))
249 doi_def = NULL;
250
251doi_getdef_return:
252 rcu_read_unlock();
253 return doi_def;
254}
255
256/**
257 * calipso_doi_putdef - Releases a reference for the given DOI definition
258 * @doi_def: the DOI definition
259 *
260 * Description:
261 * Releases a DOI definition reference obtained from calipso_doi_getdef().
262 *
263 */
264static void calipso_doi_putdef(struct calipso_doi *doi_def)
265{
266 if (!doi_def)
267 return;
268
269 if (!atomic_dec_and_test(&doi_def->refcount))
270 return;
271 spin_lock(&calipso_doi_list_lock);
272 list_del_rcu(&doi_def->list);
273 spin_unlock(&calipso_doi_list_lock);
274
275 call_rcu(&doi_def->rcu, calipso_doi_free_rcu);
276}
277
e1ce69df
HD
278/**
279 * calipso_doi_walk - Iterate through the DOI definitions
280 * @skip_cnt: skip past this number of DOI definitions, updated
281 * @callback: callback for each DOI definition
282 * @cb_arg: argument for the callback function
283 *
284 * Description:
285 * Iterate over the DOI definition list, skipping the first @skip_cnt entries.
286 * For each entry call @callback, if @callback returns a negative value stop
287 * 'walking' through the list and return. Updates the value in @skip_cnt upon
288 * return. Returns zero on success, negative values on failure.
289 *
290 */
291static int calipso_doi_walk(u32 *skip_cnt,
292 int (*callback)(struct calipso_doi *doi_def,
293 void *arg),
294 void *cb_arg)
295{
296 int ret_val = -ENOENT;
297 u32 doi_cnt = 0;
298 struct calipso_doi *iter_doi;
299
300 rcu_read_lock();
301 list_for_each_entry_rcu(iter_doi, &calipso_doi_list, list)
302 if (atomic_read(&iter_doi->refcount) > 0) {
303 if (doi_cnt++ < *skip_cnt)
304 continue;
305 ret_val = callback(iter_doi, cb_arg);
306 if (ret_val < 0) {
307 doi_cnt--;
308 goto doi_walk_return;
309 }
310 }
311
312doi_walk_return:
313 rcu_read_unlock();
314 *skip_cnt = doi_cnt;
315 return ret_val;
316}
317
ceba1832
HD
318/**
319 * calipso_map_cat_hton - Perform a category mapping from host to network
320 * @doi_def: the DOI definition
321 * @secattr: the security attributes
322 * @net_cat: the zero'd out category bitmap in network/CALIPSO format
323 * @net_cat_len: the length of the CALIPSO bitmap in bytes
324 *
325 * Description:
326 * Perform a label mapping to translate a local MLS category bitmap to the
327 * correct CALIPSO bitmap using the given DOI definition. Returns the minimum
328 * size in bytes of the network bitmap on success, negative values otherwise.
329 *
330 */
331static int calipso_map_cat_hton(const struct calipso_doi *doi_def,
332 const struct netlbl_lsm_secattr *secattr,
333 unsigned char *net_cat,
334 u32 net_cat_len)
335{
336 int spot = -1;
337 u32 net_spot_max = 0;
338 u32 net_clen_bits = net_cat_len * 8;
339
340 for (;;) {
341 spot = netlbl_catmap_walk(secattr->attr.mls.cat,
342 spot + 1);
343 if (spot < 0)
344 break;
345 if (spot >= net_clen_bits)
346 return -ENOSPC;
347 netlbl_bitmap_setbit(net_cat, spot, 1);
348
349 if (spot > net_spot_max)
350 net_spot_max = spot;
351 }
352
353 return (net_spot_max / 32 + 1) * 4;
354}
355
356/**
357 * calipso_map_cat_ntoh - Perform a category mapping from network to host
358 * @doi_def: the DOI definition
359 * @net_cat: the category bitmap in network/CALIPSO format
360 * @net_cat_len: the length of the CALIPSO bitmap in bytes
361 * @secattr: the security attributes
362 *
363 * Description:
364 * Perform a label mapping to translate a CALIPSO bitmap to the correct local
365 * MLS category bitmap using the given DOI definition. Returns zero on
366 * success, negative values on failure.
367 *
368 */
369static int calipso_map_cat_ntoh(const struct calipso_doi *doi_def,
370 const unsigned char *net_cat,
371 u32 net_cat_len,
372 struct netlbl_lsm_secattr *secattr)
373{
374 int ret_val;
375 int spot = -1;
376 u32 net_clen_bits = net_cat_len * 8;
377
378 for (;;) {
379 spot = netlbl_bitmap_walk(net_cat,
380 net_clen_bits,
381 spot + 1,
382 1);
383 if (spot < 0) {
384 if (spot == -2)
385 return -EFAULT;
386 return 0;
387 }
388
389 ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat,
390 spot,
391 GFP_ATOMIC);
392 if (ret_val != 0)
393 return ret_val;
394 }
395
396 return -EINVAL;
397}
398
399/**
400 * calipso_pad_write - Writes pad bytes in TLV format
401 * @buf: the buffer
402 * @offset: offset from start of buffer to write padding
403 * @count: number of pad bytes to write
404 *
405 * Description:
406 * Write @count bytes of TLV padding into @buffer starting at offset @offset.
407 * @count should be less than 8 - see RFC 4942.
408 *
409 */
410static int calipso_pad_write(unsigned char *buf, unsigned int offset,
411 unsigned int count)
412{
413 if (WARN_ON_ONCE(count >= 8))
414 return -EINVAL;
415
416 switch (count) {
417 case 0:
418 break;
419 case 1:
420 buf[offset] = IPV6_TLV_PAD1;
421 break;
422 default:
423 buf[offset] = IPV6_TLV_PADN;
424 buf[offset + 1] = count - 2;
425 if (count > 2)
426 memset(buf + offset + 2, 0, count - 2);
427 break;
428 }
429 return 0;
430}
431
432/**
433 * calipso_genopt - Generate a CALIPSO option
434 * @buf: the option buffer
435 * @start: offset from which to write
436 * @buf_len: the size of opt_buf
437 * @doi_def: the CALIPSO DOI to use
438 * @secattr: the security attributes
439 *
440 * Description:
441 * Generate a CALIPSO option using the DOI definition and security attributes
442 * passed to the function. This also generates upto three bytes of leading
443 * padding that ensures that the option is 4n + 2 aligned. It returns the
444 * number of bytes written (including any initial padding).
445 */
446static int calipso_genopt(unsigned char *buf, u32 start, u32 buf_len,
447 const struct calipso_doi *doi_def,
448 const struct netlbl_lsm_secattr *secattr)
449{
450 int ret_val;
451 u32 len, pad;
452 u16 crc;
453 static const unsigned char padding[4] = {2, 1, 0, 3};
454 unsigned char *calipso;
455
456 /* CALIPSO has 4n + 2 alignment */
457 pad = padding[start & 3];
458 if (buf_len <= start + pad + CALIPSO_HDR_LEN)
459 return -ENOSPC;
460
461 if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0)
462 return -EPERM;
463
464 len = CALIPSO_HDR_LEN;
465
466 if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
467 ret_val = calipso_map_cat_hton(doi_def,
468 secattr,
469 buf + start + pad + len,
470 buf_len - start - pad - len);
471 if (ret_val < 0)
472 return ret_val;
473 len += ret_val;
474 }
475
476 calipso_pad_write(buf, start, pad);
477 calipso = buf + start + pad;
478
479 calipso[0] = IPV6_TLV_CALIPSO;
480 calipso[1] = len - 2;
481 *(__be32 *)(calipso + 2) = htonl(doi_def->doi);
482 calipso[6] = (len - CALIPSO_HDR_LEN) / 4;
483 calipso[7] = secattr->attr.mls.lvl,
484 crc = ~crc_ccitt(0xffff, calipso, len);
485 calipso[8] = crc & 0xff;
486 calipso[9] = (crc >> 8) & 0xff;
487 return pad + len;
488}
489
490/* Hop-by-hop hdr helper functions
491 */
492
493/**
494 * calipso_opt_update - Replaces socket's hop options with a new set
495 * @sk: the socket
496 * @hop: new hop options
497 *
498 * Description:
499 * Replaces @sk's hop options with @hop. @hop may be NULL to leave
500 * the socket with no hop options.
501 *
502 */
503static int calipso_opt_update(struct sock *sk, struct ipv6_opt_hdr *hop)
504{
505 struct ipv6_txoptions *old = txopt_get(inet6_sk(sk)), *txopts;
506
507 txopts = ipv6_renew_options_kern(sk, old, IPV6_HOPOPTS,
508 hop, hop ? ipv6_optlen(hop) : 0);
509 txopt_put(old);
510 if (IS_ERR(txopts))
511 return PTR_ERR(txopts);
512
513 txopts = ipv6_update_options(sk, txopts);
514 if (txopts) {
515 atomic_sub(txopts->tot_len, &sk->sk_omem_alloc);
516 txopt_put(txopts);
517 }
518
519 return 0;
520}
521
522/**
523 * calipso_tlv_len - Returns the length of the TLV
524 * @opt: the option header
525 * @offset: offset of the TLV within the header
526 *
527 * Description:
528 * Returns the length of the TLV option at offset @offset within
529 * the option header @opt. Checks that the entire TLV fits inside
530 * the option header, returns a negative value if this is not the case.
531 */
532static int calipso_tlv_len(struct ipv6_opt_hdr *opt, unsigned int offset)
533{
534 unsigned char *tlv = (unsigned char *)opt;
535 unsigned int opt_len = ipv6_optlen(opt), tlv_len;
536
537 if (offset < sizeof(*opt) || offset >= opt_len)
538 return -EINVAL;
539 if (tlv[offset] == IPV6_TLV_PAD1)
540 return 1;
541 if (offset + 1 >= opt_len)
542 return -EINVAL;
543 tlv_len = tlv[offset + 1] + 2;
544 if (offset + tlv_len > opt_len)
545 return -EINVAL;
546 return tlv_len;
547}
548
549/**
550 * calipso_opt_find - Finds the CALIPSO option in an IPv6 hop options header
551 * @hop: the hop options header
552 * @start: on return holds the offset of any leading padding
553 * @end: on return holds the offset of the first non-pad TLV after CALIPSO
554 *
555 * Description:
556 * Finds the space occupied by a CALIPSO option (including any leading and
557 * trailing padding).
558 *
559 * If a CALIPSO option exists set @start and @end to the
560 * offsets within @hop of the start of padding before the first
561 * CALIPSO option and the end of padding after the first CALIPSO
562 * option. In this case the function returns 0.
563 *
564 * In the absence of a CALIPSO option, @start and @end will be
565 * set to the start and end of any trailing padding in the header.
566 * This is useful when appending a new option, as the caller may want
567 * to overwrite some of this padding. In this case the function will
568 * return -ENOENT.
569 */
570static int calipso_opt_find(struct ipv6_opt_hdr *hop, unsigned int *start,
571 unsigned int *end)
572{
573 int ret_val = -ENOENT, tlv_len;
574 unsigned int opt_len, offset, offset_s = 0, offset_e = 0;
575 unsigned char *opt = (unsigned char *)hop;
576
577 opt_len = ipv6_optlen(hop);
578 offset = sizeof(*hop);
579
580 while (offset < opt_len) {
581 tlv_len = calipso_tlv_len(hop, offset);
582 if (tlv_len < 0)
583 return tlv_len;
584
585 switch (opt[offset]) {
586 case IPV6_TLV_PAD1:
587 case IPV6_TLV_PADN:
588 if (offset_e)
589 offset_e = offset;
590 break;
591 case IPV6_TLV_CALIPSO:
592 ret_val = 0;
593 offset_e = offset;
594 break;
595 default:
596 if (offset_e == 0)
597 offset_s = offset;
598 else
599 goto out;
600 }
601 offset += tlv_len;
602 }
603
604out:
605 if (offset_s)
606 *start = offset_s + calipso_tlv_len(hop, offset_s);
607 else
608 *start = sizeof(*hop);
609 if (offset_e)
610 *end = offset_e + calipso_tlv_len(hop, offset_e);
611 else
612 *end = opt_len;
613
614 return ret_val;
615}
616
617/**
618 * calipso_opt_insert - Inserts a CALIPSO option into an IPv6 hop opt hdr
619 * @hop: the original hop options header
620 * @doi_def: the CALIPSO DOI to use
621 * @secattr: the specific security attributes of the socket
622 *
623 * Description:
624 * Creates a new hop options header based on @hop with a
625 * CALIPSO option added to it. If @hop already contains a CALIPSO
626 * option this is overwritten, otherwise the new option is appended
627 * after any existing options. If @hop is NULL then the new header
628 * will contain just the CALIPSO option and any needed padding.
629 *
630 */
631static struct ipv6_opt_hdr *
632calipso_opt_insert(struct ipv6_opt_hdr *hop,
633 const struct calipso_doi *doi_def,
634 const struct netlbl_lsm_secattr *secattr)
635{
636 unsigned int start, end, buf_len, pad, hop_len;
637 struct ipv6_opt_hdr *new;
638 int ret_val;
639
640 if (hop) {
641 hop_len = ipv6_optlen(hop);
642 ret_val = calipso_opt_find(hop, &start, &end);
643 if (ret_val && ret_val != -ENOENT)
644 return ERR_PTR(ret_val);
645 } else {
646 hop_len = 0;
647 start = sizeof(*hop);
648 end = 0;
649 }
650
651 buf_len = hop_len + start - end + CALIPSO_OPT_LEN_MAX_WITH_PAD;
652 new = kzalloc(buf_len, GFP_ATOMIC);
653 if (!new)
654 return ERR_PTR(-ENOMEM);
655
656 if (start > sizeof(*hop))
657 memcpy(new, hop, start);
658 ret_val = calipso_genopt((unsigned char *)new, start, buf_len, doi_def,
659 secattr);
660 if (ret_val < 0)
661 return ERR_PTR(ret_val);
662
663 buf_len = start + ret_val;
664 /* At this point buf_len aligns to 4n, so (buf_len & 4) pads to 8n */
665 pad = ((buf_len & 4) + (end & 7)) & 7;
666 calipso_pad_write((unsigned char *)new, buf_len, pad);
667 buf_len += pad;
668
669 if (end != hop_len) {
670 memcpy((char *)new + buf_len, (char *)hop + end, hop_len - end);
671 buf_len += hop_len - end;
672 }
673 new->nexthdr = 0;
674 new->hdrlen = buf_len / 8 - 1;
675
676 return new;
677}
678
679/**
680 * calipso_opt_del - Removes the CALIPSO option from an option header
681 * @hop: the original header
682 * @new: the new header
683 *
684 * Description:
685 * Creates a new header based on @hop without any CALIPSO option. If @hop
686 * doesn't contain a CALIPSO option it returns -ENOENT. If @hop contains
687 * no other non-padding options, it returns zero with @new set to NULL.
688 * Otherwise it returns zero, creates a new header without the CALIPSO
689 * option (and removing as much padding as possible) and returns with
690 * @new set to that header.
691 *
692 */
693static int calipso_opt_del(struct ipv6_opt_hdr *hop,
694 struct ipv6_opt_hdr **new)
695{
696 int ret_val;
697 unsigned int start, end, delta, pad, hop_len;
698
699 ret_val = calipso_opt_find(hop, &start, &end);
700 if (ret_val)
701 return ret_val;
702
703 hop_len = ipv6_optlen(hop);
704 if (start == sizeof(*hop) && end == hop_len) {
705 /* There's no other option in the header so return NULL */
706 *new = NULL;
707 return 0;
708 }
709
710 delta = (end - start) & ~7;
711 *new = kzalloc(hop_len - delta, GFP_ATOMIC);
712 if (!*new)
713 return -ENOMEM;
714
715 memcpy(*new, hop, start);
716 (*new)->hdrlen -= delta / 8;
717 pad = (end - start) & 7;
718 calipso_pad_write((unsigned char *)*new, start, pad);
719 if (end != hop_len)
720 memcpy((char *)*new + start + pad, (char *)hop + end,
721 hop_len - end);
722
723 return 0;
724}
725
726/**
727 * calipso_opt_getattr - Get the security attributes from a memory block
728 * @calipso: the CALIPSO option
729 * @secattr: the security attributes
730 *
731 * Description:
732 * Inspect @calipso and return the security attributes in @secattr.
733 * Returns zero on success and negative values on failure.
734 *
735 */
736static int calipso_opt_getattr(const unsigned char *calipso,
737 struct netlbl_lsm_secattr *secattr)
738{
739 int ret_val = -ENOMSG;
740 u32 doi, len = calipso[1], cat_len = calipso[6] * 4;
741 struct calipso_doi *doi_def;
742
743 if (cat_len + 8 > len)
744 return -EINVAL;
745
746 doi = get_unaligned_be32(calipso + 2);
747 rcu_read_lock();
748 doi_def = calipso_doi_search(doi);
749 if (!doi_def)
750 goto getattr_return;
751
752 secattr->attr.mls.lvl = calipso[7];
753 secattr->flags |= NETLBL_SECATTR_MLS_LVL;
754
755 if (cat_len) {
756 ret_val = calipso_map_cat_ntoh(doi_def,
757 calipso + 10,
758 cat_len,
759 secattr);
760 if (ret_val != 0) {
761 netlbl_catmap_free(secattr->attr.mls.cat);
762 goto getattr_return;
763 }
764
765 secattr->flags |= NETLBL_SECATTR_MLS_CAT;
766 }
767
768 secattr->type = NETLBL_NLTYPE_CALIPSO;
769
770getattr_return:
771 rcu_read_unlock();
772 return ret_val;
773}
774
775/* sock functions.
776 */
777
778/**
779 * calipso_sock_getattr - Get the security attributes from a sock
780 * @sk: the sock
781 * @secattr: the security attributes
782 *
783 * Description:
784 * Query @sk to see if there is a CALIPSO option attached to the sock and if
785 * there is return the CALIPSO security attributes in @secattr. This function
786 * requires that @sk be locked, or privately held, but it does not do any
787 * locking itself. Returns zero on success and negative values on failure.
788 *
789 */
790static int calipso_sock_getattr(struct sock *sk,
791 struct netlbl_lsm_secattr *secattr)
792{
793 struct ipv6_opt_hdr *hop;
794 int opt_len, len, ret_val = -ENOMSG, offset;
795 unsigned char *opt;
796 struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk));
797
798 if (!txopts || !txopts->hopopt)
799 goto done;
800
801 hop = txopts->hopopt;
802 opt = (unsigned char *)hop;
803 opt_len = ipv6_optlen(hop);
804 offset = sizeof(*hop);
805 while (offset < opt_len) {
806 len = calipso_tlv_len(hop, offset);
807 if (len < 0) {
808 ret_val = len;
809 goto done;
810 }
811 switch (opt[offset]) {
812 case IPV6_TLV_CALIPSO:
813 if (len < CALIPSO_HDR_LEN)
814 ret_val = -EINVAL;
815 else
816 ret_val = calipso_opt_getattr(&opt[offset],
817 secattr);
818 goto done;
819 default:
820 offset += len;
821 break;
822 }
823 }
824done:
825 txopt_put(txopts);
826 return ret_val;
827}
828
829/**
830 * calipso_sock_setattr - Add a CALIPSO option to a socket
831 * @sk: the socket
832 * @doi_def: the CALIPSO DOI to use
833 * @secattr: the specific security attributes of the socket
834 *
835 * Description:
836 * Set the CALIPSO option on the given socket using the DOI definition and
837 * security attributes passed to the function. This function requires
838 * exclusive access to @sk, which means it either needs to be in the
839 * process of being created or locked. Returns zero on success and negative
840 * values on failure.
841 *
842 */
843static int calipso_sock_setattr(struct sock *sk,
844 const struct calipso_doi *doi_def,
845 const struct netlbl_lsm_secattr *secattr)
846{
847 int ret_val;
848 struct ipv6_opt_hdr *old, *new;
849 struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk));
850
851 old = NULL;
852 if (txopts)
853 old = txopts->hopopt;
854
855 new = calipso_opt_insert(old, doi_def, secattr);
856 txopt_put(txopts);
857 if (IS_ERR(new))
858 return PTR_ERR(new);
859
860 ret_val = calipso_opt_update(sk, new);
861
862 kfree(new);
863 return ret_val;
864}
865
866/**
867 * calipso_sock_delattr - Delete the CALIPSO option from a socket
868 * @sk: the socket
869 *
870 * Description:
871 * Removes the CALIPSO option from a socket, if present.
872 *
873 */
874static void calipso_sock_delattr(struct sock *sk)
875{
876 struct ipv6_opt_hdr *new_hop;
877 struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk));
878
879 if (!txopts || !txopts->hopopt)
880 goto done;
881
882 if (calipso_opt_del(txopts->hopopt, &new_hop))
883 goto done;
884
885 calipso_opt_update(sk, new_hop);
886 kfree(new_hop);
887
888done:
889 txopt_put(txopts);
890}
891
cb72d382
HD
892static const struct netlbl_calipso_ops ops = {
893 .doi_add = calipso_doi_add,
894 .doi_free = calipso_doi_free,
d7cce015 895 .doi_remove = calipso_doi_remove,
a5e34490
HD
896 .doi_getdef = calipso_doi_getdef,
897 .doi_putdef = calipso_doi_putdef,
e1ce69df 898 .doi_walk = calipso_doi_walk,
ceba1832
HD
899 .sock_getattr = calipso_sock_getattr,
900 .sock_setattr = calipso_sock_setattr,
901 .sock_delattr = calipso_sock_delattr,
cb72d382
HD
902};
903
904/**
905 * calipso_init - Initialize the CALIPSO module
906 *
907 * Description:
908 * Initialize the CALIPSO module and prepare it for use. Returns zero on
909 * success and negative values on failure.
910 *
911 */
912int __init calipso_init(void)
913{
914 netlbl_calipso_ops_register(&ops);
915 return 0;
916}
917
918void calipso_exit(void)
919{
920 netlbl_calipso_ops_register(NULL);
921}
This page took 0.059077 seconds and 5 git commands to generate.