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
rpctls_svc_run(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
rpctls_loadcrlfile(SSL_CTX * ctx)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
rpctls_checkcrl(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
rpctls_verbose_out(const char * fmt,...)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
rpctls_checkhost(struct sockaddr * sad,X509 * cert,unsigned int wildcard)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
rpctls_gethost(int s,struct sockaddr * sad,char * hostip,size_t hostlen)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