Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144456996
D28430.id83196.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
72 KB
Referenced Files
None
Subscribers
None
D28430.id83196.diff
View Options
diff --git a/usr.sbin/Makefile.sav b/usr.sbin/Makefile
--- a/usr.sbin/Makefile.sav
+++ b/usr.sbin/Makefile
@@ -105,6 +105,11 @@
zic \
zonectl
+.if ${MK_OPENSSL_KTLS} != "no"
+SUBDIR+= rpc.tlsclntd \
+ rpc.tlsservd
+.endif
+
# NB: keep these sorted by MK_* knobs
SUBDIR.${MK_ACCT}+= accton
diff --git a/usr.sbin/rpc.tlsclntd/Makefile.sav b/usr.sbin/rpc.tlsclntd/Makefile
--- a/usr.sbin/rpc.tlsclntd/Makefile.sav
+++ b/usr.sbin/rpc.tlsclntd/Makefile
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+PROG= rpc.tlsclntd
+MAN= rpc.tlsclntd.8
+SRCS= rpc.tlsclntd.c rpc.tlscommon.c rpctlscd.h rpctlscd_svc.c rpctlscd_xdr.c
+
+CFLAGS+= -I. -I${SRCTOP}/usr.sbin/rpc.tlsservd
+
+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 ${SRCTOP}/usr.sbin/rpc.tlsservd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.8.sav b/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.8
--- a/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.8.sav
+++ b/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.8
@@ -0,0 +1,190 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@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 January 29, 2021
+.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) with
+.Xr ktls 4
+enabled.
+.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
+and will shut down any extant connections that presented certificates
+during TLS handshake that have been revoked.
+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 first certificate needs to be in a file named
+.Dq cert.pem
+and the associated key in a file named
+.Dq certkey.pem .
+The
+.Xr mount_nfs 8
+option
+.Fl tlscertname
+can be used to override the default certificate for a given
+NFS mount, where the files use the alternate naming specified by the option.
+If there is a passphrase on the
+.Dq certkey.pem
+file, this daemon will prompt for the passphrase during startup.
+The keys for alternate certificates cannot have passphrases.
+.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.
+.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 ktls 4 ,
+.Xr mount_nfs 8 ,
+.Xr rpc.tlsservd 8 ,
+.Xr syslogd 8
+.Sh STANDARDS
+The implementation is based on the specification in
+.Rs
+.%B "RFC NNNN"
+.%T "Towards Remote Procedure Call Encryption By Default"
+.Re
+.Sh HISTORY
+The
+.Nm
+manual page first appeared in
+.Fx 13.0 .
+.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.
diff --git a/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.c.sav b/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.c
--- a/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.c.sav
+++ b/usr.sbin/rpc.tlsclntd/rpc.tlsclntd.c
@@ -0,0 +1,723 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr@rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred@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.
+ */
+
+/*
+ * Extensively modified from /usr/src/usr.sbin/gssd.c r344402 for
+ * the client side of kernel RPC-over-TLS by Rick Macklem.
+ */
+
+#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/sysctl.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"
+#include "rpc.tlscommon.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
+
+/* Global variables also used by rpc.tlscommon.c. */
+int rpctls_debug_level;
+bool rpctls_verbose;
+SSL_CTX *rpctls_ctx = NULL;
+const char *rpctls_verify_cafile = NULL;
+const char *rpctls_verify_capath = NULL;
+char *rpctls_crlfile = NULL;
+bool rpctls_cert = false;
+bool rpctls_gothup = false;
+struct ssl_list rpctls_ssllist;
+
+static struct pidfh *rpctls_pfh = 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 void rpctlscd_terminate(int);
+static SSL_CTX *rpctls_setupcl_ssl(void);
+static SSL *rpctls_connect(SSL_CTX *ctx, int s, char *certname,
+ u_int certlen, X509 **certp);
+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 ch, fd, oldmask;
+ SVCXPRT *xprt;
+ bool tls_enable;
+ struct timeval tm;
+ struct timezone tz;
+ pid_t otherpid;
+ size_t tls_enable_len;
+
+ /* 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");
+ }
+
+ /* Check to see that the ktls is enabled. */
+ tls_enable_len = sizeof(tls_enable);
+ if (sysctlbyname("kern.ipc.tls.enable", &tls_enable, &tls_enable_len,
+ NULL, 0) != 0 || !tls_enable)
+ errx(1, "Kernel TLS not enabled");
+
+ /* 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;
+ 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':
+ rpctls_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();
+ if (rpctls_ctx == NULL) {
+ if (rpctls_debug_level == 0) {
+ syslog(LOG_ERR, "Can't set up TLS context");
+ exit(1);
+ }
+ err(1, "Can't set up TLS 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);
+
+ rpctls_svc_run();
+
+ rpctls_syscall(RPCTLS_SYSC_CLSHUTDOWN, "");
+
+ SSL_CTX_free(rpctls_ctx);
+ EVP_cleanup();
+ return (0);
+}
+
+bool_t
+rpctlscd_null_1_svc(__unused void *argp, __unused void *result,
+ __unused struct svc_req *rqstp)
+{
+
+ rpctls_verbose_out("rpctlscd_null: done\n");
+ return (TRUE);
+}
+
+bool_t
+rpctlscd_connect_1_svc(struct rpctlscd_connect_arg *argp,
+ struct rpctlscd_connect_res *result, __unused struct svc_req *rqstp)
+{
+ int s;
+ SSL *ssl;
+ struct ssl_entry *newslp;
+ X509 *cert;
+
+ rpctls_verbose_out("rpctlsd_connect: started\n");
+ /* Get the socket fd from the kernel. */
+ s = rpctls_syscall(RPCTLS_SYSC_CLSOCKET, "");
+ if (s < 0) {
+ result->reterr = RPCTLSERR_NOSOCKET;
+ return (TRUE);
+ }
+
+ /* Do a TLS connect handshake. */
+ ssl = rpctls_connect(rpctls_ctx, s, argp->certname.certname_val,
+ argp->certname.certname_len, &cert);
+ if (ssl == NULL) {
+ rpctls_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->shutoff = false;
+ newslp->ssl = ssl;
+ newslp->cert = cert;
+ 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, __unused struct svc_req *rqstp)
+{
+ struct ssl_entry *slp;
+ int ret;
+ char junk;
+
+ 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) {
+ rpctls_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);
+ 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, __unused struct svc_req *rqstp)
+{
+ struct ssl_entry *slp;
+ int ret;
+
+ 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) {
+ rpctls_verbose_out("rpctlscd_disconnect: fd=%d closed\n",
+ slp->s);
+ LIST_REMOVE(slp, next);
+ if (!slp->shutoff) {
+ ret = SSL_get_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.
+ */
+ if (!slp->shutoff)
+ 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(__unused SVCXPRT *transp, __unused xdrproc_t xdr_result,
+ __unused 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(void)
+{
+ 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) {
+ rpctls_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) {
+ rpctls_verbose_out("rpctls_setupcl_ssl: "
+ "SSL_CTX_set_cipher_list failed to set any ciphers\n");
+ SSL_CTX_free(ctx);
+ return (NULL);
+ }
+
+ /*
+ * If rpctls_cert is true, a certificate and key exists in
+ * rpctls_certdir, so that it can do mutual authentication.
+ */
+ if (rpctls_cert) {
+ /* Get the cert.pem and certkey.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) {
+ rpctls_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], "certkey.pem", rlen) != 11) {
+ SSL_CTX_free(ctx);
+ return (NULL);
+ }
+ ret = SSL_CTX_use_PrivateKey_file(ctx, path,
+ SSL_FILETYPE_PEM);
+ if (ret != 1) {
+ rpctls_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) {
+ rpctls_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) {
+ rpctls_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, according to the IETF draft.*/
+#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);
+ SSL_CTX_clear_mode(ctx, SSL_MODE_NO_KTLS_TX | SSL_MODE_NO_KTLS_RX);
+ return (ctx);
+}
+
+static SSL *
+rpctls_connect(SSL_CTX *ctx, int s, char *certname, u_int certlen, X509 **certp)
+{
+ SSL *ssl;
+ X509 *cert;
+ struct sockaddr_storage ad;
+ struct sockaddr *sad;
+ char hostnam[NI_MAXHOST], path[PATH_MAX];
+ int gethostret, ret;
+ char *cp, *cp2;
+ size_t len, rlen;
+
+ *certp = NULL;
+ sad = (struct sockaddr *)&ad;
+ ssl = SSL_new(ctx);
+ if (ssl == NULL) {
+ rpctls_verbose_out("rpctls_connect: "
+ "SSL_new failed\n");
+ return (NULL);
+ }
+ if (SSL_set_fd(ssl, s) != 1) {
+ rpctls_verbose_out("rpctls_connect: "
+ "SSL_set_fd failed\n");
+ SSL_free(ssl);
+ return (NULL);
+ }
+
+ /*
+ * If rpctls_cert is true and certname is set, a alternate certificate
+ * and key exists in files named <certname>.pem and <certname>key.pem
+ * in rpctls_certdir that is to be used for mutual authentication.
+ */
+ if (rpctls_cert && certlen > 0) {
+ len = strlcpy(path, rpctls_certdir, sizeof(path));
+ rlen = sizeof(path) - len;
+ if (rlen <= certlen) {
+ SSL_free(ssl);
+ return (NULL);
+ }
+ memcpy(&path[len], certname, certlen);
+ rlen -= certlen;
+ len += certlen;
+ path[len] = '\0';
+ if (strlcpy(&path[len], ".pem", rlen) != 4) {
+ SSL_free(ssl);
+ return (NULL);
+ }
+ ret = SSL_use_certificate_file(ssl, path, SSL_FILETYPE_PEM);
+ if (ret != 1) {
+ rpctls_verbose_out("rpctls_connect: can't use "
+ "certificate file path=%s ret=%d\n", path, ret);
+ SSL_free(ssl);
+ return (NULL);
+ }
+ if (strlcpy(&path[len], "key.pem", rlen) != 7) {
+ SSL_free(ssl);
+ return (NULL);
+ }
+ ret = SSL_use_PrivateKey_file(ssl, path, SSL_FILETYPE_PEM);
+ if (ret != 1) {
+ rpctls_verbose_out("rpctls_connect: Can't use "
+ "private key path=%s ret=%d\n", path, ret);
+ SSL_free(ssl);
+ return (NULL);
+ }
+ }
+
+ ret = SSL_connect(ssl);
+ if (ret != 1) {
+ rpctls_verbose_out("rpctls_connect: "
+ "SSL_connect failed %d\n",
+ ret);
+ SSL_free(ssl);
+ return (NULL);
+ }
+
+ cert = SSL_get_peer_certificate(ssl);
+ if (cert == NULL) {
+ rpctls_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, X509_CHECK_FLAG_NO_WILDCARDS) != 1))
+ ret = X509_V_ERR_HOSTNAME_MISMATCH;
+ 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));
+ }
+ X509_free(cert);
+ SSL_free(ssl);
+ return (NULL);
+ }
+
+ /* Check to see if ktls is enabled on the connection. */
+ ret = BIO_get_ktls_send(SSL_get_wbio(ssl));
+ rpctls_verbose_out("rpctls_connect: BIO_get_ktls_send=%d\n", ret);
+ if (ret != 0) {
+ ret = BIO_get_ktls_recv(SSL_get_rbio(ssl));
+ rpctls_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);
+}
+
+static void
+rpctls_huphandler(int sig __unused)
+{
+
+ rpctls_gothup = true;
+}
diff --git a/usr.sbin/rpc.tlsservd/Makefile.sav b/usr.sbin/rpc.tlsservd/Makefile
--- a/usr.sbin/rpc.tlsservd/Makefile.sav
+++ b/usr.sbin/rpc.tlsservd/Makefile
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+PROG= rpc.tlsservd
+MAN= rpc.tlsservd.8
+SRCS= rpc.tlsservd.c rpc.tlscommon.c rpctlssd.h rpctlssd_svc.c rpctlssd_xdr.c
+
+CFLAGS+= -I.
+
+LIBADD= ssl crypto util
+
+CLEANFILES= rpctlssd_svc.c rpctlssd_xdr.c rpctlssd.h
+
+RPCSRC= ${SRCTOP}/sys/rpc/rpcsec_tls/rpctlssd.x
+RPCGEN= RPCGEN_CPP=${CPP:Q} rpcgen -L -C -M
+
+rpctlssd_svc.c: ${RPCSRC} rpctlssd.h
+ ${RPCGEN} -m -o ${.TARGET} ${RPCSRC}
+
+rpctlssd_xdr.c: ${RPCSRC} rpctlssd.h
+ ${RPCGEN} -c -o ${.TARGET} ${RPCSRC}
+
+rpctlssd.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+.PATH: ${SRCTOP}/sys/rpc/rpcsec_tls
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.tlsservd/rpc.tlscommon.h.sav b/usr.sbin/rpc.tlsservd/rpc.tlscommon.h
--- a/usr.sbin/rpc.tlsservd/rpc.tlscommon.h.sav
+++ b/usr.sbin/rpc.tlsservd/rpc.tlscommon.h
@@ -0,0 +1,68 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Rick Macklem
+ *
+ * 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$
+ */
+
+/*
+ * Functions in rpc.tlscommon.c used by both rpc.tlsservd.c and rpc.tlsclntd.c.
+ */
+int rpctls_gethost(int s, struct sockaddr *sad,
+ char *hostip, size_t hostlen);
+int rpctls_checkhost(struct sockaddr *sad, X509 *cert,
+ unsigned int wildcard);
+int rpctls_loadcrlfile(SSL_CTX *ctx);
+void rpctls_checkcrl(void);
+void rpctls_verbose_out(const char *fmt, ...);
+void rpctls_svc_run(void);
+
+/*
+ * 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;
+ bool shutoff;
+ SSL *ssl;
+ X509 *cert;
+};
+
+/* Global variables shared between rpc.tlscommon.c and the daemons. */
+extern int rpctls_debug_level;
+extern bool rpctls_verbose;
+extern SSL_CTX *rpctls_ctx;
+extern const char *rpctls_verify_cafile;
+extern const char *rpctls_verify_capath;
+extern char *rpctls_crlfile;
+extern bool rpctls_cert;
+extern bool rpctls_gothup;
+extern struct ssl_list rpctls_ssllist;
+
diff --git a/usr.sbin/rpc.tlsservd/rpc.tlscommon.c.sav b/usr.sbin/rpc.tlsservd/rpc.tlscommon.c
--- a/usr.sbin/rpc.tlsservd/rpc.tlscommon.c.sav
+++ b/usr.sbin/rpc.tlsservd/rpc.tlscommon.c
@@ -0,0 +1,295 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Rick Macklem
+ *
+ * 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.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/queue.h>
+#include <sys/syslog.h>
+#include <sys/select.h>
+#include <sys/time.h>
+
+#include <netdb.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <rpc/rpc.h>
+
+#include <openssl/opensslconf.h>
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+
+#include "rpc.tlscommon.h"
+
+/*
+ * 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
+
+void
+rpctls_svc_run(void)
+{
+ int ret;
+ struct timeval tv;
+ fd_set readfds;
+ uint64_t curtime, nexttime;
+ struct timespec tp;
+ sigset_t sighup_mask;
+
+ /* 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
+ rpctls_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.
+ */
+ tv.tv_sec = 0;
+ if (rpctls_gothup)
+ tv.tv_usec = 0;
+ else
+ tv.tv_usec = RELOADDELAY;
+ readfds = svc_fdset;
+ switch (select(svc_maxfd + 1, &readfds, NULL, NULL, &tv)) {
+ case -1:
+ if (errno == EINTR) {
+ /* Allow a reload now. */
+ nexttime = 0;
+ continue;
+ }
+ syslog(LOG_ERR, "rpc.tls daemon died: select: %m");
+ exit(1);
+ case 0:
+ /* Allow a reload now. */
+ nexttime = 0;
+ continue;
+ default:
+ svc_getreqset(&readfds);
+ }
+ }
+}
+
+/*
+ * (re)load the CRLfile into the certificate verification store.
+ */
+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) {
+ rpctls_verbose_out(
+ "rpctls_loadcrlfile: Can't"
+ " load CRLfile=%s\n",
+ rpctls_crlfile);
+ return (ret);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Read the CRL file and check for any extant connections
+ * that might now be revoked.
+ */
+void
+rpctls_checkcrl(void)
+{
+ struct ssl_entry *slp;
+ BIO *infile;
+ X509_CRL *crl;
+ X509_REVOKED *revoked;
+ char *cp, *cp2, nullstr[1];
+ int ret;
+
+ if (rpctls_crlfile == NULL || (rpctls_verify_cafile == NULL &&
+ rpctls_verify_capath == NULL))
+ return;
+ infile = BIO_new(BIO_s_file());
+ if (infile == NULL) {
+ rpctls_verbose_out("rpctls_checkcrl: Cannot BIO_new\n");
+ return;
+ }
+ ret = BIO_read_filename(infile, rpctls_crlfile);
+ if (ret != 1) {
+ rpctls_verbose_out("rpctls_checkcrl: Cannot read CRL file\n");
+ BIO_free(infile);
+ return;
+ }
+
+ nullstr[0] = '\0';
+ for (crl = PEM_read_bio_X509_CRL(infile, NULL, NULL, nullstr);
+ crl != NULL; crl = PEM_read_bio_X509_CRL(infile, NULL, NULL,
+ nullstr)) {
+ LIST_FOREACH(slp, &rpctls_ssllist, next) {
+ if (slp->cert != NULL) {
+ ret = X509_CRL_get0_by_cert(crl, &revoked,
+ slp->cert);
+ /*
+ * 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) {
+ cp2 = X509_NAME_oneline(
+ X509_get_subject_name(slp->cert),
+ NULL, 0);
+ cp = X509_NAME_oneline(
+ X509_get_issuer_name(slp->cert),
+ NULL, 0);
+ if (rpctls_debug_level == 0)
+ syslog(LOG_INFO | LOG_DAEMON,
+ "rpctls_daemon: Certificate"
+ " Revoked "
+ "issuerName=%s "
+ "subjectName=%s: "
+ "TCP connection closed",
+ cp, cp2);
+ else
+ fprintf(stderr,
+ "rpctls_daemon: Certificate"
+ " Revoked "
+ "issuerName=%s "
+ "subjectName=%s: "
+ "TCP connection closed",
+ cp, cp2);
+ shutdown(slp->s, SHUT_WR);
+ slp->shutoff = true;
+ }
+ }
+ }
+ X509_CRL_free(crl);
+ }
+ BIO_free(infile);
+}
+
+void
+rpctls_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);
+ }
+}
+
+/*
+ * Check a IP address against any host address in the
+ * certificate. Basically getnameinfo(3) and
+ * X509_check_host().
+ */
+int
+rpctls_checkhost(struct sockaddr *sad, X509 *cert, unsigned int wildcard)
+{
+ 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);
+ rpctls_verbose_out("rpctls_checkhost: DNS %s\n",
+ hostnam);
+ ret = X509_check_host(cert, hostnam, strlen(hostnam),
+ wildcard, NULL);
+ return (ret);
+}
+
+/*
+ * Get the peer's IP address.
+ */
+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) {
+ rpctls_verbose_out("rpctls_gethost: %s\n",
+ hostip);
+ ret = 1;
+ }
+ return (ret);
+}
diff --git a/usr.sbin/rpc.tlsservd/rpc.tlsservd.8.sav b/usr.sbin/rpc.tlsservd/rpc.tlsservd.8
--- a/usr.sbin/rpc.tlsservd/rpc.tlsservd.8.sav
+++ b/usr.sbin/rpc.tlsservd/rpc.tlsservd.8
@@ -0,0 +1,348 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@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.tlsservd.8 by Rick Macklem.
+.Dd January 29, 2021
+.Dt RPC.TLSSERVD 8
+.Os
+.Sh NAME
+.Nm rpc.tlsservd
+.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 n Ar domain
+.Op Fl p Ar CApath
+.Op Fl r Ar CRLfile
+.Op Fl u
+.Op Fl v
+.Op Fl W
+.Op Fl w
+.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 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) with
+.Xr ktls 4
+enabled.
+Note that the
+.Fl tls
+option in the
+.Xr exports 5
+file specifies that the client must use RPC over TLS.
+The
+.Fl tlscert
+option in the
+.Xr exports 5
+file specifies that the client must provide a certificate
+that verifies.
+The
+.Fl tlscertuser
+option in the
+.Xr exports 5
+file specifies that the client must provide a certificate
+that verifies and has a otherName:1.3.6.1.4.1.2238.1.1.1;UTF8: field of
+subjectAltName of the form
+.Dq user@domain
+where
+.Dq domain
+matches the one for this server and
+.Dq user
+is a valid user name that maps to a <uid, gid_list>.
+For the latter two cases, the
+.Fl m
+and either the
+.Fl l
+or
+.Fl p
+options must be specified.
+The
+.Fl tlscertuser
+option also requires that the
+.Fl u
+option on this daemon 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 either the
+.Fl l
+or
+.Fl p
+options to require that the client certificate have the correct
+Fully Qualified Domain Name (FQDN) in it.
+.Pp
+A certificate and associated key must exist in /etc/rpc.tlsservd
+(or the
+.Dq certdir
+specified by the
+.Fl D
+option)
+in files named
+.Dq cert.pem
+and
+.Dq certkey.pem .
+.Pp
+If a SIGHUP signal is sent to the daemon it will reload the
+.Dq CRLfile
+and will shut down any extant connections that presented certificates
+during TLS handshake that have been revoked.
+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 m
+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.tlsservd as the location for the
+certificate in a file called
+.Dq cert.pem
+and associated key in
+.Dq certkey.pem .
+.It Fl d , Fl Fl debuglevel
+Run in debug mode.
+In this mode,
+.Nm
+will not fork when it starts.
+.It Fl h , Fl Fl checkhost
+This option specifies that the client must provide a certificate
+that both verifies and has a FQDN that matches the reverse
+DNS name for the IP address that
+the client uses to connect to the server.
+The FQDN should be
+in the DNS field of the subjectAltName, but is also allowed
+to be in the CN field of the
+subjectName in the certificate.
+By default, a wildcard "*" in the FQDN is not allowed.
+With this option, a failure to verify the client certificate
+or match the FQDN will result in the
+server sending AUTH_REJECTEDCRED replies to all client RPCs.
+This option requires the
+.Fl m
+and either the
+.Fl l
+or
+.Fl p
+options.
+.It Fl l Ar CAfile , Fl Fl verifylocs= Ns 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 library calls.
+Note that this is a path name for the file and is not assumed to be
+in
+.Dq certdir .
+Either this option or the
+.Fl p
+option must be specified when the
+.Fl m
+option is specified so that the daemon can verify the client's
+certificate.
+.It Fl m , Fl Fl mutualverf
+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, either the
+.Xr exports 5
+option
+.Fl tlscert
+or
+.Fl tlscertuser
+may be used to require a client to provide a certificate
+that verifies.
+See
+.Xr exports 5 .
+.It Fl n Ar domain , Fl Fl domain= Ns Ar domain
+This option specifies what the
+.Dq domain
+is for use with the
+.Fl u
+option, overriding the domain taken from the
+.Xr gethostname 2
+of the server this daemon is running on.
+If you have specified the
+.Fl domain
+command line option for
+.Xr nfsuserd 8
+then you should specify this option with the same
+.Dq domain
+that was specified for
+.Xr nfsuserd 8 .
+This option is only meaningful when used with the
+.Fl u
+option.
+.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_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.
+.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.
+This option is only meaningful when either the
+.Fl l
+or
+.Fl p
+have been specified.
+.It Fl u , Fl Fl certuser
+This option specifies that if the client provides a certificate
+that both verifies and has a subjectAltName with an otherName
+component of the form
+.Dq otherName:1.3.6.1.4.1.2238.1.1.1;UTF8:user@domain
+where
+.Dq domain
+matches the one for this server,
+then the daemon will attempt to map
+.Dq user
+in the above
+to a user credential <uid, gid_list>.
+There should only be one of these otherName components for each
+.Dq domain .
+If
+.Dq user
+is a valid username in the password database,
+then the <uid, gid_list> for
+.Dq user
+will be used for all
+RPCs on the mount instead of the credentials in the RPC request
+header.
+This option requires the
+.Fl m
+and either the
+.Fl l
+or
+.Fl p
+options.
+Use of this option might not conform to RFC-NNNN, which does
+not allow certificates to be used for user authentication.
+.It Fl v , Fl Fl verbose
+Run in verbose mode.
+In this mode,
+.Nm
+will log activity messages to
+.Xr syslogd 8
+using LOG_INFO | LOG_DAEMON or to
+stderr, if the
+.Fl d
+option has also been specified.
+.It Fl W , Fl Fl multiwild
+This option is used with the
+.Fl h
+option to allow use of a wildcard
+.Dq *
+that matches multiple
+components of the reverse DNS name for the client's IP
+address.
+For example, the FQDN
+.Dq *.uoguelph.ca
+would match both
+.Dq laptop21.uoguelph.ca
+and
+.Dq laptop3.cis.uoguelph.ca .
+.It Fl w , Fl Fl singlewild
+Similar to
+.Fl W
+but allows the wildcard
+.Dq *
+to match a single component of the reverse DNS name.
+For example, the FQDN
+.Dq *.uoguelph.ca
+would match
+.Dq laptop21.uoguelph.ca
+but not
+.Dq laptop3.cis.uoguelph.ca .
+Only one of the
+.Fl W
+and
+.Fl w
+options is allowed.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr openssl 1 ,
+.Xr ktls 4 ,
+.Xr exports 5 ,
+.Xr mount_nfs 8 ,
+.Xr nfsuserd 8 ,
+.Xr rpc.tlsclntd 8 ,
+.Xr syslogd 8
+.Sh STANDARDS
+The implementation is based on the specification in
+.Rs
+.%B "RFC NNNN"
+.%T "Towards Remote Procedure Call Encryption By Default"
+.Re
+.Sh HISTORY
+The
+.Nm
+manual page first appeared in
+.Fx 13.0 .
+.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.
diff --git a/usr.sbin/rpc.tlsservd/rpc.tlsservd.c.sav b/usr.sbin/rpc.tlsservd/rpc.tlsservd.c
--- a/usr.sbin/rpc.tlsservd/rpc.tlsservd.c.sav
+++ b/usr.sbin/rpc.tlsservd/rpc.tlsservd.c
@@ -0,0 +1,883 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr@rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred@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.
+ */
+
+/*
+ * Extensively modified from /usr/src/usr.sbin/gssd.c r344402 for
+ * the server side of kernel RPC-over-TLS by Rick Macklem.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+#include <err.h>
+#include <getopt.h>
+#include <libutil.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.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 "rpctlssd.h"
+#include "rpc.tlscommon.h"
+
+#ifndef _PATH_RPCTLSSDSOCK
+#define _PATH_RPCTLSSDSOCK "/var/run/rpc.tlsservd.sock"
+#endif
+#ifndef _PATH_CERTANDKEY
+#define _PATH_CERTANDKEY "/etc/rpc.tlsservd/"
+#endif
+#ifndef _PATH_RPCTLSSDPID
+#define _PATH_RPCTLSSDPID "/var/run/rpc.tlsservd.pid"
+#endif
+#ifndef _PREFERRED_CIPHERS
+#define _PREFERRED_CIPHERS "AES128-GCM-SHA256"
+#endif
+
+/* Global variables also used by rpc.tlscommon.c. */
+int rpctls_debug_level;
+bool rpctls_verbose;
+SSL_CTX *rpctls_ctx = NULL;
+const char *rpctls_verify_cafile = NULL;
+const char *rpctls_verify_capath = NULL;
+char *rpctls_crlfile = NULL;
+bool rpctls_gothup = false;
+struct ssl_list rpctls_ssllist;
+
+static struct pidfh *rpctls_pfh = NULL;
+static bool rpctls_do_mutual = false;
+static const char *rpctls_certdir = _PATH_CERTANDKEY;
+static bool rpctls_comparehost = false;
+static unsigned int rpctls_wildcard = X509_CHECK_FLAG_NO_WILDCARDS;
+static uint64_t rpctls_ssl_refno = 0;
+static uint64_t rpctls_ssl_sec = 0;
+static uint64_t rpctls_ssl_usec = 0;
+static bool rpctls_cnuser = false;
+static char *rpctls_dnsname;
+static const char *rpctls_cnuseroid = "1.3.6.1.4.1.2238.1.1.1";
+
+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, X509 **certp);
+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);
+
+extern void rpctlssd_1(struct svc_req *rqstp, SVCXPRT *transp);
+
+static struct option longopts[] = {
+ { "certdir", required_argument, NULL, 'D' },
+ { "debuglevel", no_argument, NULL, 'd' },
+ { "checkhost", no_argument, NULL, 'h' },
+ { "verifylocs", required_argument, NULL, 'l' },
+ { "mutualverf", no_argument, NULL, 'm' },
+ { "domain", required_argument, NULL, 'n' },
+ { "verifydir", required_argument, NULL, 'p' },
+ { "crl", required_argument, NULL, 'r' },
+ { "certuser", no_argument, NULL, 'u' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "multiwild", no_argument, NULL, 'W' },
+ { "singlewild", no_argument, NULL, 'w' },
+ { 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 ch, debug, fd, oldmask;
+ SVCXPRT *xprt;
+ struct timeval tm;
+ struct timezone tz;
+ char hostname[MAXHOSTNAMELEN + 2];
+ pid_t otherpid;
+ bool tls_enable;
+ size_t tls_enable_len;
+
+ /* Check that another rpctlssd isn't already running. */
+ rpctls_pfh = pidfile_open(_PATH_RPCTLSSDPID, 0600, &otherpid);
+ if (rpctls_pfh == NULL) {
+ if (errno == EEXIST)
+ errx(1, "rpctlssd already running, pid: %d.", otherpid);
+ warn("cannot open or create pidfile");
+ }
+
+ /* Check to see that the ktls is enabled. */
+ tls_enable_len = sizeof(tls_enable);
+ if (sysctlbyname("kern.ipc.tls.enable", &tls_enable, &tls_enable_len,
+ NULL, 0) != 0 || !tls_enable)
+ errx(1, "Kernel TLS not enabled");
+
+ /* Get the time when this daemon is started. */
+ gettimeofday(&tm, &tz);
+ rpctls_ssl_sec = tm.tv_sec;
+ rpctls_ssl_usec = tm.tv_usec;
+
+ /* Set the dns name for the server. */
+ rpctls_dnsname = rpctls_getdnsname(hostname);
+ if (rpctls_dnsname == NULL) {
+ strcpy(hostname, "@default.domain");
+ rpctls_dnsname = hostname;
+ }
+
+ debug = 0;
+ rpctls_verbose = false;
+ while ((ch = getopt_long(argc, argv, "D:dhl:n:mp:r:uvWw", longopts,
+ NULL)) != -1) {
+ switch (ch) {
+ case 'D':
+ rpctls_certdir = optarg;
+ break;
+ case 'd':
+ rpctls_debug_level++;
+ break;
+ case 'h':
+ rpctls_comparehost = true;
+ break;
+ case 'l':
+ rpctls_verify_cafile = optarg;
+ break;
+ case 'm':
+ rpctls_do_mutual = true;
+ break;
+ case 'n':
+ hostname[0] = '@';
+ strlcpy(&hostname[1], optarg, MAXHOSTNAMELEN + 1);
+ rpctls_dnsname = hostname;
+ break;
+ case 'p':
+ rpctls_verify_capath = optarg;
+ break;
+ case 'r':
+ rpctls_crlfile = optarg;
+ break;
+ case 'u':
+ rpctls_cnuser = true;
+ break;
+ case 'v':
+ rpctls_verbose = true;
+ break;
+ case 'W':
+ if (rpctls_wildcard != X509_CHECK_FLAG_NO_WILDCARDS)
+ errx(1, "options -w and -W are mutually "
+ "exclusive");
+ rpctls_wildcard = X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS;
+ break;
+ case 'w':
+ if (rpctls_wildcard != X509_CHECK_FLAG_NO_WILDCARDS)
+ errx(1, "options -w and -W are mutually "
+ "exclusive");
+ rpctls_wildcard = 0;
+ break;
+ default:
+ fprintf(stderr, "usage: %s "
+ "[-D/--certdir certdir] [-d/--debuglevel] "
+ "[-h/--checkhost] "
+ "[-l/--verifylocs CAfile] [-m/--mutualverf] "
+ "[-n/--domain domain_name] "
+ "[-p/--verifydir CApath] [-r/--crl CRLfile] "
+ "[-u/--certuser] [-v/--verbose] [-W/--multiwild] "
+ "[-w/--singlewild]\n", argv[0]);
+ exit(1);
+ }
+ }
+ 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 plus the "
+ "-l <CAfile> and/or -p <CApath> options");
+ if (!rpctls_comparehost && rpctls_wildcard !=
+ X509_CHECK_FLAG_NO_WILDCARDS)
+ errx(1, "The -w or -W options require the -h option");
+ if (rpctls_cnuser && (!rpctls_do_mutual ||
+ (rpctls_verify_cafile == NULL && rpctls_verify_capath == NULL)))
+ errx(1, "-u requires the -m plus 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");
+ }
+
+ if (rpctls_debug_level == 0) {
+ if (daemon(0, 0) != 0)
+ err(1, "Can't daemonize");
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ }
+ signal(SIGTERM, rpctlssd_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_RPCTLSSDSOCK);
+ strcpy(sun.sun_path, _PATH_RPCTLSSDSOCK);
+ 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 rpctlssd socket");
+ exit(1);
+ }
+ err(1, "Can't create local rpctlssd 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 rpctlssd socket");
+ exit(1);
+ }
+ err(1, "Can't bind local rpctlssd socket");
+ }
+ umask(oldmask);
+ if (listen(fd, SOMAXCONN) < 0) {
+ if (rpctls_debug_level == 0) {
+ syslog(LOG_ERR,
+ "Can't listen on local rpctlssd socket");
+ exit(1);
+ }
+ err(1, "Can't listen on local rpctlssd 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 rpctlssd socket");
+ exit(1);
+ }
+ err(1, "Can't create transport for local rpctlssd socket");
+ }
+ if (!svc_reg(xprt, RPCTLSSD, RPCTLSSDVERS, rpctlssd_1, NULL)) {
+ if (rpctls_debug_level == 0) {
+ syslog(LOG_ERR,
+ "Can't register service for local rpctlssd socket");
+ exit(1);
+ }
+ err(1, "Can't register service for local rpctlssd socket");
+ }
+
+ rpctls_ctx = rpctls_setup_ssl(rpctls_certdir);
+ if (rpctls_ctx == NULL) {
+ if (rpctls_debug_level == 0) {
+ syslog(LOG_ERR, "Can't create SSL context");
+ exit(1);
+ }
+ err(1, "Can't create SSL context");
+ }
+ rpctls_gothup = false;
+ LIST_INIT(&rpctls_ssllist);
+
+ rpctls_syscall(RPCTLS_SYSC_SRVSETPATH, _PATH_RPCTLSSDSOCK);
+
+ rpctls_svc_run();
+
+ rpctls_syscall(RPCTLS_SYSC_SRVSHUTDOWN, "");
+
+ SSL_CTX_free(rpctls_ctx);
+ EVP_cleanup();
+ return (0);
+}
+
+bool_t
+rpctlssd_null_1_svc(__unused void *argp, __unused void *result,
+ __unused struct svc_req *rqstp)
+{
+
+ rpctls_verbose_out("rpctlssd_null_svc: done\n");
+ return (TRUE);
+}
+
+bool_t
+rpctlssd_connect_1_svc(__unused void *argp,
+ struct rpctlssd_connect_res *result, __unused struct svc_req *rqstp)
+{
+ int ngrps, s;
+ SSL *ssl;
+ uint32_t flags;
+ struct ssl_entry *newslp;
+ uint32_t uid;
+ uint32_t *gidp;
+ X509 *cert;
+
+ rpctls_verbose_out("rpctlsd_connect_svc: started\n");
+ memset(result, 0, sizeof(*result));
+ /* Get the socket fd from the kernel. */
+ s = rpctls_syscall(RPCTLS_SYSC_SRVSOCKET, "");
+ if (s < 0)
+ return (FALSE);
+
+ /* Do the server side of a TLS handshake. */
+ gidp = calloc(NGROUPS, sizeof(*gidp));
+ ssl = rpctls_server(rpctls_ctx, s, &flags, &uid, &ngrps, gidp, &cert);
+ if (ssl == NULL) {
+ free(gidp);
+ rpctls_verbose_out("rpctlssd_connect_svc: ssl "
+ "accept failed\n");
+ /*
+ * For RPC-over-TLS, this upcall is expected
+ * to close off the socket upon handshake failure.
+ */
+ close(s);
+ return (FALSE);
+ } else {
+ rpctls_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 ((flags & RPCTLS_FLAGS_CERTUSER) != 0) {
+ result->uid = uid;
+ result->gid.gid_len = ngrps;
+ result->gid.gid_val = gidp;
+ } else {
+ result->uid = 0;
+ result->gid.gid_len = 0;
+ result->gid.gid_val = gidp;
+ }
+ }
+
+ /* Maintain list of all current SSL *'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);
+}
+
+bool_t
+rpctlssd_handlerecord_1_svc(struct rpctlssd_handlerecord_arg *argp,
+ struct rpctlssd_handlerecord_res *result, __unused struct svc_req *rqstp)
+{
+ struct ssl_entry *slp;
+ int ret;
+ char junk;
+
+ 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) {
+ rpctls_verbose_out("rpctlssd_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);
+ 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
+rpctlssd_disconnect_1_svc(struct rpctlssd_disconnect_arg *argp,
+ struct rpctlssd_disconnect_res *result, __unused struct svc_req *rqstp)
+{
+ struct ssl_entry *slp;
+ int ret;
+
+ 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) {
+ rpctls_verbose_out("rpctlssd_disconnect fd=%d closed\n",
+ slp->s);
+ LIST_REMOVE(slp, next);
+ if (!slp->shutoff) {
+ ret = SSL_get_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.
+ */
+ if (!slp->shutoff)
+ shutdown(slp->s, SHUT_WR);
+ close(slp->s);
+ free(slp);
+ result->reterr = RPCTLSERR_OK;
+ } else
+ result->reterr = RPCTLSERR_NOCLOSE;
+ return (TRUE);
+}
+
+int
+rpctlssd_1_freeresult(__unused SVCXPRT *transp, xdrproc_t xdr_result,
+ caddr_t result)
+{
+ rpctlssd_connect_res *res;
+
+ if (xdr_result == (xdrproc_t)xdr_rpctlssd_connect_res) {
+ res = (rpctlssd_connect_res *)(void *)result;
+ free(res->gid.gid_val);
+ }
+ return (TRUE);
+}
+
+static void
+rpctlssd_terminate(int sig __unused)
+{
+ struct ssl_entry *slp;
+
+ rpctls_syscall(RPCTLS_SYSC_SRVSHUTDOWN, "");
+ pidfile_remove(rpctls_pfh);
+
+ LIST_FOREACH(slp, &rpctls_ssllist, next)
+ shutdown(slp->s, SHUT_RD);
+ exit(0);
+}
+
+/* Allow the handshake to proceed. */
+static int
+rpctls_verify_callback(__unused int preverify_ok,
+ __unused X509_STORE_CTX *x509_ctx)
+{
+
+ return (1);
+}
+
+static SSL_CTX *
+rpctls_setup_ssl(const char *certdir)
+{
+ SSL_CTX *ctx;
+ 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_server_method());
+ if (ctx == NULL) {
+ rpctls_verbose_out("rpctls_setup_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) {
+ rpctls_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 certkey.pem files from the directory certdir. */
+ len = strlcpy(path, 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) {
+ rpctls_verbose_out("rpctls_setup_ssl: can't use certificate "
+ "file path=%s ret=%d\n", path, ret);
+ SSL_CTX_free(ctx);
+ return (NULL);
+ }
+ if (strlcpy(&path[len], "certkey.pem", rlen) != 11) {
+ SSL_CTX_free(ctx);
+ return (NULL);
+ }
+ ret = SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM);
+ if (ret != 1) {
+ rpctls_verbose_out("rpctls_setup_ssl: Can't use private "
+ "key path=%s ret=%d\n", path, ret);
+ SSL_CTX_free(ctx);
+ return (NULL);
+ }
+
+ /* Set Mutual authentication, as required. */
+ if (rpctls_do_mutual) {
+ if (rpctls_verify_cafile != NULL ||
+ rpctls_verify_capath != NULL) {
+ if (rpctls_crlfile != NULL) {
+ ret = rpctls_loadcrlfile(ctx);
+ if (ret == 0) {
+ rpctls_verbose_out("rpctls_setup_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) {
+ rpctls_verbose_out("rpctls_setup_ssl: "
+ "Can't load verify locations\n");
+ SSL_CTX_free(ctx);
+ return (NULL);
+ }
+ if (rpctls_verify_cafile != NULL)
+ SSL_CTX_set_client_CA_list(ctx,
+ SSL_load_client_CA_file(
+ rpctls_verify_cafile));
+ }
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER,
+ rpctls_verify_callback);
+ }
+ SSL_CTX_clear_mode(ctx, SSL_MODE_NO_KTLS_TX | SSL_MODE_NO_KTLS_RX);
+ return (ctx);
+}
+
+static SSL *
+rpctls_server(SSL_CTX *ctx, int s, uint32_t *flags, uint32_t *uidp,
+ int *ngrps, uint32_t *gidp, X509 **certp)
+{
+ SSL *ssl;
+ X509 *cert;
+ struct sockaddr *sad;
+ struct sockaddr_storage ad;
+ char hostnam[NI_MAXHOST];
+ int gethostret, ret;
+ char *cp, *cp2;
+
+ *flags = 0;
+ *certp = NULL;
+ sad = (struct sockaddr *)&ad;
+ ssl = SSL_new(ctx);
+ if (ssl == NULL) {
+ rpctls_verbose_out("rpctls_server: SSL_new failed\n");
+ return (NULL);
+ }
+ if (SSL_set_fd(ssl, s) != 1) {
+ rpctls_verbose_out("rpctls_server: SSL_set_fd failed\n");
+ SSL_free(ssl);
+ return (NULL);
+ }
+ ret = SSL_accept(ssl);
+ if (ret != 1) {
+ rpctls_verbose_out("rpctls_server: SSL_accept "
+ "failed ret=%d\n", ret);
+ SSL_free(ssl);
+ return (NULL);
+ }
+ *flags |= RPCTLS_FLAGS_HANDSHAKE;
+ if (rpctls_do_mutual) {
+ cert = SSL_get_peer_certificate(ssl);
+ if (cert != NULL) {
+ gethostret = rpctls_gethost(s, sad, hostnam,
+ sizeof(hostnam));
+ if (gethostret == 0)
+ hostnam[0] = '\0';
+ cp2 = X509_NAME_oneline(
+ X509_get_subject_name(cert), NULL, 0);
+ *flags |= RPCTLS_FLAGS_GOTCERT;
+ ret = SSL_get_verify_result(ssl);
+ if (ret != X509_V_OK) {
+ cp = X509_NAME_oneline(
+ X509_get_issuer_name(cert), NULL, 0);
+ if (rpctls_debug_level == 0)
+ syslog(LOG_INFO | LOG_DAEMON,
+ "rpctls_server: client IP %s "
+ "issuerName=%s subjectName=%s"
+ " verify failed %s\n", hostnam,
+ cp, cp2,
+ X509_verify_cert_error_string(ret));
+ else
+ fprintf(stderr,
+ "rpctls_server: client IP %s "
+ "issuerName=%s subjectName=%s"
+ " verify failed %s\n", hostnam,
+ cp, cp2,
+ X509_verify_cert_error_string(ret));
+ }
+ if (ret ==
+ X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
+ ret == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN)
+ *flags |= RPCTLS_FLAGS_SELFSIGNED;
+ else if (ret == X509_V_OK) {
+ if (rpctls_comparehost) {
+ ret = 0;
+ if (gethostret != 0)
+ ret = rpctls_checkhost(sad,
+ cert, rpctls_wildcard);
+ if (ret != 1) {
+ *flags |=
+ RPCTLS_FLAGS_DISABLED;
+ rpctls_verbose_out(
+ "rpctls_server: "
+ "checkhost "
+ "failed\n");
+ }
+ }
+ if (rpctls_cnuser) {
+ ret = rpctls_cnname(cert, uidp,
+ ngrps, gidp);
+ if (ret != 0)
+ *flags |= RPCTLS_FLAGS_CERTUSER;
+ }
+ *flags |= RPCTLS_FLAGS_VERIFIED;
+ *certp = cert;
+ cert = NULL;
+ }
+ if (cert != NULL)
+ X509_free(cert);
+ } else
+ rpctls_verbose_out("rpctls_server: "
+ "No peer certificate\n");
+ }
+
+ /* Check to see that ktls is working for the connection. */
+ ret = BIO_get_ktls_send(SSL_get_wbio(ssl));
+ rpctls_verbose_out("rpctls_server: BIO_get_ktls_send=%d\n", ret);
+ if (ret != 0) {
+ ret = BIO_get_ktls_recv(SSL_get_rbio(ssl));
+ rpctls_verbose_out("rpctls_server: BIO_get_ktls_recv=%d\n",
+ ret);
+ }
+ if (ret == 0) {
+ if (rpctls_debug_level == 0)
+ syslog(LOG_ERR, "ktls not working");
+ else
+ fprintf(stderr, "ktls not working\n");
+ /*
+ * The handshake has completed, so all that can be
+ * done is disable the connection.
+ */
+ *flags |= RPCTLS_FLAGS_DISABLED;
+ }
+
+ return (ssl);
+}
+
+/*
+ * Acquire the dnsname for this server.
+ */
+static char *
+rpctls_getdnsname(char *hostname)
+{
+ char *cp, *dnsname;
+ struct addrinfo *aip, hints;
+ int error;
+
+ dnsname = NULL;
+ if (gethostname(hostname, MAXHOSTNAMELEN) == 0) {
+ if ((cp = strchr(hostname, '.')) != NULL &&
+ *(cp + 1) != '\0') {
+ *cp = '@';
+ dnsname = cp;
+ } else {
+ memset((void *)&hints, 0, sizeof (hints));
+ hints.ai_flags = AI_CANONNAME;
+ error = getaddrinfo(hostname, NULL, &hints, &aip);
+ if (error == 0) {
+ if (aip->ai_canonname != NULL &&
+ (cp = strchr(aip->ai_canonname, '.')) !=
+ NULL && *(cp + 1) != '\0') {
+ hostname[0] = '@';
+ strlcpy(&hostname[1], cp + 1,
+ MAXHOSTNAMELEN + 1);
+ dnsname = hostname;
+ }
+ freeaddrinfo(aip);
+ }
+ }
+ }
+ return (dnsname);
+}
+
+/*
+ * Check for an otherName component of subjectAltName where the OID
+ * matches and the "domain" matches that of this server.
+ * If found, map "user" to a <uid, gidlist> for it.
+ */
+static int
+rpctls_cnname(X509 *cert, uint32_t *uidp, int *ngrps, uint32_t *gidp)
+{
+ char *cp, usern[1024 + 1];
+ struct passwd *pwd;
+ gid_t gids[NGROUPS];
+ int i, j;
+ GENERAL_NAMES *genlist;
+ GENERAL_NAME *genname;
+ OTHERNAME *val;
+ size_t slen;
+
+ /* First, find the otherName in the subjectAltName. */
+ genlist = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+ if (genlist == NULL)
+ return (0);
+ cp = NULL;
+ for (i = 0; i < sk_GENERAL_NAME_num(genlist); i++) {
+ genname = sk_GENERAL_NAME_value(genlist, i);
+ if (genname->type != GEN_OTHERNAME)
+ continue;
+ val = genname->d.otherName;
+
+ /* Check to see that it is the correct OID. */
+ slen = i2t_ASN1_OBJECT(usern, sizeof(usern), val->type_id);
+ if (slen != strlen(rpctls_cnuseroid) || memcmp(usern,
+ rpctls_cnuseroid, slen) != 0)
+ continue;
+
+ /* Sanity check the otherName. */
+ if (val->value->type != V_ASN1_UTF8STRING ||
+ val->value->value.utf8string->length < 3 ||
+ (size_t)val->value->value.utf8string->length > sizeof(usern)
+ - 1) {
+ rpctls_verbose_out("rpctls_cnname: invalid cnuser "
+ "type=%d\n", val->value->type);
+ continue;
+ }
+
+ /* Look for a "user" in the otherName */
+ memcpy(usern, val->value->value.utf8string->data,
+ val->value->value.utf8string->length);
+ usern[val->value->value.utf8string->length] = '\0';
+
+ /* Now, look for the @dnsname suffix in the commonName. */
+ cp = strcasestr(usern, rpctls_dnsname);
+ if (cp == NULL)
+ continue;
+ if (*(cp + strlen(rpctls_dnsname)) != '\0') {
+ cp = NULL;
+ continue;
+ }
+ *cp = '\0';
+ break;
+ }
+ if (cp == NULL)
+ return (0);
+
+ /* See if the "user" is in the passwd database. */
+ pwd = getpwnam(usern);
+ if (pwd == NULL)
+ return (0);
+ *uidp = pwd->pw_uid;
+ *ngrps = NGROUPS;
+ if (getgrouplist(pwd->pw_name, pwd->pw_gid, gids, ngrps) < 0)
+ return (0);
+ rpctls_verbose_out("mapped user=%s ngrps=%d uid=%d\n", pwd->pw_name,
+ *ngrps, pwd->pw_uid);
+ for (j = 0; j < *ngrps; j++)
+ gidp[j] = gids[j];
+ return (1);
+}
+
+static void
+rpctls_huphandler(int sig __unused)
+{
+
+ rpctls_gothup = true;
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Feb 9, 1:16 PM (20 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28582787
Default Alt Text
D28430.id83196.diff (72 KB)
Attached To
Mode
D28430: add nfs-over-tls daemons to head/main
Attached
Detach File
Event Timeline
Log In to Comment