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

NFS: Add an "xprtsec=" NFS mount option



After some discussion, we decided that controlling transport layer
security policy should be separate from the setting for the user
authentication flavor. To accomplish this, add a new NFS mount
option to select a transport layer security policy for RPC
operations associated with the mount point.

  xprtsec=none     - Transport layer security is forced off.

  xprtsec=tls      - Establish an encryption-only TLS session. If
                     the initial handshake fails, the mount fails.
                     If TLS is not available on a reconnect, drop
                     the connection and try again.

  xprtsec=mtls     - Both sides authenticate and an encrypted
                     session is created. If the initial handshake
                     fails, the mount fails. If TLS is not available
                     on a reconnect, drop the connection and try
                     again.

To support client peer authentication (mtls), the handshake daemon
will have configurable default authentication material (certificate
or pre-shared key). In the future, mount options can be added that
can provide this material on a per-mount basis.

Updates to mount.nfs (to support xprtsec=auto) and nfs(5) will be
sent under separate cover.

Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
parent 6c0a8c5f
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -463,6 +463,7 @@ void nfs_init_timeout_values(struct rpc_timeout *to, int proto,

	switch (proto) {
	case XPRT_TRANSPORT_TCP:
	case XPRT_TRANSPORT_TCP_TLS:
	case XPRT_TRANSPORT_RDMA:
		if (retrans == NFS_UNSPEC_RETRANS)
			to->to_retries = NFS_DEF_TCP_RETRANS;
@@ -515,6 +516,7 @@ int nfs_create_rpc_client(struct nfs_client *clp,
		.version	= clp->rpc_ops->version,
		.authflavor	= flavor,
		.cred		= cl_init->cred,
		.xprtsec	= cl_init->xprtsec,
	};

	if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags))
@@ -680,9 +682,7 @@ static int nfs_init_server(struct nfs_server *server,
		.cred = server->cred,
		.nconnect = ctx->nfs_server.nconnect,
		.init_flags = (1UL << NFS_CS_REUSEPORT),
		.xprtsec = {
			.policy = RPC_XPRTSEC_NONE,
		},
		.xprtsec = ctx->xprtsec,
	};
	struct nfs_client *clp;
	int error;
+62 −0
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@
#include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
#include <linux/nfs4_mount.h>

#include <net/handshake.h>

#include "nfs.h"
#include "internal.h"

@@ -88,6 +91,7 @@ enum nfs_param {
	Opt_vers,
	Opt_wsize,
	Opt_write,
	Opt_xprtsec,
};

enum {
@@ -194,6 +198,7 @@ static const struct fs_parameter_spec nfs_fs_parameters[] = {
	fsparam_string("vers",		Opt_vers),
	fsparam_enum  ("write",		Opt_write, nfs_param_enums_write),
	fsparam_u32   ("wsize",		Opt_wsize),
	fsparam_string("xprtsec",	Opt_xprtsec),
	{}
};

@@ -267,6 +272,20 @@ static const struct constant_table nfs_secflavor_tokens[] = {
	{}
};

enum {
	Opt_xprtsec_none,
	Opt_xprtsec_tls,
	Opt_xprtsec_mtls,
	nr__Opt_xprtsec
};

static const struct constant_table nfs_xprtsec_policies[] = {
	{ "none",	Opt_xprtsec_none },
	{ "tls",	Opt_xprtsec_tls },
	{ "mtls",	Opt_xprtsec_mtls },
	{}
};

/*
 * Sanity-check a server address provided by the mount command.
 *
@@ -320,9 +339,21 @@ static int nfs_validate_transport_protocol(struct fs_context *fc,
	default:
		ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP;
	}

	if (ctx->xprtsec.policy != RPC_XPRTSEC_NONE)
		switch (ctx->nfs_server.protocol) {
		case XPRT_TRANSPORT_TCP:
			ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP_TLS;
			break;
		default:
			goto out_invalid_xprtsec_policy;
	}

	return 0;
out_invalid_transport_udp:
	return nfs_invalf(fc, "NFS: Unsupported transport protocol udp");
out_invalid_xprtsec_policy:
	return nfs_invalf(fc, "NFS: Transport does not support xprtsec");
}

/*
@@ -430,6 +461,29 @@ static int nfs_parse_security_flavors(struct fs_context *fc,
	return 0;
}

static int nfs_parse_xprtsec_policy(struct fs_context *fc,
				    struct fs_parameter *param)
{
	struct nfs_fs_context *ctx = nfs_fc2context(fc);

	trace_nfs_mount_assign(param->key, param->string);

	switch (lookup_constant(nfs_xprtsec_policies, param->string, -1)) {
	case Opt_xprtsec_none:
		ctx->xprtsec.policy = RPC_XPRTSEC_NONE;
		break;
	case Opt_xprtsec_tls:
		ctx->xprtsec.policy = RPC_XPRTSEC_TLS_ANON;
		break;
	case Opt_xprtsec_mtls:
		ctx->xprtsec.policy = RPC_XPRTSEC_TLS_X509;
		break;
	default:
		return nfs_invalf(fc, "NFS: Unrecognized transport security policy");
	}
	return 0;
}

static int nfs_parse_version_string(struct fs_context *fc,
				    const char *string)
{
@@ -696,6 +750,11 @@ static int nfs_fs_context_parse_param(struct fs_context *fc,
		if (ret < 0)
			return ret;
		break;
	case Opt_xprtsec:
		ret = nfs_parse_xprtsec_policy(fc, param);
		if (ret < 0)
			return ret;
		break;

	case Opt_proto:
		if (!param->string)
@@ -1574,6 +1633,9 @@ static int nfs_init_fs_context(struct fs_context *fc)
		ctx->selected_flavor	= RPC_AUTH_MAXFLAVOR;
		ctx->minorversion	= 0;
		ctx->need_mount		= true;
		ctx->xprtsec.policy	= RPC_XPRTSEC_NONE;
		ctx->xprtsec.cert_serial	= TLS_NO_CERT;
		ctx->xprtsec.privkey_serial	= TLS_NO_PRIVKEY;

		fc->s_iflags		|= SB_I_STABLE_WRITES;
	}
+1 −0
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@ struct nfs_fs_context {
	unsigned int		bsize;
	struct nfs_auth_info	auth_info;
	rpc_authflavor_t	selected_flavor;
	struct xprtsec_parms	xprtsec;
	char			*client_address;
	unsigned int		version;
	unsigned int		minorversion;
+6 −2
Original line number Diff line number Diff line
@@ -103,8 +103,12 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,
		return ERR_PTR(-EINVAL);
	cl_init.hostname = buf;

	if (mds_clp->cl_nconnect > 1 && ds_proto == XPRT_TRANSPORT_TCP)
	switch (ds_proto) {
	case XPRT_TRANSPORT_TCP:
	case XPRT_TRANSPORT_TCP_TLS:
		if (mds_clp->cl_nconnect > 1)
			cl_init.nconnect = mds_clp->cl_nconnect;
	}

	if (mds_srv->flags & NFS_MOUNT_NORESVPORT)
		__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
+18 −10
Original line number Diff line number Diff line
@@ -918,8 +918,11 @@ static int nfs4_set_client(struct nfs_server *server,
		__set_bit(NFS_CS_REUSEPORT, &cl_init.init_flags);
	else
		cl_init.max_connect = max_connect;
	if (proto == XPRT_TRANSPORT_TCP)
	switch (proto) {
	case XPRT_TRANSPORT_TCP:
	case XPRT_TRANSPORT_TCP_TLS:
		cl_init.nconnect = nconnect;
	}

	if (server->flags & NFS_MOUNT_NORESVPORT)
		__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
@@ -988,10 +991,14 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,
		return ERR_PTR(-EINVAL);
	cl_init.hostname = buf;

	if (mds_clp->cl_nconnect > 1 && ds_proto == XPRT_TRANSPORT_TCP) {
	switch (ds_proto) {
	case XPRT_TRANSPORT_TCP:
	case XPRT_TRANSPORT_TCP_TLS:
		if (mds_clp->cl_nconnect > 1) {
			cl_init.nconnect = mds_clp->cl_nconnect;
			cl_init.max_connect = NFS_MAX_TRANSPORTS;
		}
	}

	if (mds_srv->flags & NFS_MOUNT_NORESVPORT)
		__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
@@ -1130,9 +1137,6 @@ static int nfs4_server_common_setup(struct nfs_server *server,
static int nfs4_init_server(struct nfs_server *server, struct fs_context *fc)
{
	struct nfs_fs_context *ctx = nfs_fc2context(fc);
	struct xprtsec_parms xprtsec = {
		.policy		= RPC_XPRTSEC_NONE,
	};
	struct rpc_timeout timeparms;
	int error;

@@ -1164,7 +1168,7 @@ static int nfs4_init_server(struct nfs_server *server, struct fs_context *fc)
				ctx->nfs_server.nconnect,
				ctx->nfs_server.max_connect,
				fc->net_ns,
				&xprtsec);
				&ctx->xprtsec);
	if (error < 0)
		return error;

@@ -1226,8 +1230,8 @@ struct nfs_server *nfs4_create_referral_server(struct fs_context *fc)
	struct nfs_fs_context *ctx = nfs_fc2context(fc);
	struct nfs_client *parent_client;
	struct nfs_server *server, *parent_server;
	int proto, error;
	bool auth_probe;
	int error;

	server = nfs_alloc_server();
	if (!server)
@@ -1260,13 +1264,16 @@ struct nfs_server *nfs4_create_referral_server(struct fs_context *fc)
		goto init_server;
#endif	/* IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) */

	proto = XPRT_TRANSPORT_TCP;
	if (parent_client->cl_xprtsec.policy != RPC_XPRTSEC_NONE)
		proto = XPRT_TRANSPORT_TCP_TLS;
	rpc_set_port(&ctx->nfs_server.address, NFS_PORT);
	error = nfs4_set_client(server,
				ctx->nfs_server.hostname,
				&ctx->nfs_server._address,
				ctx->nfs_server.addrlen,
				parent_client->cl_ipaddr,
				XPRT_TRANSPORT_TCP,
				proto,
				parent_server->client->cl_timeout,
				parent_client->cl_mvops->minor_version,
				parent_client->cl_nconnect,
@@ -1323,6 +1330,7 @@ int nfs4_update_server(struct nfs_server *server, const char *hostname,
		.dstaddr	= (struct sockaddr *)sap,
		.addrlen	= salen,
		.servername	= hostname,
		/* cel: bleh. We might need to pass TLS parameters here */
	};
	char buf[INET6_ADDRSTRLEN + 1];
	struct sockaddr_storage address;
Loading