0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016 #include <linux/init.h>
0017 #include <linux/types.h>
0018 #include <linux/rcupdate.h>
0019 #include <linux/list.h>
0020 #include <linux/spinlock.h>
0021 #include <linux/string.h>
0022 #include <linux/jhash.h>
0023 #include <linux/audit.h>
0024 #include <linux/slab.h>
0025 #include <net/ip.h>
0026 #include <net/icmp.h>
0027 #include <net/tcp.h>
0028 #include <net/netlabel.h>
0029 #include <net/calipso.h>
0030 #include <linux/atomic.h>
0031 #include <linux/bug.h>
0032 #include <asm/unaligned.h>
0033 #include <linux/crc-ccitt.h>
0034
0035
0036
0037
0038 #define CALIPSO_OPT_LEN_MAX (2 + 252)
0039
0040
0041
0042
0043 #define CALIPSO_HDR_LEN (2 + 8)
0044
0045
0046
0047
0048
0049 #define CALIPSO_OPT_LEN_MAX_WITH_PAD (3 + CALIPSO_OPT_LEN_MAX + 7)
0050
0051
0052
0053
0054
0055 #define CALIPSO_MAX_BUFFER (6 + CALIPSO_OPT_LEN_MAX)
0056
0057
0058 static DEFINE_SPINLOCK(calipso_doi_list_lock);
0059 static LIST_HEAD(calipso_doi_list);
0060
0061
0062 int calipso_cache_enabled = 1;
0063 int calipso_cache_bucketsize = 10;
0064 #define CALIPSO_CACHE_BUCKETBITS 7
0065 #define CALIPSO_CACHE_BUCKETS BIT(CALIPSO_CACHE_BUCKETBITS)
0066 #define CALIPSO_CACHE_REORDERLIMIT 10
0067 struct calipso_map_cache_bkt {
0068 spinlock_t lock;
0069 u32 size;
0070 struct list_head list;
0071 };
0072
0073 struct calipso_map_cache_entry {
0074 u32 hash;
0075 unsigned char *key;
0076 size_t key_len;
0077
0078 struct netlbl_lsm_cache *lsm_data;
0079
0080 u32 activity;
0081 struct list_head list;
0082 };
0083
0084 static struct calipso_map_cache_bkt *calipso_cache;
0085
0086 static void calipso_cache_invalidate(void);
0087 static void calipso_doi_putdef(struct calipso_doi *doi_def);
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101 static void calipso_cache_entry_free(struct calipso_map_cache_entry *entry)
0102 {
0103 if (entry->lsm_data)
0104 netlbl_secattr_cache_free(entry->lsm_data);
0105 kfree(entry->key);
0106 kfree(entry);
0107 }
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118 static u32 calipso_map_cache_hash(const unsigned char *key, u32 key_len)
0119 {
0120 return jhash(key, key_len, 0);
0121 }
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132 static int __init calipso_cache_init(void)
0133 {
0134 u32 iter;
0135
0136 calipso_cache = kcalloc(CALIPSO_CACHE_BUCKETS,
0137 sizeof(struct calipso_map_cache_bkt),
0138 GFP_KERNEL);
0139 if (!calipso_cache)
0140 return -ENOMEM;
0141
0142 for (iter = 0; iter < CALIPSO_CACHE_BUCKETS; iter++) {
0143 spin_lock_init(&calipso_cache[iter].lock);
0144 calipso_cache[iter].size = 0;
0145 INIT_LIST_HEAD(&calipso_cache[iter].list);
0146 }
0147
0148 return 0;
0149 }
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159 static void calipso_cache_invalidate(void)
0160 {
0161 struct calipso_map_cache_entry *entry, *tmp_entry;
0162 u32 iter;
0163
0164 for (iter = 0; iter < CALIPSO_CACHE_BUCKETS; iter++) {
0165 spin_lock_bh(&calipso_cache[iter].lock);
0166 list_for_each_entry_safe(entry,
0167 tmp_entry,
0168 &calipso_cache[iter].list, list) {
0169 list_del(&entry->list);
0170 calipso_cache_entry_free(entry);
0171 }
0172 calipso_cache[iter].size = 0;
0173 spin_unlock_bh(&calipso_cache[iter].lock);
0174 }
0175 }
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198
0199 static int calipso_cache_check(const unsigned char *key,
0200 u32 key_len,
0201 struct netlbl_lsm_secattr *secattr)
0202 {
0203 u32 bkt;
0204 struct calipso_map_cache_entry *entry;
0205 struct calipso_map_cache_entry *prev_entry = NULL;
0206 u32 hash;
0207
0208 if (!calipso_cache_enabled)
0209 return -ENOENT;
0210
0211 hash = calipso_map_cache_hash(key, key_len);
0212 bkt = hash & (CALIPSO_CACHE_BUCKETS - 1);
0213 spin_lock_bh(&calipso_cache[bkt].lock);
0214 list_for_each_entry(entry, &calipso_cache[bkt].list, list) {
0215 if (entry->hash == hash &&
0216 entry->key_len == key_len &&
0217 memcmp(entry->key, key, key_len) == 0) {
0218 entry->activity += 1;
0219 refcount_inc(&entry->lsm_data->refcount);
0220 secattr->cache = entry->lsm_data;
0221 secattr->flags |= NETLBL_SECATTR_CACHE;
0222 secattr->type = NETLBL_NLTYPE_CALIPSO;
0223 if (!prev_entry) {
0224 spin_unlock_bh(&calipso_cache[bkt].lock);
0225 return 0;
0226 }
0227
0228 if (prev_entry->activity > 0)
0229 prev_entry->activity -= 1;
0230 if (entry->activity > prev_entry->activity &&
0231 entry->activity - prev_entry->activity >
0232 CALIPSO_CACHE_REORDERLIMIT) {
0233 __list_del(entry->list.prev, entry->list.next);
0234 __list_add(&entry->list,
0235 prev_entry->list.prev,
0236 &prev_entry->list);
0237 }
0238
0239 spin_unlock_bh(&calipso_cache[bkt].lock);
0240 return 0;
0241 }
0242 prev_entry = entry;
0243 }
0244 spin_unlock_bh(&calipso_cache[bkt].lock);
0245
0246 return -ENOENT;
0247 }
0248
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264 static int calipso_cache_add(const unsigned char *calipso_ptr,
0265 const struct netlbl_lsm_secattr *secattr)
0266 {
0267 int ret_val = -EPERM;
0268 u32 bkt;
0269 struct calipso_map_cache_entry *entry = NULL;
0270 struct calipso_map_cache_entry *old_entry = NULL;
0271 u32 calipso_ptr_len;
0272
0273 if (!calipso_cache_enabled || calipso_cache_bucketsize <= 0)
0274 return 0;
0275
0276 calipso_ptr_len = calipso_ptr[1];
0277
0278 entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
0279 if (!entry)
0280 return -ENOMEM;
0281 entry->key = kmemdup(calipso_ptr + 2, calipso_ptr_len, GFP_ATOMIC);
0282 if (!entry->key) {
0283 ret_val = -ENOMEM;
0284 goto cache_add_failure;
0285 }
0286 entry->key_len = calipso_ptr_len;
0287 entry->hash = calipso_map_cache_hash(calipso_ptr, calipso_ptr_len);
0288 refcount_inc(&secattr->cache->refcount);
0289 entry->lsm_data = secattr->cache;
0290
0291 bkt = entry->hash & (CALIPSO_CACHE_BUCKETS - 1);
0292 spin_lock_bh(&calipso_cache[bkt].lock);
0293 if (calipso_cache[bkt].size < calipso_cache_bucketsize) {
0294 list_add(&entry->list, &calipso_cache[bkt].list);
0295 calipso_cache[bkt].size += 1;
0296 } else {
0297 old_entry = list_entry(calipso_cache[bkt].list.prev,
0298 struct calipso_map_cache_entry, list);
0299 list_del(&old_entry->list);
0300 list_add(&entry->list, &calipso_cache[bkt].list);
0301 calipso_cache_entry_free(old_entry);
0302 }
0303 spin_unlock_bh(&calipso_cache[bkt].lock);
0304
0305 return 0;
0306
0307 cache_add_failure:
0308 if (entry)
0309 calipso_cache_entry_free(entry);
0310 return ret_val;
0311 }
0312
0313
0314
0315
0316
0317
0318
0319
0320
0321
0322
0323
0324
0325 static struct calipso_doi *calipso_doi_search(u32 doi)
0326 {
0327 struct calipso_doi *iter;
0328
0329 list_for_each_entry_rcu(iter, &calipso_doi_list, list)
0330 if (iter->doi == doi && refcount_read(&iter->refcount))
0331 return iter;
0332 return NULL;
0333 }
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347
0348 static int calipso_doi_add(struct calipso_doi *doi_def,
0349 struct netlbl_audit *audit_info)
0350 {
0351 int ret_val = -EINVAL;
0352 u32 doi;
0353 u32 doi_type;
0354 struct audit_buffer *audit_buf;
0355
0356 doi = doi_def->doi;
0357 doi_type = doi_def->type;
0358
0359 if (doi_def->doi == CALIPSO_DOI_UNKNOWN)
0360 goto doi_add_return;
0361
0362 refcount_set(&doi_def->refcount, 1);
0363
0364 spin_lock(&calipso_doi_list_lock);
0365 if (calipso_doi_search(doi_def->doi)) {
0366 spin_unlock(&calipso_doi_list_lock);
0367 ret_val = -EEXIST;
0368 goto doi_add_return;
0369 }
0370 list_add_tail_rcu(&doi_def->list, &calipso_doi_list);
0371 spin_unlock(&calipso_doi_list_lock);
0372 ret_val = 0;
0373
0374 doi_add_return:
0375 audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_ADD, audit_info);
0376 if (audit_buf) {
0377 const char *type_str;
0378
0379 switch (doi_type) {
0380 case CALIPSO_MAP_PASS:
0381 type_str = "pass";
0382 break;
0383 default:
0384 type_str = "(unknown)";
0385 }
0386 audit_log_format(audit_buf,
0387 " calipso_doi=%u calipso_type=%s res=%u",
0388 doi, type_str, ret_val == 0 ? 1 : 0);
0389 audit_log_end(audit_buf);
0390 }
0391
0392 return ret_val;
0393 }
0394
0395
0396
0397
0398
0399
0400
0401
0402
0403 static void calipso_doi_free(struct calipso_doi *doi_def)
0404 {
0405 kfree(doi_def);
0406 }
0407
0408
0409
0410
0411
0412
0413
0414
0415
0416
0417
0418 static void calipso_doi_free_rcu(struct rcu_head *entry)
0419 {
0420 struct calipso_doi *doi_def;
0421
0422 doi_def = container_of(entry, struct calipso_doi, rcu);
0423 calipso_doi_free(doi_def);
0424 }
0425
0426
0427
0428
0429
0430
0431
0432
0433
0434
0435
0436
0437 static int calipso_doi_remove(u32 doi, struct netlbl_audit *audit_info)
0438 {
0439 int ret_val;
0440 struct calipso_doi *doi_def;
0441 struct audit_buffer *audit_buf;
0442
0443 spin_lock(&calipso_doi_list_lock);
0444 doi_def = calipso_doi_search(doi);
0445 if (!doi_def) {
0446 spin_unlock(&calipso_doi_list_lock);
0447 ret_val = -ENOENT;
0448 goto doi_remove_return;
0449 }
0450 list_del_rcu(&doi_def->list);
0451 spin_unlock(&calipso_doi_list_lock);
0452
0453 calipso_doi_putdef(doi_def);
0454 ret_val = 0;
0455
0456 doi_remove_return:
0457 audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_DEL, audit_info);
0458 if (audit_buf) {
0459 audit_log_format(audit_buf,
0460 " calipso_doi=%u res=%u",
0461 doi, ret_val == 0 ? 1 : 0);
0462 audit_log_end(audit_buf);
0463 }
0464
0465 return ret_val;
0466 }
0467
0468
0469
0470
0471
0472
0473
0474
0475
0476
0477
0478 static struct calipso_doi *calipso_doi_getdef(u32 doi)
0479 {
0480 struct calipso_doi *doi_def;
0481
0482 rcu_read_lock();
0483 doi_def = calipso_doi_search(doi);
0484 if (!doi_def)
0485 goto doi_getdef_return;
0486 if (!refcount_inc_not_zero(&doi_def->refcount))
0487 doi_def = NULL;
0488
0489 doi_getdef_return:
0490 rcu_read_unlock();
0491 return doi_def;
0492 }
0493
0494
0495
0496
0497
0498
0499
0500
0501
0502 static void calipso_doi_putdef(struct calipso_doi *doi_def)
0503 {
0504 if (!doi_def)
0505 return;
0506
0507 if (!refcount_dec_and_test(&doi_def->refcount))
0508 return;
0509
0510 calipso_cache_invalidate();
0511 call_rcu(&doi_def->rcu, calipso_doi_free_rcu);
0512 }
0513
0514
0515
0516
0517
0518
0519
0520
0521
0522
0523
0524
0525
0526
0527 static int calipso_doi_walk(u32 *skip_cnt,
0528 int (*callback)(struct calipso_doi *doi_def,
0529 void *arg),
0530 void *cb_arg)
0531 {
0532 int ret_val = -ENOENT;
0533 u32 doi_cnt = 0;
0534 struct calipso_doi *iter_doi;
0535
0536 rcu_read_lock();
0537 list_for_each_entry_rcu(iter_doi, &calipso_doi_list, list)
0538 if (refcount_read(&iter_doi->refcount) > 0) {
0539 if (doi_cnt++ < *skip_cnt)
0540 continue;
0541 ret_val = callback(iter_doi, cb_arg);
0542 if (ret_val < 0) {
0543 doi_cnt--;
0544 goto doi_walk_return;
0545 }
0546 }
0547
0548 doi_walk_return:
0549 rcu_read_unlock();
0550 *skip_cnt = doi_cnt;
0551 return ret_val;
0552 }
0553
0554
0555
0556
0557
0558
0559
0560
0561
0562
0563
0564
0565
0566
0567
0568
0569
0570 bool calipso_validate(const struct sk_buff *skb, const unsigned char *option)
0571 {
0572 struct calipso_doi *doi_def;
0573 bool ret_val;
0574 u16 crc, len = option[1] + 2;
0575 static const u8 zero[2];
0576
0577
0578
0579 crc = crc_ccitt(0xffff, option, 8);
0580 crc = crc_ccitt(crc, zero, sizeof(zero));
0581 if (len > 10)
0582 crc = crc_ccitt(crc, option + 10, len - 10);
0583 crc = ~crc;
0584 if (option[8] != (crc & 0xff) || option[9] != ((crc >> 8) & 0xff))
0585 return false;
0586
0587 rcu_read_lock();
0588 doi_def = calipso_doi_search(get_unaligned_be32(option + 2));
0589 ret_val = !!doi_def;
0590 rcu_read_unlock();
0591
0592 return ret_val;
0593 }
0594
0595
0596
0597
0598
0599
0600
0601
0602
0603
0604
0605
0606
0607
0608 static int calipso_map_cat_hton(const struct calipso_doi *doi_def,
0609 const struct netlbl_lsm_secattr *secattr,
0610 unsigned char *net_cat,
0611 u32 net_cat_len)
0612 {
0613 int spot = -1;
0614 u32 net_spot_max = 0;
0615 u32 net_clen_bits = net_cat_len * 8;
0616
0617 for (;;) {
0618 spot = netlbl_catmap_walk(secattr->attr.mls.cat,
0619 spot + 1);
0620 if (spot < 0)
0621 break;
0622 if (spot >= net_clen_bits)
0623 return -ENOSPC;
0624 netlbl_bitmap_setbit(net_cat, spot, 1);
0625
0626 if (spot > net_spot_max)
0627 net_spot_max = spot;
0628 }
0629
0630 return (net_spot_max / 32 + 1) * 4;
0631 }
0632
0633
0634
0635
0636
0637
0638
0639
0640
0641
0642
0643
0644
0645
0646 static int calipso_map_cat_ntoh(const struct calipso_doi *doi_def,
0647 const unsigned char *net_cat,
0648 u32 net_cat_len,
0649 struct netlbl_lsm_secattr *secattr)
0650 {
0651 int ret_val;
0652 int spot = -1;
0653 u32 net_clen_bits = net_cat_len * 8;
0654
0655 for (;;) {
0656 spot = netlbl_bitmap_walk(net_cat,
0657 net_clen_bits,
0658 spot + 1,
0659 1);
0660 if (spot < 0) {
0661 if (spot == -2)
0662 return -EFAULT;
0663 return 0;
0664 }
0665
0666 ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat,
0667 spot,
0668 GFP_ATOMIC);
0669 if (ret_val != 0)
0670 return ret_val;
0671 }
0672
0673 return -EINVAL;
0674 }
0675
0676
0677
0678
0679
0680
0681
0682
0683
0684
0685
0686
0687 static int calipso_pad_write(unsigned char *buf, unsigned int offset,
0688 unsigned int count)
0689 {
0690 if (WARN_ON_ONCE(count >= 8))
0691 return -EINVAL;
0692
0693 switch (count) {
0694 case 0:
0695 break;
0696 case 1:
0697 buf[offset] = IPV6_TLV_PAD1;
0698 break;
0699 default:
0700 buf[offset] = IPV6_TLV_PADN;
0701 buf[offset + 1] = count - 2;
0702 if (count > 2)
0703 memset(buf + offset + 2, 0, count - 2);
0704 break;
0705 }
0706 return 0;
0707 }
0708
0709
0710
0711
0712
0713
0714
0715
0716
0717
0718
0719
0720
0721
0722
0723 static int calipso_genopt(unsigned char *buf, u32 start, u32 buf_len,
0724 const struct calipso_doi *doi_def,
0725 const struct netlbl_lsm_secattr *secattr)
0726 {
0727 int ret_val;
0728 u32 len, pad;
0729 u16 crc;
0730 static const unsigned char padding[4] = {2, 1, 0, 3};
0731 unsigned char *calipso;
0732
0733
0734 pad = padding[start & 3];
0735 if (buf_len <= start + pad + CALIPSO_HDR_LEN)
0736 return -ENOSPC;
0737
0738 if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0)
0739 return -EPERM;
0740
0741 len = CALIPSO_HDR_LEN;
0742
0743 if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
0744 ret_val = calipso_map_cat_hton(doi_def,
0745 secattr,
0746 buf + start + pad + len,
0747 buf_len - start - pad - len);
0748 if (ret_val < 0)
0749 return ret_val;
0750 len += ret_val;
0751 }
0752
0753 calipso_pad_write(buf, start, pad);
0754 calipso = buf + start + pad;
0755
0756 calipso[0] = IPV6_TLV_CALIPSO;
0757 calipso[1] = len - 2;
0758 *(__be32 *)(calipso + 2) = htonl(doi_def->doi);
0759 calipso[6] = (len - CALIPSO_HDR_LEN) / 4;
0760 calipso[7] = secattr->attr.mls.lvl;
0761 crc = ~crc_ccitt(0xffff, calipso, len);
0762 calipso[8] = crc & 0xff;
0763 calipso[9] = (crc >> 8) & 0xff;
0764 return pad + len;
0765 }
0766
0767
0768
0769
0770
0771
0772
0773
0774
0775
0776
0777
0778
0779
0780 static int calipso_opt_update(struct sock *sk, struct ipv6_opt_hdr *hop)
0781 {
0782 struct ipv6_txoptions *old = txopt_get(inet6_sk(sk)), *txopts;
0783
0784 txopts = ipv6_renew_options(sk, old, IPV6_HOPOPTS, hop);
0785 txopt_put(old);
0786 if (IS_ERR(txopts))
0787 return PTR_ERR(txopts);
0788
0789 txopts = ipv6_update_options(sk, txopts);
0790 if (txopts) {
0791 atomic_sub(txopts->tot_len, &sk->sk_omem_alloc);
0792 txopt_put(txopts);
0793 }
0794
0795 return 0;
0796 }
0797
0798
0799
0800
0801
0802
0803
0804
0805
0806
0807
0808 static int calipso_tlv_len(struct ipv6_opt_hdr *opt, unsigned int offset)
0809 {
0810 unsigned char *tlv = (unsigned char *)opt;
0811 unsigned int opt_len = ipv6_optlen(opt), tlv_len;
0812
0813 if (offset < sizeof(*opt) || offset >= opt_len)
0814 return -EINVAL;
0815 if (tlv[offset] == IPV6_TLV_PAD1)
0816 return 1;
0817 if (offset + 1 >= opt_len)
0818 return -EINVAL;
0819 tlv_len = tlv[offset + 1] + 2;
0820 if (offset + tlv_len > opt_len)
0821 return -EINVAL;
0822 return tlv_len;
0823 }
0824
0825
0826
0827
0828
0829
0830
0831
0832
0833
0834
0835
0836
0837
0838
0839
0840
0841
0842
0843
0844
0845
0846 static int calipso_opt_find(struct ipv6_opt_hdr *hop, unsigned int *start,
0847 unsigned int *end)
0848 {
0849 int ret_val = -ENOENT, tlv_len;
0850 unsigned int opt_len, offset, offset_s = 0, offset_e = 0;
0851 unsigned char *opt = (unsigned char *)hop;
0852
0853 opt_len = ipv6_optlen(hop);
0854 offset = sizeof(*hop);
0855
0856 while (offset < opt_len) {
0857 tlv_len = calipso_tlv_len(hop, offset);
0858 if (tlv_len < 0)
0859 return tlv_len;
0860
0861 switch (opt[offset]) {
0862 case IPV6_TLV_PAD1:
0863 case IPV6_TLV_PADN:
0864 if (offset_e)
0865 offset_e = offset;
0866 break;
0867 case IPV6_TLV_CALIPSO:
0868 ret_val = 0;
0869 offset_e = offset;
0870 break;
0871 default:
0872 if (offset_e == 0)
0873 offset_s = offset;
0874 else
0875 goto out;
0876 }
0877 offset += tlv_len;
0878 }
0879
0880 out:
0881 if (offset_s)
0882 *start = offset_s + calipso_tlv_len(hop, offset_s);
0883 else
0884 *start = sizeof(*hop);
0885 if (offset_e)
0886 *end = offset_e + calipso_tlv_len(hop, offset_e);
0887 else
0888 *end = opt_len;
0889
0890 return ret_val;
0891 }
0892
0893
0894
0895
0896
0897
0898
0899
0900
0901
0902
0903
0904
0905
0906
0907 static struct ipv6_opt_hdr *
0908 calipso_opt_insert(struct ipv6_opt_hdr *hop,
0909 const struct calipso_doi *doi_def,
0910 const struct netlbl_lsm_secattr *secattr)
0911 {
0912 unsigned int start, end, buf_len, pad, hop_len;
0913 struct ipv6_opt_hdr *new;
0914 int ret_val;
0915
0916 if (hop) {
0917 hop_len = ipv6_optlen(hop);
0918 ret_val = calipso_opt_find(hop, &start, &end);
0919 if (ret_val && ret_val != -ENOENT)
0920 return ERR_PTR(ret_val);
0921 } else {
0922 hop_len = 0;
0923 start = sizeof(*hop);
0924 end = 0;
0925 }
0926
0927 buf_len = hop_len + start - end + CALIPSO_OPT_LEN_MAX_WITH_PAD;
0928 new = kzalloc(buf_len, GFP_ATOMIC);
0929 if (!new)
0930 return ERR_PTR(-ENOMEM);
0931
0932 if (start > sizeof(*hop))
0933 memcpy(new, hop, start);
0934 ret_val = calipso_genopt((unsigned char *)new, start, buf_len, doi_def,
0935 secattr);
0936 if (ret_val < 0) {
0937 kfree(new);
0938 return ERR_PTR(ret_val);
0939 }
0940
0941 buf_len = start + ret_val;
0942
0943 pad = ((buf_len & 4) + (end & 7)) & 7;
0944 calipso_pad_write((unsigned char *)new, buf_len, pad);
0945 buf_len += pad;
0946
0947 if (end != hop_len) {
0948 memcpy((char *)new + buf_len, (char *)hop + end, hop_len - end);
0949 buf_len += hop_len - end;
0950 }
0951 new->nexthdr = 0;
0952 new->hdrlen = buf_len / 8 - 1;
0953
0954 return new;
0955 }
0956
0957
0958
0959
0960
0961
0962
0963
0964
0965
0966
0967
0968
0969
0970
0971 static int calipso_opt_del(struct ipv6_opt_hdr *hop,
0972 struct ipv6_opt_hdr **new)
0973 {
0974 int ret_val;
0975 unsigned int start, end, delta, pad, hop_len;
0976
0977 ret_val = calipso_opt_find(hop, &start, &end);
0978 if (ret_val)
0979 return ret_val;
0980
0981 hop_len = ipv6_optlen(hop);
0982 if (start == sizeof(*hop) && end == hop_len) {
0983
0984 *new = NULL;
0985 return 0;
0986 }
0987
0988 delta = (end - start) & ~7;
0989 *new = kzalloc(hop_len - delta, GFP_ATOMIC);
0990 if (!*new)
0991 return -ENOMEM;
0992
0993 memcpy(*new, hop, start);
0994 (*new)->hdrlen -= delta / 8;
0995 pad = (end - start) & 7;
0996 calipso_pad_write((unsigned char *)*new, start, pad);
0997 if (end != hop_len)
0998 memcpy((char *)*new + start + pad, (char *)hop + end,
0999 hop_len - end);
1000
1001 return 0;
1002 }
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014 static int calipso_opt_getattr(const unsigned char *calipso,
1015 struct netlbl_lsm_secattr *secattr)
1016 {
1017 int ret_val = -ENOMSG;
1018 u32 doi, len = calipso[1], cat_len = calipso[6] * 4;
1019 struct calipso_doi *doi_def;
1020
1021 if (cat_len + 8 > len)
1022 return -EINVAL;
1023
1024 if (calipso_cache_check(calipso + 2, calipso[1], secattr) == 0)
1025 return 0;
1026
1027 doi = get_unaligned_be32(calipso + 2);
1028 rcu_read_lock();
1029 doi_def = calipso_doi_search(doi);
1030 if (!doi_def)
1031 goto getattr_return;
1032
1033 secattr->attr.mls.lvl = calipso[7];
1034 secattr->flags |= NETLBL_SECATTR_MLS_LVL;
1035
1036 if (cat_len) {
1037 ret_val = calipso_map_cat_ntoh(doi_def,
1038 calipso + 10,
1039 cat_len,
1040 secattr);
1041 if (ret_val != 0) {
1042 netlbl_catmap_free(secattr->attr.mls.cat);
1043 goto getattr_return;
1044 }
1045
1046 if (secattr->attr.mls.cat)
1047 secattr->flags |= NETLBL_SECATTR_MLS_CAT;
1048 }
1049
1050 secattr->type = NETLBL_NLTYPE_CALIPSO;
1051
1052 getattr_return:
1053 rcu_read_unlock();
1054 return ret_val;
1055 }
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072 static int calipso_sock_getattr(struct sock *sk,
1073 struct netlbl_lsm_secattr *secattr)
1074 {
1075 struct ipv6_opt_hdr *hop;
1076 int opt_len, len, ret_val = -ENOMSG, offset;
1077 unsigned char *opt;
1078 struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk));
1079
1080 if (!txopts || !txopts->hopopt)
1081 goto done;
1082
1083 hop = txopts->hopopt;
1084 opt = (unsigned char *)hop;
1085 opt_len = ipv6_optlen(hop);
1086 offset = sizeof(*hop);
1087 while (offset < opt_len) {
1088 len = calipso_tlv_len(hop, offset);
1089 if (len < 0) {
1090 ret_val = len;
1091 goto done;
1092 }
1093 switch (opt[offset]) {
1094 case IPV6_TLV_CALIPSO:
1095 if (len < CALIPSO_HDR_LEN)
1096 ret_val = -EINVAL;
1097 else
1098 ret_val = calipso_opt_getattr(&opt[offset],
1099 secattr);
1100 goto done;
1101 default:
1102 offset += len;
1103 break;
1104 }
1105 }
1106 done:
1107 txopt_put(txopts);
1108 return ret_val;
1109 }
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125 static int calipso_sock_setattr(struct sock *sk,
1126 const struct calipso_doi *doi_def,
1127 const struct netlbl_lsm_secattr *secattr)
1128 {
1129 int ret_val;
1130 struct ipv6_opt_hdr *old, *new;
1131 struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk));
1132
1133 old = NULL;
1134 if (txopts)
1135 old = txopts->hopopt;
1136
1137 new = calipso_opt_insert(old, doi_def, secattr);
1138 txopt_put(txopts);
1139 if (IS_ERR(new))
1140 return PTR_ERR(new);
1141
1142 ret_val = calipso_opt_update(sk, new);
1143
1144 kfree(new);
1145 return ret_val;
1146 }
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156 static void calipso_sock_delattr(struct sock *sk)
1157 {
1158 struct ipv6_opt_hdr *new_hop;
1159 struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk));
1160
1161 if (!txopts || !txopts->hopopt)
1162 goto done;
1163
1164 if (calipso_opt_del(txopts->hopopt, &new_hop))
1165 goto done;
1166
1167 calipso_opt_update(sk, new_hop);
1168 kfree(new_hop);
1169
1170 done:
1171 txopt_put(txopts);
1172 }
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189 static int calipso_req_setattr(struct request_sock *req,
1190 const struct calipso_doi *doi_def,
1191 const struct netlbl_lsm_secattr *secattr)
1192 {
1193 struct ipv6_txoptions *txopts;
1194 struct inet_request_sock *req_inet = inet_rsk(req);
1195 struct ipv6_opt_hdr *old, *new;
1196 struct sock *sk = sk_to_full_sk(req_to_sk(req));
1197
1198 if (req_inet->ipv6_opt && req_inet->ipv6_opt->hopopt)
1199 old = req_inet->ipv6_opt->hopopt;
1200 else
1201 old = NULL;
1202
1203 new = calipso_opt_insert(old, doi_def, secattr);
1204 if (IS_ERR(new))
1205 return PTR_ERR(new);
1206
1207 txopts = ipv6_renew_options(sk, req_inet->ipv6_opt, IPV6_HOPOPTS, new);
1208
1209 kfree(new);
1210
1211 if (IS_ERR(txopts))
1212 return PTR_ERR(txopts);
1213
1214 txopts = xchg(&req_inet->ipv6_opt, txopts);
1215 if (txopts) {
1216 atomic_sub(txopts->tot_len, &sk->sk_omem_alloc);
1217 txopt_put(txopts);
1218 }
1219
1220 return 0;
1221 }
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231 static void calipso_req_delattr(struct request_sock *req)
1232 {
1233 struct inet_request_sock *req_inet = inet_rsk(req);
1234 struct ipv6_opt_hdr *new;
1235 struct ipv6_txoptions *txopts;
1236 struct sock *sk = sk_to_full_sk(req_to_sk(req));
1237
1238 if (!req_inet->ipv6_opt || !req_inet->ipv6_opt->hopopt)
1239 return;
1240
1241 if (calipso_opt_del(req_inet->ipv6_opt->hopopt, &new))
1242 return;
1243
1244 txopts = ipv6_renew_options(sk, req_inet->ipv6_opt, IPV6_HOPOPTS, new);
1245
1246 if (!IS_ERR(txopts)) {
1247 txopts = xchg(&req_inet->ipv6_opt, txopts);
1248 if (txopts) {
1249 atomic_sub(txopts->tot_len, &sk->sk_omem_alloc);
1250 txopt_put(txopts);
1251 }
1252 }
1253 kfree(new);
1254 }
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268 static unsigned char *calipso_skbuff_optptr(const struct sk_buff *skb)
1269 {
1270 const struct ipv6hdr *ip6_hdr = ipv6_hdr(skb);
1271 int offset;
1272
1273 if (ip6_hdr->nexthdr != NEXTHDR_HOP)
1274 return NULL;
1275
1276 offset = ipv6_find_tlv(skb, sizeof(*ip6_hdr), IPV6_TLV_CALIPSO);
1277 if (offset >= 0)
1278 return (unsigned char *)ip6_hdr + offset;
1279
1280 return NULL;
1281 }
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294 static int calipso_skbuff_setattr(struct sk_buff *skb,
1295 const struct calipso_doi *doi_def,
1296 const struct netlbl_lsm_secattr *secattr)
1297 {
1298 int ret_val;
1299 struct ipv6hdr *ip6_hdr;
1300 struct ipv6_opt_hdr *hop;
1301 unsigned char buf[CALIPSO_MAX_BUFFER];
1302 int len_delta, new_end, pad, payload;
1303 unsigned int start, end;
1304
1305 ip6_hdr = ipv6_hdr(skb);
1306 if (ip6_hdr->nexthdr == NEXTHDR_HOP) {
1307 hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
1308 ret_val = calipso_opt_find(hop, &start, &end);
1309 if (ret_val && ret_val != -ENOENT)
1310 return ret_val;
1311 } else {
1312 start = 0;
1313 end = 0;
1314 }
1315
1316 memset(buf, 0, sizeof(buf));
1317 ret_val = calipso_genopt(buf, start & 3, sizeof(buf), doi_def, secattr);
1318 if (ret_val < 0)
1319 return ret_val;
1320
1321 new_end = start + ret_val;
1322
1323 pad = ((new_end & 4) + (end & 7)) & 7;
1324 len_delta = new_end - (int)end + pad;
1325 ret_val = skb_cow(skb, skb_headroom(skb) + len_delta);
1326 if (ret_val < 0)
1327 return ret_val;
1328
1329 ip6_hdr = ipv6_hdr(skb);
1330
1331 if (len_delta) {
1332 if (len_delta > 0)
1333 skb_push(skb, len_delta);
1334 else
1335 skb_pull(skb, -len_delta);
1336 memmove((char *)ip6_hdr - len_delta, ip6_hdr,
1337 sizeof(*ip6_hdr) + start);
1338 skb_reset_network_header(skb);
1339 ip6_hdr = ipv6_hdr(skb);
1340 payload = ntohs(ip6_hdr->payload_len);
1341 ip6_hdr->payload_len = htons(payload + len_delta);
1342 }
1343
1344 hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
1345 if (start == 0) {
1346 struct ipv6_opt_hdr *new_hop = (struct ipv6_opt_hdr *)buf;
1347
1348 new_hop->nexthdr = ip6_hdr->nexthdr;
1349 new_hop->hdrlen = len_delta / 8 - 1;
1350 ip6_hdr->nexthdr = NEXTHDR_HOP;
1351 } else {
1352 hop->hdrlen += len_delta / 8;
1353 }
1354 memcpy((char *)hop + start, buf + (start & 3), new_end - start);
1355 calipso_pad_write((unsigned char *)hop, new_end, pad);
1356
1357 return 0;
1358 }
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369 static int calipso_skbuff_delattr(struct sk_buff *skb)
1370 {
1371 int ret_val;
1372 struct ipv6hdr *ip6_hdr;
1373 struct ipv6_opt_hdr *old_hop;
1374 u32 old_hop_len, start = 0, end = 0, delta, size, pad;
1375
1376 if (!calipso_skbuff_optptr(skb))
1377 return 0;
1378
1379
1380 ret_val = skb_cow(skb, skb_headroom(skb));
1381 if (ret_val < 0)
1382 return ret_val;
1383
1384 ip6_hdr = ipv6_hdr(skb);
1385 old_hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
1386 old_hop_len = ipv6_optlen(old_hop);
1387
1388 ret_val = calipso_opt_find(old_hop, &start, &end);
1389 if (ret_val)
1390 return ret_val;
1391
1392 if (start == sizeof(*old_hop) && end == old_hop_len) {
1393
1394
1395 delta = old_hop_len;
1396 size = sizeof(*ip6_hdr);
1397 ip6_hdr->nexthdr = old_hop->nexthdr;
1398 } else {
1399 delta = (end - start) & ~7;
1400 if (delta)
1401 old_hop->hdrlen -= delta / 8;
1402 pad = (end - start) & 7;
1403 size = sizeof(*ip6_hdr) + start + pad;
1404 calipso_pad_write((unsigned char *)old_hop, start, pad);
1405 }
1406
1407 if (delta) {
1408 skb_pull(skb, delta);
1409 memmove((char *)ip6_hdr + delta, ip6_hdr, size);
1410 skb_reset_network_header(skb);
1411 }
1412
1413 return 0;
1414 }
1415
1416 static const struct netlbl_calipso_ops ops = {
1417 .doi_add = calipso_doi_add,
1418 .doi_free = calipso_doi_free,
1419 .doi_remove = calipso_doi_remove,
1420 .doi_getdef = calipso_doi_getdef,
1421 .doi_putdef = calipso_doi_putdef,
1422 .doi_walk = calipso_doi_walk,
1423 .sock_getattr = calipso_sock_getattr,
1424 .sock_setattr = calipso_sock_setattr,
1425 .sock_delattr = calipso_sock_delattr,
1426 .req_setattr = calipso_req_setattr,
1427 .req_delattr = calipso_req_delattr,
1428 .opt_getattr = calipso_opt_getattr,
1429 .skbuff_optptr = calipso_skbuff_optptr,
1430 .skbuff_setattr = calipso_skbuff_setattr,
1431 .skbuff_delattr = calipso_skbuff_delattr,
1432 .cache_invalidate = calipso_cache_invalidate,
1433 .cache_add = calipso_cache_add
1434 };
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444 int __init calipso_init(void)
1445 {
1446 int ret_val;
1447
1448 ret_val = calipso_cache_init();
1449 if (!ret_val)
1450 netlbl_calipso_ops_register(&ops);
1451 return ret_val;
1452 }
1453
1454 void calipso_exit(void)
1455 {
1456 netlbl_calipso_ops_register(NULL);
1457 calipso_cache_invalidate();
1458 kfree(calipso_cache);
1459 }