@@ -79,6 +79,7 @@ struct gss_cl_ctx {
struct gss_upcall_msg;
struct gss_cred {
struct rpc_cred gc_base;
+ key_serial_t gc_serial;
enum rpc_gss_svc gc_service;
struct gss_cl_ctx __rcu *gc_ctx;
struct gss_upcall_msg *gc_upcall;
@@ -161,13 +161,19 @@ gss_put_ctx(struct gss_cl_ctx *ctx)
* and a new one is protected by the pipe->lock.
*/
static void
-gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx)
+gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx, struct key *key)
{
struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags))
return;
gss_get_ctx(ctx);
+ if (key != NULL)
+ gss_cred->gc_serial = key->serial;
+ else
+ pr_warn_ratelimited("RPC no gss-ctx key for uid %d "
+ "gss_cred and context will not be destroyed "
+ " upon kdestroy\n", cred->cr_uid);
rcu_assign_pointer(gss_cred->gc_ctx, ctx);
set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
smp_mb__before_clear_bit();
@@ -302,6 +308,8 @@ err:
struct gss_upcall_msg {
atomic_t count;
kuid_t uid;
+ struct key *key;
+ char *cc_name;
struct rpc_pipe_msg msg;
struct list_head list;
struct gss_auth *auth;
@@ -347,7 +355,11 @@ gss_release_msg(struct gss_upcall_msg *gss_msg)
BUG_ON(!list_empty(&gss_msg->list));
if (gss_msg->ctx != NULL)
gss_put_ctx(gss_msg->ctx);
+ if (gss_msg->key != NULL)
+ /* balance key_get in keyring_search */
+ key_put(gss_msg->key);
rpc_destroy_wait_queue(&gss_msg->rpc_waitqueue);
+ kfree(gss_msg->cc_name);
kfree(gss_msg);
}
@@ -417,7 +429,8 @@ gss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss
if (gss_msg->ctx == NULL)
break;
clear_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags);
- gss_cred_set_ctx(&gss_cred->gc_base, gss_msg->ctx);
+ gss_cred_set_ctx(&gss_cred->gc_base, gss_msg->ctx,
+ gss_msg->key);
break;
case -EKEYEXPIRED:
set_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags);
@@ -427,6 +440,67 @@ gss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss
rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
}
+
+#define GSS_CTX_ID_MAXLEN 20 /* "gss-ctx_" + NFS_UINT_MAXLEN + 1 */
+
+static void
+gss_find_ctx_key(uid_t uid, struct gss_upcall_msg *msg)
+{
+ key_ref_t rkey;
+ struct key *key;
+ struct user_key_payload *payload;
+ char *id;
+ const struct cred *cred = get_current_cred();
+ int ret;
+
+ if (!cred->user->session_keyring)
+ goto out;
+
+ id = kzalloc(GSS_CTX_ID_MAXLEN, GFP_NOFS);
+ if (!id)
+ goto out;
+
+ snprintf(id, GSS_CTX_ID_MAXLEN, "gss-ctx_%d", uid);
+
+ /* Search the session keyring. Note that the user key create
+ * needs to also use the same keyring. */
+ rkey = keyring_search(make_key_ref(cred->user->session_keyring, 1),
+ &key_type_gss_ctx, id);
+ kfree(id);
+
+ if (IS_ERR(rkey))
+ goto out;
+
+ key = key_ref_to_ptr(rkey);
+
+ rcu_read_lock();
+ key->perm |= KEY_USR_VIEW;
+
+ ret = key_validate(key);
+ if (ret < 0)
+ goto out_unlock;
+
+ payload = rcu_dereference(key->payload.data);
+ if (IS_ERR_OR_NULL(payload))
+ goto out_unlock;
+
+ if (key->uid != uid || payload->datalen <= 0)
+ goto out_unlock;
+
+ msg->cc_name = kzalloc(payload->datalen + 1, GFP_NOFS);
+ if (!msg->cc_name)
+ goto out_unlock;
+
+ memcpy(msg->cc_name, payload->data, payload->datalen);
+ msg->key = key;
+ dprintk("RPC krb5 ccache %s has gss-ctx key with serial %u \n",
+ msg->cc_name, msg->key->serial);
+out_unlock:
+ rcu_read_unlock();
+out:
+ return;
+}
+
static void
gss_upcall_callback(struct rpc_task *task)
{
@@ -478,6 +552,11 @@ static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
p += len;
gss_msg->msg.len += len;
}
+ if (gss_msg->cc_name != NULL) {
+ len = sprintf(p, "ccache=%s ", gss_msg->cc_name);
+ p += len;
+ gss_msg->msg.len += len;
+ }
len = sprintf(p, "\n");
gss_msg->msg.len += len;
@@ -500,6 +579,9 @@ gss_alloc_msg(struct gss_auth *gss_auth,
kfree(gss_msg);
return ERR_PTR(vers);
}
+ /* Use a gss-ctx key if available */
+ gss_find_ctx_key(uid, gss_msg);
+
gss_msg->pipe = gss_auth->gss_pipe[vers]->pipe;
INIT_LIST_HEAD(&gss_msg->list);
rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
@@ -532,6 +614,9 @@ gss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred)
int res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg);
if (res) {
gss_unhash_msg(gss_new);
+ if (gss_new->key)
+ /* balance key_get in keyring_search */
+ key_put(gss_new->key);
gss_msg = ERR_PTR(res);
}
} else
@@ -654,7 +739,7 @@ retry:
schedule();
}
if (gss_msg->ctx)
- gss_cred_set_ctx(cred, gss_msg->ctx);
+ gss_cred_set_ctx(cred, gss_msg->ctx, gss_msg->key);
else
err = gss_msg->msg.errno;
spin_unlock(&pipe->lock);