]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/rpc.tlsservd/rpc.tlscommon.c
Merge/update to bmake-20230126
[FreeBSD/FreeBSD.git] / usr.sbin / rpc.tlsservd / rpc.tlscommon.c
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
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
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
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
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
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
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 }