xref: /freebsd/usr.sbin/rpc.tlsservd/rpc.tlscommon.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
1b9cbc85dSRick Macklem /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3b9cbc85dSRick Macklem  *
4b9cbc85dSRick Macklem  * Copyright (c) 2021 Rick Macklem
5b9cbc85dSRick Macklem  *
6b9cbc85dSRick Macklem  * Redistribution and use in source and binary forms, with or without
7b9cbc85dSRick Macklem  * modification, are permitted provided that the following conditions
8b9cbc85dSRick Macklem  * are met:
9b9cbc85dSRick Macklem  * 1. Redistributions of source code must retain the above copyright
10b9cbc85dSRick Macklem  *    notice, this list of conditions and the following disclaimer.
11b9cbc85dSRick Macklem  * 2. Redistributions in binary form must reproduce the above copyright
12b9cbc85dSRick Macklem  *    notice, this list of conditions and the following disclaimer in the
13b9cbc85dSRick Macklem  *    documentation and/or other materials provided with the distribution.
14b9cbc85dSRick Macklem  *
15b9cbc85dSRick Macklem  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16b9cbc85dSRick Macklem  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17b9cbc85dSRick Macklem  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18b9cbc85dSRick Macklem  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19b9cbc85dSRick Macklem  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20b9cbc85dSRick Macklem  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21b9cbc85dSRick Macklem  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22b9cbc85dSRick Macklem  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23b9cbc85dSRick Macklem  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24b9cbc85dSRick Macklem  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25b9cbc85dSRick Macklem  * SUCH DAMAGE.
26b9cbc85dSRick Macklem  *
27b9cbc85dSRick Macklem  */
28b9cbc85dSRick Macklem 
29b9cbc85dSRick Macklem #include <sys/cdefs.h>
30b9cbc85dSRick Macklem #include <sys/queue.h>
31b9cbc85dSRick Macklem #include <sys/syslog.h>
32b9cbc85dSRick Macklem #include <sys/select.h>
33b9cbc85dSRick Macklem #include <sys/time.h>
34b9cbc85dSRick Macklem 
35b9cbc85dSRick Macklem #include <netdb.h>
36b9cbc85dSRick Macklem #include <signal.h>
37b9cbc85dSRick Macklem #include <stdarg.h>
38b9cbc85dSRick Macklem #include <stdbool.h>
39b9cbc85dSRick Macklem #include <string.h>
40b9cbc85dSRick Macklem 
41b9cbc85dSRick Macklem #include <rpc/rpc.h>
42b9cbc85dSRick Macklem 
43b9cbc85dSRick Macklem #include <openssl/opensslconf.h>
44b9cbc85dSRick Macklem #include <openssl/bio.h>
45b9cbc85dSRick Macklem #include <openssl/ssl.h>
46b9cbc85dSRick Macklem #include <openssl/err.h>
47b9cbc85dSRick Macklem #include <openssl/x509v3.h>
48b9cbc85dSRick Macklem 
49b9cbc85dSRick Macklem #include "rpc.tlscommon.h"
50b9cbc85dSRick Macklem 
51b9cbc85dSRick Macklem /*
52b9cbc85dSRick Macklem  * How long to delay a reload of the CRL when there are RPC request(s)
53b9cbc85dSRick Macklem  * to process, in usec.  Must be less than 1second.
54b9cbc85dSRick Macklem  */
55b9cbc85dSRick Macklem #define	RELOADDELAY	250000
56b9cbc85dSRick Macklem 
57b9cbc85dSRick Macklem void
rpctls_svc_run(void)58b9cbc85dSRick Macklem rpctls_svc_run(void)
59b9cbc85dSRick Macklem {
60b9cbc85dSRick Macklem 	int ret;
61b9cbc85dSRick Macklem 	struct timeval tv;
62b9cbc85dSRick Macklem 	fd_set readfds;
63b9cbc85dSRick Macklem 	uint64_t curtime, nexttime;
64b9cbc85dSRick Macklem 	struct timespec tp;
65b9cbc85dSRick Macklem 	sigset_t sighup_mask;
66b9cbc85dSRick Macklem 
67b9cbc85dSRick Macklem 	/* Expand svc_run() here so that we can call rpctls_loadcrlfile(). */
68b9cbc85dSRick Macklem 	curtime = nexttime = 0;
69b9cbc85dSRick Macklem 	sigemptyset(&sighup_mask);
70b9cbc85dSRick Macklem 	sigaddset(&sighup_mask, SIGHUP);
71b9cbc85dSRick Macklem 	for (;;) {
72b9cbc85dSRick Macklem 		clock_gettime(CLOCK_MONOTONIC, &tp);
73b9cbc85dSRick Macklem 		curtime = tp.tv_sec;
74b9cbc85dSRick Macklem 		curtime = curtime * 1000000 + tp.tv_nsec / 1000;
75b9cbc85dSRick Macklem 		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
76b9cbc85dSRick Macklem 		if (rpctls_gothup && curtime >= nexttime) {
77b9cbc85dSRick Macklem 			rpctls_gothup = false;
78b9cbc85dSRick Macklem 			sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
79b9cbc85dSRick Macklem 			ret = rpctls_loadcrlfile(rpctls_ctx);
80b9cbc85dSRick Macklem 			if (ret != 0)
81b9cbc85dSRick Macklem 				rpctls_checkcrl();
82b9cbc85dSRick Macklem 			else
83b9cbc85dSRick Macklem 				rpctls_verbose_out("rpc.tlsservd: Can't "
84b9cbc85dSRick Macklem 				    "reload CRLfile\n");
85b9cbc85dSRick Macklem 			clock_gettime(CLOCK_MONOTONIC, &tp);
86b9cbc85dSRick Macklem 			nexttime = tp.tv_sec;
87b9cbc85dSRick Macklem 			nexttime = nexttime * 1000000 + tp.tv_nsec / 1000 +
88b9cbc85dSRick Macklem 			    RELOADDELAY;
89b9cbc85dSRick Macklem 		} else
90b9cbc85dSRick Macklem 			sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
91b9cbc85dSRick Macklem 
92b9cbc85dSRick Macklem 		/*
93b9cbc85dSRick Macklem 		 * If a reload is pending, poll for received request(s),
94b9cbc85dSRick Macklem 		 * otherwise set a RELOADDELAY timeout, since a SIGHUP
95b9cbc85dSRick Macklem 		 * could be processed between the got_sighup test and
96b9cbc85dSRick Macklem 		 * the select() system call.
97b9cbc85dSRick Macklem 		 */
98b9cbc85dSRick Macklem 		tv.tv_sec = 0;
99b9cbc85dSRick Macklem 		if (rpctls_gothup)
100b9cbc85dSRick Macklem 			tv.tv_usec = 0;
101b9cbc85dSRick Macklem 		else
102b9cbc85dSRick Macklem 			tv.tv_usec = RELOADDELAY;
103b9cbc85dSRick Macklem 		readfds = svc_fdset;
104b9cbc85dSRick Macklem 		switch (select(svc_maxfd + 1, &readfds, NULL, NULL, &tv)) {
105b9cbc85dSRick Macklem 		case -1:
106b9cbc85dSRick Macklem 			if (errno == EINTR) {
107b9cbc85dSRick Macklem 				/* Allow a reload now. */
108b9cbc85dSRick Macklem 				nexttime = 0;
109b9cbc85dSRick Macklem 				continue;
110b9cbc85dSRick Macklem 			}
111b9cbc85dSRick Macklem 			syslog(LOG_ERR, "rpc.tls daemon died: select: %m");
112b9cbc85dSRick Macklem 			exit(1);
113b9cbc85dSRick Macklem 		case 0:
114b9cbc85dSRick Macklem 			/* Allow a reload now. */
115b9cbc85dSRick Macklem 			nexttime = 0;
116b9cbc85dSRick Macklem 			continue;
117b9cbc85dSRick Macklem 		default:
118b9cbc85dSRick Macklem 			svc_getreqset(&readfds);
119b9cbc85dSRick Macklem 		}
120b9cbc85dSRick Macklem 	}
121b9cbc85dSRick Macklem }
122b9cbc85dSRick Macklem 
123b9cbc85dSRick Macklem /*
124b9cbc85dSRick Macklem  * (re)load the CRLfile into the certificate verification store.
125b9cbc85dSRick Macklem  */
126b9cbc85dSRick Macklem int
rpctls_loadcrlfile(SSL_CTX * ctx)127b9cbc85dSRick Macklem rpctls_loadcrlfile(SSL_CTX *ctx)
128b9cbc85dSRick Macklem {
129b9cbc85dSRick Macklem 	X509_STORE *certstore;
130b9cbc85dSRick Macklem 	X509_LOOKUP *certlookup;
131b9cbc85dSRick Macklem 	int ret;
132b9cbc85dSRick Macklem 
133b9cbc85dSRick Macklem 	if ((rpctls_verify_cafile != NULL ||
134b9cbc85dSRick Macklem 	    rpctls_verify_capath != NULL) &&
135b9cbc85dSRick Macklem 	    rpctls_crlfile != NULL) {
136b9cbc85dSRick Macklem 		certstore = SSL_CTX_get_cert_store(ctx);
137b9cbc85dSRick Macklem 		certlookup = X509_STORE_add_lookup(
138b9cbc85dSRick Macklem 		    certstore, X509_LOOKUP_file());
139b9cbc85dSRick Macklem 		ret = 0;
140b9cbc85dSRick Macklem 		if (certlookup != NULL)
141b9cbc85dSRick Macklem 			ret = X509_load_crl_file(certlookup,
142b9cbc85dSRick Macklem 			    rpctls_crlfile, X509_FILETYPE_PEM);
143b9cbc85dSRick Macklem 		if (ret != 0)
144b9cbc85dSRick Macklem 			ret = X509_STORE_set_flags(certstore,
145b9cbc85dSRick Macklem 			    X509_V_FLAG_CRL_CHECK |
146b9cbc85dSRick Macklem 			    X509_V_FLAG_CRL_CHECK_ALL);
147b9cbc85dSRick Macklem 		if (ret == 0) {
148b9cbc85dSRick Macklem 			rpctls_verbose_out(
149b9cbc85dSRick Macklem 			    "rpctls_loadcrlfile: Can't"
150b9cbc85dSRick Macklem 			    " load CRLfile=%s\n",
151b9cbc85dSRick Macklem 			    rpctls_crlfile);
152b9cbc85dSRick Macklem 			return (ret);
153b9cbc85dSRick Macklem 		}
154b9cbc85dSRick Macklem 	}
155b9cbc85dSRick Macklem 	return (1);
156b9cbc85dSRick Macklem }
157b9cbc85dSRick Macklem 
158b9cbc85dSRick Macklem /*
159b9cbc85dSRick Macklem  * Read the CRL file and check for any extant connections
160b9cbc85dSRick Macklem  * that might now be revoked.
161b9cbc85dSRick Macklem  */
162b9cbc85dSRick Macklem void
rpctls_checkcrl(void)163b9cbc85dSRick Macklem rpctls_checkcrl(void)
164b9cbc85dSRick Macklem {
165b9cbc85dSRick Macklem 	struct ssl_entry *slp;
166b9cbc85dSRick Macklem 	BIO *infile;
167b9cbc85dSRick Macklem 	X509_CRL *crl;
168b9cbc85dSRick Macklem 	X509_REVOKED *revoked;
169b9cbc85dSRick Macklem 	char *cp, *cp2, nullstr[1];
170b9cbc85dSRick Macklem 	int ret;
171b9cbc85dSRick Macklem 
172b9cbc85dSRick Macklem 	if (rpctls_crlfile == NULL || (rpctls_verify_cafile == NULL &&
173b9cbc85dSRick Macklem 	    rpctls_verify_capath == NULL))
174b9cbc85dSRick Macklem 		return;
175b9cbc85dSRick Macklem 	infile = BIO_new(BIO_s_file());
176b9cbc85dSRick Macklem 	if (infile == NULL) {
177b9cbc85dSRick Macklem 		rpctls_verbose_out("rpctls_checkcrl: Cannot BIO_new\n");
178b9cbc85dSRick Macklem 		return;
179b9cbc85dSRick Macklem 	}
180b9cbc85dSRick Macklem 	ret = BIO_read_filename(infile, rpctls_crlfile);
181b9cbc85dSRick Macklem 	if (ret != 1) {
182b9cbc85dSRick Macklem 		rpctls_verbose_out("rpctls_checkcrl: Cannot read CRL file\n");
183b9cbc85dSRick Macklem 		BIO_free(infile);
184b9cbc85dSRick Macklem 		return;
185b9cbc85dSRick Macklem 	}
186b9cbc85dSRick Macklem 
187b9cbc85dSRick Macklem 	nullstr[0] = '\0';
188b9cbc85dSRick Macklem 	for (crl = PEM_read_bio_X509_CRL(infile, NULL, NULL, nullstr);
189b9cbc85dSRick Macklem 	    crl != NULL; crl = PEM_read_bio_X509_CRL(infile, NULL, NULL,
190b9cbc85dSRick Macklem 	    nullstr)) {
191b9cbc85dSRick Macklem 		LIST_FOREACH(slp, &rpctls_ssllist, next) {
192b9cbc85dSRick Macklem 			if (slp->cert != NULL) {
193b9cbc85dSRick Macklem 				ret = X509_CRL_get0_by_cert(crl, &revoked,
194b9cbc85dSRick Macklem 				    slp->cert);
195b9cbc85dSRick Macklem 				/*
196b9cbc85dSRick Macklem 				 * Do a shutdown on the socket, so that it
197b9cbc85dSRick Macklem 				 * can no longer be used.  The kernel RPC
198b9cbc85dSRick Macklem 				 * code will notice the socket is disabled
199b9cbc85dSRick Macklem 				 * and will do a disconnect upcall, which will
200b9cbc85dSRick Macklem 				 * close the socket.
201b9cbc85dSRick Macklem 				 */
202b9cbc85dSRick Macklem 				if (ret == 1) {
203b9cbc85dSRick Macklem 					cp2 = X509_NAME_oneline(
204b9cbc85dSRick Macklem 					    X509_get_subject_name(slp->cert),
205b9cbc85dSRick Macklem 					    NULL, 0);
206b9cbc85dSRick Macklem 					cp = X509_NAME_oneline(
207b9cbc85dSRick Macklem 					    X509_get_issuer_name(slp->cert),
208b9cbc85dSRick Macklem 					    NULL, 0);
209b9cbc85dSRick Macklem 					if (rpctls_debug_level == 0)
210b9cbc85dSRick Macklem 						syslog(LOG_INFO | LOG_DAEMON,
211b9cbc85dSRick Macklem 						    "rpctls_daemon: Certificate"
212b9cbc85dSRick Macklem 						    " Revoked "
213b9cbc85dSRick Macklem 						    "issuerName=%s "
214b9cbc85dSRick Macklem 						    "subjectName=%s: "
215b9cbc85dSRick Macklem 						    "TCP connection closed",
216b9cbc85dSRick Macklem 						    cp, cp2);
217b9cbc85dSRick Macklem 					else
218b9cbc85dSRick Macklem 						fprintf(stderr,
219b9cbc85dSRick Macklem 						    "rpctls_daemon: Certificate"
220b9cbc85dSRick Macklem 						    " Revoked "
221b9cbc85dSRick Macklem 						    "issuerName=%s "
222b9cbc85dSRick Macklem 						    "subjectName=%s: "
223b9cbc85dSRick Macklem 						    "TCP connection closed",
224b9cbc85dSRick Macklem 						    cp, cp2);
225b9cbc85dSRick Macklem 					shutdown(slp->s, SHUT_WR);
226b9cbc85dSRick Macklem 					slp->shutoff = true;
227b9cbc85dSRick Macklem 				}
228b9cbc85dSRick Macklem 			}
229b9cbc85dSRick Macklem 		}
230b9cbc85dSRick Macklem 		X509_CRL_free(crl);
231b9cbc85dSRick Macklem 	}
232b9cbc85dSRick Macklem 	BIO_free(infile);
233b9cbc85dSRick Macklem }
234b9cbc85dSRick Macklem 
235b9cbc85dSRick Macklem void
rpctls_verbose_out(const char * fmt,...)236b9cbc85dSRick Macklem rpctls_verbose_out(const char *fmt, ...)
237b9cbc85dSRick Macklem {
238b9cbc85dSRick Macklem 	va_list ap;
239b9cbc85dSRick Macklem 
240b9cbc85dSRick Macklem 	if (rpctls_verbose) {
241b9cbc85dSRick Macklem 		va_start(ap, fmt);
242b9cbc85dSRick Macklem 		if (rpctls_debug_level == 0)
243b9cbc85dSRick Macklem 			vsyslog(LOG_INFO | LOG_DAEMON, fmt, ap);
244b9cbc85dSRick Macklem 		else
245b9cbc85dSRick Macklem 			vfprintf(stderr, fmt, ap);
246b9cbc85dSRick Macklem 		va_end(ap);
247b9cbc85dSRick Macklem 	}
248b9cbc85dSRick Macklem }
249b9cbc85dSRick Macklem 
250b9cbc85dSRick Macklem /*
251b9cbc85dSRick Macklem  * Check a IP address against any host address in the
252b9cbc85dSRick Macklem  * certificate.  Basically getnameinfo(3) and
253b9cbc85dSRick Macklem  * X509_check_host().
254b9cbc85dSRick Macklem  */
255b9cbc85dSRick Macklem int
rpctls_checkhost(struct sockaddr * sad,X509 * cert,unsigned int wildcard)256b9cbc85dSRick Macklem rpctls_checkhost(struct sockaddr *sad, X509 *cert, unsigned int wildcard)
257b9cbc85dSRick Macklem {
258b9cbc85dSRick Macklem 	char hostnam[NI_MAXHOST];
259b9cbc85dSRick Macklem 	int ret;
260b9cbc85dSRick Macklem 
261b9cbc85dSRick Macklem 	if (getnameinfo((const struct sockaddr *)sad,
262b9cbc85dSRick Macklem 	    sad->sa_len, hostnam, sizeof(hostnam),
263b9cbc85dSRick Macklem 	    NULL, 0, NI_NAMEREQD) != 0)
264b9cbc85dSRick Macklem 		return (0);
265b9cbc85dSRick Macklem 	rpctls_verbose_out("rpctls_checkhost: DNS %s\n",
266b9cbc85dSRick Macklem 	    hostnam);
267b9cbc85dSRick Macklem 	ret = X509_check_host(cert, hostnam, strlen(hostnam),
268b9cbc85dSRick Macklem 	    wildcard, NULL);
269b9cbc85dSRick Macklem 	return (ret);
270b9cbc85dSRick Macklem }
271b9cbc85dSRick Macklem 
272b9cbc85dSRick Macklem /*
273b9cbc85dSRick Macklem  * Get the peer's IP address.
274b9cbc85dSRick Macklem  */
275b9cbc85dSRick Macklem int
rpctls_gethost(int s,struct sockaddr * sad,char * hostip,size_t hostlen)276b9cbc85dSRick Macklem rpctls_gethost(int s, struct sockaddr *sad, char *hostip, size_t hostlen)
277b9cbc85dSRick Macklem {
278b9cbc85dSRick Macklem 	socklen_t slen;
279b9cbc85dSRick Macklem 	int ret;
280b9cbc85dSRick Macklem 
281b9cbc85dSRick Macklem 	slen = sizeof(struct sockaddr_storage);
282b9cbc85dSRick Macklem 	if (getpeername(s, sad, &slen) < 0)
283b9cbc85dSRick Macklem 		return (0);
284b9cbc85dSRick Macklem 	ret = 0;
285b9cbc85dSRick Macklem 	if (getnameinfo((const struct sockaddr *)sad,
286b9cbc85dSRick Macklem 	    sad->sa_len, hostip, hostlen,
287b9cbc85dSRick Macklem 	    NULL, 0, NI_NUMERICHOST) == 0) {
288b9cbc85dSRick Macklem 		rpctls_verbose_out("rpctls_gethost: %s\n",
289b9cbc85dSRick Macklem 		    hostip);
290b9cbc85dSRick Macklem 		ret = 1;
291b9cbc85dSRick Macklem 	}
292b9cbc85dSRick Macklem 	return (ret);
293b9cbc85dSRick Macklem }
294