All of lore.kernel.org
 help / color / mirror / Atom feed
From: Chuck Lever <chuck.lever@oracle.com>
To: netdev@vger.kernel.org, linux-nfs@vger.kernel.org,
	linux-nvme@lists.infradead.org, linux-cifs@vger.kernel.org,
	linux-fsdevel@vger.kernel.org
Cc: ak@tempesta-tech.com, borisp@nvidia.com, simo@redhat.com
Subject: [PATCH RFC 10/15] SUNRPC: Expose TLS policy via the rpc_create() API
Date: Mon, 18 Apr 2022 12:52:15 -0400	[thread overview]
Message-ID: <165030073541.5246.4496835069767281458.stgit@oracle-102.nfsv4.dev> (raw)
In-Reply-To: <165030062272.5246.16956092606399079004.stgit@oracle-102.nfsv4.dev>

Consumers use this API to specify RPC-over-TLS security policy for
each rpc_clnt. For the moment, this checks for TLS availability only
at the time the struct rpc_clnt is created.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 include/linux/sunrpc/clnt.h     |    1 
 include/linux/sunrpc/xprt.h     |    1 
 include/linux/sunrpc/xprtsock.h |    1 
 include/trace/events/sunrpc.h   |   41 +++++++++++++----
 net/sunrpc/clnt.c               |   81 +++++++++++++++++++++++++++++++---
 net/sunrpc/xprtsock.c           |   93 +++++++++++++++++++++++++++++++++++++++
 6 files changed, 201 insertions(+), 17 deletions(-)

diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index 14f169aec5c8..e10a19d136ca 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -146,6 +146,7 @@ struct rpc_create_args {
 	struct svc_xprt		*bc_xprt;	/* NFSv4.1 backchannel */
 	const struct cred	*cred;
 	unsigned int		max_connect;
+	enum rpc_xprtsec	xprtsec_policy;
 };
 
 struct rpc_add_xprt_test {
diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
index 522bbf937957..8d654bc35dce 100644
--- a/include/linux/sunrpc/xprt.h
+++ b/include/linux/sunrpc/xprt.h
@@ -158,6 +158,7 @@ struct rpc_xprt_ops {
 	int		(*enable_swap)(struct rpc_xprt *xprt);
 	void		(*disable_swap)(struct rpc_xprt *xprt);
 	void		(*inject_disconnect)(struct rpc_xprt *xprt);
+	int		(*tls_handshake_sync)(struct rpc_xprt *xprt);
 	int		(*bc_setup)(struct rpc_xprt *xprt,
 				    unsigned int min_reqs);
 	size_t		(*bc_maxpayload)(struct rpc_xprt *xprt);
diff --git a/include/linux/sunrpc/xprtsock.h b/include/linux/sunrpc/xprtsock.h
index 426c3bd516fe..d738a302b38b 100644
--- a/include/linux/sunrpc/xprtsock.h
+++ b/include/linux/sunrpc/xprtsock.h
@@ -58,6 +58,7 @@ struct sock_xprt {
 	struct work_struct	error_worker;
 	struct work_struct	recv_worker;
 	struct mutex		recv_mutex;
+	struct completion	handshake_done;
 	struct sockaddr_storage	srcaddr;
 	unsigned short		srcport;
 	int			xprt_err;
diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h
index 8ffc9c07bc69..a73b68e25a8c 100644
--- a/include/trace/events/sunrpc.h
+++ b/include/trace/events/sunrpc.h
@@ -149,36 +149,56 @@ TRACE_DEFINE_ENUM(RPC_XPRTSEC_MTLS);
 		{ RPC_XPRTSEC_TLS,		"tls" },		\
 		{ RPC_XPRTSEC_MTLS,		"mtls" })
 
+#define rpc_show_create_flags(flags)					\
+	__print_flags(flags, "|",					\
+		{ RPC_CLNT_CREATE_HARDRTRY,	"HARDRTRY" },		\
+		{ RPC_CLNT_CREATE_AUTOBIND,	"AUTOBIND" },		\
+		{ RPC_CLNT_CREATE_NONPRIVPORT,	"NONPRIVPORT" },	\
+		{ RPC_CLNT_CREATE_NOPING,	"NOPING" },		\
+		{ RPC_CLNT_CREATE_DISCRTRY,	"DISCRTRY" },		\
+		{ RPC_CLNT_CREATE_QUIET,	"QUIET" },		\
+		{ RPC_CLNT_CREATE_INFINITE_SLOTS,	"INFINITE_SLOTS" }, \
+		{ RPC_CLNT_CREATE_NO_IDLE_TIMEOUT,	"NO_IDLE_TIMEOUT" }, \
+		{ RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT,	"NO_RETRANS_TIMEOUT" }, \
+		{ RPC_CLNT_CREATE_SOFTERR,	"SOFTERR" },		\
+		{ RPC_CLNT_CREATE_REUSEPORT,	"REUSEPORT" })
+
 TRACE_EVENT(rpc_clnt_new,
 	TP_PROTO(
 		const struct rpc_clnt *clnt,
 		const struct rpc_xprt *xprt,
-		const char *program,
-		const char *server
+		const struct rpc_create_args *args
 	),
 
-	TP_ARGS(clnt, xprt, program, server),
+	TP_ARGS(clnt, xprt, args),
 
 	TP_STRUCT__entry(
 		__field(unsigned int, client_id)
+		__field(unsigned long, xprtsec)
+		__field(unsigned long, flags)
+		__string(program, clnt->cl_program->name)
+		__string(server, xprt->servername)
 		__string(addr, xprt->address_strings[RPC_DISPLAY_ADDR])
 		__string(port, xprt->address_strings[RPC_DISPLAY_PORT])
-		__string(program, program)
-		__string(server, server)
 	),
 
 	TP_fast_assign(
 		__entry->client_id = clnt->cl_clid;
+		__entry->xprtsec = args->xprtsec_policy;
+		__entry->flags = args->flags;
+		__assign_str(program, clnt->cl_program->name);
+		__assign_str(server, xprt->servername);
 		__assign_str(addr, xprt->address_strings[RPC_DISPLAY_ADDR]);
 		__assign_str(port, xprt->address_strings[RPC_DISPLAY_PORT]);
-		__assign_str(program, program);
-		__assign_str(server, server);
 	),
 
-	TP_printk("client=" SUNRPC_TRACE_CLID_SPECIFIER
-		  " peer=[%s]:%s program=%s server=%s",
+	TP_printk("client=" SUNRPC_TRACE_CLID_SPECIFIER " peer=[%s]:%s"
+		" program=%s server=%s xprtsec=%s flags=%s",
 		__entry->client_id, __get_str(addr), __get_str(port),
-		__get_str(program), __get_str(server))
+		__get_str(program), __get_str(server),
+		rpc_show_xprtsec_policy(__entry->xprtsec),
+		rpc_show_create_flags(__entry->flags)
+	)
 );
 
 TRACE_EVENT(rpc_clnt_new_err,
@@ -1585,6 +1605,7 @@ DECLARE_EVENT_CLASS(rpc_tls_class,
 			), \
 			TP_ARGS(clnt, xprt))
 
+DEFINE_RPC_TLS_EVENT(unsupported);
 DEFINE_RPC_TLS_EVENT(unavailable);
 DEFINE_RPC_TLS_EVENT(not_started);
 
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 856581018f10..1601a06deaf5 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -76,6 +76,7 @@ static int	rpc_encode_header(struct rpc_task *task,
 static int	rpc_decode_header(struct rpc_task *task,
 				  struct xdr_stream *xdr);
 static int	rpc_ping(struct rpc_clnt *clnt);
+static int	rpc_starttls_sync(struct rpc_clnt *clnt);
 static void	rpc_check_timeout(struct rpc_task *task);
 
 static void rpc_register_client(struct rpc_clnt *clnt)
@@ -433,7 +434,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args,
 	if (parent)
 		refcount_inc(&parent->cl_count);
 
-	trace_rpc_clnt_new(clnt, xprt, program->name, args->servername);
+	trace_rpc_clnt_new(clnt, xprt, args);
 	return clnt;
 
 out_no_path:
@@ -457,6 +458,7 @@ static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
 {
 	struct rpc_clnt *clnt = NULL;
 	struct rpc_xprt_switch *xps;
+	int err;
 
 	if (args->bc_xprt && args->bc_xprt->xpt_bc_xps) {
 		WARN_ON_ONCE(!(args->protocol & XPRT_TRANSPORT_BC));
@@ -477,13 +479,23 @@ static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
 	if (IS_ERR(clnt))
 		return clnt;
 
-	clnt->cl_xprtsec_policy = RPC_XPRTSEC_NONE;
-	if (!(args->flags & RPC_CLNT_CREATE_NOPING)) {
-		int err = rpc_ping(clnt);
-		if (err != 0) {
-			rpc_shutdown_client(clnt);
-			return ERR_PTR(err);
+	clnt->cl_xprtsec_policy = args->xprtsec_policy;
+	switch (args->xprtsec_policy) {
+	case RPC_XPRTSEC_NONE:
+		if (!(args->flags & RPC_CLNT_CREATE_NOPING)) {
+			err = rpc_ping(clnt);
+			if (err != 0)
+				goto out_shutdown;
 		}
+		break;
+	case RPC_XPRTSEC_TLS:
+		err = rpc_starttls_sync(clnt);
+		if (err != 0)
+			goto out_shutdown;
+		break;
+	default:
+		err = -EINVAL;
+		goto out_shutdown;
 	}
 
 	clnt->cl_softrtry = 1;
@@ -503,6 +515,10 @@ static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
 		clnt->cl_chatty = 1;
 
 	return clnt;
+
+out_shutdown:
+	rpc_shutdown_client(clnt);
+	return ERR_PTR(err);
 }
 
 /**
@@ -690,6 +706,7 @@ rpc_clone_client_set_auth(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
 		.version	= clnt->cl_vers,
 		.authflavor	= flavor,
 		.cred		= clnt->cl_cred,
+		.xprtsec_policy	= clnt->cl_xprtsec_policy,
 	};
 	return __rpc_clone_client(&args, clnt);
 }
@@ -2762,6 +2779,56 @@ static int rpc_ping(struct rpc_clnt *clnt)
 	return status;
 }
 
+/**
+ * rpc_starttls_sync - Synchronously establish a TLS session
+ * @clnt: A fresh RPC client
+ *
+ * Return values:
+ *   %0: TLS session established
+ *   %-ENOPROTOOPT: underlying xprt does not support TLS
+ *   %-EPERM: peer does not support TLS
+ *   %-EACCES: TLS session could not be established
+ */
+static int rpc_starttls_sync(struct rpc_clnt *clnt)
+{
+	struct rpc_xprt *xprt = xprt_get(rcu_dereference(clnt->cl_xprt));
+	struct rpc_message msg = {
+		.rpc_proc = &rpcproc_null,
+	};
+	int err;
+
+	if (!xprt->ops->tls_handshake_sync) {
+		trace_rpc_tls_unsupported(clnt, xprt);
+		err = -ENOPROTOOPT;
+		goto out_put;
+	}
+
+	err = rpc_call_sync(clnt, &msg,
+			    RPC_TASK_SOFT | RPC_TASK_SOFTCONN |
+			    RPC_TASK_TLSCRED);
+	switch (err) {
+	case 0:
+		break;
+	case -EACCES:
+	case -EIO:
+		trace_rpc_tls_unavailable(clnt, xprt);
+		err = -EPERM;
+		fallthrough;
+	default:
+		goto out_put;
+	}
+
+	if (xprt->ops->tls_handshake_sync(xprt)) {
+		trace_rpc_tls_not_started(clnt, xprt);
+		err = -EACCES;
+		goto out_put;
+	}
+
+out_put:
+	xprt_put(xprt);
+	return err;
+}
+
 struct rpc_cb_add_xprt_calldata {
 	struct rpc_xprt_switch *xps;
 	struct rpc_xprt *xprt;
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index e42ae84d7359..bbba9747f68d 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -48,6 +48,7 @@
 #include <net/udp.h>
 #include <net/tcp.h>
 #include <net/tls.h>
+#include <net/tlsh.h>
 
 #include <linux/bvec.h>
 #include <linux/highmem.h>
@@ -197,6 +198,11 @@ static struct ctl_table sunrpc_table[] = {
  */
 #define XS_IDLE_DISC_TO		(5U * 60 * HZ)
 
+/*
+ * TLS handshake timeout.
+ */
+#define XS_TLS_HANDSHAKE_TO	(20U * HZ)
+
 #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
 # undef  RPC_DEBUG_DATA
 # define RPCDBG_FACILITY	RPCDBG_TRANS
@@ -2304,6 +2310,92 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
 	return kernel_connect(sock, xs_addr(xprt), xprt->addrlen, O_NONBLOCK);
 }
 
+#if IS_ENABLED(CONFIG_TLS)
+
+static void xs_tcp_clear_discard(struct sock_xprt *transport)
+{
+	transport->recv.ignore_dr = false;
+	transport->recv.copied = 0;
+	transport->recv.offset = 0;
+	transport->recv.len = 0;
+}
+
+/**
+ * xs_tcp_tls_handshake_done - TLS handshake completion handler
+ * @data: address of xprt to wake
+ * @status: status of handshake
+ *
+ */
+static void xs_tcp_tls_handshake_done(void *data, int status)
+{
+	struct rpc_xprt *xprt = data;
+	struct sock_xprt *transport =
+				container_of(xprt, struct sock_xprt, xprt);
+
+	transport->xprt_err = status ? -EACCES : 0;
+	complete(&transport->handshake_done);
+	xprt_put(xprt);
+}
+
+/**
+ * xs_tcp_tls_handshake_sync - Perform a full TLS client handshake
+ * @xprt: transport on which to perform handshake
+ *
+ * Caller ensures there will be no other traffic on this transport.
+ *
+ * Return values:
+ *   %0: Handshake completed successfully.
+ *   Negative errno: handshake not started, or failed.
+ */
+static int xs_tcp_tls_handshake_sync(struct rpc_xprt *xprt)
+{
+	struct sock_xprt *transport =
+				container_of(xprt, struct sock_xprt, xprt);
+	int rc;
+
+	/* XXX: make it an XPRT_ flag instead? */
+	WRITE_ONCE(transport->recv.ignore_dr, true);
+
+	init_completion(&transport->handshake_done);
+
+	transport->xprt_err = -ETIMEDOUT;
+	rc = tls_client_hello_x509(transport->sock,
+				   xs_tcp_tls_handshake_done, xprt_get(xprt),
+				   TLSH_DEFAULT_PRIORITIES, TLSH_NO_PEERID,
+				   TLSH_NO_CERT);
+	if (rc)
+		goto out;
+
+	rc = wait_for_completion_interruptible_timeout(&transport->handshake_done,
+						       XS_TLS_HANDSHAKE_TO);
+	if (rc < 0)
+		goto out;
+
+	rc = transport->xprt_err;
+
+out:
+	xs_tcp_clear_discard(transport);
+	return rc;
+}
+
+#else /* CONFIG_TLS */
+
+/**
+ * xs_tcp_tls_handshake_sync - Perform a full TLS client handshake
+ * @xprt: transport on which to perform handshake
+ *
+ * Caller ensures there will be no other traffic on this transport.
+ *
+ * Return values:
+ *   %-EACCES: handshake was not started.
+ */
+static int xs_tcp_tls_handshake_sync(struct rpc_xprt *xprt)
+{
+	return -EACCES;
+}
+
+#endif /*CONFIG_TLS */
+
 /**
  * xs_tcp_setup_socket - create a TCP socket and connect to a remote endpoint
  * @work: queued work item
@@ -2752,6 +2844,7 @@ static const struct rpc_xprt_ops xs_tcp_ops = {
 	.enable_swap		= xs_enable_swap,
 	.disable_swap		= xs_disable_swap,
 	.inject_disconnect	= xs_inject_disconnect,
+	.tls_handshake_sync	= xs_tcp_tls_handshake_sync,
 #ifdef CONFIG_SUNRPC_BACKCHANNEL
 	.bc_setup		= xprt_setup_bc,
 	.bc_maxpayload		= xs_tcp_bc_maxpayload,



  parent reply	other threads:[~2022-04-18 16:54 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-18 16:51 [PATCH RFC 00/15] Prototype implementation of RPC-with-TLS Chuck Lever
2022-04-18 16:51 ` [PATCH RFC 01/15] SUNRPC: Replace dprintk() call site in xs_data_ready Chuck Lever
2022-04-18 16:51 ` [PATCH RFC 02/15] SUNRPC: Ignore data_ready callbacks during TLS handshakes Chuck Lever
2022-04-18 16:51 ` [PATCH RFC 03/15] SUNRPC: Capture cmsg metadata on client-side receive Chuck Lever
2022-04-18 16:51 ` [PATCH RFC 04/15] SUNRPC: Fail faster on bad verifier Chuck Lever
2022-04-18 16:51 ` [PATCH RFC 05/15] SUNRPC: Widen rpc_task::tk_flags Chuck Lever
2022-04-18 16:51 ` [PATCH RFC 06/15] SUNRPC: Add RPC client support for the RPC_AUTH_TLS authentication flavor Chuck Lever
2022-04-18 16:51 ` [PATCH RFC 07/15] SUNRPC: Refactor rpc_call_null_helper() Chuck Lever
2022-04-18 16:52 ` [PATCH RFC 08/15] SUNRPC: Add RPC_TASK_CORK flag Chuck Lever
2022-04-19  2:57   ` Trond Myklebust
2022-04-19 18:16     ` Chuck Lever III
2022-04-19 19:04       ` Trond Myklebust
2022-04-19 19:40         ` Chuck Lever III
2022-04-19 22:08           ` Trond Myklebust
2022-04-20  0:34             ` Chuck Lever III
2022-04-18 16:52 ` [PATCH RFC 09/15] SUNRPC: Add a cl_xprtsec_policy field Chuck Lever
2022-04-18 16:52 ` Chuck Lever [this message]
2022-04-18 16:52 ` [PATCH RFC 11/15] SUNRPC: Add infrastructure for async RPC_AUTH_TLS probe Chuck Lever
2022-04-18 16:52 ` [PATCH RFC 12/15] SUNRPC: Add FSM machinery to handle RPC_AUTH_TLS on reconnect Chuck Lever
2022-04-18 16:52 ` [PATCH RFC 13/15] NFS: Replace fs_context-related dprintk() call sites with tracepoints Chuck Lever
2022-04-18 16:52 ` [PATCH RFC 14/15] NFS: Have struct nfs_client carry a TLS policy field Chuck Lever
2022-04-18 16:52 ` [PATCH RFC 15/15] NFS: Add an "xprtsec=" NFS mount option Chuck Lever
2022-04-19  3:31 ` [PATCH RFC 00/15] Prototype implementation of RPC-with-TLS Trond Myklebust
2022-04-19 16:00   ` Chuck Lever III
2022-04-19 18:48     ` Trond Myklebust
2022-04-19 18:53       ` Chuck Lever III
2022-04-19 20:49         ` Rick Macklem

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=165030073541.5246.4496835069767281458.stgit@oracle-102.nfsv4.dev \
    --to=chuck.lever@oracle.com \
    --cc=ak@tempesta-tech.com \
    --cc=borisp@nvidia.com \
    --cc=linux-cifs@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-nfs@vger.kernel.org \
    --cc=linux-nvme@lists.infradead.org \
    --cc=netdev@vger.kernel.org \
    --cc=simo@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.