svn commit: r365818 - projects/nfs-over-tls/usr.sbin/rpc.tlsservd
Rick Macklem
rmacklem at FreeBSD.org
Wed Sep 16 22:42:28 UTC 2020
Author: rmacklem
Date: Wed Sep 16 22:42:27 2020
New Revision: 365818
URL: https://svnweb.freebsd.org/changeset/base/365818
Log:
Add support to the rpc.tlsservd daemon for shutting down connections that
were verified with no longer valid certificates.
When SIGHUP is posted to rpc.tlsservd, the CRL file is reloaded if one
was specified when the daemon was started. This patch adds code that
also scans the extant connections after a reload and, for any that
presented a certificate that is no longer valid, shuts the connection down.
This required the code to be changed so that the reload would happen
right away, instead of waiting until the next TLS handshake.
Modified:
projects/nfs-over-tls/usr.sbin/rpc.tlsservd/rpc.tlsservd.c
Modified: projects/nfs-over-tls/usr.sbin/rpc.tlsservd/rpc.tlsservd.c
==============================================================================
--- projects/nfs-over-tls/usr.sbin/rpc.tlsservd/rpc.tlsservd.c Wed Sep 16 22:37:44 2020 (r365817)
+++ projects/nfs-over-tls/usr.sbin/rpc.tlsservd/rpc.tlsservd.c Wed Sep 16 22:42:27 2020 (r365818)
@@ -78,6 +78,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;
@@ -108,7 +114,9 @@ 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;
@@ -116,7 +124,7 @@ 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, uint32_t *uidp,
- int *ngrps, uint32_t *gidp);
+ int *ngrps, uint32_t *gidp, 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);
@@ -125,6 +133,8 @@ static int rpctls_cnname(X509 *cert, uint32_t *uidp,
int *ngrps, uint32_t *gidp);
static char *rpctls_getdnsname(char *dnsname);
static void rpctls_huphandler(int sig __unused);
+static void rpctls_checkcrl(void);
+static void rpctlssd_verbose_out(const char *fmt, ...);
extern void rpctlssd_1(struct svc_req *rqstp, SVCXPRT *transp);
@@ -153,12 +163,16 @@ main(int argc, char **argv)
* TLS handshake.
*/
struct sockaddr_un sun;
- int fd, oldmask, ch, debug;
+ int ch, debug, fd, oldmask, ret;
SVCXPRT *xprt;
struct timeval tm;
struct timezone tz;
char hostname[MAXHOSTNAMELEN + 2];
pid_t otherpid;
+ fd_set readfds;
+ uint64_t curtime, nexttime;
+ struct timespec tp;
+ sigset_t sighup_mask;
/* Check that another rpctlssd isn't already running. */
rpctls_pfh = pidfile_open(_PATH_RPCTLSSDPID, 0600, &otherpid);
@@ -347,7 +361,62 @@ fprintf(stderr, "dnsname=%s\n", rpctls_dnsname);
LIST_INIT(&rpctls_ssllist);
rpctls_syscall(RPCTLS_SYSC_SRVSETPATH, _PATH_RPCTLSSDSOCK);
- 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
+ rpctlssd_verbose_out("rpc.tlsservd: 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_SRVSHUTDOWN, "");
SSL_CTX_free(rpctls_ctx);
@@ -388,6 +457,7 @@ rpctlssd_connect_1_svc(void *argp,
struct ssl_entry *newslp;
uint32_t uid;
uint32_t *gidp;
+ X509 *cert;
rpctlssd_verbose_out("rpctlsd_connect_svc: started\n");
memset(result, 0, sizeof(*result));
@@ -399,7 +469,7 @@ rpctlssd_verbose_out("rpctlsd_connect_svc s=%d\n", s);
/* Do the server side of a TLS handshake. */
gidp = calloc(NGROUPS, sizeof(*gidp));
- ssl = rpctls_server(rpctls_ctx, s, &flags, &uid, &ngrps, gidp);
+ ssl = rpctls_server(rpctls_ctx, s, &flags, &uid, &ngrps, gidp, &cert);
if (ssl == NULL) {
free(gidp);
rpctlssd_verbose_out("rpctlssd_connect_svc: ssl "
@@ -435,7 +505,9 @@ rpctlssd_verbose_out("rpctlsd_connect_svc s=%d\n", s);
newslp = malloc(sizeof(*newslp));
newslp->ssl = ssl;
newslp->s = s;
+ newslp->shutoff = false;
newslp->refno = rpctls_ssl_refno;
+ newslp->cert = cert;
LIST_INSERT_HEAD(&rpctls_ssllist, newslp, next);
return (TRUE);
}
@@ -504,21 +576,25 @@ rpctlssd_disconnect_1_svc(struct rpctlssd_disconnect_a
rpctlssd_verbose_out("rpctlssd_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);
rpctlssd_verbose_out("get_shutdown1=%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);
/*
* 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->cert);
free(slp);
result->reterr = RPCTLSERR_OK;
} else
@@ -547,10 +623,6 @@ rpctlssd_terminate(int sig __unused)
rpctls_syscall(RPCTLS_SYSC_SRVSHUTDOWN, "");
pidfile_remove(rpctls_pfh);
- /*
- * Shut down all TCP connections, so that any compromised TLS
- * connection is no longer usable.
- */
LIST_FOREACH(slp, &rpctls_ssllist, next)
shutdown(slp->s, SHUT_RD);
exit(0);
@@ -665,7 +737,7 @@ rpctls_setup_ssl(const char *certdir)
static SSL *
rpctls_server(SSL_CTX *ctx, int s, uint32_t *flags, uint32_t *uidp,
- int *ngrps, uint32_t *gidp)
+ int *ngrps, uint32_t *gidp, X509 **certp)
{
SSL *ssl;
X509 *cert;
@@ -676,14 +748,8 @@ rpctls_server(SSL_CTX *ctx, int s, uint32_t *flags, ui
char *cp, *cp2;
*flags = 0;
+ *certp = NULL;
sad = (struct sockaddr *)&ad;
- if (rpctls_gothup) {
- rpctls_gothup = false;
- ret = rpctls_loadcrlfile(ctx);
- if (ret == 0)
- rpctlssd_verbose_out("rpctls_server: Can't "
- "reload CRLfile\n");
- }
ssl = SSL_new(ctx);
if (ssl == NULL) {
rpctlssd_verbose_out("rpctls_server: SSL_new failed\n");
@@ -758,8 +824,11 @@ rpctlssd_verbose_out("%s\n", cp2);
*flags |= RPCTLS_FLAGS_CERTUSER;
}
*flags |= RPCTLS_FLAGS_VERIFIED;
+ *certp = cert;
+ cert = NULL;
}
- X509_free(cert);
+ if (cert != NULL)
+ X509_free(cert);
} else
rpctlssd_verbose_out("rpctls_server: "
"No peer certificate\n");
@@ -991,3 +1060,52 @@ 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;
+
+ infile = BIO_new(BIO_s_file());
+ if (infile == NULL) {
+ rpctlssd_verbose_out("rpctls_checkcrl: Cannot BIO_new\n");
+ return;
+ }
+ ret = BIO_read_filename(infile, rpctls_crlfile);
+ if (ret != 1) {
+ rpctlssd_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);
+rpctlssd_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);
+}
More information about the svn-src-projects
mailing list