svn commit: r365310 - projects/nfs-over-tls/usr.sbin/rpc.tlsclntd
Rick Macklem
rmacklem at FreeBSD.org
Thu Sep 3 21:52:50 UTC 2020
Author: rmacklem
Date: Thu Sep 3 21:52:49 2020
New Revision: 365310
URL: https://svnweb.freebsd.org/changeset/base/365310
Log:
Add the renamed client daemon. Also, add long options for those who like them.
Added:
projects/nfs-over-tls/usr.sbin/rpc.tlsclntd/
projects/nfs-over-tls/usr.sbin/rpc.tlsclntd/Makefile (contents, props changed)
projects/nfs-over-tls/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.8 (contents, props changed)
projects/nfs-over-tls/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.c (contents, props changed)
Added: projects/nfs-over-tls/usr.sbin/rpc.tlsclntd/Makefile
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ projects/nfs-over-tls/usr.sbin/rpc.tlsclntd/Makefile Thu Sep 3 21:52:49 2020 (r365310)
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+PROG= rpc.tlsclntd
+MAN= rpc.tlsclntd.8
+SRCS= rpc.tlsclntd.c rpctlscd.h rpctlscd_svc.c rpctlscd_xdr.c
+
+CFLAGS+= -I.
+
+CFLAGS+= -I/usr/ktls/include
+LDFLAGS+= -L/usr/ktls/lib
+
+LIBADD= ssl crypto util
+
+CLEANFILES= rpctlscd_svc.c rpctlscd_xdr.c rpctlscd.h
+
+RPCSRC= ${SRCTOP}/sys/rpc/rpcsec_tls/rpctlscd.x
+RPCGEN= RPCGEN_CPP=${CPP:Q} rpcgen -L -C -M
+
+rpctlscd_svc.c: ${RPCSRC} rpctlscd.h
+ ${RPCGEN} -m -o ${.TARGET} ${RPCSRC}
+
+rpctlscd_xdr.c: ${RPCSRC} rpctlscd.h
+ ${RPCGEN} -c -o ${.TARGET} ${RPCSRC}
+
+rpctlscd.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+.PATH: ${SRCTOP}/sys/rpc/rpcsec_tls
+
+.include <bsd.prog.mk>
Added: projects/nfs-over-tls/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.8
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ projects/nfs-over-tls/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.8 Thu Sep 3 21:52:49 2020 (r365310)
@@ -0,0 +1,176 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr at rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred at FreeBSD.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" Modified from gssd.8 for rpc.tlsclntd.8 by Rick Macklem.
+.Dd September 3, 2020
+.Dt RPC.TLSCLNTD 8
+.Os
+.Sh NAME
+.Nm rpc.tlsclntd
+.Nd "Sun RPC over TLS Client Daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl D Ar certdir
+.Op Fl d
+.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 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.
+This daemon requires that the kernel be built with
+.Dq options KERNEL_TLS
+and be running on an architecture such as
+.Dq amd64
+that supports a direct map (not i386).
+.Pp
+If either of the
+.Fl l
+or
+.Fl p
+options have been specified, the daemon will require the server's
+certificate to verify
+and have a Fully Qualified Domain Name (FQDN) in it.
+This FQDN must match
+the reverse DNS name for the IP address that
+the server is using for the TCP connection.
+The FQDN may be
+in either the DNS field of the subjectAltName or the CN field of the
+subjectName in the certificate and
+cannot have a wildcard
+.Dq *
+in it.
+.Pp
+If a SIGHUP signal is sent to the daemon it will reload the
+.Dq CRLfile .
+If the
+.Fl r
+option was not specified, the SIGHUP signal will be ignored.
+.Pp
+The daemon will log failed certificate verifications via
+.Xr syslogd 8
+using LOG_INFO | LOG_DAEMON when the
+.Fl l
+or
+.Fl p
+option has been specified.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl D Ar certdir , Fl Fl certdir= Ns Ar certdir
+Use
+.Dq certdir
+instead of /etc/rpc.tlsclntd for the
+.Fl m
+option.
+.It Fl d , Fl Fl debuglevel
+Run in debug mode.
+In this mode,
+.Nm
+will not fork when it starts.
+.It Fl l Ar CAfile , Fl Fl verifylocs= Ns 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))
+openssl library calls.
+Note that this is a path name for the file and is not assumed to be
+in
+.Dq certdir .
+.It Fl m , Fl Fl mutualverf
+Enable support for mutual authentication.
+A certificate and associated key must be found in /etc/rpc.tlsclntd
+(or the directory specified by the
+.Fl D
+option)
+in case a server requests a peer certificate.
+The certificate needs to be in a file named
+.Dq cert.pem
+and the key in a file named
+.Dq key.pem .
+If there is a passphrase on the
+.Dq key.pem
+file, this daemon will prompt for the passphrase during startup.
+.It Fl p Ar CApath , Fl Fl verifydir= Ns 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 is 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 , Fl Fl crl= Ns 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 of the server's certificate.
+This option is meaningless unless either the
+.Fl l
+or
+.Fl p
+have been specified.
+.It Fl v , Fl Fl verbose
+Run in verbose mode.
+In this mode,
+.Nm
+will log activity messages to syslog using LOG_INFO | LOG_DAEMON or to
+stderr, if the
+.Fl d
+option has also been specified.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr openssl 1 ,
+.Xr mount_nfs 8 ,
+.Xr rpc.tlsservd 8 ,
+.Xr syslogd 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 .
Added: projects/nfs-over-tls/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ projects/nfs-over-tls/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.c Thu Sep 3 21:52:49 2020 (r365310)
@@ -0,0 +1,783 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr at rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred at freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Modified from gssd.c for the client side of kernel RPC-over-TLS. */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.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 <getopt.h>
+#include <libutil.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+#include <rpc/rpc_com.h>
+#include <rpc/rpcsec_tls.h>
+
+#include <openssl/opensslconf.h>
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+
+#include "rpctlscd.h"
+
+#ifndef _PATH_RPCTLSCDSOCK
+#define _PATH_RPCTLSCDSOCK "/var/run/rpc.tlsclntd.sock"
+#endif
+#ifndef _PATH_CERTANDKEY
+#define _PATH_CERTANDKEY "/etc/rpc.tlsclntd/"
+#endif
+#ifndef _PATH_RPCTLSCDPID
+#define _PATH_RPCTLSCDPID "/var/run/rpc.tlsclntd.pid"
+#endif
+#ifndef _PREFERRED_CIPHERS
+#define _PREFERRED_CIPHERS "AES128-GCM-SHA256"
+#endif
+
+static struct pidfh *rpctls_pfh = NULL;
+static int rpctls_debug_level;
+static bool rpctls_verbose;
+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 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_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);
+
+extern void rpctlscd_1(struct svc_req *rqstp, SVCXPRT *transp);
+
+static struct option longopts[] = {
+ { "certdir", required_argument, NULL, 'D' },
+ { "debuglevel", no_argument, NULL, 'd' },
+ { "verifylocs", required_argument, NULL, 'l' },
+ { "mutualverf", no_argument, NULL, 'm' },
+ { "verifydir", required_argument, NULL, 'p' },
+ { "crl", required_argument, NULL, 'r' },
+ { "verbose", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0 }
+};
+
+int
+main(int argc, char **argv)
+{
+ /*
+ * We provide an RPC service on a local-domain socket. The
+ * kernel rpctls code will upcall to this daemon to do the initial
+ * TLS handshake.
+ */
+ struct sockaddr_un sun;
+ int fd, oldmask, ch;
+ SVCXPRT *xprt;
+ bool cert;
+ struct timeval tm;
+ struct timezone tz;
+ pid_t otherpid;
+
+ /* Check that another rpctlscd isn't already running. */
+ rpctls_pfh = pidfile_open(_PATH_RPCTLSCDPID, 0600, &otherpid);
+ if (rpctls_pfh == NULL) {
+ if (errno == EEXIST)
+ errx(1, "rpctlscd already running, pid: %d.", otherpid);
+ warn("cannot open or create pidfile");
+ }
+
+ if (modfind("ktls_ocf") < 0) {
+ /* Not present in kernel, try loading it */
+ if (kldload("ktls_ocf") < 0 || modfind("ktls_ocf") < 0)
+ errx(1, "Cannot load ktls_ocf");
+ }
+ if (modfind("aesni") < 0) {
+ /* Not present in kernel, try loading it */
+ kldload("aesni");
+ }
+
+ /* 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;
+ cert = false;
+ while ((ch = getopt_long(argc, argv, "D:dl:mp:r:v", longopts, NULL)) !=
+ -1) {
+ switch (ch) {
+ case 'D':
+ rpctls_certdir = optarg;
+ break;
+ case 'd':
+ rpctls_debug_level++;
+ break;
+ 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 'v':
+ rpctls_verbose = true;
+ break;
+ default:
+ fprintf(stderr, "usage: %s "
+ "[-D/--certdir certdir] [-d/--debuglevel] "
+ "[-l/--verifylocs CAfile] [-m/--mutualverf] "
+ "[-p/--verifydir CApath] [-r/--crl CRLfile] "
+ "[-v/--verbose]\n", argv[0]);
+ exit(1);
+ break;
+ }
+ }
+ if (rpctls_crlfile != NULL && rpctls_verify_cafile == NULL &&
+ rpctls_verify_capath == NULL)
+ errx(1, "-r requires the -l <CAfile> and/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");
+ }
+
+ /*
+ * Set up the SSL_CTX *.
+ * Do it now, before daemonizing, in case the private key
+ * is encrypted and requires a passphrase to be entered.
+ */
+ rpctls_ctx = rpctls_setupcl_ssl(cert);
+ if (rpctls_ctx == NULL) {
+ if (rpctls_debug_level == 0) {
+ syslog(LOG_ERR, "Can't set up TSL context");
+ exit(1);
+ }
+ err(1, "Can't set up TSL context");
+ }
+ LIST_INIT(&rpctls_ssllist);
+
+ if (!rpctls_debug_level) {
+ if (daemon(0, 0) != 0)
+ err(1, "Can't daemonize");
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ }
+ signal(SIGTERM, rpctlscd_terminate);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, rpctls_huphandler);
+
+ pidfile_write(rpctls_pfh);
+
+ memset(&sun, 0, sizeof sun);
+ sun.sun_family = AF_LOCAL;
+ unlink(_PATH_RPCTLSCDSOCK);
+ strcpy(sun.sun_path, _PATH_RPCTLSCDSOCK);
+ sun.sun_len = SUN_LEN(&sun);
+ fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (fd < 0) {
+ if (rpctls_debug_level == 0) {
+ syslog(LOG_ERR, "Can't create local rpctlscd socket");
+ exit(1);
+ }
+ err(1, "Can't create local rpctlscd socket");
+ }
+ oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
+ if (bind(fd, (struct sockaddr *)&sun, sun.sun_len) < 0) {
+ if (rpctls_debug_level == 0) {
+ syslog(LOG_ERR, "Can't bind local rpctlscd socket");
+ exit(1);
+ }
+ err(1, "Can't bind local rpctlscd socket");
+ }
+ umask(oldmask);
+ if (listen(fd, SOMAXCONN) < 0) {
+ if (rpctls_debug_level == 0) {
+ syslog(LOG_ERR,
+ "Can't listen on local rpctlscd socket");
+ exit(1);
+ }
+ err(1, "Can't listen on local rpctlscd socket");
+ }
+ xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
+ if (!xprt) {
+ if (rpctls_debug_level == 0) {
+ syslog(LOG_ERR,
+ "Can't create transport for local rpctlscd socket");
+ exit(1);
+ }
+ err(1, "Can't create transport for local rpctlscd socket");
+ }
+ if (!svc_reg(xprt, RPCTLSCD, RPCTLSCDVERS, rpctlscd_1, NULL)) {
+ if (rpctls_debug_level == 0) {
+ syslog(LOG_ERR,
+ "Can't register service for local rpctlscd socket");
+ exit(1);
+ }
+ err(1, "Can't register service for local rpctlscd socket");
+ }
+
+ rpctls_syscall(RPCTLS_SYSC_CLSETPATH, _PATH_RPCTLSCDSOCK);
+ svc_run();
+ rpctls_syscall(RPCTLS_SYSC_CLSHUTDOWN, "");
+
+ SSL_CTX_free(rpctls_ctx);
+ EVP_cleanup();
+ return (0);
+}
+
+static void
+rpctlscd_verbose_out(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (rpctls_verbose) {
+ va_start(ap, fmt);
+ if (rpctls_debug_level == 0)
+ vsyslog(LOG_INFO | LOG_DAEMON, fmt, ap);
+ else
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+}
+
+bool_t
+rpctlscd_null_1_svc(void *argp, void *result, struct svc_req *rqstp)
+{
+
+ rpctlscd_verbose_out("rpctlscd_null: done\n");
+ return (TRUE);
+}
+
+bool_t
+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. */
+ s = rpctls_syscall(RPCTLS_SYSC_CLSOCKET, "");
+rpctlscd_verbose_out("rpctlsd_connect s=%d\n", s);
+ if (s < 0) {
+ result->reterr = RPCTLSERR_NOSOCKET;
+ return (TRUE);
+ }
+
+ /* Do a TLS connect handshake. */
+ ssl = rpctls_connect(rpctls_ctx, s);
+ if (ssl == NULL) {
+ rpctlscd_verbose_out("rpctlsd_connect: can't do TLS "
+ "handshake\n");
+ result->reterr = RPCTLSERR_NOSSL;
+ } else {
+ result->reterr = RPCTLSERR_OK;
+ 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 (ssl == NULL) {
+ /*
+ * For RPC-over-TLS, this upcall is expected
+ * to close off the socket.
+ */
+ close(s);
+ return (TRUE);
+ }
+
+ /* 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_handlerecord_1_svc(struct rpctlscd_handlerecord_arg *argp,
+ struct rpctlscd_handlerecord_res *result, struct svc_req *rqstp)
+{
+ struct ssl_entry *slp;
+ int ret;
+ char junk;
+
+rpctlscd_verbose_out("handlerec sslref=%jx\n", (uintmax_t)slp->refno);
+ 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_handlerecord fd=%d\n",
+ slp->s);
+ /*
+ * An SSL_read() of 0 bytes should fail, but it should
+ * handle the non-application data record before doing so.
+ */
+ ret = SSL_read(slp->ssl, &junk, 0);
+ if (ret <= 0) {
+ /* Check to see if this was a close alert. */
+ ret = SSL_get_shutdown(slp->ssl);
+rpctlscd_verbose_out("get_shutdown2=%d\n", ret);
+ if ((ret & (SSL_SENT_SHUTDOWN |
+ SSL_RECEIVED_SHUTDOWN)) == SSL_RECEIVED_SHUTDOWN)
+ SSL_shutdown(slp->ssl);
+ } else {
+ if (rpctls_debug_level == 0)
+ syslog(LOG_ERR, "SSL_read returned %d", ret);
+ else
+ fprintf(stderr, "SSL_read returned %d\n", ret);
+ }
+ result->reterr = RPCTLSERR_OK;
+ } else
+ result->reterr = RPCTLSERR_NOSSL;
+ return (TRUE);
+}
+
+bool_t
+rpctlscd_disconnect_1_svc(struct rpctlscd_disconnect_arg *argp,
+ struct rpctlscd_disconnect_res *result, struct svc_req *rqstp)
+{
+ struct ssl_entry *slp;
+ int ret;
+
+rpctlscd_verbose_out("disconnect refno=%jx\n", (uintmax_t)slp->refno);
+ 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);
+ 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);
+ 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);
+ result->reterr = RPCTLSERR_OK;
+ } else
+ result->reterr = RPCTLSERR_NOCLOSE;
+ return (TRUE);
+}
+
+int
+rpctlscd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
+{
+
+ return (TRUE);
+}
+
+static void
+rpctlscd_terminate(int sig __unused)
+{
+
+ rpctls_syscall(RPCTLS_SYSC_CLSHUTDOWN, "");
+ pidfile_remove(rpctls_pfh);
+ exit(0);
+}
+
+static SSL_CTX *
+rpctls_setupcl_ssl(bool cert)
+{
+ SSL_CTX *ctx;
+ long flags;
+ char path[PATH_MAX];
+ size_t len, rlen;
+ int ret;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+ OpenSSL_add_all_algorithms();
+
+ ctx = SSL_CTX_new(TLS_client_method());
+ if (ctx == NULL) {
+ rpctlscd_verbose_out("rpctls_setupcl_ssl: SSL_CTX_new "
+ "failed\n");
+ return (NULL);
+ }
+ 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.
+ */
+ if (cert) {
+ /* Get the cert.pem and key.pem files. */
+ len = strlcpy(path, rpctls_certdir, sizeof(path));
+ rlen = sizeof(path) - len;
+ if (strlcpy(&path[len], "cert.pem", rlen) != 8) {
+ SSL_CTX_free(ctx);
+ return (NULL);
+ }
+ ret = SSL_CTX_use_certificate_file(ctx, path,
+ SSL_FILETYPE_PEM);
+ if (ret != 1) {
+ rpctlscd_verbose_out("rpctls_setupcl_ssl: can't use "
+ "certificate file path=%s ret=%d\n", path, ret);
+ SSL_CTX_free(ctx);
+ return (NULL);
+ }
+ if (strlcpy(&path[len], "key.pem", rlen) != 7) {
+ SSL_CTX_free(ctx);
+ return (NULL);
+ }
+ ret = SSL_CTX_use_PrivateKey_file(ctx, path,
+ SSL_FILETYPE_PEM);
+ if (ret != 1) {
+ rpctlscd_verbose_out("rpctls_setupcl_ssl: Can't use "
+ "private key path=%s ret=%d\n", path, ret);
+ SSL_CTX_free(ctx);
+ return (NULL);
+ }
+ }
+ if (rpctls_verify_cafile != NULL || rpctls_verify_capath != NULL) {
+ if (rpctls_crlfile != NULL) {
+ ret = rpctls_loadcrlfile(ctx);
+ if (ret == 0) {
+ rpctlscd_verbose_out("rpctls_setupcl_ssl: "
+ "Load CRLfile failed\n");
+ SSL_CTX_free(ctx);
+ return (NULL);
+ }
+ }
+#if OPENSSL_VERSION_NUMBER >= 0x30000000
+ ret = 1;
+ if (rpctls_verify_cafile != NULL)
+ ret = SSL_CTX_load_verify_file(ctx,
+ rpctls_verify_cafile);
+ if (ret != 0 && rpctls_verify_capath != NULL)
+ ret = SSL_CTX_load_verify_dir(ctx,
+ rpctls_verify_capath);
+#else
+ ret = SSL_CTX_load_verify_locations(ctx,
+ rpctls_verify_cafile, rpctls_verify_capath);
+#endif
+ if (ret == 0) {
+ 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. */
+#ifdef notyet
+ flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 |
+ SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2;
+#else
+ flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1_3;
+#endif
+ SSL_CTX_set_options(ctx, flags);
+ return (ctx);
+}
+
+static SSL *
+rpctls_connect(SSL_CTX *ctx, int s)
+{
+ SSL *ssl;
+ X509 *cert;
+ struct sockaddr *sad;
+ struct sockaddr_storage ad;
+ char hostnam[NI_MAXHOST];
+ 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");
+ }
+ ssl = SSL_new(ctx);
+ if (ssl == NULL) {
+ rpctlscd_verbose_out("rpctls_connect: "
+ "SSL_new failed\n");
+ return (NULL);
+ }
+ if (SSL_set_fd(ssl, s) != 1) {
+ rpctlscd_verbose_out("rpctls_connect: "
+ "SSL_set_fd failed\n");
+ SSL_free(ssl);
+ return (NULL);
+ }
+rpctlscd_verbose_out("at SSL_connect\n");
+ ret = SSL_connect(ssl);
+rpctlscd_verbose_out("aft SSL_connect ret=%d\n", ret);
+ if (ret != 1) {
+ rpctlscd_verbose_out("rpctls_connect: "
+ "SSL_connect failed %d\n",
+ ret);
+ SSL_free(ssl);
+ return (NULL);
+ }
+
+ cert = SSL_get_peer_certificate(ssl);
+ if (cert == NULL) {
+ rpctlscd_verbose_out("rpctls_connect: get peer"
+ " certificate failed\n");
+ SSL_free(ssl);
+ return (NULL);
+ }
+ gethostret = rpctls_gethost(s, sad, hostnam, sizeof(hostnam));
+ if (gethostret == 0)
+ hostnam[0] = '\0';
+ ret = SSL_get_verify_result(ssl);
+ if (ret == X509_V_OK && (rpctls_verify_cafile != NULL ||
+ 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) {
+ cp = X509_NAME_oneline(X509_get_issuer_name(cert),
+ NULL, 0);
+ cp2 = X509_NAME_oneline(X509_get_subject_name(cert),
+ NULL, 0);
+ if (rpctls_debug_level == 0)
+ syslog(LOG_INFO | LOG_DAEMON,
+ "rpctls_connect: client IP %s "
+ "issuerName=%s subjectName=%s verify "
+ "failed %s\n", hostnam, cp, cp2,
+ X509_verify_cert_error_string(ret));
+ else
+ fprintf(stderr,
+ "rpctls_connect: client IP %s "
+ "issuerName=%s subjectName=%s verify "
+ "failed %s\n", hostnam, cp, cp2,
+ X509_verify_cert_error_string(ret));
+ }
+ SSL_free(ssl);
+ return (NULL);
+ }
+
+ /* Check to see if ktls is enabled on the connection. */
+ ret = BIO_get_ktls_send(SSL_get_wbio(ssl));
+ 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);
+ }
+ if (ret == 0) {
+ if (rpctls_debug_level == 0)
+ syslog(LOG_ERR, "ktls not working\n");
+ else
+ fprintf(stderr, "ktls not working\n");
+ SSL_free(ssl);
+ return (NULL);
+ }
+
+ return (ssl);
+}
+
+/*
+ * Get the server's IP address.
+ */
+static int
+rpctls_gethost(int s, struct sockaddr *sad, char *hostip, size_t hostlen)
+{
+ socklen_t slen;
+ int ret;
+
+ slen = sizeof(struct sockaddr_storage);
+ if (getpeername(s, sad, &slen) < 0)
+ return (0);
+ ret = 0;
+ if (getnameinfo((const struct sockaddr *)sad,
+ sad->sa_len, hostip, hostlen,
+ NULL, 0, NI_NUMERICHOST) == 0) {
+ rpctlscd_verbose_out("rpctls_gethost: %s\n",
+ hostip);
+ ret = 1;
+ }
+ return (ret);
+}
+
+/*
+ * Check a server IP address against any host address in the
+ * certificate. Basically getnameinfo(3) and
+ * X509_check_host().
+ */
+static int
+rpctls_checkhost(struct sockaddr *sad, X509 *cert)
+{
+ char hostnam[NI_MAXHOST];
+ int ret;
+
+ if (getnameinfo((const struct sockaddr *)sad,
+ sad->sa_len, hostnam, sizeof(hostnam),
+ NULL, 0, NI_NAMEREQD) != 0)
+ return (0);
+ rpctlscd_verbose_out("rpctls_checkhost: DNS %s\n",
+ hostnam);
+ ret = X509_check_host(cert, hostnam, strlen(hostnam),
+ X509_CHECK_FLAG_NO_WILDCARDS, NULL);
+ return (ret);
+}
+
+/*
+ * (re)load the CRLfile into the certificate verification store.
+ */
+static int
+rpctls_loadcrlfile(SSL_CTX *ctx)
+{
+ X509_STORE *certstore;
+ X509_LOOKUP *certlookup;
+ int ret;
+
+ if ((rpctls_verify_cafile != NULL ||
+ rpctls_verify_capath != NULL) &&
+ 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_loadcrlfile: Can't"
+ " load CRLfile=%s\n",
+ rpctls_crlfile);
+ return (ret);
+ }
+ }
+ return (1);
+}
+
+static void
+rpctls_huphandler(int sig __unused)
+{
+
+ rpctls_gothup = true;
+}
+
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-projects
mailing list