mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-08 15:04:45 +00:00
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: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
This commit is contained in:
parent
6c0a8c5fcf
commit
c8407f2e56
@ -463,6 +463,7 @@ void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
|
|||||||
|
|
||||||
switch (proto) {
|
switch (proto) {
|
||||||
case XPRT_TRANSPORT_TCP:
|
case XPRT_TRANSPORT_TCP:
|
||||||
|
case XPRT_TRANSPORT_TCP_TLS:
|
||||||
case XPRT_TRANSPORT_RDMA:
|
case XPRT_TRANSPORT_RDMA:
|
||||||
if (retrans == NFS_UNSPEC_RETRANS)
|
if (retrans == NFS_UNSPEC_RETRANS)
|
||||||
to->to_retries = NFS_DEF_TCP_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,
|
.version = clp->rpc_ops->version,
|
||||||
.authflavor = flavor,
|
.authflavor = flavor,
|
||||||
.cred = cl_init->cred,
|
.cred = cl_init->cred,
|
||||||
|
.xprtsec = cl_init->xprtsec,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags))
|
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,
|
.cred = server->cred,
|
||||||
.nconnect = ctx->nfs_server.nconnect,
|
.nconnect = ctx->nfs_server.nconnect,
|
||||||
.init_flags = (1UL << NFS_CS_REUSEPORT),
|
.init_flags = (1UL << NFS_CS_REUSEPORT),
|
||||||
.xprtsec = {
|
.xprtsec = ctx->xprtsec,
|
||||||
.policy = RPC_XPRTSEC_NONE,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
struct nfs_client *clp;
|
struct nfs_client *clp;
|
||||||
int error;
|
int error;
|
||||||
|
@ -18,6 +18,9 @@
|
|||||||
#include <linux/nfs_fs.h>
|
#include <linux/nfs_fs.h>
|
||||||
#include <linux/nfs_mount.h>
|
#include <linux/nfs_mount.h>
|
||||||
#include <linux/nfs4_mount.h>
|
#include <linux/nfs4_mount.h>
|
||||||
|
|
||||||
|
#include <net/handshake.h>
|
||||||
|
|
||||||
#include "nfs.h"
|
#include "nfs.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
@ -88,6 +91,7 @@ enum nfs_param {
|
|||||||
Opt_vers,
|
Opt_vers,
|
||||||
Opt_wsize,
|
Opt_wsize,
|
||||||
Opt_write,
|
Opt_write,
|
||||||
|
Opt_xprtsec,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -194,6 +198,7 @@ static const struct fs_parameter_spec nfs_fs_parameters[] = {
|
|||||||
fsparam_string("vers", Opt_vers),
|
fsparam_string("vers", Opt_vers),
|
||||||
fsparam_enum ("write", Opt_write, nfs_param_enums_write),
|
fsparam_enum ("write", Opt_write, nfs_param_enums_write),
|
||||||
fsparam_u32 ("wsize", Opt_wsize),
|
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.
|
* 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:
|
default:
|
||||||
ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP;
|
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;
|
return 0;
|
||||||
out_invalid_transport_udp:
|
out_invalid_transport_udp:
|
||||||
return nfs_invalf(fc, "NFS: Unsupported transport protocol 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;
|
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,
|
static int nfs_parse_version_string(struct fs_context *fc,
|
||||||
const char *string)
|
const char *string)
|
||||||
{
|
{
|
||||||
@ -696,6 +750,11 @@ static int nfs_fs_context_parse_param(struct fs_context *fc,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
break;
|
break;
|
||||||
|
case Opt_xprtsec:
|
||||||
|
ret = nfs_parse_xprtsec_policy(fc, param);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
break;
|
||||||
|
|
||||||
case Opt_proto:
|
case Opt_proto:
|
||||||
if (!param->string)
|
if (!param->string)
|
||||||
@ -1574,6 +1633,9 @@ static int nfs_init_fs_context(struct fs_context *fc)
|
|||||||
ctx->selected_flavor = RPC_AUTH_MAXFLAVOR;
|
ctx->selected_flavor = RPC_AUTH_MAXFLAVOR;
|
||||||
ctx->minorversion = 0;
|
ctx->minorversion = 0;
|
||||||
ctx->need_mount = true;
|
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;
|
fc->s_iflags |= SB_I_STABLE_WRITES;
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,7 @@ struct nfs_fs_context {
|
|||||||
unsigned int bsize;
|
unsigned int bsize;
|
||||||
struct nfs_auth_info auth_info;
|
struct nfs_auth_info auth_info;
|
||||||
rpc_authflavor_t selected_flavor;
|
rpc_authflavor_t selected_flavor;
|
||||||
|
struct xprtsec_parms xprtsec;
|
||||||
char *client_address;
|
char *client_address;
|
||||||
unsigned int version;
|
unsigned int version;
|
||||||
unsigned int minorversion;
|
unsigned int minorversion;
|
||||||
|
@ -103,8 +103,12 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,
|
|||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
cl_init.hostname = buf;
|
cl_init.hostname = buf;
|
||||||
|
|
||||||
if (mds_clp->cl_nconnect > 1 && ds_proto == XPRT_TRANSPORT_TCP)
|
switch (ds_proto) {
|
||||||
cl_init.nconnect = mds_clp->cl_nconnect;
|
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)
|
if (mds_srv->flags & NFS_MOUNT_NORESVPORT)
|
||||||
__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
|
__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
|
||||||
|
@ -918,8 +918,11 @@ static int nfs4_set_client(struct nfs_server *server,
|
|||||||
__set_bit(NFS_CS_REUSEPORT, &cl_init.init_flags);
|
__set_bit(NFS_CS_REUSEPORT, &cl_init.init_flags);
|
||||||
else
|
else
|
||||||
cl_init.max_connect = max_connect;
|
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;
|
cl_init.nconnect = nconnect;
|
||||||
|
}
|
||||||
|
|
||||||
if (server->flags & NFS_MOUNT_NORESVPORT)
|
if (server->flags & NFS_MOUNT_NORESVPORT)
|
||||||
__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
|
__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
|
||||||
@ -988,9 +991,13 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,
|
|||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
cl_init.hostname = buf;
|
cl_init.hostname = buf;
|
||||||
|
|
||||||
if (mds_clp->cl_nconnect > 1 && ds_proto == XPRT_TRANSPORT_TCP) {
|
switch (ds_proto) {
|
||||||
cl_init.nconnect = mds_clp->cl_nconnect;
|
case XPRT_TRANSPORT_TCP:
|
||||||
cl_init.max_connect = NFS_MAX_TRANSPORTS;
|
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)
|
if (mds_srv->flags & NFS_MOUNT_NORESVPORT)
|
||||||
@ -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)
|
static int nfs4_init_server(struct nfs_server *server, struct fs_context *fc)
|
||||||
{
|
{
|
||||||
struct nfs_fs_context *ctx = nfs_fc2context(fc);
|
struct nfs_fs_context *ctx = nfs_fc2context(fc);
|
||||||
struct xprtsec_parms xprtsec = {
|
|
||||||
.policy = RPC_XPRTSEC_NONE,
|
|
||||||
};
|
|
||||||
struct rpc_timeout timeparms;
|
struct rpc_timeout timeparms;
|
||||||
int error;
|
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.nconnect,
|
||||||
ctx->nfs_server.max_connect,
|
ctx->nfs_server.max_connect,
|
||||||
fc->net_ns,
|
fc->net_ns,
|
||||||
&xprtsec);
|
&ctx->xprtsec);
|
||||||
if (error < 0)
|
if (error < 0)
|
||||||
return error;
|
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_fs_context *ctx = nfs_fc2context(fc);
|
||||||
struct nfs_client *parent_client;
|
struct nfs_client *parent_client;
|
||||||
struct nfs_server *server, *parent_server;
|
struct nfs_server *server, *parent_server;
|
||||||
|
int proto, error;
|
||||||
bool auth_probe;
|
bool auth_probe;
|
||||||
int error;
|
|
||||||
|
|
||||||
server = nfs_alloc_server();
|
server = nfs_alloc_server();
|
||||||
if (!server)
|
if (!server)
|
||||||
@ -1260,13 +1264,16 @@ struct nfs_server *nfs4_create_referral_server(struct fs_context *fc)
|
|||||||
goto init_server;
|
goto init_server;
|
||||||
#endif /* IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) */
|
#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);
|
rpc_set_port(&ctx->nfs_server.address, NFS_PORT);
|
||||||
error = nfs4_set_client(server,
|
error = nfs4_set_client(server,
|
||||||
ctx->nfs_server.hostname,
|
ctx->nfs_server.hostname,
|
||||||
&ctx->nfs_server._address,
|
&ctx->nfs_server._address,
|
||||||
ctx->nfs_server.addrlen,
|
ctx->nfs_server.addrlen,
|
||||||
parent_client->cl_ipaddr,
|
parent_client->cl_ipaddr,
|
||||||
XPRT_TRANSPORT_TCP,
|
proto,
|
||||||
parent_server->client->cl_timeout,
|
parent_server->client->cl_timeout,
|
||||||
parent_client->cl_mvops->minor_version,
|
parent_client->cl_mvops->minor_version,
|
||||||
parent_client->cl_nconnect,
|
parent_client->cl_nconnect,
|
||||||
@ -1323,6 +1330,7 @@ int nfs4_update_server(struct nfs_server *server, const char *hostname,
|
|||||||
.dstaddr = (struct sockaddr *)sap,
|
.dstaddr = (struct sockaddr *)sap,
|
||||||
.addrlen = salen,
|
.addrlen = salen,
|
||||||
.servername = hostname,
|
.servername = hostname,
|
||||||
|
/* cel: bleh. We might need to pass TLS parameters here */
|
||||||
};
|
};
|
||||||
char buf[INET6_ADDRSTRLEN + 1];
|
char buf[INET6_ADDRSTRLEN + 1];
|
||||||
struct sockaddr_storage address;
|
struct sockaddr_storage address;
|
||||||
|
@ -59,6 +59,8 @@
|
|||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/nfs_ssc.h>
|
#include <linux/nfs_ssc.h>
|
||||||
|
|
||||||
|
#include <uapi/linux/tls.h>
|
||||||
|
|
||||||
#include "nfs4_fs.h"
|
#include "nfs4_fs.h"
|
||||||
#include "callback.h"
|
#include "callback.h"
|
||||||
#include "delegation.h"
|
#include "delegation.h"
|
||||||
@ -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, ",timeo=%lu", 10U * nfss->client->cl_timeout->to_initval / HZ);
|
||||||
seq_printf(m, ",retrans=%u", nfss->client->cl_timeout->to_retries);
|
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));
|
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)
|
if (version != 4)
|
||||||
nfs_show_mountd_options(m, nfss, showdefaults);
|
nfs_show_mountd_options(m, nfss, showdefaults);
|
||||||
|
Loading…
Reference in New Issue
Block a user