]> git.ipfire.org Git - thirdparty/linux.git/blobdiff - security/keys/keyctl.c
keys: Add a keyctl to move a key between keyrings
[thirdparty/linux.git] / security / keys / keyctl.c
index 0f947bcbad46cf6f19434bda784dd98aecb6b1a0..bbfe7d92d41c413413e63d07889a2f8be022e98c 100644 (file)
@@ -572,6 +572,52 @@ error:
        return ret;
 }
 
+/*
+ * Move a link to a key from one keyring to another, displacing any matching
+ * key from the destination keyring.
+ *
+ * The key must grant the caller Link permission and both keyrings must grant
+ * the caller Write permission.  There must also be a link in the from keyring
+ * to the key.  If both keyrings are the same, nothing is done.
+ *
+ * If successful, 0 will be returned.
+ */
+long keyctl_keyring_move(key_serial_t id, key_serial_t from_ringid,
+                        key_serial_t to_ringid, unsigned int flags)
+{
+       key_ref_t key_ref, from_ref, to_ref;
+       long ret;
+
+       if (flags & ~KEYCTL_MOVE_EXCL)
+               return -EINVAL;
+
+       key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_LINK);
+       if (IS_ERR(key_ref))
+               return PTR_ERR(key_ref);
+
+       from_ref = lookup_user_key(from_ringid, 0, KEY_NEED_WRITE);
+       if (IS_ERR(from_ref)) {
+               ret = PTR_ERR(from_ref);
+               goto error2;
+       }
+
+       to_ref = lookup_user_key(to_ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);
+       if (IS_ERR(to_ref)) {
+               ret = PTR_ERR(to_ref);
+               goto error3;
+       }
+
+       ret = key_move(key_ref_to_ptr(key_ref), key_ref_to_ptr(from_ref),
+                      key_ref_to_ptr(to_ref), flags);
+
+       key_ref_put(to_ref);
+error3:
+       key_ref_put(from_ref);
+error2:
+       key_ref_put(key_ref);
+       return ret;
+}
+
 /*
  * Return a description of a key to userspace.
  *
@@ -1772,6 +1818,12 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
                        (const void __user *)arg4,
                        (const void __user *)arg5);
 
+       case KEYCTL_MOVE:
+               return keyctl_keyring_move((key_serial_t)arg2,
+                                          (key_serial_t)arg3,
+                                          (key_serial_t)arg4,
+                                          (unsigned int)arg5);
+
        default:
                return -EOPNOTSUPP;
        }