Skip to content

Commit

Permalink
NFS: Add an "xprtsec=" NFS mount option
Browse files Browse the repository at this point in the history
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: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
  • Loading branch information
Chuck Lever authored and Trond Myklebust committed Jun 19, 2023
1 parent 6c0a8c5 commit c8407f2
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 15 deletions.
6 changes: 3 additions & 3 deletions fs/nfs/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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;
Expand Down
62 changes: 62 additions & 0 deletions fs/nfs/fs_context.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"

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

enum {
Expand Down Expand Up @@ -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),
{}
};

Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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");
}

/*
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions fs/nfs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 6 additions & 2 deletions fs/nfs/nfs3client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
cl_init.nconnect = mds_clp->cl_nconnect;
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);
Expand Down
28 changes: 18 additions & 10 deletions fs/nfs/nfs4client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -988,9 +991,13 @@ 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) {
cl_init.nconnect = mds_clp->cl_nconnect;
cl_init.max_connect = NFS_MAX_TRANSPORTS;
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)
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
12 changes: 12 additions & 0 deletions fs/nfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
#include <linux/uaccess.h>
#include <linux/nfs_ssc.h>

#include <uapi/linux/tls.h>

#include "nfs4_fs.h"
#include "callback.h"
#include "delegation.h"
Expand Down Expand Up @@ -491,6 +493,16 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
seq_printf(m, ",timeo=%lu", 10U * nfss->client->cl_timeout->to_initval / HZ);
seq_printf(m, ",retrans=%u", nfss->client->cl_timeout->to_retries);
seq_printf(m, ",sec=%s", nfs_pseudoflavour_to_name(nfss->client->cl_auth->au_flavor));
switch (clp->cl_xprtsec.policy) {
case RPC_XPRTSEC_TLS_ANON:
seq_puts(m, ",xprtsec=tls");
break;
case RPC_XPRTSEC_TLS_X509:
seq_puts(m, ",xprtsec=mtls");
break;
default:
break;
}

if (version != 4)
nfs_show_mountd_options(m, nfss, showdefaults);
Expand Down

0 comments on commit c8407f2

Please sign in to comment.