static int sec_install_rctx_kr(struct ptlrpc_sec *sec,
struct ptlrpc_svc_ctx *svc_ctx);
-static void request_key_unlink(struct key *key);
+static void request_key_unlink(struct key *key, bool fullsearch);
/*
* the timeout is only for the case that upcall child process die abnormally.
/* must invalidate the key, or others may find it during lookup */
key_invalidate_locked(key);
- request_key_unlink(key);
+ request_key_unlink(key, false);
key_set_payload(key, 0, NULL);
ctx2gctx_keyring(ctx)->gck_key = NULL;
#endif
/*
- * unlink request key from it's ring, which is linked during request_key().
- * sadly, we have to 'guess' which keyring it's linked to.
+ * Get the appropriate destination keyring for the request.
+ *
+ * The keyring selected is returned with an extra reference upon it which the
+ * caller must release.
+ */
+/*
+ * Function inspired from the kernel's one, unfortunately not exported.
*/
-static void request_key_unlink(struct key *key)
+static int construct_get_dest_keyring(struct key **_dest_keyring)
{
- struct cred *cred = (struct cred *)current_cred(), *new_cred = NULL;
- kuid_t kuid_orig = current_cred()->user->uid;
-#ifdef HAVE_USER_UID_KEYRING
- struct key *root_uid_keyring = NULL;
-#endif
- const struct cred *old_cred = NULL;
- struct key *ring = NULL;
- uid_t uid, key_uid;
- int res;
-
- uid = from_kuid(current_user_ns(), kuid_orig);
- key_uid = from_kuid(&init_user_ns, key->uid);
- /* unlink key with user's creds if it's a user key */
- if (key_uid != uid) {
- new_cred = prepare_creds();
- if (new_cred == NULL)
- goto search;
-
- new_cred->uid = key->uid;
- new_cred->user->uid = key->uid;
- new_cred->user_ns = &init_user_ns;
-#ifdef HAVE_USER_UID_KEYRING
- root_uid_keyring = current_cred()->user->uid_keyring;
- new_cred->user->uid_keyring = NULL;
-#endif
- old_cred = override_creds(new_cred);
- cred = new_cred;
- }
+ struct key *dest_keyring = *_dest_keyring;
+ const struct cred *cred = current_cred();
- /* User keys are linked to the user keyring. So get it now. */
- if (key_uid) {
- /* Getting a key(ring) normally increases its refcount by 1.
- * But if we overrode creds above, calling get_user_keyring()
- * will add one more ref, because of the user switch.
- */
- ring = get_user_keyring(cred);
- goto do_unlink;
+ if (dest_keyring) {
+ /* the caller supplied one */
+ key_get(dest_keyring);
+ return 0;
}
-search:
switch (cred->jit_keyring) {
case KEY_REQKEY_DEFL_DEFAULT:
case KEY_REQKEY_DEFL_REQUESTOR_KEYRING:
down_read(&authkey->sem);
rka = get_request_key_auth(authkey);
if (!test_bit(KEY_FLAG_REVOKED, &authkey->flags))
- ring = key_get(rka->dest_keyring);
+ dest_keyring = key_get(rka->dest_keyring);
up_read(&authkey->sem);
- if (ring)
+ if (dest_keyring)
break;
}
#endif
fallthrough;
case KEY_REQKEY_DEFL_THREAD_KEYRING:
- ring = key_get(cred->thread_keyring);
- if (ring)
+ dest_keyring = key_get(cred->thread_keyring);
+ if (dest_keyring)
break;
fallthrough;
case KEY_REQKEY_DEFL_PROCESS_KEYRING:
- ring = key_get(cred->process_keyring);
- if (ring)
+ dest_keyring = key_get(cred->process_keyring);
+ if (dest_keyring)
break;
fallthrough;
case KEY_REQKEY_DEFL_SESSION_KEYRING:
rcu_read_lock();
- ring = key_get(rcu_dereference(cred->session_keyring));
+ dest_keyring = key_get(rcu_dereference(cred->session_keyring));
rcu_read_unlock();
- if (ring)
- break;
+ if (dest_keyring) {
+ if (!test_bit(KEY_FLAG_REVOKED, &dest_keyring->flags))
+ break;
+ key_put(dest_keyring);
+ }
fallthrough;
case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
- ring = get_user_session_keyring(cred);
+ dest_keyring = get_user_session_keyring(cred);
break;
case KEY_REQKEY_DEFL_USER_KEYRING:
- ring = get_user_keyring(cred);
+ dest_keyring = get_user_keyring(cred);
break;
case KEY_REQKEY_DEFL_GROUP_KEYRING:
default:
LBUG();
}
-do_unlink:
+ *_dest_keyring = dest_keyring;
+ return 0;
+}
+
+/*
+ * Unlink key from its keyring, which was linked during request_key().
+ */
+static void request_key_unlink(struct key *key, bool fullsearch)
+{
+ kuid_t kuid_orig = current_cred()->user->uid;
+#ifdef HAVE_USER_UID_KEYRING
+ struct key *root_uid_keyring = NULL;
+#endif
+ const struct cred *old_cred = NULL;
+ struct cred *new_cred = NULL;
+ struct key *ring = NULL;
+ uid_t uid, key_uid;
+ int res;
+
+ uid = from_kuid(current_user_ns(), kuid_orig);
+ key_uid = from_kuid(&init_user_ns, key->uid);
+ /* unlink key with user's creds if it's a user key */
+ if (key_uid != uid) {
+ new_cred = prepare_creds();
+ if (new_cred == NULL)
+ goto search;
+
+ new_cred->uid = key->uid;
+ new_cred->user->uid = key->uid;
+ new_cred->user_ns = &init_user_ns;
+#ifdef HAVE_USER_UID_KEYRING
+ root_uid_keyring = current_cred()->user->uid_keyring;
+ new_cred->user->uid_keyring = NULL;
+#endif
+ old_cred = override_creds(new_cred);
+ }
+
+ /* User keys are linked to the user keyring. So get it now. */
+ if (key_uid && !fullsearch) {
+ /* Getting a key(ring) normally increases its refcount by 1.
+ * But if we overrode creds above, calling get_user_keyring()
+ * will add one more ref, because of the user switch.
+ */
+ ring = get_user_keyring(current_cred());
+ } else {
+search:
+ if (construct_get_dest_keyring(&ring))
+ ring = NULL;
+ }
+
if (ring) {
res = key_unlink(ring, key);
CDEBUG(D_SEC,
}
CDEBUG(D_SEC, "obtained key %08x for %s\n", key->serial, desc);
- /* We want user keys to be linked to the user keyring (see call to
- * keyctl_instantiate() from prepare_and_instantiate() in userspace).
- * But internally request_key() tends to also link the key to the
- * session keyring. So do our best to avoid that by trying to unlink
- * the key from the session keyring right now. It will spare us pain
- * when we need to remove the key later on.
- */
- if (!is_root && current_cred()->session_keyring) {
- key_get(current_cred()->session_keyring);
- (void)key_unlink(current_cred()->session_keyring, key);
- key_put(current_cred()->session_keyring);
- }
/* once payload.data was pointed to a ctx, it never changes until
* we de-associate them; but parallel request_key() may return
* a key with payload.data == NULL at the same time. so we still
up_write(&key->sem);
- if (is_root && create_new)
- request_key_unlink(key);
+ /* We want user keys to be linked to the user keyring (see call to
+ * keyctl_instantiate() from prepare_and_instantiate() in userspace).
+ * But internally request_key() links the key to the session or
+ * user session keyring, depending on jit_keyring value. Avoid that by
+ * unlinking the key from this keyring. It will spare
+ * us pain when we need to remove the key later on.
+ */
+ if (!is_root || create_new)
+ request_key_unlink(key, true);
key_put(key);
out:
* Note here we suppose only to flush _my_ context, the "uid" will
* be ignored in the search.
*/
-static
-void flush_user_ctx_cache_kr(struct ptlrpc_sec *sec,
- uid_t uid,
- int grace, int force)
+static void flush_user_ctx_cache_kr(struct ptlrpc_sec *sec, uid_t uid,
+ int grace, int force)
{
- struct key *key;
- char desc[24];
+ const struct cred *old_cred = NULL;
+ struct cred *new_cred = NULL;
+ struct key *key;
+ char desc[24];
- /* nothing to do for reverse or rootonly sec */
- if (sec_is_reverse(sec) || sec_is_rootonly(sec))
- return;
+ /* nothing to do for reverse or rootonly sec */
+ if (sec_is_reverse(sec) || sec_is_rootonly(sec))
+ return;
- construct_key_desc(desc, sizeof(desc), sec, uid);
+ construct_key_desc(desc, sizeof(desc), sec, uid);
+
+ if (uid) {
+ /* If the session keyring is revoked, it must not be used by
+ * request_key(), otherwise we would get -EKEYREVOKED and
+ * the user keyring would not even be searched.
+ * So prepare new creds with no session keyring.
+ */
+ if (current_cred()->session_keyring &&
+ test_bit(KEY_FLAG_REVOKED,
+ ¤t_cred()->session_keyring->flags)) {
+ new_cred = prepare_creds();
+ if (new_cred) {
+ new_cred->session_keyring = NULL;
+ old_cred = override_creds(new_cred);
+ }
+ }
+ }
/* there should be only one valid key, but we put it in the
* loop in case of any weird cases */
for (;;) {
key = request_key(&gss_key_type, desc, NULL);
if (IS_ERR(key)) {
- CDEBUG(D_SEC, "No more key found for current user\n");
+ CDEBUG(D_SEC,
+ "No more key found for current user: rc=%ld\n",
+ PTR_ERR(key));
break;
}
up_write(&key->sem);
key_put(key);
}
+
+ if (old_cred) {
+ revert_creds(old_cred);
+ put_cred(new_cred);
+ }
}
/*