1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 __FBSDID("$FreeBSD$");
31
32 #include <sys/queue.h>
33 #include <sys/syslog.h>
34 #include <sys/select.h>
35 #include <sys/time.h>
36
37 #include <netdb.h>
38 #include <signal.h>
39 #include <stdarg.h>
40 #include <stdbool.h>
41 #include <string.h>
42
43 #include <rpc/rpc.h>
44
45 #include <openssl/opensslconf.h>
46 #include <openssl/bio.h>
47 #include <openssl/ssl.h>
48 #include <openssl/err.h>
49 #include <openssl/x509v3.h>
50
51 #include "rpc.tlscommon.h"
52
53 /*
54 * How long to delay a reload of the CRL when there are RPC request(s)
55 * to process, in usec. Must be less than 1second.
56 */
57 #define RELOADDELAY 250000
58
59 void
rpctls_svc_run(void)60 rpctls_svc_run(void)
61 {
62 int ret;
63 struct timeval tv;
64 fd_set readfds;
65 uint64_t curtime, nexttime;
66 struct timespec tp;
67 sigset_t sighup_mask;
68
69 /* Expand svc_run() here so that we can call rpctls_loadcrlfile(). */
70 curtime = nexttime = 0;
71 sigemptyset(&sighup_mask);
72 sigaddset(&sighup_mask, SIGHUP);
73 for (;;) {
74 clock_gettime(CLOCK_MONOTONIC, &tp);
75 curtime = tp.tv_sec;
76 curtime = curtime * 1000000 + tp.tv_nsec / 1000;
77 sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
78 if (rpctls_gothup && curtime >= nexttime) {
79 rpctls_gothup = false;
80 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
81 ret = rpctls_loadcrlfile(rpctls_ctx);
82 if (ret != 0)
83 rpctls_checkcrl();
84 else
85 rpctls_verbose_out("rpc.tlsservd: Can't "
86 "reload CRLfile\n");
87 clock_gettime(CLOCK_MONOTONIC, &tp);
88 nexttime = tp.tv_sec;
89 nexttime = nexttime * 1000000 + tp.tv_nsec / 1000 +
90 RELOADDELAY;
91 } else
92 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
93
94 /*
95 * If a reload is pending, poll for received request(s),
96 * otherwise set a RELOADDELAY timeout, since a SIGHUP
97 * could be processed between the got_sighup test and
98 * the select() system call.
99 */
100 tv.tv_sec = 0;
101 if (rpctls_gothup)
102 tv.tv_usec = 0;
103 else
104 tv.tv_usec = RELOADDELAY;
105 readfds = svc_fdset;
106 switch (select(svc_maxfd + 1, &readfds, NULL, NULL, &tv)) {
107 case -1:
108 if (errno == EINTR) {
109 /* Allow a reload now. */
110 nexttime = 0;
111 continue;
112 }
113 syslog(LOG_ERR, "rpc.tls daemon died: select: %m");
114 exit(1);
115 case 0:
116 /* Allow a reload now. */
117 nexttime = 0;
118 continue;
119 default:
120 svc_getreqset(&readfds);
121 }
122 }
123 }
124
125 /*
126 * (re)load the CRLfile into the certificate verification store.
127 */
128 int
rpctls_loadcrlfile(SSL_CTX * ctx)129 rpctls_loadcrlfile(SSL_CTX *ctx)
130 {
131 X509_STORE *certstore;
132 X509_LOOKUP *certlookup;
133 int ret;
134
135 if ((rpctls_verify_cafile != NULL ||
136 rpctls_verify_capath != NULL) &&
137 rpctls_crlfile != NULL) {
138 certstore = SSL_CTX_get_cert_store(ctx);
139 certlookup = X509_STORE_add_lookup(
140 certstore, X509_LOOKUP_file());
141 ret = 0;
142 if (certlookup != NULL)
143 ret = X509_load_crl_file(certlookup,
144 rpctls_crlfile, X509_FILETYPE_PEM);
145 if (ret != 0)
146 ret = X509_STORE_set_flags(certstore,
147 X509_V_FLAG_CRL_CHECK |
148 X509_V_FLAG_CRL_CHECK_ALL);
149 if (ret == 0) {
150 rpctls_verbose_out(
151 "rpctls_loadcrlfile: Can't"
152 " load CRLfile=%s\n",
153 rpctls_crlfile);
154 return (ret);
155 }
156 }
157 return (1);
158 }
159
160 /*
161 * Read the CRL file and check for any extant connections
162 * that might now be revoked.
163 */
164 void
rpctls_checkcrl(void)165 rpctls_checkcrl(void)
166 {
167 struct ssl_entry *slp;
168 BIO *infile;
169 X509_CRL *crl;
170 X509_REVOKED *revoked;
171 char *cp, *cp2, nullstr[1];
172 int ret;
173
174 if (rpctls_crlfile == NULL || (rpctls_verify_cafile == NULL &&
175 rpctls_verify_capath == NULL))
176 return;
177 infile = BIO_new(BIO_s_file());
178 if (infile == NULL) {
179 rpctls_verbose_out("rpctls_checkcrl: Cannot BIO_new\n");
180 return;
181 }
182 ret = BIO_read_filename(infile, rpctls_crlfile);
183 if (ret != 1) {
184 rpctls_verbose_out("rpctls_checkcrl: Cannot read CRL file\n");
185 BIO_free(infile);
186 return;
187 }
188
189 nullstr[0] = '\0';
190 for (crl = PEM_read_bio_X509_CRL(infile, NULL, NULL, nullstr);
191 crl != NULL; crl = PEM_read_bio_X509_CRL(infile, NULL, NULL,
192 nullstr)) {
193 LIST_FOREACH(slp, &rpctls_ssllist, next) {
194 if (slp->cert != NULL) {
195 ret = X509_CRL_get0_by_cert(crl, &revoked,
196 slp->cert);
197 /*
198 * Do a shutdown on the socket, so that it
199 * can no longer be used. The kernel RPC
200 * code will notice the socket is disabled
201 * and will do a disconnect upcall, which will
202 * close the socket.
203 */
204 if (ret == 1) {
205 cp2 = X509_NAME_oneline(
206 X509_get_subject_name(slp->cert),
207 NULL, 0);
208 cp = X509_NAME_oneline(
209 X509_get_issuer_name(slp->cert),
210 NULL, 0);
211 if (rpctls_debug_level == 0)
212 syslog(LOG_INFO | LOG_DAEMON,
213 "rpctls_daemon: Certificate"
214 " Revoked "
215 "issuerName=%s "
216 "subjectName=%s: "
217 "TCP connection closed",
218 cp, cp2);
219 else
220 fprintf(stderr,
221 "rpctls_daemon: Certificate"
222 " Revoked "
223 "issuerName=%s "
224 "subjectName=%s: "
225 "TCP connection closed",
226 cp, cp2);
227 shutdown(slp->s, SHUT_WR);
228 slp->shutoff = true;
229 }
230 }
231 }
232 X509_CRL_free(crl);
233 }
234 BIO_free(infile);
235 }
236
237 void
rpctls_verbose_out(const char * fmt,...)238 rpctls_verbose_out(const char *fmt, ...)
239 {
240 va_list ap;
241
242 if (rpctls_verbose) {
243 va_start(ap, fmt);
244 if (rpctls_debug_level == 0)
245 vsyslog(LOG_INFO | LOG_DAEMON, fmt, ap);
246 else
247 vfprintf(stderr, fmt, ap);
248 va_end(ap);
249 }
250 }
251
252 /*
253 * Check a IP address against any host address in the
254 * certificate. Basically getnameinfo(3) and
255 * X509_check_host().
256 */
257 int
rpctls_checkhost(struct sockaddr * sad,X509 * cert,unsigned int wildcard)258 rpctls_checkhost(struct sockaddr *sad, X509 *cert, unsigned int wildcard)
259 {
260 char hostnam[NI_MAXHOST];
261 int ret;
262
263 if (getnameinfo((const struct sockaddr *)sad,
264 sad->sa_len, hostnam, sizeof(hostnam),
265 NULL, 0, NI_NAMEREQD) != 0)
266 return (0);
267 rpctls_verbose_out("rpctls_checkhost: DNS %s\n",
268 hostnam);
269 ret = X509_check_host(cert, hostnam, strlen(hostnam),
270 wildcard, NULL);
271 return (ret);
272 }
273
274 /*
275 * Get the peer's IP address.
276 */
277 int
rpctls_gethost(int s,struct sockaddr * sad,char * hostip,size_t hostlen)278 rpctls_gethost(int s, struct sockaddr *sad, char *hostip, size_t hostlen)
279 {
280 socklen_t slen;
281 int ret;
282
283 slen = sizeof(struct sockaddr_storage);
284 if (getpeername(s, sad, &slen) < 0)
285 return (0);
286 ret = 0;
287 if (getnameinfo((const struct sockaddr *)sad,
288 sad->sa_len, hostip, hostlen,
289 NULL, 0, NI_NUMERICHOST) == 0) {
290 rpctls_verbose_out("rpctls_gethost: %s\n",
291 hostip);
292 ret = 1;
293 }
294 return (ret);
295 }
296