[PATCH] keys: let keyctl_chown() change a key's owner

Let keyctl_chown() change a key's owner, including attempting to transfer the
quota burden to the new user.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
Fredrik Tolf 2006-06-26 00:24:51 -07:00 committed by Linus Torvalds
parent 31204ed925
commit 5801649d8b
1 changed files with 48 additions and 8 deletions

View File

@ -673,6 +673,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
*/
long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
{
struct key_user *newowner, *zapowner = NULL;
struct key *key;
key_ref_t key_ref;
long ret;
@ -696,19 +697,50 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
if (!capable(CAP_SYS_ADMIN)) {
/* only the sysadmin can chown a key to some other UID */
if (uid != (uid_t) -1 && key->uid != uid)
goto no_access;
goto error_put;
/* only the sysadmin can set the key's GID to a group other
* than one of those that the current process subscribes to */
if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid))
goto no_access;
goto error_put;
}
/* change the UID (have to update the quotas) */
/* change the UID */
if (uid != (uid_t) -1 && uid != key->uid) {
/* don't support UID changing yet */
ret = -EOPNOTSUPP;
goto no_access;
ret = -ENOMEM;
newowner = key_user_lookup(uid);
if (!newowner)
goto error_put;
/* transfer the quota burden to the new user */
if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
spin_lock(&newowner->lock);
if (newowner->qnkeys + 1 >= KEYQUOTA_MAX_KEYS ||
newowner->qnbytes + key->quotalen >=
KEYQUOTA_MAX_BYTES)
goto quota_overrun;
newowner->qnkeys++;
newowner->qnbytes += key->quotalen;
spin_unlock(&newowner->lock);
spin_lock(&key->user->lock);
key->user->qnkeys--;
key->user->qnbytes -= key->quotalen;
spin_unlock(&key->user->lock);
}
atomic_dec(&key->user->nkeys);
atomic_inc(&newowner->nkeys);
if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
atomic_dec(&key->user->nikeys);
atomic_inc(&newowner->nikeys);
}
zapowner = key->user;
key->user = newowner;
key->uid = uid;
}
/* change the GID */
@ -717,12 +749,20 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
ret = 0;
no_access:
error_put:
up_write(&key->sem);
key_put(key);
error:
if (zapowner)
key_user_put(zapowner);
error:
return ret;
quota_overrun:
spin_unlock(&newowner->lock);
zapowner = newowner;
ret = -EDQUOT;
goto error_put;
} /* end keyctl_chown_key() */
/*****************************************************************************/