Merge branch 'modules-next' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty...
[firefly-linux-kernel-4.4.55.git] / security / keys / keyctl.c
index 3364fbf46807bb384e61a761f2f6f2fab9ebb888..5d34b4e827d6349a46f6b892573e129e500e49db 100644 (file)
@@ -46,6 +46,9 @@ static int key_get_type_from_user(char *type,
  * Extract the description of a new key from userspace and either add it as a
  * new key to the specified keyring or update a matching key in that keyring.
  *
+ * If the description is NULL or an empty string, the key type is asked to
+ * generate one from the payload.
+ *
  * The keyring must be writable so that we can attach the key to it.
  *
  * If successful, the new key's serial number is returned, otherwise an error
@@ -72,10 +75,17 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
        if (ret < 0)
                goto error;
 
-       description = strndup_user(_description, PAGE_SIZE);
-       if (IS_ERR(description)) {
-               ret = PTR_ERR(description);
-               goto error;
+       description = NULL;
+       if (_description) {
+               description = strndup_user(_description, PAGE_SIZE);
+               if (IS_ERR(description)) {
+                       ret = PTR_ERR(description);
+                       goto error;
+               }
+               if (!*description) {
+                       kfree(description);
+                       description = NULL;
+               }
        }
 
        /* pull the payload in if one was supplied */
@@ -569,8 +579,8 @@ okay:
        ret = snprintf(tmpbuf, PAGE_SIZE - 1,
                       "%s;%d;%d;%08x;%s",
                       key->type->name,
-                      key->uid,
-                      key->gid,
+                      from_kuid_munged(current_user_ns(), key->uid),
+                      from_kgid_munged(current_user_ns(), key->gid),
                       key->perm,
                       key->description ?: "");
 
@@ -766,15 +776,25 @@ error:
  *
  * If successful, 0 will be returned.
  */
-long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
+long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)
 {
        struct key_user *newowner, *zapowner = NULL;
        struct key *key;
        key_ref_t key_ref;
        long ret;
+       kuid_t uid;
+       kgid_t gid;
+
+       uid = make_kuid(current_user_ns(), user);
+       gid = make_kgid(current_user_ns(), group);
+       ret = -EINVAL;
+       if ((user != (uid_t) -1) && !uid_valid(uid))
+               goto error;
+       if ((group != (gid_t) -1) && !gid_valid(gid))
+               goto error;
 
        ret = 0;
-       if (uid == (uid_t) -1 && gid == (gid_t) -1)
+       if (user == (uid_t) -1 && group == (gid_t) -1)
                goto error;
 
        key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
@@ -792,27 +812,27 @@ 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)
+               if (user != (uid_t) -1 && !uid_eq(key->uid, uid))
                        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))
+               if (group != (gid_t) -1 && !gid_eq(gid, key->gid) && !in_group_p(gid))
                        goto error_put;
        }
 
        /* change the UID */
-       if (uid != (uid_t) -1 && uid != key->uid) {
+       if (user != (uid_t) -1 && !uid_eq(uid, key->uid)) {
                ret = -ENOMEM;
-               newowner = key_user_lookup(uid, current_user_ns());
+               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)) {
-                       unsigned maxkeys = (uid == 0) ?
+                       unsigned maxkeys = uid_eq(uid, GLOBAL_ROOT_UID) ?
                                key_quota_root_maxkeys : key_quota_maxkeys;
-                       unsigned maxbytes = (uid == 0) ?
+                       unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ?
                                key_quota_root_maxbytes : key_quota_maxbytes;
 
                        spin_lock(&newowner->lock);
@@ -846,7 +866,7 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
        }
 
        /* change the GID */
-       if (gid != (gid_t) -1)
+       if (group != (gid_t) -1)
                key->gid = gid;
 
        ret = 0;
@@ -897,7 +917,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
        down_write(&key->sem);
 
        /* if we're not the sysadmin, we can only change a key that we own */
-       if (capable(CAP_SYS_ADMIN) || key->uid == current_fsuid()) {
+       if (capable(CAP_SYS_ADMIN) || uid_eq(key->uid, current_fsuid())) {
                key->perm = perm;
                ret = 0;
        }
@@ -1486,7 +1506,6 @@ long keyctl_session_to_parent(void)
        oldwork = NULL;
        parent = me->real_parent;
 
-       task_lock(parent);
        /* the parent mustn't be init and mustn't be a kernel thread */
        if (parent->pid <= 1 || !parent->mm)
                goto unlock;
@@ -1507,18 +1526,18 @@ long keyctl_session_to_parent(void)
 
        /* the parent must have the same effective ownership and mustn't be
         * SUID/SGID */
-       if (pcred->uid  != mycred->euid ||
-           pcred->euid != mycred->euid ||
-           pcred->suid != mycred->euid ||
-           pcred->gid  != mycred->egid ||
-           pcred->egid != mycred->egid ||
-           pcred->sgid != mycred->egid)
+       if (!uid_eq(pcred->uid,  mycred->euid) ||
+           !uid_eq(pcred->euid, mycred->euid) ||
+           !uid_eq(pcred->suid, mycred->euid) ||
+           !gid_eq(pcred->gid,  mycred->egid) ||
+           !gid_eq(pcred->egid, mycred->egid) ||
+           !gid_eq(pcred->sgid, mycred->egid))
                goto unlock;
 
        /* the keyrings must have the same UID */
        if ((pcred->tgcred->session_keyring &&
-            pcred->tgcred->session_keyring->uid != mycred->euid) ||
-           mycred->tgcred->session_keyring->uid != mycred->euid)
+            !uid_eq(pcred->tgcred->session_keyring->uid, mycred->euid)) ||
+           !uid_eq(mycred->tgcred->session_keyring->uid, mycred->euid))
                goto unlock;
 
        /* cancel an already pending keyring replacement */
@@ -1530,7 +1549,6 @@ long keyctl_session_to_parent(void)
        if (!ret)
                newwork = NULL;
 unlock:
-       task_unlock(parent);
        write_unlock_irq(&tasklist_lock);
        rcu_read_unlock();
        if (oldwork)