[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

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) long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
{ {
struct key_user *newowner, *zapowner = NULL;
struct key *key; struct key *key;
key_ref_t key_ref; key_ref_t key_ref;
long ret; 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)) { if (!capable(CAP_SYS_ADMIN)) {
/* only the sysadmin can chown a key to some other UID */ /* only the sysadmin can chown a key to some other UID */
if (uid != (uid_t) -1 && key->uid != 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 /* only the sysadmin can set the key's GID to a group other
* than one of those that the current process subscribes to */ * than one of those that the current process subscribes to */
if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid)) 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) { if (uid != (uid_t) -1 && uid != key->uid) {
/* don't support UID changing yet */ ret = -ENOMEM;
ret = -EOPNOTSUPP; newowner = key_user_lookup(uid);
goto no_access; 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 */ /* change the GID */
@ -717,12 +749,20 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
ret = 0; ret = 0;
no_access: error_put:
up_write(&key->sem); up_write(&key->sem);
key_put(key); key_put(key);
error: if (zapowner)
key_user_put(zapowner);
error:
return ret; return ret;
quota_overrun:
spin_unlock(&newowner->lock);
zapowner = newowner;
ret = -EDQUOT;
goto error_put;
} /* end keyctl_chown_key() */ } /* end keyctl_chown_key() */
/*****************************************************************************/ /*****************************************************************************/