From owner-svn-src-projects@freebsd.org Thu Sep 3 21:58:19 2020 Return-Path: Delivered-To: svn-src-projects@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 569373CC3DD for ; Thu, 3 Sep 2020 21:58:19 +0000 (UTC) (envelope-from rmacklem@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4BjF6z1jwqz43xN; Thu, 3 Sep 2020 21:58:19 +0000 (UTC) (envelope-from rmacklem@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 1DD3B224A5; Thu, 3 Sep 2020 21:58:19 +0000 (UTC) (envelope-from rmacklem@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id 083LwJmF038451; Thu, 3 Sep 2020 21:58:19 GMT (envelope-from rmacklem@FreeBSD.org) Received: (from rmacklem@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id 083LwIor038448; Thu, 3 Sep 2020 21:58:18 GMT (envelope-from rmacklem@FreeBSD.org) Message-Id: <202009032158.083LwIor038448@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: rmacklem set sender to rmacklem@FreeBSD.org using -f From: Rick Macklem Date: Thu, 3 Sep 2020 21:58:18 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r365311 - projects/nfs-over-tls/usr.sbin/rpc.tlsservd X-SVN-Group: projects X-SVN-Commit-Author: rmacklem X-SVN-Commit-Paths: projects/nfs-over-tls/usr.sbin/rpc.tlsservd X-SVN-Commit-Revision: 365311 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.33 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 03 Sep 2020 21:58:19 -0000 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 + +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 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 +.\" Developed with Red Inc: Alfred Perlstein +.\" +.\" 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 . +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 . +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 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 + * Developed with Red Inc: Alfred Perlstein + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 and/or " + "-p options"); + if (rpctls_comparehost && (!rpctls_do_mutual || + (rpctls_verify_cafile == NULL && rpctls_verify_capath == NULL))) + errx(1, "-h requires the -m plus the " + "-l and/or -p 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 and/or -p 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 ***