diff options
Diffstat (limited to 'security/keys')
-rw-r--r-- | security/keys/keyring.c | 87 |
1 files changed, 64 insertions, 23 deletions
diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 5d22c0388b32..09d92d52ef75 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c | |||
@@ -684,15 +684,31 @@ static void keyring_link_rcu_disposal(struct rcu_head *rcu) | |||
684 | 684 | ||
685 | /*****************************************************************************/ | 685 | /*****************************************************************************/ |
686 | /* | 686 | /* |
687 | * dispose of a keyring list after the RCU grace period, freeing the unlinked | ||
688 | * key | ||
689 | */ | ||
690 | static void keyring_unlink_rcu_disposal(struct rcu_head *rcu) | ||
691 | { | ||
692 | struct keyring_list *klist = | ||
693 | container_of(rcu, struct keyring_list, rcu); | ||
694 | |||
695 | key_put(klist->keys[klist->delkey]); | ||
696 | kfree(klist); | ||
697 | |||
698 | } /* end keyring_unlink_rcu_disposal() */ | ||
699 | |||
700 | /*****************************************************************************/ | ||
701 | /* | ||
687 | * link a key into to a keyring | 702 | * link a key into to a keyring |
688 | * - must be called with the keyring's semaphore write-locked | 703 | * - must be called with the keyring's semaphore write-locked |
704 | * - discard already extant link to matching key if there is one | ||
689 | */ | 705 | */ |
690 | int __key_link(struct key *keyring, struct key *key) | 706 | int __key_link(struct key *keyring, struct key *key) |
691 | { | 707 | { |
692 | struct keyring_list *klist, *nklist; | 708 | struct keyring_list *klist, *nklist; |
693 | unsigned max; | 709 | unsigned max; |
694 | size_t size; | 710 | size_t size; |
695 | int ret; | 711 | int loop, ret; |
696 | 712 | ||
697 | ret = -EKEYREVOKED; | 713 | ret = -EKEYREVOKED; |
698 | if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) | 714 | if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) |
@@ -714,6 +730,48 @@ int __key_link(struct key *keyring, struct key *key) | |||
714 | goto error2; | 730 | goto error2; |
715 | } | 731 | } |
716 | 732 | ||
733 | /* see if there's a matching key we can displace */ | ||
734 | klist = keyring->payload.subscriptions; | ||
735 | |||
736 | if (klist && klist->nkeys > 0) { | ||
737 | struct key_type *type = key->type; | ||
738 | |||
739 | for (loop = klist->nkeys - 1; loop >= 0; loop--) { | ||
740 | if (klist->keys[loop]->type == type && | ||
741 | strcmp(klist->keys[loop]->description, | ||
742 | key->description) == 0 | ||
743 | ) { | ||
744 | /* found a match - replace with new key */ | ||
745 | size = sizeof(struct key *) * klist->maxkeys; | ||
746 | size += sizeof(*klist); | ||
747 | BUG_ON(size > PAGE_SIZE); | ||
748 | |||
749 | ret = -ENOMEM; | ||
750 | nklist = kmalloc(size, GFP_KERNEL); | ||
751 | if (!nklist) | ||
752 | goto error2; | ||
753 | |||
754 | memcpy(nklist, klist, size); | ||
755 | |||
756 | /* replace matched key */ | ||
757 | atomic_inc(&key->usage); | ||
758 | nklist->keys[loop] = key; | ||
759 | |||
760 | rcu_assign_pointer( | ||
761 | keyring->payload.subscriptions, | ||
762 | nklist); | ||
763 | |||
764 | /* dispose of the old keyring list and the | ||
765 | * displaced key */ | ||
766 | klist->delkey = loop; | ||
767 | call_rcu(&klist->rcu, | ||
768 | keyring_unlink_rcu_disposal); | ||
769 | |||
770 | goto done; | ||
771 | } | ||
772 | } | ||
773 | } | ||
774 | |||
717 | /* check that we aren't going to overrun the user's quota */ | 775 | /* check that we aren't going to overrun the user's quota */ |
718 | ret = key_payload_reserve(keyring, | 776 | ret = key_payload_reserve(keyring, |
719 | keyring->datalen + KEYQUOTA_LINK_BYTES); | 777 | keyring->datalen + KEYQUOTA_LINK_BYTES); |
@@ -730,8 +788,6 @@ int __key_link(struct key *keyring, struct key *key) | |||
730 | smp_wmb(); | 788 | smp_wmb(); |
731 | klist->nkeys++; | 789 | klist->nkeys++; |
732 | smp_wmb(); | 790 | smp_wmb(); |
733 | |||
734 | ret = 0; | ||
735 | } | 791 | } |
736 | else { | 792 | else { |
737 | /* grow the key list */ | 793 | /* grow the key list */ |
@@ -769,16 +825,16 @@ int __key_link(struct key *keyring, struct key *key) | |||
769 | /* dispose of the old keyring list */ | 825 | /* dispose of the old keyring list */ |
770 | if (klist) | 826 | if (klist) |
771 | call_rcu(&klist->rcu, keyring_link_rcu_disposal); | 827 | call_rcu(&klist->rcu, keyring_link_rcu_disposal); |
772 | |||
773 | ret = 0; | ||
774 | } | 828 | } |
775 | 829 | ||
776 | error2: | 830 | done: |
831 | ret = 0; | ||
832 | error2: | ||
777 | up_write(&keyring_serialise_link_sem); | 833 | up_write(&keyring_serialise_link_sem); |
778 | error: | 834 | error: |
779 | return ret; | 835 | return ret; |
780 | 836 | ||
781 | error3: | 837 | error3: |
782 | /* undo the quota changes */ | 838 | /* undo the quota changes */ |
783 | key_payload_reserve(keyring, | 839 | key_payload_reserve(keyring, |
784 | keyring->datalen - KEYQUOTA_LINK_BYTES); | 840 | keyring->datalen - KEYQUOTA_LINK_BYTES); |
@@ -809,21 +865,6 @@ EXPORT_SYMBOL(key_link); | |||
809 | 865 | ||
810 | /*****************************************************************************/ | 866 | /*****************************************************************************/ |
811 | /* | 867 | /* |
812 | * dispose of a keyring list after the RCU grace period, freeing the unlinked | ||
813 | * key | ||
814 | */ | ||
815 | static void keyring_unlink_rcu_disposal(struct rcu_head *rcu) | ||
816 | { | ||
817 | struct keyring_list *klist = | ||
818 | container_of(rcu, struct keyring_list, rcu); | ||
819 | |||
820 | key_put(klist->keys[klist->delkey]); | ||
821 | kfree(klist); | ||
822 | |||
823 | } /* end keyring_unlink_rcu_disposal() */ | ||
824 | |||
825 | /*****************************************************************************/ | ||
826 | /* | ||
827 | * unlink the first link to a key from a keyring | 868 | * unlink the first link to a key from a keyring |
828 | */ | 869 | */ |
829 | int key_unlink(struct key *keyring, struct key *key) | 870 | int key_unlink(struct key *keyring, struct key *key) |