aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/keys/keyctl.c56
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 */
674long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) 674long 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: 752error_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);
757error:
724 return ret; 758 return ret;
725 759
760quota_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/*****************************************************************************/