diff options
Diffstat (limited to 'security')
-rw-r--r-- | security/keys/keyctl.c | 56 |
1 files changed, 48 insertions, 8 deletions
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index d74458522e98..329411cf8768 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c | |||
@@ -673,6 +673,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) | |||
673 | */ | 673 | */ |
674 | long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) | 674 | long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) |
675 | { | 675 | { |
676 | struct key_user *newowner, *zapowner = NULL; | ||
676 | struct key *key; | 677 | struct key *key; |
677 | key_ref_t key_ref; | 678 | key_ref_t key_ref; |
678 | long ret; | 679 | long ret; |
@@ -696,19 +697,50 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) | |||
696 | if (!capable(CAP_SYS_ADMIN)) { | 697 | if (!capable(CAP_SYS_ADMIN)) { |
697 | /* only the sysadmin can chown a key to some other UID */ | 698 | /* only the sysadmin can chown a key to some other UID */ |
698 | if (uid != (uid_t) -1 && key->uid != uid) | 699 | if (uid != (uid_t) -1 && key->uid != uid) |
699 | goto no_access; | 700 | goto error_put; |
700 | 701 | ||
701 | /* only the sysadmin can set the key's GID to a group other | 702 | /* only the sysadmin can set the key's GID to a group other |
702 | * than one of those that the current process subscribes to */ | 703 | * than one of those that the current process subscribes to */ |
703 | if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid)) | 704 | if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid)) |
704 | goto no_access; | 705 | goto error_put; |
705 | } | 706 | } |
706 | 707 | ||
707 | /* change the UID (have to update the quotas) */ | 708 | /* change the UID */ |
708 | if (uid != (uid_t) -1 && uid != key->uid) { | 709 | if (uid != (uid_t) -1 && uid != key->uid) { |
709 | /* don't support UID changing yet */ | 710 | ret = -ENOMEM; |
710 | ret = -EOPNOTSUPP; | 711 | newowner = key_user_lookup(uid); |
711 | goto no_access; | 712 | if (!newowner) |
713 | goto error_put; | ||
714 | |||
715 | /* transfer the quota burden to the new user */ | ||
716 | if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { | ||
717 | spin_lock(&newowner->lock); | ||
718 | if (newowner->qnkeys + 1 >= KEYQUOTA_MAX_KEYS || | ||
719 | newowner->qnbytes + key->quotalen >= | ||
720 | KEYQUOTA_MAX_BYTES) | ||
721 | goto quota_overrun; | ||
722 | |||
723 | newowner->qnkeys++; | ||
724 | newowner->qnbytes += key->quotalen; | ||
725 | spin_unlock(&newowner->lock); | ||
726 | |||
727 | spin_lock(&key->user->lock); | ||
728 | key->user->qnkeys--; | ||
729 | key->user->qnbytes -= key->quotalen; | ||
730 | spin_unlock(&key->user->lock); | ||
731 | } | ||
732 | |||
733 | atomic_dec(&key->user->nkeys); | ||
734 | atomic_inc(&newowner->nkeys); | ||
735 | |||
736 | if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { | ||
737 | atomic_dec(&key->user->nikeys); | ||
738 | atomic_inc(&newowner->nikeys); | ||
739 | } | ||
740 | |||
741 | zapowner = key->user; | ||
742 | key->user = newowner; | ||
743 | key->uid = uid; | ||
712 | } | 744 | } |
713 | 745 | ||
714 | /* change the GID */ | 746 | /* change the GID */ |
@@ -717,12 +749,20 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) | |||
717 | 749 | ||
718 | ret = 0; | 750 | ret = 0; |
719 | 751 | ||
720 | no_access: | 752 | error_put: |
721 | up_write(&key->sem); | 753 | up_write(&key->sem); |
722 | key_put(key); | 754 | key_put(key); |
723 | error: | 755 | if (zapowner) |
756 | key_user_put(zapowner); | ||
757 | error: | ||
724 | return ret; | 758 | return ret; |
725 | 759 | ||
760 | quota_overrun: | ||
761 | spin_unlock(&newowner->lock); | ||
762 | zapowner = newowner; | ||
763 | ret = -EDQUOT; | ||
764 | goto error_put; | ||
765 | |||
726 | } /* end keyctl_chown_key() */ | 766 | } /* end keyctl_chown_key() */ |
727 | 767 | ||
728 | /*****************************************************************************/ | 768 | /*****************************************************************************/ |