Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 3 Sep 2020 21:58:18 +0000 (UTC)
From:      Rick Macklem <rmacklem@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r365311 - projects/nfs-over-tls/usr.sbin/rpc.tlsservd
Message-ID:  <202009032158.083LwIor038448@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: rmacklem
Date: Thu Sep  3 21:58:18 2020
New Revision: 365311
URL: https://svnweb.freebsd.org/changeset/base/365311

Log:
  Add the renamed server daemon. Also add long options for those who like them.

Added:
  projects/nfs-over-tls/usr.sbin/rpc.tlsservd/
  projects/nfs-over-tls/usr.sbin/rpc.tlsservd/Makefile   (contents, props changed)
  projects/nfs-over-tls/usr.sbin/rpc.tlsservd/rpc.tlsservd.8   (contents, props changed)
  projects/nfs-over-tls/usr.sbin/rpc.tlsservd/rpc.tlsservd.c   (contents, props changed)

Added: projects/nfs-over-tls/usr.sbin/rpc.tlsservd/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/nfs-over-tls/usr.sbin/rpc.tlsservd/Makefile	Thu Sep  3 21:58:18 2020	(r365311)
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+PROG=	rpc.tlsservd
+MAN=	rpc.tlsservd.8
+SRCS=	rpc.tlsservd.c rpctlssd.h rpctlssd_svc.c rpctlssd_xdr.c
+
+CFLAGS+= -I.
+
+CFLAGS+= -I/usr/ktls/include
+LDFLAGS+= -L/usr/ktls/lib
+
+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>

Added: projects/nfs-over-tls/usr.sbin/rpc.tlsservd/rpc.tlsservd.8
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/nfs-over-tls/usr.sbin/rpc.tlsservd/rpc.tlsservd.8	Thu Sep  3 21:58:18 2020	(r365311)
@@ -0,0 +1,332 @@
+.\" 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 September 3, 2020
+.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_name
+.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).
+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@dns_domain
+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/rpctlssd
+(or the
+.Dq certdir
+specified by the
+.Fl D
+option)
+in files named
+.Dq cert.pem
+and
+.Dq key.pem .
+.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 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/rpctlssd as the location for the
+certificate in a file called
+.Dq cert.pem
+and key in
+.Dq key.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 export 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_name , Fl Fl domain= Ns Ar domain_name
+This option specifies what the
+.Dq domain_name
+is for use with the
+.Fl u
+option, overriding the domain_name 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_name
+that was specified for
+.Xr nfsuserd 8 .
+.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.
+(I was not able to determine if/when this matters, but
+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.
+This option is meaningless unless 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 of the form
+.Dq otherName:1.3.6.1.4.1.2238.1.1.1;UTF8:user@dns_domain
+the daemon will attempt to map
+.Dq user@dns_domain
+in the above
+to a <uid, gid_list>.
+The mapping of
+.Dq user@dns_domain
+is done in the same manner as the
+.Xr nfsuserd 8
+daemon, where
+.Dq dns_domain
+is the domain of the NFS server (or the one set via the
+.Fl n
+option) and
+.Dq user
+is a valid username in the password database.
+If this mapping is successful, 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 does not conform to RFC-X, 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 exports 5 ,
+.Xr mount_nfs 8 ,
+.Xr nfsuserd 8 ,
+.Xr rpc.tlsclntd 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.tlsservd/rpc.tlsservd.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/nfs-over-tls/usr.sbin/rpc.tlsservd/rpc.tlsservd.c	Thu Sep  3 21:58:18 2020	(r365311)
@@ -0,0 +1,993 @@
+/*-
+ * 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.
+ */
+
+/* Modified from gssd.c for the server side of kernel RPC-over-TLS. */
+
+#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/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"
+
+#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
+
+static struct pidfh	*rpctls_pfh = NULL;
+static int		rpctls_debug_level;
+static bool		rpctls_verbose;
+static SSL_CTX		*rpctls_ctx = NULL;
+static bool		rpctls_do_mutual = false;
+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 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_gothup = false;
+static bool		rpctls_cnuser = false;
+static char		*rpctls_dnsname;
+static const char	*rpctls_cnuseroid = "1.3.6.1.4.1.2238.1.1.1";
+
+/*
+ * A linked list of all current "SSL *"s and socket "fd"s
+ * for kernel RPC TLS connections is maintained.
+ * The "refno" field is a unique 64bit value used to
+ * identify which entry a kernel RPC upcall refers to.
+ */
+LIST_HEAD(ssl_list, ssl_entry);
+struct ssl_entry {
+	LIST_ENTRY(ssl_entry)	next;
+	uint64_t		refno;
+	int			s;
+	SSL			*ssl;
+};
+static struct ssl_list	rpctls_ssllist;
+
+static void		rpctlssd_terminate(int);
+static SSL_CTX		*rpctls_setup_ssl(const char *certdir);
+static SSL		*rpctls_server(SSL_CTX *ctx, int s,
+			    uint32_t *flags, uint32_t *uidp,
+			    int *ngrps, uint32_t *gidp);
+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 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 fd, oldmask, ch, debug;
+	SVCXPRT *xprt;
+	struct timeval tm;
+	struct timezone tz;
+	char hostname[MAXHOSTNAMELEN + 2];
+	pid_t otherpid;
+
+	/* 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");
+	}
+
+	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;
+
+	/* Set the dns name for the server. */
+	rpctls_dnsname = rpctls_getdnsname(hostname);
+	if (rpctls_dnsname == NULL) {
+		strcpy(hostname, "@default.domain");
+		rpctls_dnsname = hostname;
+	}
+fprintf(stderr, "dnsname=%s\n", rpctls_dnsname);
+
+	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);
+	svc_run();
+	rpctls_syscall(RPCTLS_SYSC_SRVSHUTDOWN, "");
+
+	SSL_CTX_free(rpctls_ctx);
+	EVP_cleanup();
+	return (0);
+}
+
+static void
+rpctlssd_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
+rpctlssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp)
+{
+
+	rpctlssd_verbose_out("rpctlssd_null_svc: done\n");
+	return (TRUE);
+}
+
+bool_t
+rpctlssd_connect_1_svc(void *argp,
+    struct rpctlssd_connect_res *result, struct svc_req *rqstp)
+{
+	int ngrps, s;
+	SSL *ssl;
+	uint32_t flags;
+	struct ssl_entry *newslp;
+	uint32_t uid;
+	uint32_t *gidp;
+
+	rpctlssd_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, "");
+rpctlssd_verbose_out("rpctlsd_connect_svc s=%d\n", s);
+	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);
+	if (ssl == NULL) {
+		free(gidp);
+		rpctlssd_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 {
+		rpctlssd_verbose_out("rpctlssd_connect_svc: "
+		    "succeeded flags=0x%x\n", flags);
+		result->flags = flags;
+		result->sec = rpctls_ssl_sec;
+		result->usec = rpctls_ssl_usec;
+		result->ssl = ++rpctls_ssl_refno;
+		/* Hard to believe this could ever wrap around.. */
+		if (rpctls_ssl_refno == 0)
+			result->ssl = ++rpctls_ssl_refno;
+		if ((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->refno = rpctls_ssl_refno;
+	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, 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) {
+		rpctlssd_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);
+rpctlssd_verbose_out("get_shutdown=%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
+rpctlssd_disconnect_1_svc(struct rpctlssd_disconnect_arg *argp,
+    struct rpctlssd_disconnect_res *result, 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) {
+		rpctlssd_verbose_out("rpctlssd_disconnect fd=%d closed\n",
+		    slp->s);
+		LIST_REMOVE(slp, next);
+		ret = SSL_get_shutdown(slp->ssl);
+rpctlssd_verbose_out("get_shutdown1=%d\n", ret);
+		/*
+		 * Do an SSL_shutdown() unless a close alert has
+		 * already been sent.
+		 */
+		if ((ret & SSL_SENT_SHUTDOWN) == 0)
+			SSL_shutdown(slp->ssl);
+		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
+rpctlssd_1_freeresult(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 *)result;
+		if (res->gid.gid_val != NULL)
+			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);
+
+	/*
+	 * Shut down all TCP connections, so that any compromised TLS
+	 * connection is no longer usable.
+	 */
+	LIST_FOREACH(slp, &rpctls_ssllist, next)
+		shutdown(slp->s, SHUT_RD);
+	exit(0);
+}
+
+/* Allow the handshake to proceed. */
+static int
+rpctls_verify_callback(int preverify_ok, 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) {
+		rpctlssd_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) {
+		rpctlssd_verbose_out("rpctls_setup_ssl: "
+		    "SSL_CTX_set_cipher_list failed to set any ciphers\n");
+		SSL_CTX_free(ctx);
+		return (NULL);
+	}
+
+	/* Get the cert.pem and key.pem files from the directory certdir. */
+	len = strlcpy(path, certdir, sizeof(path));
+	rlen = sizeof(path) - len;
+	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) {
+		rpctlssd_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], "key.pem", rlen) != 7) {
+		SSL_CTX_free(ctx);
+		return (NULL);
+	}
+	ret = SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM);
+	if (ret != 1) {
+		rpctlssd_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) {

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



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