|
@@ -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() */
|
|
|
|
|
|
/*****************************************************************************/
|