diff options
Diffstat (limited to 'net/ipv4/cipso_ipv4.c')
-rw-r--r-- | net/ipv4/cipso_ipv4.c | 656 |
1 files changed, 461 insertions, 195 deletions
diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index 2c0e4572cc90..490e035c6d90 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c | |||
@@ -13,7 +13,7 @@ | |||
13 | */ | 13 | */ |
14 | 14 | ||
15 | /* | 15 | /* |
16 | * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 | 16 | * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008 |
17 | * | 17 | * |
18 | * This program is free software; you can redistribute it and/or modify | 18 | * This program is free software; you can redistribute it and/or modify |
19 | * it under the terms of the GNU General Public License as published by | 19 | * it under the terms of the GNU General Public License as published by |
@@ -47,17 +47,7 @@ | |||
47 | #include <asm/bug.h> | 47 | #include <asm/bug.h> |
48 | #include <asm/unaligned.h> | 48 | #include <asm/unaligned.h> |
49 | 49 | ||
50 | struct cipso_v4_domhsh_entry { | ||
51 | char *domain; | ||
52 | u32 valid; | ||
53 | struct list_head list; | ||
54 | struct rcu_head rcu; | ||
55 | }; | ||
56 | |||
57 | /* List of available DOI definitions */ | 50 | /* List of available DOI definitions */ |
58 | /* XXX - Updates should be minimal so having a single lock for the | ||
59 | * cipso_v4_doi_list and the cipso_v4_doi_list->dom_list should be | ||
60 | * okay. */ | ||
61 | /* XXX - This currently assumes a minimal number of different DOIs in use, | 51 | /* XXX - This currently assumes a minimal number of different DOIs in use, |
62 | * if in practice there are a lot of different DOIs this list should | 52 | * if in practice there are a lot of different DOIs this list should |
63 | * probably be turned into a hash table or something similar so we | 53 | * probably be turned into a hash table or something similar so we |
@@ -119,6 +109,19 @@ int cipso_v4_rbm_strictvalid = 1; | |||
119 | * be omitted. */ | 109 | * be omitted. */ |
120 | #define CIPSO_V4_TAG_RNG_CAT_MAX 8 | 110 | #define CIPSO_V4_TAG_RNG_CAT_MAX 8 |
121 | 111 | ||
112 | /* Base length of the local tag (non-standard tag). | ||
113 | * Tag definition (may change between kernel versions) | ||
114 | * | ||
115 | * 0 8 16 24 32 | ||
116 | * +----------+----------+----------+----------+ | ||
117 | * | 10000000 | 00000110 | 32-bit secid value | | ||
118 | * +----------+----------+----------+----------+ | ||
119 | * | in (host byte order)| | ||
120 | * +----------+----------+ | ||
121 | * | ||
122 | */ | ||
123 | #define CIPSO_V4_TAG_LOC_BLEN 6 | ||
124 | |||
122 | /* | 125 | /* |
123 | * Helper Functions | 126 | * Helper Functions |
124 | */ | 127 | */ |
@@ -194,25 +197,6 @@ static void cipso_v4_bitmap_setbit(unsigned char *bitmap, | |||
194 | } | 197 | } |
195 | 198 | ||
196 | /** | 199 | /** |
197 | * cipso_v4_doi_domhsh_free - Frees a domain list entry | ||
198 | * @entry: the entry's RCU field | ||
199 | * | ||
200 | * Description: | ||
201 | * This function is designed to be used as a callback to the call_rcu() | ||
202 | * function so that the memory allocated to a domain list entry can be released | ||
203 | * safely. | ||
204 | * | ||
205 | */ | ||
206 | static void cipso_v4_doi_domhsh_free(struct rcu_head *entry) | ||
207 | { | ||
208 | struct cipso_v4_domhsh_entry *ptr; | ||
209 | |||
210 | ptr = container_of(entry, struct cipso_v4_domhsh_entry, rcu); | ||
211 | kfree(ptr->domain); | ||
212 | kfree(ptr); | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * cipso_v4_cache_entry_free - Frees a cache entry | 200 | * cipso_v4_cache_entry_free - Frees a cache entry |
217 | * @entry: the entry to free | 201 | * @entry: the entry to free |
218 | * | 202 | * |
@@ -457,7 +441,7 @@ static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi) | |||
457 | struct cipso_v4_doi *iter; | 441 | struct cipso_v4_doi *iter; |
458 | 442 | ||
459 | list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list) | 443 | list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list) |
460 | if (iter->doi == doi && iter->valid) | 444 | if (iter->doi == doi && atomic_read(&iter->refcount)) |
461 | return iter; | 445 | return iter; |
462 | return NULL; | 446 | return NULL; |
463 | } | 447 | } |
@@ -496,14 +480,17 @@ int cipso_v4_doi_add(struct cipso_v4_doi *doi_def) | |||
496 | if (doi_def->type != CIPSO_V4_MAP_PASS) | 480 | if (doi_def->type != CIPSO_V4_MAP_PASS) |
497 | return -EINVAL; | 481 | return -EINVAL; |
498 | break; | 482 | break; |
483 | case CIPSO_V4_TAG_LOCAL: | ||
484 | if (doi_def->type != CIPSO_V4_MAP_LOCAL) | ||
485 | return -EINVAL; | ||
486 | break; | ||
499 | default: | 487 | default: |
500 | return -EINVAL; | 488 | return -EINVAL; |
501 | } | 489 | } |
502 | } | 490 | } |
503 | 491 | ||
504 | doi_def->valid = 1; | 492 | atomic_set(&doi_def->refcount, 1); |
505 | INIT_RCU_HEAD(&doi_def->rcu); | 493 | INIT_RCU_HEAD(&doi_def->rcu); |
506 | INIT_LIST_HEAD(&doi_def->dom_list); | ||
507 | 494 | ||
508 | spin_lock(&cipso_v4_doi_list_lock); | 495 | spin_lock(&cipso_v4_doi_list_lock); |
509 | if (cipso_v4_doi_search(doi_def->doi) != NULL) | 496 | if (cipso_v4_doi_search(doi_def->doi) != NULL) |
@@ -519,59 +506,129 @@ doi_add_failure: | |||
519 | } | 506 | } |
520 | 507 | ||
521 | /** | 508 | /** |
509 | * cipso_v4_doi_free - Frees a DOI definition | ||
510 | * @entry: the entry's RCU field | ||
511 | * | ||
512 | * Description: | ||
513 | * This function frees all of the memory associated with a DOI definition. | ||
514 | * | ||
515 | */ | ||
516 | void cipso_v4_doi_free(struct cipso_v4_doi *doi_def) | ||
517 | { | ||
518 | if (doi_def == NULL) | ||
519 | return; | ||
520 | |||
521 | switch (doi_def->type) { | ||
522 | case CIPSO_V4_MAP_TRANS: | ||
523 | kfree(doi_def->map.std->lvl.cipso); | ||
524 | kfree(doi_def->map.std->lvl.local); | ||
525 | kfree(doi_def->map.std->cat.cipso); | ||
526 | kfree(doi_def->map.std->cat.local); | ||
527 | break; | ||
528 | } | ||
529 | kfree(doi_def); | ||
530 | } | ||
531 | |||
532 | /** | ||
533 | * cipso_v4_doi_free_rcu - Frees a DOI definition via the RCU pointer | ||
534 | * @entry: the entry's RCU field | ||
535 | * | ||
536 | * Description: | ||
537 | * This function is designed to be used as a callback to the call_rcu() | ||
538 | * function so that the memory allocated to the DOI definition can be released | ||
539 | * safely. | ||
540 | * | ||
541 | */ | ||
542 | static void cipso_v4_doi_free_rcu(struct rcu_head *entry) | ||
543 | { | ||
544 | struct cipso_v4_doi *doi_def; | ||
545 | |||
546 | doi_def = container_of(entry, struct cipso_v4_doi, rcu); | ||
547 | cipso_v4_doi_free(doi_def); | ||
548 | } | ||
549 | |||
550 | /** | ||
522 | * cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine | 551 | * cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine |
523 | * @doi: the DOI value | 552 | * @doi: the DOI value |
524 | * @audit_secid: the LSM secid to use in the audit message | 553 | * @audit_secid: the LSM secid to use in the audit message |
525 | * @callback: the DOI cleanup/free callback | ||
526 | * | 554 | * |
527 | * Description: | 555 | * Description: |
528 | * Removes a DOI definition from the CIPSO engine, @callback is called to | 556 | * Removes a DOI definition from the CIPSO engine. The NetLabel routines will |
529 | * free any memory. The NetLabel routines will be called to release their own | 557 | * be called to release their own LSM domain mappings as well as our own |
530 | * LSM domain mappings as well as our own domain list. Returns zero on | 558 | * domain list. Returns zero on success and negative values on failure. |
531 | * success and negative values on failure. | ||
532 | * | 559 | * |
533 | */ | 560 | */ |
534 | int cipso_v4_doi_remove(u32 doi, | 561 | int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info) |
535 | struct netlbl_audit *audit_info, | ||
536 | void (*callback) (struct rcu_head * head)) | ||
537 | { | 562 | { |
538 | struct cipso_v4_doi *doi_def; | 563 | struct cipso_v4_doi *doi_def; |
539 | struct cipso_v4_domhsh_entry *dom_iter; | ||
540 | 564 | ||
541 | spin_lock(&cipso_v4_doi_list_lock); | 565 | spin_lock(&cipso_v4_doi_list_lock); |
542 | doi_def = cipso_v4_doi_search(doi); | 566 | doi_def = cipso_v4_doi_search(doi); |
543 | if (doi_def != NULL) { | 567 | if (doi_def == NULL) { |
544 | doi_def->valid = 0; | ||
545 | list_del_rcu(&doi_def->list); | ||
546 | spin_unlock(&cipso_v4_doi_list_lock); | 568 | spin_unlock(&cipso_v4_doi_list_lock); |
547 | rcu_read_lock(); | 569 | return -ENOENT; |
548 | list_for_each_entry_rcu(dom_iter, &doi_def->dom_list, list) | 570 | } |
549 | if (dom_iter->valid) | 571 | if (!atomic_dec_and_test(&doi_def->refcount)) { |
550 | netlbl_cfg_map_del(dom_iter->domain, | 572 | spin_unlock(&cipso_v4_doi_list_lock); |
551 | audit_info); | 573 | return -EBUSY; |
552 | rcu_read_unlock(); | ||
553 | cipso_v4_cache_invalidate(); | ||
554 | call_rcu(&doi_def->rcu, callback); | ||
555 | return 0; | ||
556 | } | 574 | } |
575 | list_del_rcu(&doi_def->list); | ||
557 | spin_unlock(&cipso_v4_doi_list_lock); | 576 | spin_unlock(&cipso_v4_doi_list_lock); |
558 | 577 | ||
559 | return -ENOENT; | 578 | cipso_v4_cache_invalidate(); |
579 | call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu); | ||
580 | |||
581 | return 0; | ||
560 | } | 582 | } |
561 | 583 | ||
562 | /** | 584 | /** |
563 | * cipso_v4_doi_getdef - Returns a pointer to a valid DOI definition | 585 | * cipso_v4_doi_getdef - Returns a reference to a valid DOI definition |
564 | * @doi: the DOI value | 586 | * @doi: the DOI value |
565 | * | 587 | * |
566 | * Description: | 588 | * Description: |
567 | * Searches for a valid DOI definition and if one is found it is returned to | 589 | * Searches for a valid DOI definition and if one is found it is returned to |
568 | * the caller. Otherwise NULL is returned. The caller must ensure that | 590 | * the caller. Otherwise NULL is returned. The caller must ensure that |
569 | * rcu_read_lock() is held while accessing the returned definition. | 591 | * rcu_read_lock() is held while accessing the returned definition and the DOI |
592 | * definition reference count is decremented when the caller is done. | ||
570 | * | 593 | * |
571 | */ | 594 | */ |
572 | struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi) | 595 | struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi) |
573 | { | 596 | { |
574 | return cipso_v4_doi_search(doi); | 597 | struct cipso_v4_doi *doi_def; |
598 | |||
599 | rcu_read_lock(); | ||
600 | doi_def = cipso_v4_doi_search(doi); | ||
601 | if (doi_def == NULL) | ||
602 | goto doi_getdef_return; | ||
603 | if (!atomic_inc_not_zero(&doi_def->refcount)) | ||
604 | doi_def = NULL; | ||
605 | |||
606 | doi_getdef_return: | ||
607 | rcu_read_unlock(); | ||
608 | return doi_def; | ||
609 | } | ||
610 | |||
611 | /** | ||
612 | * cipso_v4_doi_putdef - Releases a reference for the given DOI definition | ||
613 | * @doi_def: the DOI definition | ||
614 | * | ||
615 | * Description: | ||
616 | * Releases a DOI definition reference obtained from cipso_v4_doi_getdef(). | ||
617 | * | ||
618 | */ | ||
619 | void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def) | ||
620 | { | ||
621 | if (doi_def == NULL) | ||
622 | return; | ||
623 | |||
624 | if (!atomic_dec_and_test(&doi_def->refcount)) | ||
625 | return; | ||
626 | spin_lock(&cipso_v4_doi_list_lock); | ||
627 | list_del_rcu(&doi_def->list); | ||
628 | spin_unlock(&cipso_v4_doi_list_lock); | ||
629 | |||
630 | cipso_v4_cache_invalidate(); | ||
631 | call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu); | ||
575 | } | 632 | } |
576 | 633 | ||
577 | /** | 634 | /** |
@@ -597,7 +654,7 @@ int cipso_v4_doi_walk(u32 *skip_cnt, | |||
597 | 654 | ||
598 | rcu_read_lock(); | 655 | rcu_read_lock(); |
599 | list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list) | 656 | list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list) |
600 | if (iter_doi->valid) { | 657 | if (atomic_read(&iter_doi->refcount) > 0) { |
601 | if (doi_cnt++ < *skip_cnt) | 658 | if (doi_cnt++ < *skip_cnt) |
602 | continue; | 659 | continue; |
603 | ret_val = callback(iter_doi, cb_arg); | 660 | ret_val = callback(iter_doi, cb_arg); |
@@ -613,85 +670,6 @@ doi_walk_return: | |||
613 | return ret_val; | 670 | return ret_val; |
614 | } | 671 | } |
615 | 672 | ||
616 | /** | ||
617 | * cipso_v4_doi_domhsh_add - Adds a domain entry to a DOI definition | ||
618 | * @doi_def: the DOI definition | ||
619 | * @domain: the domain to add | ||
620 | * | ||
621 | * Description: | ||
622 | * Adds the @domain to the DOI specified by @doi_def, this function | ||
623 | * should only be called by external functions (i.e. NetLabel). This function | ||
624 | * does allocate memory. Returns zero on success, negative values on failure. | ||
625 | * | ||
626 | */ | ||
627 | int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain) | ||
628 | { | ||
629 | struct cipso_v4_domhsh_entry *iter; | ||
630 | struct cipso_v4_domhsh_entry *new_dom; | ||
631 | |||
632 | new_dom = kzalloc(sizeof(*new_dom), GFP_KERNEL); | ||
633 | if (new_dom == NULL) | ||
634 | return -ENOMEM; | ||
635 | if (domain) { | ||
636 | new_dom->domain = kstrdup(domain, GFP_KERNEL); | ||
637 | if (new_dom->domain == NULL) { | ||
638 | kfree(new_dom); | ||
639 | return -ENOMEM; | ||
640 | } | ||
641 | } | ||
642 | new_dom->valid = 1; | ||
643 | INIT_RCU_HEAD(&new_dom->rcu); | ||
644 | |||
645 | spin_lock(&cipso_v4_doi_list_lock); | ||
646 | list_for_each_entry(iter, &doi_def->dom_list, list) | ||
647 | if (iter->valid && | ||
648 | ((domain != NULL && iter->domain != NULL && | ||
649 | strcmp(iter->domain, domain) == 0) || | ||
650 | (domain == NULL && iter->domain == NULL))) { | ||
651 | spin_unlock(&cipso_v4_doi_list_lock); | ||
652 | kfree(new_dom->domain); | ||
653 | kfree(new_dom); | ||
654 | return -EEXIST; | ||
655 | } | ||
656 | list_add_tail_rcu(&new_dom->list, &doi_def->dom_list); | ||
657 | spin_unlock(&cipso_v4_doi_list_lock); | ||
658 | |||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | /** | ||
663 | * cipso_v4_doi_domhsh_remove - Removes a domain entry from a DOI definition | ||
664 | * @doi_def: the DOI definition | ||
665 | * @domain: the domain to remove | ||
666 | * | ||
667 | * Description: | ||
668 | * Removes the @domain from the DOI specified by @doi_def, this function | ||
669 | * should only be called by external functions (i.e. NetLabel). Returns zero | ||
670 | * on success and negative values on error. | ||
671 | * | ||
672 | */ | ||
673 | int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def, | ||
674 | const char *domain) | ||
675 | { | ||
676 | struct cipso_v4_domhsh_entry *iter; | ||
677 | |||
678 | spin_lock(&cipso_v4_doi_list_lock); | ||
679 | list_for_each_entry(iter, &doi_def->dom_list, list) | ||
680 | if (iter->valid && | ||
681 | ((domain != NULL && iter->domain != NULL && | ||
682 | strcmp(iter->domain, domain) == 0) || | ||
683 | (domain == NULL && iter->domain == NULL))) { | ||
684 | iter->valid = 0; | ||
685 | list_del_rcu(&iter->list); | ||
686 | spin_unlock(&cipso_v4_doi_list_lock); | ||
687 | call_rcu(&iter->rcu, cipso_v4_doi_domhsh_free); | ||
688 | return 0; | ||
689 | } | ||
690 | spin_unlock(&cipso_v4_doi_list_lock); | ||
691 | |||
692 | return -ENOENT; | ||
693 | } | ||
694 | |||
695 | /* | 673 | /* |
696 | * Label Mapping Functions | 674 | * Label Mapping Functions |
697 | */ | 675 | */ |
@@ -712,7 +690,7 @@ static int cipso_v4_map_lvl_valid(const struct cipso_v4_doi *doi_def, u8 level) | |||
712 | switch (doi_def->type) { | 690 | switch (doi_def->type) { |
713 | case CIPSO_V4_MAP_PASS: | 691 | case CIPSO_V4_MAP_PASS: |
714 | return 0; | 692 | return 0; |
715 | case CIPSO_V4_MAP_STD: | 693 | case CIPSO_V4_MAP_TRANS: |
716 | if (doi_def->map.std->lvl.cipso[level] < CIPSO_V4_INV_LVL) | 694 | if (doi_def->map.std->lvl.cipso[level] < CIPSO_V4_INV_LVL) |
717 | return 0; | 695 | return 0; |
718 | break; | 696 | break; |
@@ -741,7 +719,7 @@ static int cipso_v4_map_lvl_hton(const struct cipso_v4_doi *doi_def, | |||
741 | case CIPSO_V4_MAP_PASS: | 719 | case CIPSO_V4_MAP_PASS: |
742 | *net_lvl = host_lvl; | 720 | *net_lvl = host_lvl; |
743 | return 0; | 721 | return 0; |
744 | case CIPSO_V4_MAP_STD: | 722 | case CIPSO_V4_MAP_TRANS: |
745 | if (host_lvl < doi_def->map.std->lvl.local_size && | 723 | if (host_lvl < doi_def->map.std->lvl.local_size && |
746 | doi_def->map.std->lvl.local[host_lvl] < CIPSO_V4_INV_LVL) { | 724 | doi_def->map.std->lvl.local[host_lvl] < CIPSO_V4_INV_LVL) { |
747 | *net_lvl = doi_def->map.std->lvl.local[host_lvl]; | 725 | *net_lvl = doi_def->map.std->lvl.local[host_lvl]; |
@@ -775,7 +753,7 @@ static int cipso_v4_map_lvl_ntoh(const struct cipso_v4_doi *doi_def, | |||
775 | case CIPSO_V4_MAP_PASS: | 753 | case CIPSO_V4_MAP_PASS: |
776 | *host_lvl = net_lvl; | 754 | *host_lvl = net_lvl; |
777 | return 0; | 755 | return 0; |
778 | case CIPSO_V4_MAP_STD: | 756 | case CIPSO_V4_MAP_TRANS: |
779 | map_tbl = doi_def->map.std; | 757 | map_tbl = doi_def->map.std; |
780 | if (net_lvl < map_tbl->lvl.cipso_size && | 758 | if (net_lvl < map_tbl->lvl.cipso_size && |
781 | map_tbl->lvl.cipso[net_lvl] < CIPSO_V4_INV_LVL) { | 759 | map_tbl->lvl.cipso[net_lvl] < CIPSO_V4_INV_LVL) { |
@@ -812,7 +790,7 @@ static int cipso_v4_map_cat_rbm_valid(const struct cipso_v4_doi *doi_def, | |||
812 | switch (doi_def->type) { | 790 | switch (doi_def->type) { |
813 | case CIPSO_V4_MAP_PASS: | 791 | case CIPSO_V4_MAP_PASS: |
814 | return 0; | 792 | return 0; |
815 | case CIPSO_V4_MAP_STD: | 793 | case CIPSO_V4_MAP_TRANS: |
816 | cipso_cat_size = doi_def->map.std->cat.cipso_size; | 794 | cipso_cat_size = doi_def->map.std->cat.cipso_size; |
817 | cipso_array = doi_def->map.std->cat.cipso; | 795 | cipso_array = doi_def->map.std->cat.cipso; |
818 | for (;;) { | 796 | for (;;) { |
@@ -860,7 +838,7 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def, | |||
860 | u32 host_cat_size = 0; | 838 | u32 host_cat_size = 0; |
861 | u32 *host_cat_array = NULL; | 839 | u32 *host_cat_array = NULL; |
862 | 840 | ||
863 | if (doi_def->type == CIPSO_V4_MAP_STD) { | 841 | if (doi_def->type == CIPSO_V4_MAP_TRANS) { |
864 | host_cat_size = doi_def->map.std->cat.local_size; | 842 | host_cat_size = doi_def->map.std->cat.local_size; |
865 | host_cat_array = doi_def->map.std->cat.local; | 843 | host_cat_array = doi_def->map.std->cat.local; |
866 | } | 844 | } |
@@ -875,7 +853,7 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def, | |||
875 | case CIPSO_V4_MAP_PASS: | 853 | case CIPSO_V4_MAP_PASS: |
876 | net_spot = host_spot; | 854 | net_spot = host_spot; |
877 | break; | 855 | break; |
878 | case CIPSO_V4_MAP_STD: | 856 | case CIPSO_V4_MAP_TRANS: |
879 | if (host_spot >= host_cat_size) | 857 | if (host_spot >= host_cat_size) |
880 | return -EPERM; | 858 | return -EPERM; |
881 | net_spot = host_cat_array[host_spot]; | 859 | net_spot = host_cat_array[host_spot]; |
@@ -921,7 +899,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def, | |||
921 | u32 net_cat_size = 0; | 899 | u32 net_cat_size = 0; |
922 | u32 *net_cat_array = NULL; | 900 | u32 *net_cat_array = NULL; |
923 | 901 | ||
924 | if (doi_def->type == CIPSO_V4_MAP_STD) { | 902 | if (doi_def->type == CIPSO_V4_MAP_TRANS) { |
925 | net_cat_size = doi_def->map.std->cat.cipso_size; | 903 | net_cat_size = doi_def->map.std->cat.cipso_size; |
926 | net_cat_array = doi_def->map.std->cat.cipso; | 904 | net_cat_array = doi_def->map.std->cat.cipso; |
927 | } | 905 | } |
@@ -941,7 +919,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def, | |||
941 | case CIPSO_V4_MAP_PASS: | 919 | case CIPSO_V4_MAP_PASS: |
942 | host_spot = net_spot; | 920 | host_spot = net_spot; |
943 | break; | 921 | break; |
944 | case CIPSO_V4_MAP_STD: | 922 | case CIPSO_V4_MAP_TRANS: |
945 | if (net_spot >= net_cat_size) | 923 | if (net_spot >= net_cat_size) |
946 | return -EPERM; | 924 | return -EPERM; |
947 | host_spot = net_cat_array[net_spot]; | 925 | host_spot = net_cat_array[net_spot]; |
@@ -1277,7 +1255,7 @@ static int cipso_v4_gentag_rbm(const struct cipso_v4_doi *doi_def, | |||
1277 | } else | 1255 | } else |
1278 | tag_len = 4; | 1256 | tag_len = 4; |
1279 | 1257 | ||
1280 | buffer[0] = 0x01; | 1258 | buffer[0] = CIPSO_V4_TAG_RBITMAP; |
1281 | buffer[1] = tag_len; | 1259 | buffer[1] = tag_len; |
1282 | buffer[3] = level; | 1260 | buffer[3] = level; |
1283 | 1261 | ||
@@ -1373,7 +1351,7 @@ static int cipso_v4_gentag_enum(const struct cipso_v4_doi *doi_def, | |||
1373 | } else | 1351 | } else |
1374 | tag_len = 4; | 1352 | tag_len = 4; |
1375 | 1353 | ||
1376 | buffer[0] = 0x02; | 1354 | buffer[0] = CIPSO_V4_TAG_ENUM; |
1377 | buffer[1] = tag_len; | 1355 | buffer[1] = tag_len; |
1378 | buffer[3] = level; | 1356 | buffer[3] = level; |
1379 | 1357 | ||
@@ -1469,7 +1447,7 @@ static int cipso_v4_gentag_rng(const struct cipso_v4_doi *doi_def, | |||
1469 | } else | 1447 | } else |
1470 | tag_len = 4; | 1448 | tag_len = 4; |
1471 | 1449 | ||
1472 | buffer[0] = 0x05; | 1450 | buffer[0] = CIPSO_V4_TAG_RANGE; |
1473 | buffer[1] = tag_len; | 1451 | buffer[1] = tag_len; |
1474 | buffer[3] = level; | 1452 | buffer[3] = level; |
1475 | 1453 | ||
@@ -1523,6 +1501,54 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def, | |||
1523 | } | 1501 | } |
1524 | 1502 | ||
1525 | /** | 1503 | /** |
1504 | * cipso_v4_gentag_loc - Generate a CIPSO local tag (non-standard) | ||
1505 | * @doi_def: the DOI definition | ||
1506 | * @secattr: the security attributes | ||
1507 | * @buffer: the option buffer | ||
1508 | * @buffer_len: length of buffer in bytes | ||
1509 | * | ||
1510 | * Description: | ||
1511 | * Generate a CIPSO option using the local tag. Returns the size of the tag | ||
1512 | * on success, negative values on failure. | ||
1513 | * | ||
1514 | */ | ||
1515 | static int cipso_v4_gentag_loc(const struct cipso_v4_doi *doi_def, | ||
1516 | const struct netlbl_lsm_secattr *secattr, | ||
1517 | unsigned char *buffer, | ||
1518 | u32 buffer_len) | ||
1519 | { | ||
1520 | if (!(secattr->flags & NETLBL_SECATTR_SECID)) | ||
1521 | return -EPERM; | ||
1522 | |||
1523 | buffer[0] = CIPSO_V4_TAG_LOCAL; | ||
1524 | buffer[1] = CIPSO_V4_TAG_LOC_BLEN; | ||
1525 | *(u32 *)&buffer[2] = secattr->attr.secid; | ||
1526 | |||
1527 | return CIPSO_V4_TAG_LOC_BLEN; | ||
1528 | } | ||
1529 | |||
1530 | /** | ||
1531 | * cipso_v4_parsetag_loc - Parse a CIPSO local tag | ||
1532 | * @doi_def: the DOI definition | ||
1533 | * @tag: the CIPSO tag | ||
1534 | * @secattr: the security attributes | ||
1535 | * | ||
1536 | * Description: | ||
1537 | * Parse a CIPSO local tag and return the security attributes in @secattr. | ||
1538 | * Return zero on success, negatives values on failure. | ||
1539 | * | ||
1540 | */ | ||
1541 | static int cipso_v4_parsetag_loc(const struct cipso_v4_doi *doi_def, | ||
1542 | const unsigned char *tag, | ||
1543 | struct netlbl_lsm_secattr *secattr) | ||
1544 | { | ||
1545 | secattr->attr.secid = *(u32 *)&tag[2]; | ||
1546 | secattr->flags |= NETLBL_SECATTR_SECID; | ||
1547 | |||
1548 | return 0; | ||
1549 | } | ||
1550 | |||
1551 | /** | ||
1526 | * cipso_v4_validate - Validate a CIPSO option | 1552 | * cipso_v4_validate - Validate a CIPSO option |
1527 | * @option: the start of the option, on error it is set to point to the error | 1553 | * @option: the start of the option, on error it is set to point to the error |
1528 | * | 1554 | * |
@@ -1541,7 +1567,7 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def, | |||
1541 | * that is unrecognized." | 1567 | * that is unrecognized." |
1542 | * | 1568 | * |
1543 | */ | 1569 | */ |
1544 | int cipso_v4_validate(unsigned char **option) | 1570 | int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option) |
1545 | { | 1571 | { |
1546 | unsigned char *opt = *option; | 1572 | unsigned char *opt = *option; |
1547 | unsigned char *tag; | 1573 | unsigned char *tag; |
@@ -1566,7 +1592,7 @@ int cipso_v4_validate(unsigned char **option) | |||
1566 | goto validate_return_locked; | 1592 | goto validate_return_locked; |
1567 | } | 1593 | } |
1568 | 1594 | ||
1569 | opt_iter = 6; | 1595 | opt_iter = CIPSO_V4_HDR_LEN; |
1570 | tag = opt + opt_iter; | 1596 | tag = opt + opt_iter; |
1571 | while (opt_iter < opt_len) { | 1597 | while (opt_iter < opt_len) { |
1572 | for (tag_iter = 0; doi_def->tags[tag_iter] != tag[0];) | 1598 | for (tag_iter = 0; doi_def->tags[tag_iter] != tag[0];) |
@@ -1584,7 +1610,7 @@ int cipso_v4_validate(unsigned char **option) | |||
1584 | 1610 | ||
1585 | switch (tag[0]) { | 1611 | switch (tag[0]) { |
1586 | case CIPSO_V4_TAG_RBITMAP: | 1612 | case CIPSO_V4_TAG_RBITMAP: |
1587 | if (tag_len < 4) { | 1613 | if (tag_len < CIPSO_V4_TAG_RBM_BLEN) { |
1588 | err_offset = opt_iter + 1; | 1614 | err_offset = opt_iter + 1; |
1589 | goto validate_return_locked; | 1615 | goto validate_return_locked; |
1590 | } | 1616 | } |
@@ -1602,7 +1628,7 @@ int cipso_v4_validate(unsigned char **option) | |||
1602 | err_offset = opt_iter + 3; | 1628 | err_offset = opt_iter + 3; |
1603 | goto validate_return_locked; | 1629 | goto validate_return_locked; |
1604 | } | 1630 | } |
1605 | if (tag_len > 4 && | 1631 | if (tag_len > CIPSO_V4_TAG_RBM_BLEN && |
1606 | cipso_v4_map_cat_rbm_valid(doi_def, | 1632 | cipso_v4_map_cat_rbm_valid(doi_def, |
1607 | &tag[4], | 1633 | &tag[4], |
1608 | tag_len - 4) < 0) { | 1634 | tag_len - 4) < 0) { |
@@ -1612,7 +1638,7 @@ int cipso_v4_validate(unsigned char **option) | |||
1612 | } | 1638 | } |
1613 | break; | 1639 | break; |
1614 | case CIPSO_V4_TAG_ENUM: | 1640 | case CIPSO_V4_TAG_ENUM: |
1615 | if (tag_len < 4) { | 1641 | if (tag_len < CIPSO_V4_TAG_ENUM_BLEN) { |
1616 | err_offset = opt_iter + 1; | 1642 | err_offset = opt_iter + 1; |
1617 | goto validate_return_locked; | 1643 | goto validate_return_locked; |
1618 | } | 1644 | } |
@@ -1622,7 +1648,7 @@ int cipso_v4_validate(unsigned char **option) | |||
1622 | err_offset = opt_iter + 3; | 1648 | err_offset = opt_iter + 3; |
1623 | goto validate_return_locked; | 1649 | goto validate_return_locked; |
1624 | } | 1650 | } |
1625 | if (tag_len > 4 && | 1651 | if (tag_len > CIPSO_V4_TAG_ENUM_BLEN && |
1626 | cipso_v4_map_cat_enum_valid(doi_def, | 1652 | cipso_v4_map_cat_enum_valid(doi_def, |
1627 | &tag[4], | 1653 | &tag[4], |
1628 | tag_len - 4) < 0) { | 1654 | tag_len - 4) < 0) { |
@@ -1631,7 +1657,7 @@ int cipso_v4_validate(unsigned char **option) | |||
1631 | } | 1657 | } |
1632 | break; | 1658 | break; |
1633 | case CIPSO_V4_TAG_RANGE: | 1659 | case CIPSO_V4_TAG_RANGE: |
1634 | if (tag_len < 4) { | 1660 | if (tag_len < CIPSO_V4_TAG_RNG_BLEN) { |
1635 | err_offset = opt_iter + 1; | 1661 | err_offset = opt_iter + 1; |
1636 | goto validate_return_locked; | 1662 | goto validate_return_locked; |
1637 | } | 1663 | } |
@@ -1641,7 +1667,7 @@ int cipso_v4_validate(unsigned char **option) | |||
1641 | err_offset = opt_iter + 3; | 1667 | err_offset = opt_iter + 3; |
1642 | goto validate_return_locked; | 1668 | goto validate_return_locked; |
1643 | } | 1669 | } |
1644 | if (tag_len > 4 && | 1670 | if (tag_len > CIPSO_V4_TAG_RNG_BLEN && |
1645 | cipso_v4_map_cat_rng_valid(doi_def, | 1671 | cipso_v4_map_cat_rng_valid(doi_def, |
1646 | &tag[4], | 1672 | &tag[4], |
1647 | tag_len - 4) < 0) { | 1673 | tag_len - 4) < 0) { |
@@ -1649,6 +1675,19 @@ int cipso_v4_validate(unsigned char **option) | |||
1649 | goto validate_return_locked; | 1675 | goto validate_return_locked; |
1650 | } | 1676 | } |
1651 | break; | 1677 | break; |
1678 | case CIPSO_V4_TAG_LOCAL: | ||
1679 | /* This is a non-standard tag that we only allow for | ||
1680 | * local connections, so if the incoming interface is | ||
1681 | * not the loopback device drop the packet. */ | ||
1682 | if (!(skb->dev->flags & IFF_LOOPBACK)) { | ||
1683 | err_offset = opt_iter; | ||
1684 | goto validate_return_locked; | ||
1685 | } | ||
1686 | if (tag_len != CIPSO_V4_TAG_LOC_BLEN) { | ||
1687 | err_offset = opt_iter + 1; | ||
1688 | goto validate_return_locked; | ||
1689 | } | ||
1690 | break; | ||
1652 | default: | 1691 | default: |
1653 | err_offset = opt_iter; | 1692 | err_offset = opt_iter; |
1654 | goto validate_return_locked; | 1693 | goto validate_return_locked; |
@@ -1704,48 +1743,27 @@ void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway) | |||
1704 | } | 1743 | } |
1705 | 1744 | ||
1706 | /** | 1745 | /** |
1707 | * cipso_v4_sock_setattr - Add a CIPSO option to a socket | 1746 | * cipso_v4_genopt - Generate a CIPSO option |
1708 | * @sk: the socket | 1747 | * @buf: the option buffer |
1748 | * @buf_len: the size of opt_buf | ||
1709 | * @doi_def: the CIPSO DOI to use | 1749 | * @doi_def: the CIPSO DOI to use |
1710 | * @secattr: the specific security attributes of the socket | 1750 | * @secattr: the security attributes |
1711 | * | 1751 | * |
1712 | * Description: | 1752 | * Description: |
1713 | * Set the CIPSO option on the given socket using the DOI definition and | 1753 | * Generate a CIPSO option using the DOI definition and security attributes |
1714 | * security attributes passed to the function. This function requires | 1754 | * passed to the function. Returns the length of the option on success and |
1715 | * exclusive access to @sk, which means it either needs to be in the | 1755 | * negative values on failure. |
1716 | * process of being created or locked. Returns zero on success and negative | ||
1717 | * values on failure. | ||
1718 | * | 1756 | * |
1719 | */ | 1757 | */ |
1720 | int cipso_v4_sock_setattr(struct sock *sk, | 1758 | static int cipso_v4_genopt(unsigned char *buf, u32 buf_len, |
1721 | const struct cipso_v4_doi *doi_def, | 1759 | const struct cipso_v4_doi *doi_def, |
1722 | const struct netlbl_lsm_secattr *secattr) | 1760 | const struct netlbl_lsm_secattr *secattr) |
1723 | { | 1761 | { |
1724 | int ret_val = -EPERM; | 1762 | int ret_val; |
1725 | u32 iter; | 1763 | u32 iter; |
1726 | unsigned char *buf; | ||
1727 | u32 buf_len = 0; | ||
1728 | u32 opt_len; | ||
1729 | struct ip_options *opt = NULL; | ||
1730 | struct inet_sock *sk_inet; | ||
1731 | struct inet_connection_sock *sk_conn; | ||
1732 | 1764 | ||
1733 | /* In the case of sock_create_lite(), the sock->sk field is not | 1765 | if (buf_len <= CIPSO_V4_HDR_LEN) |
1734 | * defined yet but it is not a problem as the only users of these | 1766 | return -ENOSPC; |
1735 | * "lite" PF_INET sockets are functions which do an accept() call | ||
1736 | * afterwards so we will label the socket as part of the accept(). */ | ||
1737 | if (sk == NULL) | ||
1738 | return 0; | ||
1739 | |||
1740 | /* We allocate the maximum CIPSO option size here so we are probably | ||
1741 | * being a little wasteful, but it makes our life _much_ easier later | ||
1742 | * on and after all we are only talking about 40 bytes. */ | ||
1743 | buf_len = CIPSO_V4_OPT_LEN_MAX; | ||
1744 | buf = kmalloc(buf_len, GFP_ATOMIC); | ||
1745 | if (buf == NULL) { | ||
1746 | ret_val = -ENOMEM; | ||
1747 | goto socket_setattr_failure; | ||
1748 | } | ||
1749 | 1767 | ||
1750 | /* XXX - This code assumes only one tag per CIPSO option which isn't | 1768 | /* XXX - This code assumes only one tag per CIPSO option which isn't |
1751 | * really a good assumption to make but since we only support the MAC | 1769 | * really a good assumption to make but since we only support the MAC |
@@ -1772,9 +1790,14 @@ int cipso_v4_sock_setattr(struct sock *sk, | |||
1772 | &buf[CIPSO_V4_HDR_LEN], | 1790 | &buf[CIPSO_V4_HDR_LEN], |
1773 | buf_len - CIPSO_V4_HDR_LEN); | 1791 | buf_len - CIPSO_V4_HDR_LEN); |
1774 | break; | 1792 | break; |
1793 | case CIPSO_V4_TAG_LOCAL: | ||
1794 | ret_val = cipso_v4_gentag_loc(doi_def, | ||
1795 | secattr, | ||
1796 | &buf[CIPSO_V4_HDR_LEN], | ||
1797 | buf_len - CIPSO_V4_HDR_LEN); | ||
1798 | break; | ||
1775 | default: | 1799 | default: |
1776 | ret_val = -EPERM; | 1800 | return -EPERM; |
1777 | goto socket_setattr_failure; | ||
1778 | } | 1801 | } |
1779 | 1802 | ||
1780 | iter++; | 1803 | iter++; |
@@ -1782,9 +1805,58 @@ int cipso_v4_sock_setattr(struct sock *sk, | |||
1782 | iter < CIPSO_V4_TAG_MAXCNT && | 1805 | iter < CIPSO_V4_TAG_MAXCNT && |
1783 | doi_def->tags[iter] != CIPSO_V4_TAG_INVALID); | 1806 | doi_def->tags[iter] != CIPSO_V4_TAG_INVALID); |
1784 | if (ret_val < 0) | 1807 | if (ret_val < 0) |
1785 | goto socket_setattr_failure; | 1808 | return ret_val; |
1786 | cipso_v4_gentag_hdr(doi_def, buf, ret_val); | 1809 | cipso_v4_gentag_hdr(doi_def, buf, ret_val); |
1787 | buf_len = CIPSO_V4_HDR_LEN + ret_val; | 1810 | return CIPSO_V4_HDR_LEN + ret_val; |
1811 | } | ||
1812 | |||
1813 | /** | ||
1814 | * cipso_v4_sock_setattr - Add a CIPSO option to a socket | ||
1815 | * @sk: the socket | ||
1816 | * @doi_def: the CIPSO DOI to use | ||
1817 | * @secattr: the specific security attributes of the socket | ||
1818 | * | ||
1819 | * Description: | ||
1820 | * Set the CIPSO option on the given socket using the DOI definition and | ||
1821 | * security attributes passed to the function. This function requires | ||
1822 | * exclusive access to @sk, which means it either needs to be in the | ||
1823 | * process of being created or locked. Returns zero on success and negative | ||
1824 | * values on failure. | ||
1825 | * | ||
1826 | */ | ||
1827 | int cipso_v4_sock_setattr(struct sock *sk, | ||
1828 | const struct cipso_v4_doi *doi_def, | ||
1829 | const struct netlbl_lsm_secattr *secattr) | ||
1830 | { | ||
1831 | int ret_val = -EPERM; | ||
1832 | unsigned char *buf = NULL; | ||
1833 | u32 buf_len; | ||
1834 | u32 opt_len; | ||
1835 | struct ip_options *opt = NULL; | ||
1836 | struct inet_sock *sk_inet; | ||
1837 | struct inet_connection_sock *sk_conn; | ||
1838 | |||
1839 | /* In the case of sock_create_lite(), the sock->sk field is not | ||
1840 | * defined yet but it is not a problem as the only users of these | ||
1841 | * "lite" PF_INET sockets are functions which do an accept() call | ||
1842 | * afterwards so we will label the socket as part of the accept(). */ | ||
1843 | if (sk == NULL) | ||
1844 | return 0; | ||
1845 | |||
1846 | /* We allocate the maximum CIPSO option size here so we are probably | ||
1847 | * being a little wasteful, but it makes our life _much_ easier later | ||
1848 | * on and after all we are only talking about 40 bytes. */ | ||
1849 | buf_len = CIPSO_V4_OPT_LEN_MAX; | ||
1850 | buf = kmalloc(buf_len, GFP_ATOMIC); | ||
1851 | if (buf == NULL) { | ||
1852 | ret_val = -ENOMEM; | ||
1853 | goto socket_setattr_failure; | ||
1854 | } | ||
1855 | |||
1856 | ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr); | ||
1857 | if (ret_val < 0) | ||
1858 | goto socket_setattr_failure; | ||
1859 | buf_len = ret_val; | ||
1788 | 1860 | ||
1789 | /* We can't use ip_options_get() directly because it makes a call to | 1861 | /* We can't use ip_options_get() directly because it makes a call to |
1790 | * ip_options_get_alloc() which allocates memory with GFP_KERNEL and | 1862 | * ip_options_get_alloc() which allocates memory with GFP_KERNEL and |
@@ -1822,6 +1894,80 @@ socket_setattr_failure: | |||
1822 | } | 1894 | } |
1823 | 1895 | ||
1824 | /** | 1896 | /** |
1897 | * cipso_v4_sock_delattr - Delete the CIPSO option from a socket | ||
1898 | * @sk: the socket | ||
1899 | * | ||
1900 | * Description: | ||
1901 | * Removes the CIPSO option from a socket, if present. | ||
1902 | * | ||
1903 | */ | ||
1904 | void cipso_v4_sock_delattr(struct sock *sk) | ||
1905 | { | ||
1906 | u8 hdr_delta; | ||
1907 | struct ip_options *opt; | ||
1908 | struct inet_sock *sk_inet; | ||
1909 | |||
1910 | sk_inet = inet_sk(sk); | ||
1911 | opt = sk_inet->opt; | ||
1912 | if (opt == NULL || opt->cipso == 0) | ||
1913 | return; | ||
1914 | |||
1915 | if (opt->srr || opt->rr || opt->ts || opt->router_alert) { | ||
1916 | u8 cipso_len; | ||
1917 | u8 cipso_off; | ||
1918 | unsigned char *cipso_ptr; | ||
1919 | int iter; | ||
1920 | int optlen_new; | ||
1921 | |||
1922 | cipso_off = opt->cipso - sizeof(struct iphdr); | ||
1923 | cipso_ptr = &opt->__data[cipso_off]; | ||
1924 | cipso_len = cipso_ptr[1]; | ||
1925 | |||
1926 | if (opt->srr > opt->cipso) | ||
1927 | opt->srr -= cipso_len; | ||
1928 | if (opt->rr > opt->cipso) | ||
1929 | opt->rr -= cipso_len; | ||
1930 | if (opt->ts > opt->cipso) | ||
1931 | opt->ts -= cipso_len; | ||
1932 | if (opt->router_alert > opt->cipso) | ||
1933 | opt->router_alert -= cipso_len; | ||
1934 | opt->cipso = 0; | ||
1935 | |||
1936 | memmove(cipso_ptr, cipso_ptr + cipso_len, | ||
1937 | opt->optlen - cipso_off - cipso_len); | ||
1938 | |||
1939 | /* determining the new total option length is tricky because of | ||
1940 | * the padding necessary, the only thing i can think to do at | ||
1941 | * this point is walk the options one-by-one, skipping the | ||
1942 | * padding at the end to determine the actual option size and | ||
1943 | * from there we can determine the new total option length */ | ||
1944 | iter = 0; | ||
1945 | optlen_new = 0; | ||
1946 | while (iter < opt->optlen) | ||
1947 | if (opt->__data[iter] != IPOPT_NOP) { | ||
1948 | iter += opt->__data[iter + 1]; | ||
1949 | optlen_new = iter; | ||
1950 | } else | ||
1951 | iter++; | ||
1952 | hdr_delta = opt->optlen; | ||
1953 | opt->optlen = (optlen_new + 3) & ~3; | ||
1954 | hdr_delta -= opt->optlen; | ||
1955 | } else { | ||
1956 | /* only the cipso option was present on the socket so we can | ||
1957 | * remove the entire option struct */ | ||
1958 | sk_inet->opt = NULL; | ||
1959 | hdr_delta = opt->optlen; | ||
1960 | kfree(opt); | ||
1961 | } | ||
1962 | |||
1963 | if (sk_inet->is_icsk && hdr_delta > 0) { | ||
1964 | struct inet_connection_sock *sk_conn = inet_csk(sk); | ||
1965 | sk_conn->icsk_ext_hdr_len -= hdr_delta; | ||
1966 | sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie); | ||
1967 | } | ||
1968 | } | ||
1969 | |||
1970 | /** | ||
1825 | * cipso_v4_getattr - Helper function for the cipso_v4_*_getattr functions | 1971 | * cipso_v4_getattr - Helper function for the cipso_v4_*_getattr functions |
1826 | * @cipso: the CIPSO v4 option | 1972 | * @cipso: the CIPSO v4 option |
1827 | * @secattr: the security attributes | 1973 | * @secattr: the security attributes |
@@ -1859,6 +2005,9 @@ static int cipso_v4_getattr(const unsigned char *cipso, | |||
1859 | case CIPSO_V4_TAG_RANGE: | 2005 | case CIPSO_V4_TAG_RANGE: |
1860 | ret_val = cipso_v4_parsetag_rng(doi_def, &cipso[6], secattr); | 2006 | ret_val = cipso_v4_parsetag_rng(doi_def, &cipso[6], secattr); |
1861 | break; | 2007 | break; |
2008 | case CIPSO_V4_TAG_LOCAL: | ||
2009 | ret_val = cipso_v4_parsetag_loc(doi_def, &cipso[6], secattr); | ||
2010 | break; | ||
1862 | } | 2011 | } |
1863 | if (ret_val == 0) | 2012 | if (ret_val == 0) |
1864 | secattr->type = NETLBL_NLTYPE_CIPSOV4; | 2013 | secattr->type = NETLBL_NLTYPE_CIPSOV4; |
@@ -1893,6 +2042,123 @@ int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) | |||
1893 | } | 2042 | } |
1894 | 2043 | ||
1895 | /** | 2044 | /** |
2045 | * cipso_v4_skbuff_setattr - Set the CIPSO option on a packet | ||
2046 | * @skb: the packet | ||
2047 | * @secattr: the security attributes | ||
2048 | * | ||
2049 | * Description: | ||
2050 | * Set the CIPSO option on the given packet based on the security attributes. | ||
2051 | * Returns a pointer to the IP header on success and NULL on failure. | ||
2052 | * | ||
2053 | */ | ||
2054 | int cipso_v4_skbuff_setattr(struct sk_buff *skb, | ||
2055 | const struct cipso_v4_doi *doi_def, | ||
2056 | const struct netlbl_lsm_secattr *secattr) | ||
2057 | { | ||
2058 | int ret_val; | ||
2059 | struct iphdr *iph; | ||
2060 | struct ip_options *opt = &IPCB(skb)->opt; | ||
2061 | unsigned char buf[CIPSO_V4_OPT_LEN_MAX]; | ||
2062 | u32 buf_len = CIPSO_V4_OPT_LEN_MAX; | ||
2063 | u32 opt_len; | ||
2064 | int len_delta; | ||
2065 | |||
2066 | buf_len = cipso_v4_genopt(buf, buf_len, doi_def, secattr); | ||
2067 | if (buf_len < 0) | ||
2068 | return buf_len; | ||
2069 | opt_len = (buf_len + 3) & ~3; | ||
2070 | |||
2071 | /* we overwrite any existing options to ensure that we have enough | ||
2072 | * room for the CIPSO option, the reason is that we _need_ to guarantee | ||
2073 | * that the security label is applied to the packet - we do the same | ||
2074 | * thing when using the socket options and it hasn't caused a problem, | ||
2075 | * if we need to we can always revisit this choice later */ | ||
2076 | |||
2077 | len_delta = opt_len - opt->optlen; | ||
2078 | /* if we don't ensure enough headroom we could panic on the skb_push() | ||
2079 | * call below so make sure we have enough, we are also "mangling" the | ||
2080 | * packet so we should probably do a copy-on-write call anyway */ | ||
2081 | ret_val = skb_cow(skb, skb_headroom(skb) + len_delta); | ||
2082 | if (ret_val < 0) | ||
2083 | return ret_val; | ||
2084 | |||
2085 | if (len_delta > 0) { | ||
2086 | /* we assume that the header + opt->optlen have already been | ||
2087 | * "pushed" in ip_options_build() or similar */ | ||
2088 | iph = ip_hdr(skb); | ||
2089 | skb_push(skb, len_delta); | ||
2090 | memmove((char *)iph - len_delta, iph, iph->ihl << 2); | ||
2091 | skb_reset_network_header(skb); | ||
2092 | iph = ip_hdr(skb); | ||
2093 | } else if (len_delta < 0) { | ||
2094 | iph = ip_hdr(skb); | ||
2095 | memset(iph + 1, IPOPT_NOP, opt->optlen); | ||
2096 | } else | ||
2097 | iph = ip_hdr(skb); | ||
2098 | |||
2099 | if (opt->optlen > 0) | ||
2100 | memset(opt, 0, sizeof(*opt)); | ||
2101 | opt->optlen = opt_len; | ||
2102 | opt->cipso = sizeof(struct iphdr); | ||
2103 | opt->is_changed = 1; | ||
2104 | |||
2105 | /* we have to do the following because we are being called from a | ||
2106 | * netfilter hook which means the packet already has had the header | ||
2107 | * fields populated and the checksum calculated - yes this means we | ||
2108 | * are doing more work than needed but we do it to keep the core | ||
2109 | * stack clean and tidy */ | ||
2110 | memcpy(iph + 1, buf, buf_len); | ||
2111 | if (opt_len > buf_len) | ||
2112 | memset((char *)(iph + 1) + buf_len, 0, opt_len - buf_len); | ||
2113 | if (len_delta != 0) { | ||
2114 | iph->ihl = 5 + (opt_len >> 2); | ||
2115 | iph->tot_len = htons(skb->len); | ||
2116 | } | ||
2117 | ip_send_check(iph); | ||
2118 | |||
2119 | return 0; | ||
2120 | } | ||
2121 | |||
2122 | /** | ||
2123 | * cipso_v4_skbuff_delattr - Delete any CIPSO options from a packet | ||
2124 | * @skb: the packet | ||
2125 | * | ||
2126 | * Description: | ||
2127 | * Removes any and all CIPSO options from the given packet. Returns zero on | ||
2128 | * success, negative values on failure. | ||
2129 | * | ||
2130 | */ | ||
2131 | int cipso_v4_skbuff_delattr(struct sk_buff *skb) | ||
2132 | { | ||
2133 | int ret_val; | ||
2134 | struct iphdr *iph; | ||
2135 | struct ip_options *opt = &IPCB(skb)->opt; | ||
2136 | unsigned char *cipso_ptr; | ||
2137 | |||
2138 | if (opt->cipso == 0) | ||
2139 | return 0; | ||
2140 | |||
2141 | /* since we are changing the packet we should make a copy */ | ||
2142 | ret_val = skb_cow(skb, skb_headroom(skb)); | ||
2143 | if (ret_val < 0) | ||
2144 | return ret_val; | ||
2145 | |||
2146 | /* the easiest thing to do is just replace the cipso option with noop | ||
2147 | * options since we don't change the size of the packet, although we | ||
2148 | * still need to recalculate the checksum */ | ||
2149 | |||
2150 | iph = ip_hdr(skb); | ||
2151 | cipso_ptr = (unsigned char *)iph + opt->cipso; | ||
2152 | memset(cipso_ptr, IPOPT_NOOP, cipso_ptr[1]); | ||
2153 | opt->cipso = 0; | ||
2154 | opt->is_changed = 1; | ||
2155 | |||
2156 | ip_send_check(iph); | ||
2157 | |||
2158 | return 0; | ||
2159 | } | ||
2160 | |||
2161 | /** | ||
1896 | * cipso_v4_skbuff_getattr - Get the security attributes from the CIPSO option | 2162 | * cipso_v4_skbuff_getattr - Get the security attributes from the CIPSO option |
1897 | * @skb: the packet | 2163 | * @skb: the packet |
1898 | * @secattr: the security attributes | 2164 | * @secattr: the security attributes |