svn commit: r359224 - projects/nfs-over-tls/usr.sbin/rpctlssd

Rick Macklem rmacklem at FreeBSD.org
Sun Mar 22 19:23:45 UTC 2020


Author: rmacklem
Date: Sun Mar 22 19:23:43 2020
New Revision: 359224
URL: https://svnweb.freebsd.org/changeset/base/359224

Log:
  Update the rpctlssd daemon in various ways, including support for
  upcalls to TLS connections already established. It maintains a linked
  list of current TLS connections referenced by a uint64_t refno.
  There is now support for CRL file (but it doesn't yet actually
  cause the verification of a client certificate to be checked for
  revocation for some reason).
  The CAfile and CRLfile can be reloaded via SIGHUP so that the
  daemon does not need to be restarted when they are updated.

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

Modified: projects/nfs-over-tls/usr.sbin/rpctlssd/Makefile
==============================================================================
--- projects/nfs-over-tls/usr.sbin/rpctlssd/Makefile	Sun Mar 22 19:01:43 2020	(r359223)
+++ projects/nfs-over-tls/usr.sbin/rpctlssd/Makefile	Sun Mar 22 19:23:43 2020	(r359224)
@@ -7,7 +7,6 @@ MAN=	rpctlssd.8
 SRCS=	rpctlssd.c rpctlssd.h rpctlssd_svc.c rpctlssd_xdr.c
 
 CFLAGS+= -I.
-WARNS?= 1
 
 LIBADD=	ssl crypto
 

Modified: projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.c
==============================================================================
--- projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.c	Sun Mar 22 19:01:43 2020	(r359223)
+++ projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.c	Sun Mar 22 19:23:43 2020	(r359224)
@@ -34,9 +34,12 @@ __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/types.h>
-#include <sys/socket.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/queue.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>
@@ -67,22 +68,47 @@ __FBSDID("$FreeBSD$");
 #ifndef	_PATH_CERTANDKEY
 #define	_PATH_CERTANDKEY	"/etc/rpctlssd/"
 #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 bool		rpctls_do_mutual = false;
 static const char	*rpctls_verify_cafile = NULL;
-static const char	*rpctls_client_cafiles = NULL;
+static const char	*rpctls_verify_capath = NULL;
+static const char	*rpctls_crlfile = NULL;
 static const char	*rpctls_certdir = _PATH_CERTANDKEY;
 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		rpctlssd_terminate(int);
 static SSL_CTX		*rpctls_setup_ssl(const char *certdir);
 static SSL		*rpctls_server(SSL_CTX *ctx, int s,
 			    uint32_t *flags);
 static int		rpctls_checkhost(int s, X509 *cert);
+static int		rpctls_loadfiles(SSL_CTX *ctx);
+static void		rpctls_huphandler(int sig __unused);
+static int cert_crl(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x);
 
 extern void rpctlssd_1(struct svc_req *rqstp, SVCXPRT *transp);
 extern int gssd_syscall(const char *path);
@@ -98,15 +124,19 @@ main(int argc, char **argv)
 	struct sockaddr_un sun;
 	int fd, oldmask, ch, debug;
 	SVCXPRT *xprt;
+	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;
+
+
 	debug = 0;
 	rpctls_verbose = false;
-	testnossl = 0;
-	while ((ch = getopt(argc, argv, "C:D:dhl:mtv")) != -1) {
+	while ((ch = getopt(argc, argv, "D:dhl:mp:rv")) != -1) {
 		switch (ch) {
-		case 'C':
-			rpctls_client_cafiles = optarg;
-			break;
 		case 'D':
 			rpctls_certdir = optarg;
 			break;
@@ -122,22 +152,39 @@ main(int argc, char **argv)
 		case 'm':
 			rpctls_do_mutual = true;
 			break;
-		case 't':
-			testnossl = 1;
+		case 'p':
+			rpctls_verify_capath = optarg;
 			break;
+		case 'r':
+			rpctls_crlfile = optarg;
+			break;
 		case 'v':
 			rpctls_verbose = true;
 			break;
 		default:
-			fprintf(stderr, "usage: %s [-C client_calist] "
+			fprintf(stderr, "usage: %s "
 			    "[-D certdir] [-d] [-h] "
-			    "[-l verify_locations_file] "
-			    "[-m] [-v]\n", argv[0]);
+			    "[-l CAfile] [-m] "
+			    "[-p CApath] [-r CRLfile] "
+			    "[-v]\n", argv[0]);
 			exit(1);
-			break;
 		}
 	}
+	if (rpctls_do_mutual && rpctls_verify_cafile == NULL &&
+	    rpctls_verify_capath == NULL)
+		errx(1, "-m requires the -l <CAfile> and/or "
+		    "-p <CApath> options");
+	if (rpctls_comparehost && (!rpctls_do_mutual ||
+	    (rpctls_verify_cafile == NULL && rpctls_verify_capath == NULL)))
+		errx(1, "-h requires the -m and either the "
+		    "-l <CAfile> or -p <CApath> options");
 
+	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 == 0) {
 		if (daemon(0, 0) != 0)
 			err(1, "Can't daemonize");
@@ -147,6 +194,7 @@ main(int argc, char **argv)
 	}
 	signal(SIGTERM, rpctlssd_terminate);
 	signal(SIGPIPE, rpctlssd_terminate);
+	signal(SIGHUP, rpctls_huphandler);
 
 	memset(&sun, 0, sizeof sun);
 	sun.sun_family = AF_LOCAL;
@@ -204,6 +252,8 @@ main(int argc, char **argv)
 		}
 		err(1, "Can't create SSL context");
 	}
+	rpctls_gothup = false;
+	LIST_INIT(&rpctls_ssllist);
 
 	gssd_syscall(_PATH_RPCTLSSDS);
 	svc_run();
@@ -244,6 +294,7 @@ rpctlssd_connect_1_svc(void *argp,
 	int s;
 	SSL *ssl;
 	uint32_t flags;
+	struct ssl_entry *newslp;
 
 	rpctlssd_verbose_out("rpctlsd_connect_svc: started\n");
 	memset(result, 0, sizeof(*result));
@@ -253,22 +304,69 @@ rpctlssd_verbose_out("rpctlsd_connect_svc s=%d\n", s);
 	if (s < 0)
 		return (FALSE);
 
-	if (testnossl == 0) {
-		/* Do the server side of a TLS handshake. */
-		ssl = rpctls_server(rpctls_ctx, s, &flags);
-		if (ssl == NULL)
-			rpctlssd_verbose_out("rpctlssd_connect_svc: ssl "
-			    "accept failed\n");
-		else {
-			rpctlssd_verbose_out("rpctlssd_connect_svc: "
-			    "succeeded flags=0x%x\n", flags);
-			result->flags = flags;
+	/* Do the server side of a TLS handshake. */
+	ssl = rpctls_server(rpctls_ctx, s, &flags);
+	if (ssl == NULL)
+		rpctlssd_verbose_out("rpctlssd_connect_svc: ssl "
+		    "accept failed\n");
+	else {
+		rpctlssd_verbose_out("rpctlssd_connect_svc: "
+		    "succeeded flags=0x%x\n", flags);
+		result->flags = flags;
+		result->sec = rpctls_ssl_sec;
+		result->usec = rpctls_ssl_usec;
+		result->ssl = ++rpctls_ssl_refno;
+		/* Hard to believe this could ever wrap around.. */
+		if (rpctls_ssl_refno == 0)
+			result->ssl = ++rpctls_ssl_refno;
+	}
+
+	if (ssl == NULL) {
+		/*
+		 * For RPC-over-TLS, this upcall is expected
+		 * to close off the socket.
+		 */
+		close(s);
+		return (FALSE);
+	}
+
+	/* Maintain list of all current SSL *'s */
+	newslp = malloc(sizeof(*newslp));
+	newslp->ssl = ssl;
+	newslp->s = s;
+	newslp->refno = rpctls_ssl_refno;
+	LIST_INSERT_HEAD(&rpctls_ssllist, newslp, next);
+	return (TRUE);
+}
+
+bool_t
+rpctlssd_disconnect_1_svc(struct rpctlssd_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;
 		}
 	}
 
-	/* Done with socket fd, so let the kernel know. */
-	gssd_syscall("F");
-	if (testnossl == 0 && ssl == NULL)
+	if (slp != NULL) {
+		rpctlssd_verbose_out("rpctlssd_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.
+		 */
+		close(slp->s);
+		free(slp);
+	} else
 		return (FALSE);
 	return (TRUE);
 }
@@ -292,7 +390,12 @@ rpctlssd_terminate(int sig __unused)
 static int
 rpctls_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
 {
+	int err;
+	char *cp;
 
+	err = X509_STORE_CTX_get_error(x509_ctx);
+	cp = (char *)X509_STORE_CTX_get_cert_crl(x509_ctx);
+rpctlssd_verbose_out("verf cb pre=%d err=%d cercrl=%p\n", preverify_ok, err, cp);
 	return (1);
 }
 
@@ -304,6 +407,8 @@ rpctls_setup_ssl(const char *certdir)
 	size_t len, rlen;
 	int ret;
 
+	SSL_library_init();
+	SSL_load_error_strings();
 	OpenSSL_add_all_algorithms();
 
 	ctx = SSL_CTX_new(TLS_server_method());
@@ -313,6 +418,18 @@ rpctls_setup_ssl(const char *certdir)
 	}
 	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) {
+		rpctlssd_verbose_out("rpctls_setup_ssl: "
+		    "SSL_CTX_set_cipher_list failed to set any ciphers\n");
+		SSL_CTX_free(ctx);
+		return (NULL);
+	}
+
 	/* Get the cert.pem and key.pem files from the directory certdir. */
 	len = strlcpy(path, certdir, sizeof(path));
 	rlen = sizeof(path) - len;
@@ -342,19 +459,13 @@ rpctls_setup_ssl(const char *certdir)
 	/* Set Mutual authentication, as required. */
 	if (rpctls_do_mutual) {
 		rpctlssd_verbose_out("rpctls_setup_ssl: set mutual\n");
-		if (rpctls_verify_cafile != NULL) {
-			ret = SSL_CTX_load_verify_locations(ctx,
-			    rpctls_verify_cafile, NULL);
-			if (ret != 1) {
-				rpctlssd_verbose_out("rpctls_setup_ssl: "
-				    "Can't load verify locations\n");
-				SSL_CTX_free(ctx);
-				return (NULL);
-			}
+		ret = rpctls_loadfiles(ctx);
+		if (ret == 0) {
+			rpctlssd_verbose_out("rpctls_setup_ssl: "
+			    "Load CAfile, CRLfile failed\n");
+			SSL_CTX_free(ctx);
+			return (NULL);
 		}
-		if (rpctls_client_cafiles != NULL)
-			SSL_CTX_set_client_CA_list(ctx,
-			    SSL_load_client_CA_file(rpctls_client_cafiles));
 		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER,
 		    rpctls_verify_callback);
 	}
@@ -370,6 +481,13 @@ rpctls_server(SSL_CTX *ctx, int s, uint32_t *flags)
 	char *cp;
 
 	*flags = 0;
+	if (rpctls_gothup) {
+		rpctls_gothup = false;
+		ret = rpctls_loadfiles(ctx);
+		if (ret == 0)
+			rpctlssd_verbose_out("rpctls_server: Can't "
+			    "load CAfile, CRLfile\n");
+	}
 	ssl = SSL_new(ctx);
 	if (ssl == NULL) {
 		rpctlssd_verbose_out("rpctls_server: SSL_new failed\n");
@@ -394,6 +512,10 @@ rpctls_server(SSL_CTX *ctx, int s, uint32_t *flags)
 			rpctlssd_verbose_out("rpctls_server: "
 			    "No peer certificate\n");
 		else {
+			cp = X509_NAME_oneline(X509_get_issuer_name(cert),
+			    NULL, 0);
+			rpctlssd_verbose_out("rpctls_server: cert "
+			    "issuerName=%s\n", cp);
 			cp = X509_NAME_oneline(X509_get_subject_name(cert),
 			    NULL, 0);
 			rpctlssd_verbose_out("rpctls_server: cert "
@@ -431,15 +553,12 @@ rpctls_server(SSL_CTX *ctx, int s, uint32_t *flags)
  * certificate.  Basically getpeername(2), getnameinfo(3) and
  * X509_check_host().
  */
-static int
+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;
 
@@ -447,39 +566,118 @@ 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)
-			rpctlssd_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)
-			rpctlssd_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)
+		rpctlssd_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);
-	}
-	rpctlssd_verbose_out("rpctls_checkhost: hostname %s\n",
+	rpctlssd_verbose_out("rpctls_checkhost: DNS %s\n",
 	    hostnam);
 	ret = X509_check_host(cert, hostnam, strlen(hostnam), 0, NULL);
-	rpctlssd_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)
+{
+X509_STORE_set_cert_crl(certstore, cert_crl);
+				X509_STORE_set_verify_cb_func(
+				    certstore, rpctls_verify_callback);
+}
+			if (ret == 0) {
+				rpctlssd_verbose_out(
+				    "rpctls_setup_ssl: 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) {
+			rpctlssd_verbose_out("rpctls_setup_ssl: "
+			    "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;
+}
+
+static int cert_crl(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x)
+{
+    X509_REVOKED *rev;
+    int ret;
+
+rpctlssd_verbose_out("in cert_crl\n");
+    /*
+     * The rules changed for this... previously if a CRL contained unhandled
+     * critical extensions it could still be used to indicate a certificate
+     * was revoked. This has since been changed since critical extensions can
+     * change the meaning of CRL entries.
+     */
+#ifdef notnow
+    if (!(ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL)
+        && (crl->flags & EXFLAG_CRITICAL) &&
+        !verify_cb_crl(ctx, X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION))
+        return 0;
+#endif
+    /*
+     * Look for serial number of certificate in CRL.  If found, make sure
+     * reason is not removeFromCRL.
+     */
+    ret = X509_CRL_get0_by_cert(crl, &rev, x);
+rpctlssd_verbose_out("get0 cert=%d\n", ret);
+    if (ret != 0) {
+#ifdef notnow
+        if (rev->reason == CRL_REASON_REMOVE_FROM_CRL)
+{ rpctls_verbose_out("ret 2\n");
+            return 2;
+}
+        if (!verify_cb_crl(ctx, X509_V_ERR_CERT_REVOKED))
+#endif
+rpctlssd_verbose_out("ret 0\n");
+            return 0;
+    }
+
+rpctlssd_verbose_out("ret 1\n");
+    return 1;
+}


More information about the svn-src-projects mailing list