Commit 12072652 authored by Chuck Lever's avatar Chuck Lever Committed by Trond Myklebust
Browse files

SUNRPC: Add RPC client support for the RPC_AUTH_TLS auth flavor



The new authentication flavor is used only to discover peer support
for RPC-over-TLS.

Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
parent 97d1c83c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -120,6 +120,7 @@ struct rpc_authops {
						struct rpcsec_gss_info *);
	int			(*key_timeout)(struct rpc_auth *,
						struct rpc_cred *);
	int			(*ping)(struct rpc_clnt *clnt);
};

struct rpc_credops {
@@ -144,6 +145,7 @@ struct rpc_credops {

extern const struct rpc_authops	authunix_ops;
extern const struct rpc_authops	authnull_ops;
extern const struct rpc_authops	authtls_ops;

int __init		rpc_init_authunix(void);
int __init		rpcauth_init_module(void);
+1 −1
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ obj-$(CONFIG_SUNRPC_GSS) += auth_gss/
obj-$(CONFIG_SUNRPC_XPRT_RDMA) += xprtrdma/

sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \
	    auth.o auth_null.o auth_unix.o \
	    auth.o auth_null.o auth_tls.o auth_unix.o \
	    svc.o svcsock.o svcauth.o svcauth_unix.o \
	    addr.o rpcb_clnt.o timer.o xdr.o \
	    sunrpc_syms.o cache.o rpc_pipe.o sysfs.o \
+1 −1
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ static unsigned int auth_hashbits = RPC_CREDCACHE_DEFAULT_HASHBITS;
static const struct rpc_authops __rcu *auth_flavors[RPC_AUTH_MAXFLAVOR] = {
	[RPC_AUTH_NULL] = (const struct rpc_authops __force __rcu *)&authnull_ops,
	[RPC_AUTH_UNIX] = (const struct rpc_authops __force __rcu *)&authunix_ops,
	NULL,			/* others can be loadable modules */
	[RPC_AUTH_TLS]  = (const struct rpc_authops __force __rcu *)&authtls_ops,
};

static LIST_HEAD(cred_unused);

net/sunrpc/auth_tls.c

0 → 100644
+175 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2021, 2022 Oracle.  All rights reserved.
 *
 * The AUTH_TLS credential is used only to probe a remote peer
 * for RPC-over-TLS support.
 */

#include <linux/types.h>
#include <linux/module.h>
#include <linux/sunrpc/clnt.h>

static const char *starttls_token = "STARTTLS";
static const size_t starttls_len = 8;

static struct rpc_auth tls_auth;
static struct rpc_cred tls_cred;

static void tls_encode_probe(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
			     const void *obj)
{
}

static int tls_decode_probe(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
			    void *obj)
{
	return 0;
}

static const struct rpc_procinfo rpcproc_tls_probe = {
	.p_encode	= tls_encode_probe,
	.p_decode	= tls_decode_probe,
};

static void rpc_tls_probe_call_prepare(struct rpc_task *task, void *data)
{
	task->tk_flags &= ~RPC_TASK_NO_RETRANS_TIMEOUT;
	rpc_call_start(task);
}

static void rpc_tls_probe_call_done(struct rpc_task *task, void *data)
{
}

static const struct rpc_call_ops rpc_tls_probe_ops = {
	.rpc_call_prepare	= rpc_tls_probe_call_prepare,
	.rpc_call_done		= rpc_tls_probe_call_done,
};

static int tls_probe(struct rpc_clnt *clnt)
{
	struct rpc_message msg = {
		.rpc_proc	= &rpcproc_tls_probe,
	};
	struct rpc_task_setup task_setup_data = {
		.rpc_client	= clnt,
		.rpc_message	= &msg,
		.rpc_op_cred	= &tls_cred,
		.callback_ops	= &rpc_tls_probe_ops,
		.flags		= RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
	};
	struct rpc_task	*task;
	int status;

	task = rpc_run_task(&task_setup_data);
	if (IS_ERR(task))
		return PTR_ERR(task);
	status = task->tk_status;
	rpc_put_task(task);
	return status;
}

static struct rpc_auth *tls_create(const struct rpc_auth_create_args *args,
				   struct rpc_clnt *clnt)
{
	refcount_inc(&tls_auth.au_count);
	return &tls_auth;
}

static void tls_destroy(struct rpc_auth *auth)
{
}

static struct rpc_cred *tls_lookup_cred(struct rpc_auth *auth,
					struct auth_cred *acred, int flags)
{
	return get_rpccred(&tls_cred);
}

static void tls_destroy_cred(struct rpc_cred *cred)
{
}

static int tls_match(struct auth_cred *acred, struct rpc_cred *cred, int taskflags)
{
	return 1;
}

static int tls_marshal(struct rpc_task *task, struct xdr_stream *xdr)
{
	__be32 *p;

	p = xdr_reserve_space(xdr, 4 * XDR_UNIT);
	if (!p)
		return -EMSGSIZE;
	/* Credential */
	*p++ = rpc_auth_tls;
	*p++ = xdr_zero;
	/* Verifier */
	*p++ = rpc_auth_null;
	*p   = xdr_zero;
	return 0;
}

static int tls_refresh(struct rpc_task *task)
{
	set_bit(RPCAUTH_CRED_UPTODATE, &task->tk_rqstp->rq_cred->cr_flags);
	return 0;
}

static int tls_validate(struct rpc_task *task, struct xdr_stream *xdr)
{
	__be32 *p;
	void *str;

	p = xdr_inline_decode(xdr, XDR_UNIT);
	if (!p)
		return -EIO;
	if (*p != rpc_auth_null)
		return -EIO;
	if (xdr_stream_decode_opaque_inline(xdr, &str, starttls_len) != starttls_len)
		return -EIO;
	if (memcmp(str, starttls_token, starttls_len))
		return -EIO;
	return 0;
}

const struct rpc_authops authtls_ops = {
	.owner		= THIS_MODULE,
	.au_flavor	= RPC_AUTH_TLS,
	.au_name	= "NULL",
	.create		= tls_create,
	.destroy	= tls_destroy,
	.lookup_cred	= tls_lookup_cred,
	.ping		= tls_probe,
};

static struct rpc_auth tls_auth = {
	.au_cslack	= NUL_CALLSLACK,
	.au_rslack	= NUL_REPLYSLACK,
	.au_verfsize	= NUL_REPLYSLACK,
	.au_ralign	= NUL_REPLYSLACK,
	.au_ops		= &authtls_ops,
	.au_flavor	= RPC_AUTH_TLS,
	.au_count	= REFCOUNT_INIT(1),
};

static const struct rpc_credops tls_credops = {
	.cr_name	= "AUTH_TLS",
	.crdestroy	= tls_destroy_cred,
	.crmatch	= tls_match,
	.crmarshal	= tls_marshal,
	.crwrap_req	= rpcauth_wrap_req_encode,
	.crrefresh	= tls_refresh,
	.crvalidate	= tls_validate,
	.crunwrap_resp	= rpcauth_unwrap_resp_decode,
};

static struct rpc_cred tls_cred = {
	.cr_lru		= LIST_HEAD_INIT(tls_cred.cr_lru),
	.cr_auth	= &tls_auth,
	.cr_ops		= &tls_credops,
	.cr_count	= REFCOUNT_INIT(2),
	.cr_flags	= 1UL << RPCAUTH_CRED_UPTODATE,
};
+3 −0
Original line number Diff line number Diff line
@@ -2833,6 +2833,9 @@ static int rpc_ping(struct rpc_clnt *clnt)
	struct rpc_task	*task;
	int status;

	if (clnt->cl_auth->au_ops->ping)
		return clnt->cl_auth->au_ops->ping(clnt);

	task = rpc_call_null_helper(clnt, NULL, NULL, 0, NULL, NULL);
	if (IS_ERR(task))
		return PTR_ERR(task);