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