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