Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5801649d authored by Fredrik Tolf's avatar Fredrik Tolf Committed by Linus Torvalds
Browse files

[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: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 31204ed9
Loading
Loading
Loading
Loading
+48 −8
Original line number Diff line number Diff line
@@ -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);
	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() */

/*****************************************************************************/