svn commit: r359225 - in projects/nfs-over-tls/usr.sbin: rpctlscd rpctlssd

Rick Macklem rmacklem at FreeBSD.org
Sun Mar 22 19:31:13 UTC 2020


Author: rmacklem
Date: Sun Mar 22 19:31:12 2020
New Revision: 359225
URL: https://svnweb.freebsd.org/changeset/base/359225

Log:
  Update the rpctlscd daemon in various ways, including the addition of
  support for upcalls for TLS connections already established.
  The current "SSL *"s are maintained in a linked list referenced by a
  uint64_t refno.
  Also, update rpctlssd.8, which got missed in the last commit.

Modified:
  projects/nfs-over-tls/usr.sbin/rpctlscd/Makefile
  projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.8
  projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.c
  projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.8

Modified: projects/nfs-over-tls/usr.sbin/rpctlscd/Makefile
==============================================================================
--- projects/nfs-over-tls/usr.sbin/rpctlscd/Makefile	Sun Mar 22 19:23:43 2020	(r359224)
+++ projects/nfs-over-tls/usr.sbin/rpctlscd/Makefile	Sun Mar 22 19:31:12 2020	(r359225)
@@ -7,7 +7,6 @@ MAN=	rpctlscd.8
 SRCS=	rpctlscd.c rpctlscd.h rpctlscd_svc.c rpctlscd_xdr.c
 
 CFLAGS+= -I.
-WARNS?= 1
 
 LIBADD=	ssl crypto
 

Modified: projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.8
==============================================================================
--- projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.8	Sun Mar 22 19:23:43 2020	(r359224)
+++ projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.8	Sun Mar 22 19:31:12 2020	(r359225)
@@ -26,7 +26,7 @@
 .\" $FreeBSD$
 .\"
 .\" Modified from gssd.8 for rpctlscd.8 by Rick Macklem.
-.Dd January 21, 2020
+.Dd March 11, 2020
 .Dt RPCTLSCD 8
 .Os
 .Sh NAME
@@ -34,21 +34,91 @@
 .Nd "Sun RPC over TLS Client Daemon"
 .Sh SYNOPSIS
 .Nm
+.Op Fl D Ar certdir
 .Op Fl d
+.Op Fl h
+.Op Fl l Ar CAfile
+.Op Fl m
+.Op Fl p Ar CApath
+.Op Fl r Ar CRLfile
+.Op Fl V
 .Op Fl v
 .Sh DESCRIPTION
 The
 .Nm
 program provides support for the client side of the kernel Sun RPC over TLS
 implementation.
+This daemon must be running for the kernel RPC to be able to do a TLS
+connection to a server for an NFS over TLS mount.
 .Pp
 The options are as follows:
 .Bl -tag -width indent
+.It Fl D Ar certdir
+Use ``certdir'' instead of /etc/rpctlscd for the
+.Fl c
+option.
 .It Fl d
 Run in debug mode.
 In this mode,
 .Nm
 will not fork when it starts.
+.It Fl h
+This option specifies that the certificate provided by the server during
+TLS handshake must have the Fully Qualified Domain Name for the server's
+IP address in either the subjectAltName or commonName field of the
+certificate.
+This option is meaningless unless the
+.FL V
+option is also specified.
+.It Fl l Ar CAfile
+This specifies the path name of a CAfile which holds the information
+for server certificate verification.
+This path name is used in
+.Dq SSL_CTX_load_verify_locations(ctx,CAfile,NULL)
+and
+.Dq SSL_CTX_set0_CA_list(ctx,SSL_load_client_CA_file(CAfile))
+calls.
+Note that this is a path name for the file and is not assumed to be
+in ``certdir''.
+This option may need to be specified when the
+.Fl V
+option is specified.
+.It Fl m
+Enable support for mutual authentication.
+A certificate must be found in /etc/rpctlscd (or the directory specified by
+.Fl D )
+in case a server requests a peer certificate.
+The certificate needs to be in a file named ``cert.pem'' and a key in
+a file named ``key.pem'' in the directory for this option to work.
+.It Fl p Ar CApath
+This option is similar to the
+.Fl l
+option, but specifies the path of a directory with CA
+certificates in it.
+When this option is used,
+.Dq SSL_CTX_set0_CA_list(ctx,SSL_load_client_CA_file())
+is not called, so a list of CA names might not be passed
+to the server during the TLS handshake.
+The openssl documentation indicates this call is rarely needed.
+(However, I was not able to determine if/when this matters, so
+if in doubt, use the
+.Fl l
+option instead of this option.)
+.It Fl r Ar CRLfile
+This option specifies a Certificate Revocation List (CRL) file
+that is to be loaded into the verify certificate store and
+checked during verification.
+This option is meaningless unless either the
+.Fl l
+or
+.Fl p
+have been specified.
+.It Fl V
+This option specifies that the certificate provided by the server
+during the TLS handshake must verify.
+If this option is specified, the
+.Fl l
+option may also need to be specified.
 .It Fl v
 Run in verbose mode.
 In this mode,
@@ -61,10 +131,17 @@ option has also been specified.
 .Sh EXIT STATUS
 .Ex -std
 .Sh SEE ALSO
-.Xr openssl 3 ,
+.Xr openssl 1 ,
 .Xr syslog 3 ,
 .Xr mount_nfs 8 ,
 .Xr rpctlssd 8
+.Sh BUGS
+This daemon cannot be safely shut down and restarted if there are
+any active RPC-over-TLS connections.
+Doing so will orphan the KERNEL_TLS connections, so that they
+can no longer do upcalls successfully, since the
+.Dq SSL *
+structures in userspace have been lost.
 .Sh HISTORY
 The
 .Nm

Modified: projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.c
==============================================================================
--- projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.c	Sun Mar 22 19:23:43 2020	(r359224)
+++ projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.c	Sun Mar 22 19:31:12 2020	(r359225)
@@ -34,9 +34,12 @@ __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/types.h>
-#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/linker.h>
+#include <sys/module.h>
 #include <sys/stat.h>
 #include <sys/syslog.h>
+#include <sys/time.h>
 #include <err.h>
 #include <netdb.h>
 #include <signal.h>
@@ -47,8 +50,6 @@ __FBSDID("$FreeBSD$");
 #include <string.h>
 #include <unistd.h>
 
-#include <arpa/inet.h>
-
 #include <rpc/rpc.h>
 #include <rpc/rpc_com.h>
 #include <rpc/rpcsec_tls.h>
@@ -66,20 +67,46 @@ __FBSDID("$FreeBSD$");
 #ifndef	_PATH_CERTANDKEY
 #define	_PATH_CERTANDKEY	"/etc/rpctlscd/"
 #endif
+#ifndef	_PREFERRED_CIPHERS
+#define	_PREFERRED_CIPHERS	"SHA384:SHA256:!CAMELLIA"
+#endif
 
 static int		rpctls_debug_level;
 static bool		rpctls_verbose;
 static int testnossl;
 static SSL_CTX		*rpctls_ctx = NULL;
 static const char	*rpctls_verify_cafile = NULL;
+static const char	*rpctls_verify_capath = NULL;
+static const char	*rpctls_crlfile = NULL;
 static const char	*rpctls_certdir = _PATH_CERTANDKEY;
 static bool		rpctls_verify = false;
 static bool		rpctls_comparehost = false;
+static uint64_t		rpctls_ssl_refno = 0;
+static uint64_t		rpctls_ssl_sec = 0;
+static uint64_t		rpctls_ssl_usec = 0;
+static bool		rpctls_gothup = false;
 
+/*
+ * A linked list of all current "SSL *"s and socket "fd"s
+ * for kernel RPC TLS connections is maintained.
+ * The "refno" field is a unique 64bit value used to
+ * identify which entry a kernel RPC upcall refers to.
+ */
+LIST_HEAD(ssl_list, ssl_entry);
+struct ssl_entry {
+	LIST_ENTRY(ssl_entry)	next;
+	uint64_t		refno;
+	int			s;
+	SSL			*ssl;
+};
+static struct ssl_list	rpctls_ssllist;
+
 static void		rpctlscd_terminate(int);
 static SSL_CTX		*rpctls_setupcl_ssl(bool cert);
 static SSL		*rpctls_connect(SSL_CTX *ctx, int s);
 static int		rpctls_checkhost(int s, X509 *cert);
+static int		rpctls_loadfiles(SSL_CTX *ctx);
+static void		rpctls_huphandler(int sig __unused);
 
 extern void rpctlscd_1(struct svc_req *rqstp, SVCXPRT *transp);
 extern int gssd_syscall(const char *path);
@@ -96,15 +123,19 @@ main(int argc, char **argv)
 	int fd, oldmask, ch;
 	SVCXPRT *xprt;
 	bool cert;
+	struct timeval tm;
+	struct timezone tz;
 
+	/* Get the time when this daemon is started. */
+	gettimeofday(&tm, &tz);
+	rpctls_ssl_sec = tm.tv_sec;
+	rpctls_ssl_usec = tm.tv_usec;
+
 	rpctls_verbose = false;
 	testnossl = 0;
 	cert = false;
-	while ((ch = getopt(argc, argv, "cD:dhl:tVv")) != -1) {
+	while ((ch = getopt(argc, argv, "D:dhl:mp:rtVv")) != -1) {
 		switch (ch) {
-		case 'c':
-			cert = true;
-			break;
 		case 'D':
 			rpctls_certdir = optarg;
 			break;
@@ -117,6 +148,15 @@ main(int argc, char **argv)
 		case 'l':
 			rpctls_verify_cafile = optarg;
 			break;
+		case 'm':
+			cert = true;
+			break;
+		case 'p':
+			rpctls_verify_capath = optarg;
+			break;
+		case 'r':
+			rpctls_crlfile = optarg;
+			break;
 		case 't':
 			testnossl = 1;
 			break;
@@ -127,15 +167,22 @@ main(int argc, char **argv)
 			rpctls_verbose = true;
 			break;
 		default:
-			fprintf(stderr, "usage: %s [-c] "
+			fprintf(stderr, "usage: %s "
 			    "[-D certdir] [-d] [-h] "
-			    "[-l verify_locations_file] "
+			    "[-l CAfile] [-m] "
+			    "[-p CApath] [-r CRLfile] "
 			    "[-V] [-v]\n", argv[0]);
 			exit(1);
 			break;
 		}
 	}
 
+	if (modfind("krpc") < 0) {
+		/* Not present in kernel, try loading it */
+		if (kldload("krpc") < 0 || modfind("krpc") < 0)
+			errx(1, "Kernel RPC is not available");
+	}
+
 	if (!rpctls_debug_level) {
 		if (daemon(0, 0) != 0)
 			err(1, "Can't daemonize");
@@ -145,6 +192,7 @@ main(int argc, char **argv)
 	}
 	signal(SIGTERM, rpctlscd_terminate);
 	signal(SIGPIPE, rpctlscd_terminate);
+	signal(SIGHUP, rpctls_huphandler);
 
 	memset(&sun, 0, sizeof sun);
 	sun.sun_family = AF_LOCAL;
@@ -203,6 +251,7 @@ main(int argc, char **argv)
 		}
 		err(1, "Can't set up TSL context");
 	}
+	LIST_INIT(&rpctls_ssllist);
 
 	gssd_syscall(_PATH_RPCTLSCDSOCK);
 	svc_run();
@@ -237,13 +286,15 @@ rpctlscd_null_1_svc(void *argp, void *result, struct s
 }
 
 bool_t
-rpctlscd_connect_1_svc(void *argp, void *result, struct svc_req *rqstp)
+rpctlscd_connect_1_svc(void *argp,
+    struct rpctlscd_connect_res *result, struct svc_req *rqstp)
 {
 	int s;
 	bool_t res;
 	SSL *ssl;
 	char buf[1024];
 	ssize_t siz, ret;
+	struct ssl_entry *newslp;
 
 	rpctlscd_verbose_out("rpctlsd_connect: started\n");
 	/* Get the socket fd from the kernel. */
@@ -257,6 +308,14 @@ rpctlscd_verbose_out("rpctlsd_connect s=%d\n", s);
 	if (ssl == NULL)
 		rpctlscd_verbose_out("rpctlsd_connect: can't do TLS "
 		    "handshake\n");
+	else {
+		result->sec = rpctls_ssl_sec;
+		result->usec = rpctls_ssl_usec;
+		result->ssl = ++rpctls_ssl_refno;
+		/* Hard to believe this will ever wrap around.. */
+		if (rpctls_ssl_refno == 0)
+			result->ssl = ++rpctls_ssl_refno;
+	}
 	if (testnossl != 0 && ssl != NULL) {
 		/* Read the 478 bytes of junk off the socket. */
 		siz = 478;
@@ -267,13 +326,58 @@ rpctlscd_verbose_out("rpctlsd_connect s=%d\n", s);
 		}
 	}
 
-	/* Done with socket fd, so let the kernel know. */
-	gssd_syscall("D");
-	if (ssl == NULL)
+	if (ssl == NULL) {
+		/*
+		 * For RPC-over-TLS, this upcall is expected
+		 * to close off the socket.
+		 */
+		shutdown(s, SHUT_WR);
+		close(s);
 		return (FALSE);
+	}
+
+	/* Maintain list of all current SSL *'s */
+	newslp = malloc(sizeof(*newslp));
+	newslp->refno = rpctls_ssl_refno;
+	newslp->s = s;
+	newslp->ssl = ssl;
+	LIST_INSERT_HEAD(&rpctls_ssllist, newslp, next);
 	return (TRUE);
 }
 
+bool_t
+rpctlscd_disconnect_1_svc(struct rpctlscd_disconnect_arg *argp,
+    void *result, struct svc_req *rqstp)
+{
+	struct ssl_entry *slp;
+
+	slp = NULL;
+	if (argp->sec == rpctls_ssl_sec && argp->usec ==
+	    rpctls_ssl_usec) {
+		LIST_FOREACH(slp, &rpctls_ssllist, next) {
+			if (slp->refno == argp->ssl)
+				break;
+		}
+	}
+
+	if (slp != NULL) {
+		rpctlscd_verbose_out("rpctlscd_disconnect: fd=%d closed\n",
+		    slp->s);
+		LIST_REMOVE(slp, next);
+		SSL_shutdown(slp->ssl);
+		SSL_free(slp->ssl);
+		/*
+		 * For RPC-over-TLS, this upcall is expected
+		 * to close off the socket.
+		 */
+		shutdown(slp->s, SHUT_WR);
+		close(slp->s);
+		free(slp);
+	} else
+		return (FALSE);
+	return (TRUE);
+}
+
 int
 rpctlscd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
 {
@@ -298,6 +402,8 @@ rpctls_setupcl_ssl(bool cert)
 	size_t len, rlen;
 	int ret;
 
+	SSL_library_init();
+	SSL_load_error_strings();
 	OpenSSL_add_all_algorithms();
 
 	ctx = SSL_CTX_new(TLS_client_method());
@@ -309,6 +415,18 @@ rpctls_setupcl_ssl(bool cert)
 	SSL_CTX_set_ecdh_auto(ctx, 1);
 
 	/*
+	 * Set preferred ciphers, since KERN_TLS only supports a
+	 * few of them.
+	 */
+	ret = SSL_CTX_set_cipher_list(ctx, _PREFERRED_CIPHERS);
+	if (ret == 0) {
+		rpctlscd_verbose_out("rpctls_setupcl_ssl: "
+		    "SSL_CTX_set_cipher_list failed to set any ciphers\n");
+		SSL_CTX_free(ctx);
+		return (NULL);
+	}
+
+	/*
 	 * If cert is true, a certificate and key exists in
 	 * rpctls_certdir, so that it can do mutual authentication.
 	 */
@@ -341,15 +459,32 @@ rpctls_setupcl_ssl(bool cert)
 			return (NULL);
 		}
 	}
-	if (rpctls_verify_cafile != NULL) {
+	if (rpctls_verify_cafile != NULL || rpctls_verify_capath != NULL) {
+		if (rpctls_crlfile != NULL) {
+			ret = rpctls_loadfiles(ctx);
+			if (ret == 0) {
+				rpctlscd_verbose_out("rpctls_setup_ssl: "
+				    "Load CAfile, CRLfile failed\n");
+				SSL_CTX_free(ctx);
+				return (NULL);
+			}
+		}
 		ret = SSL_CTX_load_verify_locations(ctx,
-		    rpctls_verify_cafile, NULL);
+		    rpctls_verify_cafile, rpctls_verify_capath);
 		if (ret != 1) {
 			rpctlscd_verbose_out("rpctls_setupcl_ssl: "
 			    "Can't load verify locations\n");
 			SSL_CTX_free(ctx);
 			return (NULL);
 		}
+		/*
+		 * The man page says that the
+		 * SSL_CTX_set0_CA_list() call is not normally
+		 * needed, but I believe it is harmless.
+		 */
+		if (rpctls_verify_cafile != NULL)
+			SSL_CTX_set0_CA_list(ctx,
+			    SSL_load_client_CA_file(rpctls_verify_cafile));
 	}
 
 	/* RPC-over-TLS must use TLSv1.3. */
@@ -371,6 +506,13 @@ rpctls_connect(SSL_CTX *ctx, int s)
 	int ret;
 	char *cp;
 
+	if (rpctls_gothup) {
+		rpctls_gothup = false;
+		ret = rpctls_loadfiles(ctx);
+		if (ret == 0)
+			rpctlscd_verbose_out("rpctls_connect: Can't "
+			    "load CAfile, CRLfile\n");
+	}
 	ssl = SSL_new(ctx);
 	if (ssl == NULL) {
 		rpctlscd_verbose_out("rpctls_connect: "
@@ -400,6 +542,8 @@ rpctls_connect(SSL_CTX *ctx, int s)
 		SSL_free(ssl);
 		return (NULL);
 	}
+	cp = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
+	rpctlscd_verbose_out("rpctls_connect: cert issuerName=%s\n", cp);
 	cp = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
 	rpctlscd_verbose_out("rpctls_connect: cert subjectName=%s\n", cp);
 	ret = SSL_get_verify_result(ssl);
@@ -433,11 +577,8 @@ static int
 rpctls_checkhost(int s, X509 *cert)
 {
 	struct sockaddr *sad;
-	struct sockaddr_in *sin;
-	struct sockaddr_in6 *sin6;
 	struct sockaddr_storage ad;
-	char hostnam[NI_MAXHOST + 1], addrstr[INET6_ADDRSTRLEN + 1];
-	const char *cp;
+	char hostnam[NI_MAXHOST];
 	socklen_t slen;
 	int ret;
 
@@ -445,39 +586,72 @@ rpctls_checkhost(int s, X509 *cert)
 	slen = sizeof(ad);
 	if (getpeername(s, sad, &slen) < 0)
 		return (0);
-	switch (sad->sa_family) {
-	case AF_INET:
-		sin = (struct sockaddr_in *)sad;
-		cp = inet_ntop(sad->sa_family, &sin->sin_addr.s_addr,
-		    addrstr, sizeof(addrstr));
-		if (cp != NULL)
-			rpctlscd_verbose_out("rpctls_checkhost: "
-			    "peer ip %s\n", cp);
-		if (getnameinfo((const struct sockaddr *)sad,
-		    sizeof(struct sockaddr_in), hostnam,
-		    sizeof(hostnam), NULL, 0, NI_NAMEREQD) != 0)
-			return (0);
-		break;
-	case AF_INET6:
-		sin6 = (struct sockaddr_in6 *)sad;
-		cp = inet_ntop(sad->sa_family, &sin6->sin6_addr,
-		    addrstr, sizeof(addrstr));
-		if (cp != NULL)
-			rpctlscd_verbose_out("rpctls_checkhost: "
-			    "peer ip %s\n", cp);
-		if (getnameinfo((const struct sockaddr *)sad,
-		    sizeof(struct sockaddr_in6), hostnam,
-		    sizeof(hostnam), NULL, 0, NI_NAMEREQD) != 0)
-			return (0);
-		break;
-	default:
+	if (getnameinfo((const struct sockaddr *)sad,
+	    sad->sa_len, hostnam, sizeof(hostnam),
+	    NULL, 0, NI_NUMERICHOST) == 0)
+		rpctlscd_verbose_out("rpctls_checkhost: %s\n",
+		    hostnam);
+	if (getnameinfo((const struct sockaddr *)sad,
+	    sad->sa_len, hostnam, sizeof(hostnam),
+	    NULL, 0, NI_NAMEREQD) != 0)
 		return (0);
-	}
-	rpctlscd_verbose_out("rpctls_checkhost: hostname %s\n",
-	    hostnam);
+	rpctlscd_verbose_out("rpctls_checkhost: DNS %s\n", hostnam);
 	ret = X509_check_host(cert, hostnam, strlen(hostnam), 0, NULL);
-	rpctlscd_verbose_out("rpctls_checkhost: X509_check_host ret=%d\n",
-	    ret);
 	return (ret);
+}
+
+/*
+ * Load the CAfile (and optionally CRLfile) into the certificate
+ * verification store.
+ */
+static int
+rpctls_loadfiles(SSL_CTX *ctx)
+{
+	X509_STORE *certstore;
+	X509_LOOKUP *certlookup;
+	int ret;
+
+	if (rpctls_verify_cafile != NULL ||
+	    rpctls_verify_capath != NULL) {
+		if (rpctls_crlfile != NULL) {
+			certstore = SSL_CTX_get_cert_store(ctx);
+			certlookup = X509_STORE_add_lookup(
+			    certstore, X509_LOOKUP_file());
+			ret = 0;
+			if (certlookup != NULL)
+				ret = X509_load_crl_file(certlookup,
+				    rpctls_crlfile, X509_FILETYPE_PEM);
+			if (ret != 0)
+				ret = X509_STORE_set_flags(certstore,
+				    X509_V_FLAG_CRL_CHECK |
+				    X509_V_FLAG_CRL_CHECK_ALL);
+			if (ret == 0) {
+				rpctlscd_verbose_out(
+				    "rpctls_loadfiles: Can't"
+				    " load CRLfile=%s\n",
+				    rpctls_crlfile);
+				return (ret);
+			}
+		}
+		ret = SSL_CTX_load_verify_locations(ctx,
+		    rpctls_verify_cafile, rpctls_verify_capath);
+		if (ret == 0) {
+			rpctlscd_verbose_out("rpctls_loadfiles: "
+			    "Can't load verify locations\n");
+			return (ret);
+		}
+		if (rpctls_verify_cafile != NULL)
+			SSL_CTX_set_client_CA_list(ctx,
+			    SSL_load_client_CA_file(
+			    rpctls_verify_cafile));
+	}
+	return (1);
+}
+
+static void
+rpctls_huphandler(int sig __unused)
+{
+
+	rpctls_gothup = true;
 }
 

Modified: projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.8
==============================================================================
--- projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.8	Sun Mar 22 19:23:43 2020	(r359224)
+++ projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.8	Sun Mar 22 19:31:12 2020	(r359225)
@@ -26,7 +26,7 @@
 .\" $FreeBSD$
 .\"
 .\" Modified from gssd.8 for rpctlssd.8 by Rick Macklem.
-.Dd January 21, 2020
+.Dd March 11, 2020
 .Dt RPCTLSSD 8
 .Os
 .Sh NAME
@@ -34,21 +34,130 @@
 .Nd "Sun RPC over TLS Server Daemon"
 .Sh SYNOPSIS
 .Nm
+.Op Fl D Ar certdir
 .Op Fl d
+.Op Fl h
+.Op Fl l Ar CAfile
+.Op Fl m
+.Op Fl p Ar CApath
+.Op Fl r Ar CRLfile
 .Op Fl v
 .Sh DESCRIPTION
 The
 .Nm
 program provides support for the server side of the kernel Sun RPC over TLS
 implementation.
+This daemon must be running to allow the kernel RPC to perform the TLS
+handshake after a TCP client has sent the STARTTLS Null RPC request to
+the server.
+This is needed to support clients doing NFS over TLS.
+Note that the
+.Fl tls
+option in the
+.Xr exports 5
+file specifies that the client must use RPC over TLS and the
+.Fl tlscert
+option in the
+.Xr exports 5
+file specifies that the client must provide a certificate
+that verifies.
+For this latter case, the
+.Fl m
+and
+.Fl l
+options must be specified.
 .Pp
+Also, if the IP address used by the client cannot be trusted,
+the rules in
+.Xr exports 5
+cannot be applied safely.
+As such, the
+.Fl h
+option can be used along with
+.Fl m
+and
+.Fl l
+options to require that the client certificate have the correct
+Fully Qualified Domain Name in it.
+.Pp
+A certificate and associated key must exist in /etc/rpctlssd
+(or the ``certdir'' specified by the
+.Fl D
+option)
+in files named ``cert.pem'' and ``key.pem''.
+.Pp
 The options are as follows:
 .Bl -tag -width indent
+.It Fl D Ar certdir
+Use ``certdir'' instead of /etc/rpctlssd as the location for the
+certificate in a file called ``cert.pem'' and key in ``key.pem''.
 .It Fl d
 Run in debug mode.
 In this mode,
 .Nm
 will not fork when it starts.
+.It Fl h
+This option specifies that the client must provide a certificate
+that both verifies and has the Fully Qualified Domain Name (FQDN) for
+the IP address that the client uses to connect to the server
+in either the subjectAltName or commonName field of the
+certificate.
+With this option, a failure to verify the client certificate
+or find the FQDN in the certificate will result in the
+server sending AUTH_REJECTEDCRED replies to all client RPCs.
+This option requires the
+.Fl m
+and
+.Fl l
+options.
+.It Fl l Ar CAfile
+This option specifies the path name of a CA certificate(s) file
+in pem format, which is used to verify client certificates and to
+set the list of CA(s) sent to the client so that it knows which
+certificate to send to the server during the TLS handshake.
+This path name is used in
+.Dq SSL_CTX_load_verify_locations(ctx,CAfile,NULL)
+and
+.Dq SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file(CAfile))
+openssl calls.
+Note that this is a path name for the file and is not assumed to be
+in ``certdir''.
+This option should be specified when the
+.Fl m
+option is specified so that the daemon can verify the client's
+certificate.
+.It Fl m
+This option specifies that the server is to request a certificate
+from the client during the TLS handshake.
+It does not require that the client provide a certificate.
+It should be specified unless no client doing RPC over TLS is
+required to have a certificate.
+For NFS, the export option
+.Fl tlscert
+will be used to require a client to provide a certificate
+that verifies.
+.It Fl p Ar CApath
+This option is similar to the
+.Fl l
+option, but specifies the path of a directory with CA
+certificates in it.
+When this option is used,
+.Dq SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file())
+is not called, so a list of CA names might not be passed
+to the client during the TLS handshake.
+(I was not able to determine if/when this matters, but
+if in doubt, use the
+.Fl l
+option instead of this option.)
+.It Fl r Ar CRLfile
+This option specifies a Certificate Revocation List (CRL) file
+that is to be loaded into the verify certificate store and
+checked during verification.
+This option is meaningless unless either the
+.Fl l
+or
+.Fl p
+have been specified.
 .It Fl v
 Run in verbose mode.
 In this mode,
@@ -61,16 +170,20 @@ option has also been specified.
 .Sh EXIT STATUS
 .Ex -std
 .Sh SEE ALSO
-.Xr openssl 3 ,
+.Xr openssl 1 ,
 .Xr syslog 3 ,
+.Xr exports 5 ,
 .Xr mount_nfs 8 ,
 .Xr rpctlscd 8
+.Sh BUGS
+This daemon cannot be safely shut down and restarted if there are
+any active RPC-over-TLS connections.
+Doing so will orphan the KERNEL_TLS connections, so that they
+can no longer do upcalls successfully, since the
+.Dq SSL *
+structures in userspace have been lost.
 .Sh HISTORY
 The
 .Nm
 manual page first appeared in
 .Fx 13.0 .
-.Sh AUTHORS
-This
-manual page was adapted from a manual page for the gssd daemon written by
-.An Doug Rabson Aq Mt dfr at FreeBSD.org .


More information about the svn-src-projects mailing list