Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 16 Sep 2020 23:36:37 +0000 (UTC)
From:      Rick Macklem <rmacklem@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r365823 - projects/nfs-over-tls/usr.sbin/rpc.tlsclntd
Message-ID:  <202009162336.08GNabTu007673@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: rmacklem
Date: Wed Sep 16 23:36:37 2020
New Revision: 365823
URL: https://svnweb.freebsd.org/changeset/base/365823

Log:
  Add support for checking extant connections when the CRL is reloaded and
  shutting down any connections where the certificate has been revoked.
  
  This is probably a rare use of the client (usually it will not be checking
  the NFS server's certificate and maintaining a CRL in the client), but I
  cloned the code in rpc.tlsservd.c to do this the same way.

Modified:
  projects/nfs-over-tls/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.c

Modified: projects/nfs-over-tls/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.c
==============================================================================
--- projects/nfs-over-tls/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.c	Wed Sep 16 23:30:57 2020	(r365822)
+++ projects/nfs-over-tls/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.c	Wed Sep 16 23:36:37 2020	(r365823)
@@ -77,6 +77,12 @@ __FBSDID("$FreeBSD$");
 #define	_PREFERRED_CIPHERS	"AES128-GCM-SHA256"
 #endif
 
+/*
+ * How long to delay a reload of the CRL when there are RPC request(s)
+ * to process, in usec.  Must be less than 1second.
+ */
+#define	RELOADDELAY	250000
+
 static struct pidfh	*rpctls_pfh = NULL;
 static int		rpctls_debug_level;
 static bool		rpctls_verbose;
@@ -101,18 +107,22 @@ struct ssl_entry {
 	LIST_ENTRY(ssl_entry)	next;
 	uint64_t		refno;
 	int			s;
+	bool			shutoff;
 	SSL			*ssl;
+	X509			*cert;
 };
 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 SSL		*rpctls_connect(SSL_CTX *ctx, int s, X509 **certp);
 static int		rpctls_gethost(int s, struct sockaddr *sad,
 			    char *hostip, size_t hostlen);
 static int		rpctls_checkhost(struct sockaddr *sad, X509 *cert);
 static int		rpctls_loadcrlfile(SSL_CTX *ctx);
 static void		rpctls_huphandler(int sig __unused);
+static void		rpctls_checkcrl(void);
+static void		rpctlscd_verbose_out(const char *fmt, ...);
 
 extern void rpctlscd_1(struct svc_req *rqstp, SVCXPRT *transp);
 
@@ -136,12 +146,16 @@ main(int argc, char **argv)
 	 * TLS handshake.
 	 */
 	struct sockaddr_un sun;
-	int fd, oldmask, ch;
+	int ch, fd, oldmask, ret;
 	SVCXPRT *xprt;
 	bool cert;
 	struct timeval tm;
 	struct timezone tz;
 	pid_t otherpid;
+	fd_set readfds;
+	uint64_t curtime, nexttime;
+	struct timespec tp;
+	sigset_t sighup_mask;
 
 	/* Check that another rpctlscd isn't already running. */
 	rpctls_pfh = pidfile_open(_PATH_RPCTLSCDPID, 0600, &otherpid);
@@ -290,7 +304,62 @@ main(int argc, char **argv)
 	}
 
 	rpctls_syscall(RPCTLS_SYSC_CLSETPATH, _PATH_RPCTLSCDSOCK);
-	svc_run();
+
+	/* Expand svc_run() here so that we can call rpctls_loadcrlfile(). */
+	curtime = nexttime = 0;
+	sigemptyset(&sighup_mask);
+	sigaddset(&sighup_mask, SIGHUP);
+	for (;;) {
+		clock_gettime(CLOCK_MONOTONIC, &tp);
+		curtime = tp.tv_sec;
+		curtime = curtime * 1000000 + tp.tv_nsec / 1000;
+		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
+		if (rpctls_gothup && curtime >= nexttime) {
+			rpctls_gothup = false;
+			sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+			ret = rpctls_loadcrlfile(rpctls_ctx);
+			if (ret != 0)
+				rpctls_checkcrl();
+			else
+				rpctlscd_verbose_out("rpc.tlsclntd: Can't "
+				    "reload CRLfile\n");
+			clock_gettime(CLOCK_MONOTONIC, &tp);
+			nexttime = tp.tv_sec;
+			nexttime = nexttime * 1000000 + tp.tv_nsec / 1000 +
+			    RELOADDELAY;
+		} else
+			sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+
+		/*
+		 * If a reload is pending, poll for received request(s),
+		 * otherwise set a RELOADDELAY timeout, since a SIGHUP
+		 * could be processed between the got_sighup test and
+		 * the select() system call.
+		 */
+		tm.tv_sec = 0;
+		if (rpctls_gothup)
+			tm.tv_usec = 0;
+		else
+			tm.tv_usec = RELOADDELAY;
+		readfds = svc_fdset;
+		switch (select(svc_maxfd + 1, &readfds, NULL, NULL, &tm)) {
+		case -1:
+			if (errno == EINTR) {
+				/* Allow a reload now. */
+				nexttime = 0;
+				continue;
+			}
+			syslog(LOG_ERR, "rpc.tlsservd died: select: %m");
+			exit(1);
+		case 0:
+			/* Allow a reload now. */
+			nexttime = 0;
+			continue;
+		default:
+			svc_getreqset(&readfds);
+		}
+	}
+
 	rpctls_syscall(RPCTLS_SYSC_CLSHUTDOWN, "");
 
 	SSL_CTX_free(rpctls_ctx);
@@ -331,6 +400,7 @@ rpctlscd_connect_1_svc(void *argp,
 	char buf[1024];
 	ssize_t siz, ret;
 	struct ssl_entry *newslp;
+	X509 *cert;
 
 	rpctlscd_verbose_out("rpctlsd_connect: started\n");
 	/* Get the socket fd from the kernel. */
@@ -342,7 +412,7 @@ rpctlscd_verbose_out("rpctlsd_connect s=%d\n", s);
 	}
 
 	/* Do a TLS connect handshake. */
-	ssl = rpctls_connect(rpctls_ctx, s);
+	ssl = rpctls_connect(rpctls_ctx, s, &cert);
 	if (ssl == NULL) {
 		rpctlscd_verbose_out("rpctlsd_connect: can't do TLS "
 		    "handshake\n");
@@ -370,7 +440,9 @@ rpctlscd_verbose_out("rpctlsd_connect s=%d\n", s);
 	newslp = malloc(sizeof(*newslp));
 	newslp->refno = rpctls_ssl_refno;
 	newslp->s = s;
+	newslp->shutoff = false;
 	newslp->ssl = ssl;
+	newslp->cert = cert;
 	LIST_INSERT_HEAD(&rpctls_ssllist, newslp, next);
 	return (TRUE);
 }
@@ -441,20 +513,25 @@ rpctlscd_verbose_out("disconnect refno=%jx\n", (uintma
 		rpctlscd_verbose_out("rpctlscd_disconnect: fd=%d closed\n",
 		    slp->s);
 		LIST_REMOVE(slp, next);
-		ret = SSL_get_shutdown(slp->ssl);
+		if (!slp->shutoff) {
+			ret = SSL_get_shutdown(slp->ssl);
 rpctlscd_verbose_out("get_shutdown0=%d\n", ret);
-		/*
-		 * Do an SSL_shutdown() unless a close alert has
-		 * already been sent.
-		 */
-		if ((ret & SSL_SENT_SHUTDOWN) == 0)
-			SSL_shutdown(slp->ssl);
+			/*
+			 * Do an SSL_shutdown() unless a close alert has
+			 * already been sent.
+			 */
+			if ((ret & SSL_SENT_SHUTDOWN) == 0)
+				SSL_shutdown(slp->ssl);
+		}
 		SSL_free(slp->ssl);
+		if (slp->cert != NULL)
+			X509_free(slp->cert);
 		/*
 		 * For RPC-over-TLS, this upcall is expected
 		 * to close off the socket.
 		 */
-		shutdown(slp->s, SHUT_WR);
+		if (!slp->shutoff)
+			shutdown(slp->s, SHUT_WR);
 		close(slp->s);
 		free(slp);
 		result->reterr = RPCTLSERR_OK;
@@ -595,7 +672,7 @@ rpctls_setupcl_ssl(bool cert)
 }
 
 static SSL *
-rpctls_connect(SSL_CTX *ctx, int s)
+rpctls_connect(SSL_CTX *ctx, int s, X509 **certp)
 {
 	SSL *ssl;
 	X509 *cert;
@@ -605,13 +682,7 @@ rpctls_connect(SSL_CTX *ctx, int s)
 	int gethostret, ret;
 	char *cp, *cp2;
 
-	if (rpctls_gothup) {
-		rpctls_gothup = false;
-		ret = rpctls_loadcrlfile(ctx);
-		if (ret == 0)
-			rpctlscd_verbose_out("rpctls_connect: Can't "
-			    "reload CRLfile\n");
-	}
+	*certp = NULL;
 	ssl = SSL_new(ctx);
 	if (ssl == NULL) {
 		rpctlscd_verbose_out("rpctls_connect: "
@@ -650,7 +721,6 @@ rpctlscd_verbose_out("aft SSL_connect ret=%d\n", ret);
 	    rpctls_verify_capath != NULL) && (gethostret == 0 ||
 	    rpctls_checkhost(sad, cert) != 1))
 		ret = X509_V_ERR_HOSTNAME_MISMATCH;
-	X509_free(cert);
 	if (ret != X509_V_OK && (rpctls_verify_cafile != NULL ||
 	    rpctls_verify_capath != NULL)) {
 		if (ret != X509_V_OK) {
@@ -671,6 +741,7 @@ rpctlscd_verbose_out("aft SSL_connect ret=%d\n", ret);
 				    "failed %s\n", hostnam, cp, cp2,
 				    X509_verify_cert_error_string(ret));
 		}
+		X509_free(cert);
 		SSL_free(ssl);
 		return (NULL);
 	}
@@ -680,16 +751,23 @@ rpctlscd_verbose_out("aft SSL_connect ret=%d\n", ret);
 	rpctlscd_verbose_out("rpctls_connect: BIO_get_ktls_send=%d\n", ret);
 	if (ret != 0) {
 		ret = BIO_get_ktls_recv(SSL_get_rbio(ssl));
-		rpctlscd_verbose_out("rpctls_connect: BIO_get_ktls_recv=%d\n", ret);
+		rpctlscd_verbose_out("rpctls_connect: BIO_get_ktls_recv=%d\n",
+		    ret);
 	}
 	if (ret == 0) {
 		if (rpctls_debug_level == 0)
 			syslog(LOG_ERR, "ktls not working\n");
 		else
 			fprintf(stderr, "ktls not working\n");
+		X509_free(cert);
 		SSL_free(ssl);
 		return (NULL);
 	}
+	if (ret == X509_V_OK && (rpctls_verify_cafile != NULL ||
+	    rpctls_verify_capath != NULL) && rpctls_crlfile != NULL)
+		*certp = cert;
+	else
+		X509_free(cert);
 
 	return (ssl);
 }
@@ -781,3 +859,55 @@ rpctls_huphandler(int sig __unused)
 	rpctls_gothup = true;
 }
 
+/*
+ * Read the CRL file and check for any extant connections
+ * that might now be revoked.
+ */
+static void
+rpctls_checkcrl(void)
+{
+	struct ssl_entry *slp;
+	BIO *infile;
+	X509_CRL *crl;
+	X509_REVOKED *revoked;
+	int ret;
+
+	if (rpctls_crlfile == NULL || (rpctls_verify_cafile == NULL &&
+	    rpctls_verify_capath == NULL))
+		return;
+	infile = BIO_new(BIO_s_file());
+	if (infile == NULL) {
+		rpctlscd_verbose_out("rpctls_checkcrl: Cannot BIO_new\n");
+		return;
+	}
+	ret = BIO_read_filename(infile, rpctls_crlfile);
+	if (ret != 1) {
+		rpctlscd_verbose_out("rpctls_checkcrl: Cannot read CRL file\n");
+		BIO_free(infile);
+		return;
+	}
+
+	for (crl = PEM_read_bio_X509_CRL(infile, NULL, NULL, "");
+	    crl != NULL; crl = PEM_read_bio_X509_CRL(infile, NULL, NULL, "")) {
+		LIST_FOREACH(slp, &rpctls_ssllist, next) {
+			if (slp->cert != NULL) {
+				ret = X509_CRL_get0_by_cert(crl, &revoked,
+				    slp->cert);
+rpctlscd_verbose_out("get0_by_cert=%d\n", ret);
+				/*
+				 * Do a shutdown on the socket, so that it
+				 * can no longer be used.  The kernel RPC
+				 * code will notice the socket is disabled
+				 * and will do a disconnect upcall, which will
+				 * close the socket.
+				 */
+				if (ret == 1) {
+					shutdown(slp->s, SHUT_WR);
+					slp->shutoff = true;
+				}
+			}
+		}
+		X509_CRL_free(crl);
+	}
+	BIO_free(infile);
+}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202009162336.08GNabTu007673>