revert "crc32: use __BYTE_ORDER macro for endian detection"
[safe/jmp/linux-2.6] / security / keys / keyctl.c
index 8833b44..8f4dce1 100644 (file)
@@ -54,11 +54,11 @@ static int key_get_type_from_user(char *type,
  * - returns the new key's serial number
  * - implements add_key()
  */
-asmlinkage long sys_add_key(const char __user *_type,
-                           const char __user *_description,
-                           const void __user *_payload,
-                           size_t plen,
-                           key_serial_t ringid)
+SYSCALL_DEFINE5(add_key, const char __user *, _type,
+               const char __user *, _description,
+               const void __user *, _payload,
+               size_t, plen,
+               key_serial_t, ringid)
 {
        key_ref_t keyring_ref, key_ref;
        char type[32], *description;
@@ -103,7 +103,7 @@ asmlinkage long sys_add_key(const char __user *_type,
        }
 
        /* find the target keyring (which must be writable) */
-       keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+       keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);
        if (IS_ERR(keyring_ref)) {
                ret = PTR_ERR(keyring_ref);
                goto error3;
@@ -146,10 +146,10 @@ asmlinkage long sys_add_key(const char __user *_type,
  *   - if the _callout_info string is empty, it will be rendered as "-"
  * - implements request_key()
  */
-asmlinkage long sys_request_key(const char __user *_type,
-                               const char __user *_description,
-                               const char __user *_callout_info,
-                               key_serial_t destringid)
+SYSCALL_DEFINE4(request_key, const char __user *, _type,
+               const char __user *, _description,
+               const char __user *, _callout_info,
+               key_serial_t, destringid)
 {
        struct key_type *ktype;
        struct key *key;
@@ -185,7 +185,8 @@ asmlinkage long sys_request_key(const char __user *_type,
        /* get the destination keyring if specified */
        dest_ref = NULL;
        if (destringid) {
-               dest_ref = lookup_user_key(destringid, 1, 0, KEY_WRITE);
+               dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE,
+                                          KEY_WRITE);
                if (IS_ERR(dest_ref)) {
                        ret = PTR_ERR(dest_ref);
                        goto error3;
@@ -211,15 +212,15 @@ asmlinkage long sys_request_key(const char __user *_type,
        ret = key->serial;
 
        key_put(key);
- error5:
+error5:
        key_type_put(ktype);
- error4:
+error4:
        key_ref_put(dest_ref);
- error3:
+error3:
        kfree(callout_info);
- error2:
+error2:
        kfree(description);
- error:
+error:
        return ret;
 
 } /* end sys_request_key() */
@@ -233,9 +234,11 @@ asmlinkage long sys_request_key(const char __user *_type,
 long keyctl_get_keyring_ID(key_serial_t id, int create)
 {
        key_ref_t key_ref;
+       unsigned long lflags;
        long ret;
 
-       key_ref = lookup_user_key(id, create, 0, KEY_SEARCH);
+       lflags = create ? KEY_LOOKUP_CREATE : 0;
+       key_ref = lookup_user_key(id, lflags, KEY_SEARCH);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error;
@@ -243,7 +246,7 @@ long keyctl_get_keyring_ID(key_serial_t id, int create)
 
        ret = key_ref_to_ptr(key_ref)->serial;
        key_ref_put(key_ref);
- error:
+error:
        return ret;
 
 } /* end keyctl_get_keyring_ID() */
@@ -270,8 +273,9 @@ long keyctl_join_session_keyring(const char __user *_name)
 
        /* join the session */
        ret = join_session_keyring(name);
+       kfree(name);
 
- error:
+error:
        return ret;
 
 } /* end keyctl_join_session_keyring() */
@@ -308,7 +312,7 @@ long keyctl_update_key(key_serial_t id,
        }
 
        /* find the target key (which must be writable) */
-       key_ref = lookup_user_key(id, 0, 0, KEY_WRITE);
+       key_ref = lookup_user_key(id, 0, KEY_WRITE);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error2;
@@ -318,9 +322,9 @@ long keyctl_update_key(key_serial_t id,
        ret = key_update(key_ref, payload, plen);
 
        key_ref_put(key_ref);
- error2:
+error2:
        kfree(payload);
- error:
+error:
        return ret;
 
 } /* end keyctl_update_key() */
@@ -336,17 +340,23 @@ long keyctl_revoke_key(key_serial_t id)
        key_ref_t key_ref;
        long ret;
 
-       key_ref = lookup_user_key(id, 0, 0, KEY_WRITE);
+       key_ref = lookup_user_key(id, 0, KEY_WRITE);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
-               goto error;
+               if (ret != -EACCES)
+                       goto error;
+               key_ref = lookup_user_key(id, 0, KEY_SETATTR);
+               if (IS_ERR(key_ref)) {
+                       ret = PTR_ERR(key_ref);
+                       goto error;
+               }
        }
 
        key_revoke(key_ref_to_ptr(key_ref));
        ret = 0;
 
        key_ref_put(key_ref);
- error:
+error:
        return ret;
 
 } /* end keyctl_revoke_key() */
@@ -362,7 +372,7 @@ long keyctl_keyring_clear(key_serial_t ringid)
        key_ref_t keyring_ref;
        long ret;
 
-       keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+       keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);
        if (IS_ERR(keyring_ref)) {
                ret = PTR_ERR(keyring_ref);
                goto error;
@@ -371,7 +381,7 @@ long keyctl_keyring_clear(key_serial_t ringid)
        ret = keyring_clear(key_ref_to_ptr(keyring_ref));
 
        key_ref_put(keyring_ref);
- error:
+error:
        return ret;
 
 } /* end keyctl_keyring_clear() */
@@ -388,13 +398,13 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
        key_ref_t keyring_ref, key_ref;
        long ret;
 
-       keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+       keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);
        if (IS_ERR(keyring_ref)) {
                ret = PTR_ERR(keyring_ref);
                goto error;
        }
 
-       key_ref = lookup_user_key(id, 1, 0, KEY_LINK);
+       key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_LINK);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error2;
@@ -403,9 +413,9 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
        ret = key_link(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref));
 
        key_ref_put(key_ref);
- error2:
+error2:
        key_ref_put(keyring_ref);
- error:
+error:
        return ret;
 
 } /* end keyctl_keyring_link() */
@@ -422,13 +432,13 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
        key_ref_t keyring_ref, key_ref;
        long ret;
 
-       keyring_ref = lookup_user_key(ringid, 0, 0, KEY_WRITE);
+       keyring_ref = lookup_user_key(ringid, 0, KEY_WRITE);
        if (IS_ERR(keyring_ref)) {
                ret = PTR_ERR(keyring_ref);
                goto error;
        }
 
-       key_ref = lookup_user_key(id, 0, 0, 0);
+       key_ref = lookup_user_key(id, KEY_LOOKUP_FOR_UNLINK, 0);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error2;
@@ -437,9 +447,9 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
        ret = key_unlink(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref));
 
        key_ref_put(key_ref);
- error2:
+error2:
        key_ref_put(keyring_ref);
- error:
+error:
        return ret;
 
 } /* end keyctl_keyring_unlink() */
@@ -464,7 +474,7 @@ long keyctl_describe_key(key_serial_t keyid,
        char *tmpbuf;
        long ret;
 
-       key_ref = lookup_user_key(keyid, 0, 1, KEY_VIEW);
+       key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_VIEW);
        if (IS_ERR(key_ref)) {
                /* viewing a key under construction is permitted if we have the
                 * authorisation token handy */
@@ -473,7 +483,8 @@ long keyctl_describe_key(key_serial_t keyid,
                        if (!IS_ERR(instkey)) {
                                key_put(instkey);
                                key_ref = lookup_user_key(keyid,
-                                                         0, 1, 0);
+                                                         KEY_LOOKUP_PARTIAL,
+                                                         0);
                                if (!IS_ERR(key_ref))
                                        goto okay;
                        }
@@ -518,9 +529,9 @@ okay:
        }
 
        kfree(tmpbuf);
- error2:
+error2:
        key_ref_put(key_ref);
- error:
+error:
        return ret;
 
 } /* end keyctl_describe_key() */
@@ -557,7 +568,7 @@ long keyctl_keyring_search(key_serial_t ringid,
        }
 
        /* get the keyring at which to begin the search */
-       keyring_ref = lookup_user_key(ringid, 0, 0, KEY_SEARCH);
+       keyring_ref = lookup_user_key(ringid, 0, KEY_SEARCH);
        if (IS_ERR(keyring_ref)) {
                ret = PTR_ERR(keyring_ref);
                goto error2;
@@ -566,7 +577,8 @@ long keyctl_keyring_search(key_serial_t ringid,
        /* get the destination keyring if specified */
        dest_ref = NULL;
        if (destringid) {
-               dest_ref = lookup_user_key(destringid, 1, 0, KEY_WRITE);
+               dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE,
+                                          KEY_WRITE);
                if (IS_ERR(dest_ref)) {
                        ret = PTR_ERR(dest_ref);
                        goto error3;
@@ -604,17 +616,17 @@ long keyctl_keyring_search(key_serial_t ringid,
 
        ret = key_ref_to_ptr(key_ref)->serial;
 
- error6:
+error6:
        key_ref_put(key_ref);
- error5:
+error5:
        key_type_put(ktype);
- error4:
+error4:
        key_ref_put(dest_ref);
- error3:
+error3:
        key_ref_put(keyring_ref);
- error2:
+error2:
        kfree(description);
- error:
+error:
        return ret;
 
 } /* end keyctl_keyring_search() */
@@ -636,7 +648,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
        long ret;
 
        /* find the key first */
-       key_ref = lookup_user_key(keyid, 0, 0, 0);
+       key_ref = lookup_user_key(keyid, 0, 0);
        if (IS_ERR(key_ref)) {
                ret = -ENOKEY;
                goto error;
@@ -661,7 +673,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
        }
 
        /* the key is probably readable - now try to read it */
- can_read_key:
+can_read_key:
        ret = key_validate(key);
        if (ret == 0) {
                ret = -EOPNOTSUPP;
@@ -674,9 +686,9 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
                }
        }
 
- error2:
+error2:
        key_put(key);
- error:
+error:
        return ret;
 
 } /* end keyctl_read_key() */
@@ -699,7 +711,8 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
        if (uid == (uid_t) -1 && gid == (gid_t) -1)
                goto error;
 
-       key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
+       key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
+                                 KEY_SETATTR);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error;
@@ -725,7 +738,7 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
        /* change the UID */
        if (uid != (uid_t) -1 && uid != key->uid) {
                ret = -ENOMEM;
-               newowner = key_user_lookup(uid);
+               newowner = key_user_lookup(uid, current_user_ns());
                if (!newowner)
                        goto error_put;
 
@@ -804,7 +817,8 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
        if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL))
                goto error;
 
-       key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
+       key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
+                                 KEY_SETATTR);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error;
@@ -838,15 +852,15 @@ static long get_instantiation_keyring(key_serial_t ringid,
 {
        key_ref_t dkref;
 
+       *_dest_keyring = NULL;
+
        /* just return a NULL pointer if we weren't asked to make a link */
-       if (ringid == 0) {
-               *_dest_keyring = NULL;
+       if (ringid == 0)
                return 0;
-       }
 
        /* if a specific keyring is nominated by ID, then use that */
        if (ringid > 0) {
-               dkref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+               dkref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);
                if (IS_ERR(dkref))
                        return PTR_ERR(dkref);
                *_dest_keyring = key_ref_to_ptr(dkref);
@@ -859,13 +873,30 @@ static long get_instantiation_keyring(key_serial_t ringid,
        /* otherwise specify the destination keyring recorded in the
         * authorisation key (any KEY_SPEC_*_KEYRING) */
        if (ringid >= KEY_SPEC_REQUESTOR_KEYRING) {
-               *_dest_keyring = rka->dest_keyring;
+               *_dest_keyring = key_get(rka->dest_keyring);
                return 0;
        }
 
        return -ENOKEY;
 }
 
+/*
+ * change the request_key authorisation key on the current process
+ */
+static int keyctl_change_reqkey_auth(struct key *key)
+{
+       struct cred *new;
+
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+
+       key_put(new->request_key_auth);
+       new->request_key_auth = key_get(key);
+
+       return commit_creds(new);
+}
+
 /*****************************************************************************/
 /*
  * instantiate the key with the specified payload, and, if one is given, link
@@ -876,12 +907,15 @@ long keyctl_instantiate_key(key_serial_t id,
                            size_t plen,
                            key_serial_t ringid)
 {
+       const struct cred *cred = current_cred();
        struct request_key_auth *rka;
        struct key *instkey, *dest_keyring;
        void *payload;
        long ret;
        bool vm = false;
 
+       kenter("%d,,%zu,%d", id, plen, ringid);
+
        ret = -EINVAL;
        if (plen > 1024 * 1024 - 1)
                goto error;
@@ -889,7 +923,7 @@ long keyctl_instantiate_key(key_serial_t id,
        /* the appropriate instantiation authorisation key must have been
         * assumed before calling this */
        ret = -EPERM;
-       instkey = current->cred->request_key_auth;
+       instkey = cred->request_key_auth;
        if (!instkey)
                goto error;
 
@@ -931,10 +965,8 @@ long keyctl_instantiate_key(key_serial_t id,
 
        /* discard the assumed authority if it's just been disabled by
         * instantiation of the key */
-       if (ret == 0) {
-               key_put(current->cred->request_key_auth);
-               current->cred->request_key_auth = NULL;
-       }
+       if (ret == 0)
+               keyctl_change_reqkey_auth(NULL);
 
 error2:
        if (!vm)
@@ -953,14 +985,17 @@ error:
  */
 long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
 {
+       const struct cred *cred = current_cred();
        struct request_key_auth *rka;
        struct key *instkey, *dest_keyring;
        long ret;
 
+       kenter("%d,%u,%d", id, timeout, ringid);
+
        /* the appropriate instantiation authorisation key must have been
         * assumed before calling this */
        ret = -EPERM;
-       instkey = current->cred->request_key_auth;
+       instkey = cred->request_key_auth;
        if (!instkey)
                goto error;
 
@@ -982,10 +1017,8 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
 
        /* discard the assumed authority if it's just been disabled by
         * instantiation of the key */
-       if (ret == 0) {
-               key_put(current->cred->request_key_auth);
-               current->cred->request_key_auth = NULL;
-       }
+       if (ret == 0)
+               keyctl_change_reqkey_auth(NULL);
 
 error:
        return ret;
@@ -999,36 +1032,56 @@ error:
  */
 long keyctl_set_reqkey_keyring(int reqkey_defl)
 {
-       struct cred *cred = current->cred;
-       int ret;
+       struct cred *new;
+       int ret, old_setting;
+
+       old_setting = current_cred_xxx(jit_keyring);
+
+       if (reqkey_defl == KEY_REQKEY_DEFL_NO_CHANGE)
+               return old_setting;
+
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
 
        switch (reqkey_defl) {
        case KEY_REQKEY_DEFL_THREAD_KEYRING:
-               ret = install_thread_keyring();
+               ret = install_thread_keyring_to_cred(new);
                if (ret < 0)
-                       return ret;
+                       goto error;
                goto set;
 
        case KEY_REQKEY_DEFL_PROCESS_KEYRING:
-               ret = install_process_keyring();
-               if (ret < 0)
-                       return ret;
+               ret = install_process_keyring_to_cred(new);
+               if (ret < 0) {
+                       if (ret != -EEXIST)
+                               goto error;
+                       ret = 0;
+               }
+               goto set;
 
        case KEY_REQKEY_DEFL_DEFAULT:
        case KEY_REQKEY_DEFL_SESSION_KEYRING:
        case KEY_REQKEY_DEFL_USER_KEYRING:
        case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
-       set:
-               cred->jit_keyring = reqkey_defl;
+       case KEY_REQKEY_DEFL_REQUESTOR_KEYRING:
+               goto set;
 
        case KEY_REQKEY_DEFL_NO_CHANGE:
-               return cred->jit_keyring;
-
        case KEY_REQKEY_DEFL_GROUP_KEYRING:
        default:
-               return -EINVAL;
+               ret = -EINVAL;
+               goto error;
        }
 
+set:
+       new->jit_keyring = reqkey_defl;
+       commit_creds(new);
+       return old_setting;
+error:
+       abort_creds(new);
+       return -EINVAL;
+
 } /* end keyctl_set_reqkey_keyring() */
 
 /*****************************************************************************/
@@ -1043,7 +1096,8 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
        time_t expiry;
        long ret;
 
-       key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
+       key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
+                                 KEY_SETATTR);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error;
@@ -1061,6 +1115,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
        }
 
        key->expiry = expiry;
+       key_schedule_gc(key->expiry + key_gc_delay);
 
        up_write(&key->sem);
        key_put(key);
@@ -1087,9 +1142,7 @@ long keyctl_assume_authority(key_serial_t id)
 
        /* we divest ourselves of authority if given an ID of 0 */
        if (id == 0) {
-               key_put(current->cred->request_key_auth);
-               current->cred->request_key_auth = NULL;
-               ret = 0;
+               ret = keyctl_change_reqkey_auth(NULL);
                goto error;
        }
 
@@ -1104,10 +1157,12 @@ long keyctl_assume_authority(key_serial_t id)
                goto error;
        }
 
-       key_put(current->cred->request_key_auth);
-       current->cred->request_key_auth = authkey;
-       ret = authkey->serial;
+       ret = keyctl_change_reqkey_auth(authkey);
+       if (ret < 0)
+               goto error;
+       key_put(authkey);
 
+       ret = authkey->serial;
 error:
        return ret;
 
@@ -1130,7 +1185,7 @@ long keyctl_get_security(key_serial_t keyid,
        char *context;
        long ret;
 
-       key_ref = lookup_user_key(keyid, 0, 1, KEY_VIEW);
+       key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_VIEW);
        if (IS_ERR(key_ref)) {
                if (PTR_ERR(key_ref) != -EACCES)
                        return PTR_ERR(key_ref);
@@ -1139,10 +1194,10 @@ long keyctl_get_security(key_serial_t keyid,
                 * have the authorisation token handy */
                instkey = key_get_instantiation_authkey(keyid);
                if (IS_ERR(instkey))
-                       return PTR_ERR(key_ref);
+                       return PTR_ERR(instkey);
                key_put(instkey);
 
-               key_ref = lookup_user_key(keyid, 0, 1, 0);
+               key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, 0);
                if (IS_ERR(key_ref))
                        return PTR_ERR(key_ref);
        }
@@ -1173,12 +1228,115 @@ long keyctl_get_security(key_serial_t keyid,
        return ret;
 }
 
+/*
+ * attempt to install the calling process's session keyring on the process's
+ * parent process
+ * - the keyring must exist and must grant us LINK permission
+ * - implements keyctl(KEYCTL_SESSION_TO_PARENT)
+ */
+long keyctl_session_to_parent(void)
+{
+#ifdef TIF_NOTIFY_RESUME
+       struct task_struct *me, *parent;
+       const struct cred *mycred, *pcred;
+       struct cred *cred, *oldcred;
+       key_ref_t keyring_r;
+       int ret;
+
+       keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_LINK);
+       if (IS_ERR(keyring_r))
+               return PTR_ERR(keyring_r);
+
+       /* our parent is going to need a new cred struct, a new tgcred struct
+        * and new security data, so we allocate them here to prevent ENOMEM in
+        * our parent */
+       ret = -ENOMEM;
+       cred = cred_alloc_blank();
+       if (!cred)
+               goto error_keyring;
+
+       cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r);
+       keyring_r = NULL;
+
+       me = current;
+       write_lock_irq(&tasklist_lock);
+
+       parent = me->real_parent;
+       ret = -EPERM;
+
+       /* the parent mustn't be init and mustn't be a kernel thread */
+       if (parent->pid <= 1 || !parent->mm)
+               goto not_permitted;
+
+       /* the parent must be single threaded */
+       if (atomic_read(&parent->signal->count) != 1)
+               goto not_permitted;
+
+       /* the parent and the child must have different session keyrings or
+        * there's no point */
+       mycred = current_cred();
+       pcred = __task_cred(parent);
+       if (mycred == pcred ||
+           mycred->tgcred->session_keyring == pcred->tgcred->session_keyring)
+               goto already_same;
+
+       /* 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)
+               goto not_permitted;
+
+       /* the keyrings must have the same UID */
+       if (pcred->tgcred->session_keyring->uid != mycred->euid ||
+           mycred->tgcred->session_keyring->uid != mycred->euid)
+               goto not_permitted;
+
+       /* if there's an already pending keyring replacement, then we replace
+        * that */
+       oldcred = parent->replacement_session_keyring;
+
+       /* the replacement session keyring is applied just prior to userspace
+        * restarting */
+       parent->replacement_session_keyring = cred;
+       cred = NULL;
+       set_ti_thread_flag(task_thread_info(parent), TIF_NOTIFY_RESUME);
+
+       write_unlock_irq(&tasklist_lock);
+       if (oldcred)
+               put_cred(oldcred);
+       return 0;
+
+already_same:
+       ret = 0;
+not_permitted:
+       write_unlock_irq(&tasklist_lock);
+       put_cred(cred);
+       return ret;
+
+error_keyring:
+       key_ref_put(keyring_r);
+       return ret;
+
+#else /* !TIF_NOTIFY_RESUME */
+       /*
+        * To be removed when TIF_NOTIFY_RESUME has been implemented on
+        * m68k/xtensa
+        */
+#warning TIF_NOTIFY_RESUME not implemented
+       return -EOPNOTSUPP;
+#endif /* !TIF_NOTIFY_RESUME */
+}
+
 /*****************************************************************************/
 /*
  * the key control system call
  */
-asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3,
-                          unsigned long arg4, unsigned long arg5)
+SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
+               unsigned long, arg4, unsigned long, arg5)
 {
        switch (option) {
        case KEYCTL_GET_KEYRING_ID:
@@ -1255,9 +1413,12 @@ asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3,
 
        case KEYCTL_GET_SECURITY:
                return keyctl_get_security((key_serial_t) arg2,
-                                          (char *) arg3,
+                                          (char __user *) arg3,
                                           (size_t) arg4);
 
+       case KEYCTL_SESSION_TO_PARENT:
+               return keyctl_session_to_parent();
+
        default:
                return -EOPNOTSUPP;
        }