nfs-utils/nfs-utils-2.5.4-support-for-rpc-with-tls.patch

428 lines
13 KiB
Diff
Raw Normal View History

diff --git a/support/export/cache.c b/support/export/cache.c
index a5823e92..396b3b73 100644
--- a/support/export/cache.c
+++ b/support/export/cache.c
@@ -932,6 +932,7 @@ static void write_fsloc(char **bp, int *blen, struct exportent *ep)
release_replicas(servers);
}
#endif
+
static void write_secinfo(char **bp, int *blen, struct exportent *ep, int flag_mask)
{
struct sec_entry *p;
@@ -949,7 +950,20 @@ static void write_secinfo(char **bp, int *blen, struct exportent *ep, int flag_m
qword_addint(bp, blen, p->flav->fnum);
qword_addint(bp, blen, p->flags & flag_mask);
}
+}
+
+static void write_xprtsec(char **bp, int *blen, struct exportent *ep)
+{
+ struct xprtsec_entry *p;
+
+ for (p = ep->e_xprtsec; p->info; p++);
+ if (p == ep->e_xprtsec)
+ return;
+ qword_add(bp, blen, "xprtsec");
+ qword_addint(bp, blen, p - ep->e_xprtsec);
+ for (p = ep->e_xprtsec; p->info; p++)
+ qword_addint(bp, blen, p->info->number);
}
static int dump_to_cache(int f, char *buf, int blen, char *domain,
@@ -992,6 +1006,7 @@ static int dump_to_cache(int f, char *buf, int blen, char *domain,
qword_add(&bp, &blen, "uuid");
qword_addhex(&bp, &blen, u, 16);
}
+ write_xprtsec(&bp, &blen, exp);
xlog(D_AUTH, "granted access to %s for %s",
path, *domain == '$' ? domain+1 : domain);
} else {
diff --git a/support/include/nfs/export.h b/support/include/nfs/export.h
index 0eca828e..be5867cf 100644
--- a/support/include/nfs/export.h
+++ b/support/include/nfs/export.h
@@ -40,4 +40,18 @@
#define NFSEXP_OLD_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \
| NFSEXP_ALLSQUASH)
+/*
+ * Transport layer security policies that are permitted to access
+ * an export
+ */
+#define NFSEXP_XPRTSEC_NONE 0x0001
+#define NFSEXP_XPRTSEC_TLS 0x0002
+#define NFSEXP_XPRTSEC_MTLS 0x0004
+
+#define NFSEXP_XPRTSEC_NUM (3)
+
+#define NFSEXP_XPRTSEC_ALL (NFSEXP_XPRTSEC_NONE | \
+ NFSEXP_XPRTSEC_TLS | \
+ NFSEXP_XPRTSEC_MTLS)
+
#endif /* _NSF_EXPORT_H */
diff --git a/support/include/nfslib.h b/support/include/nfslib.h
index 6faba71b..61c19933 100644
--- a/support/include/nfslib.h
+++ b/support/include/nfslib.h
@@ -62,6 +62,18 @@ struct sec_entry {
int flags;
};
+#define XPRTSECMODE_COUNT 3
+
+struct xprtsec_info {
+ const char *name;
+ int number;
+};
+
+struct xprtsec_entry {
+ const struct xprtsec_info *info;
+ int flags;
+};
+
/*
* Data related to a single exports entry as returned by getexportent.
* FIXME: export options should probably be parsed at a later time to
@@ -83,6 +95,7 @@ struct exportent {
char * e_fslocdata;
char * e_uuid;
struct sec_entry e_secinfo[SECFLAVOR_COUNT+1];
+ struct xprtsec_entry e_xprtsec[XPRTSECMODE_COUNT + 1];
unsigned int e_ttl;
char * e_realpath;
};
@@ -99,6 +112,7 @@ struct rmtabent {
void setexportent(char *fname, char *type);
struct exportent * getexportent(int,int);
void secinfo_show(FILE *fp, struct exportent *ep);
+void xprtsecinfo_show(FILE *fp, struct exportent *ep);
void putexportent(struct exportent *xep);
void endexportent(void);
struct exportent * mkexportent(char *hname, char *path, char *opts);
diff --git a/support/nfs/exports.c b/support/nfs/exports.c
index ec6f8013..d36f7664 100644
--- a/support/nfs/exports.c
+++ b/support/nfs/exports.c
@@ -99,6 +99,7 @@ static void init_exportent (struct exportent *ee, int fromkernel)
ee->e_fslocmethod = FSLOC_NONE;
ee->e_fslocdata = NULL;
ee->e_secinfo[0].flav = NULL;
+ ee->e_xprtsec[0].info = NULL;
ee->e_nsquids = 0;
ee->e_nsqgids = 0;
ee->e_uuid = NULL;
@@ -122,7 +123,7 @@ getexportent(int fromkernel, int fromexports)
if (first || (ok = getexport(exp, sizeof(exp))) == 0) {
has_default_opts = 0;
has_default_subtree_opts = 0;
-
+
init_exportent(&def_ee, fromkernel);
ok = getpath(def_ee.e_path, sizeof(def_ee.e_path));
@@ -146,7 +147,7 @@ getexportent(int fromkernel, int fromexports)
if (exp[0] == '-' && !fromkernel) {
if (parseopts(exp + 1, &def_ee, 0, &has_default_subtree_opts) < 0)
return NULL;
-
+
has_default_opts = 1;
ok = getexport(exp, sizeof(exp));
@@ -239,7 +240,6 @@ void secinfo_show(FILE *fp, struct exportent *ep)
if (ep->e_secinfo[0].flav == NULL)
secinfo_addflavor(find_flavor("sys"), ep);
for (p1=ep->e_secinfo; p1->flav; p1=p2) {
-
fprintf(fp, ",sec=%s", p1->flav->flavour);
for (p2=p1+1; (p2->flav != NULL) && (p1->flags == p2->flags);
p2++) {
@@ -249,6 +249,17 @@ void secinfo_show(FILE *fp, struct exportent *ep)
}
}
+void xprtsecinfo_show(FILE *fp, struct exportent *ep)
+{
+ struct xprtsec_entry *p1, *p2;
+
+ for (p1 = ep->e_xprtsec; p1->info; p1 = p2) {
+ fprintf(fp, ",xprtsec=%s", p1->info->name);
+ for (p2 = p1 + 1; p2->info && (p1->flags == p2->flags); p2++)
+ fprintf(fp, ":%s", p2->info->name);
+ }
+}
+
static void
fprintpath(FILE *fp, const char *path)
{
@@ -345,6 +356,7 @@ putexportent(struct exportent *ep)
}
fprintf(fp, "anonuid=%d,anongid=%d", ep->e_anonuid, ep->e_anongid);
secinfo_show(fp, ep);
+ xprtsecinfo_show(fp, ep);
fprintf(fp, ")\n");
}
@@ -483,6 +495,75 @@ static unsigned int parse_flavors(char *str, struct exportent *ep)
return out;
}
+static const struct xprtsec_info xprtsec_name2info[] = {
+ { "none", NFSEXP_XPRTSEC_NONE },
+ { "tls", NFSEXP_XPRTSEC_TLS },
+ { "mtls", NFSEXP_XPRTSEC_MTLS },
+ { NULL, 0 }
+};
+
+static const struct xprtsec_info *find_xprtsec_info(const char *name)
+{
+ const struct xprtsec_info *info;
+
+ for (info = xprtsec_name2info; info->name; info++)
+ if (strcmp(info->name, name) == 0)
+ return info;
+ return NULL;
+}
+
+/*
+ * Append the given xprtsec mode to the exportent's e_xprtsec array,
+ * or do nothing if it's already there. Returns the index of flavor in
+ * the resulting array in any case.
+ */
+static int xprtsec_addmode(const struct xprtsec_info *info, struct exportent *ep)
+{
+ struct xprtsec_entry *p;
+
+ for (p = ep->e_xprtsec; p->info; p++)
+ if (p->info == info || p->info->number == info->number)
+ return p - ep->e_xprtsec;
+
+ if (p - ep->e_xprtsec >= XPRTSECMODE_COUNT) {
+ xlog(L_ERROR, "more than %d xprtsec modes on an export\n",
+ XPRTSECMODE_COUNT);
+ return -1;
+ }
+ p->info = info;
+ p->flags = ep->e_flags;
+ (p + 1)->info = NULL;
+ return p - ep->e_xprtsec;
+}
+
+/*
+ * @str is a colon seperated list of transport layer security modes.
+ * Their order is recorded in @ep, and a bitmap corresponding to the
+ * list is returned.
+ *
+ * A zero return indicates an error.
+ */
+static unsigned int parse_xprtsec(char *str, struct exportent *ep)
+{
+ unsigned int out = 0;
+ char *name;
+
+ while ((name = strsep(&str, ":"))) {
+ const struct xprtsec_info *info = find_xprtsec_info(name);
+ int bit;
+
+ if (!info) {
+ xlog(L_ERROR, "unknown xprtsec mode %s\n", name);
+ return 0;
+ }
+ bit = xprtsec_addmode(info, ep);
+ if (bit < 0)
+ return 0;
+ out |= 1 << bit;
+ }
+ return out;
+}
+
/* Sets the bits in @mask for the appropriate security flavor flags. */
static void setflags(int mask, unsigned int active, struct exportent *ep)
{
@@ -621,7 +702,7 @@ parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr)
ep->e_anonuid = strtol(opt+8, &oe, 10);
if (opt[8]=='\0' || *oe != '\0') {
xlog(L_ERROR, "%s: %d: bad anonuid \"%s\"\n",
- flname, flline, opt);
+ flname, flline, opt);
bad_option:
free(opt);
return -1;
@@ -631,7 +712,7 @@ bad_option:
ep->e_anongid = strtol(opt+8, &oe, 10);
if (opt[8]=='\0' || *oe != '\0') {
xlog(L_ERROR, "%s: %d: bad anongid \"%s\"\n",
- flname, flline, opt);
+ flname, flline, opt);
goto bad_option;
}
} else if (strncmp(opt, "squash_uids=", 12) == 0) {
@@ -649,13 +730,13 @@ bad_option:
setflags(NFSEXP_FSID, active, ep);
} else {
ep->e_fsid = strtoul(opt+5, &oe, 0);
- if (opt[5]!='\0' && *oe == '\0')
+ if (opt[5]!='\0' && *oe == '\0')
setflags(NFSEXP_FSID, active, ep);
else if (valid_uuid(opt+5))
ep->e_uuid = strdup(opt+5);
else {
xlog(L_ERROR, "%s: %d: bad fsid \"%s\"\n",
- flname, flline, opt);
+ flname, flline, opt);
goto bad_option;
}
}
@@ -688,6 +769,9 @@ bad_option:
active = parse_flavors(opt+4, ep);
if (!active)
goto bad_option;
+ } else if (strncmp(opt, "xprtsec=", 8) == 0) {
+ if (!parse_xprtsec(opt + 8, ep))
+ goto bad_option;
} else {
xlog(L_ERROR, "%s:%d: unknown keyword \"%s\"\n",
flname, flline, opt);
@@ -709,7 +793,7 @@ out:
if (warn && !had_subtree_opt)
xlog(L_WARNING, "%s [%d]: Neither 'subtree_check' or 'no_subtree_check' specified for export \"%s:%s\".\n"
" Assuming default behaviour ('no_subtree_check').\n"
- " NOTE: this default has changed since nfs-utils version 1.0.x\n",
+ " NOTE: this default has changed since nfs-utils version 1.0.x\n",
flname, flline,
ep->e_hostname, ep->e_path);
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index 6ba615d1..a87a7806 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -743,6 +743,7 @@ dump(int verbose, int export_format)
#endif
}
secinfo_show(stdout, ep);
+ xprtsecinfo_show(stdout, ep);
printf("%c\n", (c != '(')? ')' : ' ');
}
}
diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man
index 54b3f877..83dd6807 100644
--- a/utils/exportfs/exports.man
+++ b/utils/exportfs/exports.man
@@ -125,7 +125,55 @@ In that case you may include multiple sec= options, and following options
will be enforced only for access using flavors listed in the immediately
preceding sec= option. The only options that are permitted to vary in
this way are ro, rw, no_root_squash, root_squash, and all_squash.
+.SS Transport layer security
+The Linux NFS server allows the use of RPC-with-TLS (RFC 9289) to
+protect RPC traffic between itself and its clients.
+Alternately, administrators can secure NFS traffic using a VPN,
+or an ssh tunnel or similar mechanism, in a way that is transparent
+to the server.
.PP
+To enable the use of RPC-with-TLS, the server's administrator must
+install and configure
+.BR tlshd
+to handle transport layer security handshake requests from the local
+kernel.
+Clients can then choose to use RPC-with-TLS or they may continue
+operating without it.
+.PP
+Administrators may require the use of RPC-with-TLS to protect access
+to individual exports.
+This is particularly useful when using non-cryptographic security
+flavors such as
+.IR sec=sys .
+The
+.I xprtsec=
+option, followed by an unordered colon-delimited list of security policies,
+can restrict access to the export to only clients that have negotiated
+transport-layer security.
+Currently supported transport layer security policies include:
+.TP
+.IR none
+The server permits clients to access the export
+without the use of transport layer security.
+.TP
+.IR tls
+The server permits clients that have negotiated an RPC-with-TLS session
+without peer authentication (confidentiality only) to access the export.
+Clients are not required to offer an x.509 certificate
+when establishing a transport layer security session.
+.TP
+.IR mtls
+The server permits clients that have negotiated an RPC-with-TLS session
+with peer authentication to access the export.
+The server requires clients to offer an x.509 certificate
+when establishing a transport layer security session.
+.PP
+If RPC-with-TLS is configured and enabled and the
+.I xprtsec=
+option is not specified, the default setting for an export is
+.IR xprtsec=none:tls:mtls .
+With this setting, the server permits clients to use any transport
+layer security mechanism or none at all to access the export.
.SS General Options
.BR exportfs
understands the following export options:
@@ -581,7 +629,8 @@ a character class wildcard match.
.BR netgroup (5),
.BR mountd (8),
.BR nfsd (8),
-.BR showmount (8).
+.BR showmount (8),
+.BR tlshd (8).
.\".SH DIAGNOSTICS
.\"An error parsing the file is reported using syslogd(8) as level NOTICE from
.\"a DAEMON whenever
diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
index d9f34df3..dfc31a5d 100644
--- a/utils/mount/nfs.man
+++ b/utils/mount/nfs.man
@@ -574,7 +574,43 @@ The
.B sloppy
option is an alternative to specifying
.BR mount.nfs " -s " option.
-
+.TP 1.5i
+.BI xprtsec= policy
+Specifies the use of transport layer security to protect NFS network
+traffic on behalf of this mount point.
+.I policy
+can be one of
+.BR none ,
+.BR tls ,
+or
+.BR mtls .
+.IP
+If
+.B none
+is specified,
+transport layer security is forced off, even if the NFS server supports
+transport layer security.
+If
+.B tls
+is specified, the client uses RPC-with-TLS to provide in-transit
+confidentiality.
+If
+.B mtls
+is specified, the client uses RPC-with-TLS to authenticate itself and
+to provide in-transit confidentiality.
+If either
+.B tls
+or
+.B mtls
+is specified and the server does not support RPC-with-TLS or peer
+authentication fails, the mount attempt fails.
+.IP
+If the
+.B xprtsec=
+option is not specified,
+the default behavior depends on the kernel version,
+but is usually equivalent to
+.BR "xprtsec=none" .
.SS "Options for NFS versions 2 and 3 only"
Use these options, along with the options in the above subsection,
for NFS versions 2 and 3 only.