1 /*
2 * SPDX-License-Identifier: ISC
3 *
4 * Copyright (c) 2019-2021 Todd C. Miller <Todd.Miller@sudo.ws>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include "config.h"
20
21 #include <errno.h>
22 #ifdef HAVE_STDBOOL_H
23 # include <stdbool.h>
24 #else
25 # include "compat/stdbool.h"
26 #endif /* HAVE_STDBOOL_H */
27 #if defined(HAVE_STDINT_H)
28 # include <stdint.h>
29 #elif defined(HAVE_INTTYPES_H)
30 # include <inttypes.h>
31 #endif
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36
37 #if defined(HAVE_OPENSSL)
38 # include <openssl/ssl.h>
39 # include <openssl/err.h>
40 #endif
41
42 #include "sudo_compat.h"
43 #include "sudo_debug.h"
44 #include "sudo_event.h"
45 #include "sudo_fatal.h"
46 #include "sudo_gettext.h"
47 #include "sudo_util.h"
48
49 #include "hostcheck.h"
50 #include "logsrv_util.h"
51 #include "tls_common.h"
52
53 #if defined(HAVE_OPENSSL)
54
55 /*
56 * Check that the server's certificate is valid that it contains the
57 * server name or IP address.
58 * Returns 0 if the cert is invalid, else 1.
59 */
60 static int
verify_peer_identity(int preverify_ok,X509_STORE_CTX * ctx)61 verify_peer_identity(int preverify_ok, X509_STORE_CTX *ctx)
62 {
63 HostnameValidationResult result;
64 struct peer_info *peer_info;
65 SSL *ssl;
66 X509 *current_cert;
67 X509 *peer_cert;
68 debug_decl(verify_peer_identity, SUDO_DEBUG_UTIL);
69
70 /* if pre-verification of the cert failed, just propagate that result back */
71 if (preverify_ok != 1) {
72 debug_return_int(0);
73 }
74
75 /*
76 * Since this callback is called for each cert in the chain,
77 * check that current cert is the peer's certificate
78 */
79 current_cert = X509_STORE_CTX_get_current_cert(ctx);
80 peer_cert = X509_STORE_CTX_get0_cert(ctx);
81 if (current_cert != peer_cert) {
82 debug_return_int(1);
83 }
84
85 /* Fetch the attached peer_info from the ssl connection object. */
86 ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
87 peer_info = SSL_get_ex_data(ssl, 1);
88
89 /*
90 * Validate the cert based on the host name and IP address.
91 * If host name is not known, validate_hostname() can resolve it.
92 */
93 result = validate_hostname(peer_cert,
94 peer_info->name ? peer_info->name : peer_info->ipaddr,
95 peer_info->ipaddr, peer_info->name ? 0 : 1);
96
97 debug_return_int(result == MatchFound);
98 }
99
100 void
tls_connect_cb(int sock,int what,void * v)101 tls_connect_cb(int sock, int what, void *v)
102 {
103 struct tls_client_closure *tls_client = v;
104 struct sudo_event_base *evbase = tls_client->evbase;
105 const struct timespec *timeout = &tls_client->connect_timeout;
106 const char *errstr;
107 int con_stat;
108 debug_decl(tls_connect_cb, SUDO_DEBUG_UTIL);
109
110 if (what == SUDO_EV_TIMEOUT) {
111 sudo_warnx("%s", U_("TLS handshake timeout occurred"));
112 goto bad;
113 }
114
115 con_stat = SSL_connect(tls_client->ssl);
116
117 if (con_stat == 1) {
118 sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
119 "SSL_connect successful");
120 tls_client->tls_connect_state = true;
121 } else {
122 switch (SSL_get_error(tls_client->ssl, con_stat)) {
123 /* TLS handshake is not finished, reschedule event */
124 case SSL_ERROR_WANT_READ:
125 sudo_debug_printf(SUDO_DEBUG_NOTICE|SUDO_DEBUG_LINENO,
126 "SSL_connect returns SSL_ERROR_WANT_READ");
127 if (what != SUDO_EV_READ) {
128 if (sudo_ev_set(tls_client->tls_connect_ev,
129 SSL_get_fd(tls_client->ssl), SUDO_EV_READ,
130 tls_connect_cb, tls_client) == -1) {
131 sudo_warnx("%s", U_("unable to set event"));
132 goto bad;
133 }
134 }
135 if (sudo_ev_add(evbase, tls_client->tls_connect_ev, timeout, false) == -1) {
136 sudo_warnx("%s", U_("unable to add event to queue"));
137 goto bad;
138 }
139 break;
140 case SSL_ERROR_WANT_WRITE:
141 sudo_debug_printf(SUDO_DEBUG_NOTICE|SUDO_DEBUG_LINENO,
142 "SSL_connect returns SSL_ERROR_WANT_WRITE");
143 if (what != SUDO_EV_WRITE) {
144 if (sudo_ev_set(tls_client->tls_connect_ev,
145 SSL_get_fd(tls_client->ssl), SUDO_EV_WRITE,
146 tls_connect_cb, tls_client) == -1) {
147 sudo_warnx("%s", U_("unable to set event"));
148 goto bad;
149 }
150 }
151 if (sudo_ev_add(evbase, tls_client->tls_connect_ev, timeout, false) == -1) {
152 sudo_warnx("%s", U_("unable to add event to queue"));
153 goto bad;
154 }
155 break;
156 case SSL_ERROR_SYSCALL:
157 sudo_warnx(U_("TLS connection failed: %s"), strerror(errno));
158 goto bad;
159 default:
160 errstr = ERR_reason_error_string(ERR_get_error());
161 sudo_warnx(U_("TLS connection failed: %s"), errstr);
162 goto bad;
163 }
164 }
165
166 if (tls_client->tls_connect_state) {
167 sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
168 "TLS version: %s, negotiated cipher suite: %s",
169 SSL_get_version(tls_client->ssl), SSL_get_cipher(tls_client->ssl));
170
171 /* Done with TLS connect, send ClientHello */
172 sudo_ev_free(tls_client->tls_connect_ev);
173 tls_client->tls_connect_ev = NULL;
174 if (!tls_client->start_fn(tls_client))
175 goto bad;
176 }
177
178 debug_return;
179
180 bad:
181 sudo_ev_loopbreak(evbase);
182 debug_return;
183 }
184
185 bool
tls_ctx_client_setup(SSL_CTX * ssl_ctx,int sock,struct tls_client_closure * closure)186 tls_ctx_client_setup(SSL_CTX *ssl_ctx, int sock,
187 struct tls_client_closure *closure)
188 {
189 const char *errstr;
190 bool ret = false;
191 debug_decl(tls_ctx_client_setup, SUDO_DEBUG_UTIL);
192
193 if ((closure->ssl = SSL_new(ssl_ctx)) == NULL) {
194 errstr = ERR_reason_error_string(ERR_get_error());
195 sudo_warnx(U_("unable to allocate ssl object: %s"), errstr);
196 goto done;
197 }
198
199 if (SSL_set_ex_data(closure->ssl, 1, closure->peer_name) <= 0) {
200 errstr = ERR_reason_error_string(ERR_get_error());
201 sudo_warnx(U_("Unable to attach user data to the ssl object: %s"),
202 errstr);
203 goto done;
204 }
205
206 if (SSL_set_fd(closure->ssl, sock) <= 0) {
207 errstr = ERR_reason_error_string(ERR_get_error());
208 sudo_warnx(U_("Unable to attach socket to the ssl object: %s"),
209 errstr);
210 goto done;
211 }
212
213 if (sudo_ev_add(closure->evbase, closure->tls_connect_ev, NULL, false) == -1) {
214 sudo_warnx("%s", U_("unable to add event to queue"));
215 goto done;
216 }
217
218 ret = true;
219
220 done:
221 debug_return_bool(ret);
222 }
223
224 bool
tls_client_setup(int sock,const char * ca_bundle_file,const char * cert_file,const char * key_file,const char * dhparam_file,const char * ciphers_v12,const char * ciphers_v13,bool verify_server,bool check_peer,struct tls_client_closure * closure)225 tls_client_setup(int sock, const char *ca_bundle_file, const char *cert_file,
226 const char *key_file, const char *dhparam_file, const char *ciphers_v12,
227 const char *ciphers_v13, bool verify_server, bool check_peer,
228 struct tls_client_closure *closure)
229 {
230 SSL_CTX *ssl_ctx;
231 debug_decl(tls_client_setup, SUDO_DEBUG_UTIL);
232
233 ssl_ctx = init_tls_context(ca_bundle_file, cert_file, key_file,
234 dhparam_file, ciphers_v12, ciphers_v13, verify_server);
235 if (ssl_ctx == NULL) {
236 sudo_warnx(U_("unable to initialize TLS context"));
237 debug_return_bool(false);
238 }
239
240 if (check_peer) {
241 /* Verify server cert during the handshake. */
242 SSL_CTX_set_verify(ssl_ctx,
243 SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
244 verify_peer_identity);
245 }
246
247 debug_return_bool(tls_ctx_client_setup(ssl_ctx, sock, closure));
248 }
249 #endif /* HAVE_OPENSSL */
250