2
0
mirror of https://github.com/sudo-project/sudo.git synced 2025-08-22 18:08:23 +00:00

Move common TLS client code to tls_client.c and use it in sendlog.c.

This commit is contained in:
Todd C. Miller 2021-04-06 14:44:19 -06:00
parent 71e5275a1c
commit 72c40ae0e1
7 changed files with 337 additions and 192 deletions

View File

@ -358,6 +358,7 @@ logsrvd/regress/fuzz/fuzz_logsrvd_conf.c
logsrvd/regress/fuzz/fuzz_logsrvd_conf.dict logsrvd/regress/fuzz/fuzz_logsrvd_conf.dict
logsrvd/sendlog.c logsrvd/sendlog.c
logsrvd/sendlog.h logsrvd/sendlog.h
logsrvd/tls_client.c
logsrvd/tls_common.h logsrvd/tls_common.h
logsrvd/tls_init.c logsrvd/tls_init.c
m4/ax_append_flag.m4 m4/ax_append_flag.m4

View File

@ -122,7 +122,7 @@ PROGS = sudo_logsrvd sudo_sendlog
LOGSRVD_OBJS = logsrv_util.o iolog_writer.o logsrvd.o logsrvd_conf.o \ LOGSRVD_OBJS = logsrv_util.o iolog_writer.o logsrvd.o logsrvd_conf.o \
tls_init.o tls_init.o
SENDLOG_OBJS = logsrv_util.o sendlog.o tls_init.o SENDLOG_OBJS = logsrv_util.o sendlog.o tls_client.o tls_init.o
IOBJS = $(LOGSRVD_OBJS:.o=.i) $(SENDLOG_OBJS:.o=.i) IOBJS = $(LOGSRVD_OBJS:.o=.i) $(SENDLOG_OBJS:.o=.i)
@ -407,6 +407,24 @@ sendlog.i: $(srcdir)/sendlog.c $(incdir)/compat/getaddrinfo.h \
$(CC) -E -o $@ $(CPPFLAGS) $< $(CC) -E -o $@ $(CPPFLAGS) $<
sendlog.plog: sendlog.i sendlog.plog: sendlog.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/sendlog.c --i-file $< --output-file $@ rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/sendlog.c --i-file $< --output-file $@
tls_client.o: $(srcdir)/tls_client.c $(incdir)/compat/stdbool.h \
$(incdir)/hostcheck.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/logsrv_util.h \
$(srcdir)/tls_common.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/tls_client.c
tls_client.i: $(srcdir)/tls_client.c $(incdir)/compat/stdbool.h \
$(incdir)/hostcheck.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/logsrv_util.h \
$(srcdir)/tls_common.h $(top_builddir)/config.h
$(CC) -E -o $@ $(CPPFLAGS) $<
tls_client.plog: tls_client.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/tls_client.c --i-file $< --output-file $@
tls_init.o: $(srcdir)/tls_init.c $(incdir)/compat/stdbool.h \ tls_init.o: $(srcdir)/tls_init.c $(incdir)/compat/stdbool.h \
$(incdir)/hostcheck.h $(incdir)/sudo_compat.h \ $(incdir)/hostcheck.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \

View File

@ -19,6 +19,8 @@
#ifndef SUDO_LOGSRV_UTIL_H #ifndef SUDO_LOGSRV_UTIL_H
#define SUDO_LOGSRV_UTIL_H #define SUDO_LOGSRV_UTIL_H
#include <netinet/in.h> /* for INET_ADDRSTRLEN and INET6_ADDRSTRLEN */
/* Default ports to listen on */ /* Default ports to listen on */
#define DEFAULT_PORT "30343" #define DEFAULT_PORT "30343"
#define DEFAULT_PORT_TLS "30344" #define DEFAULT_PORT_TLS "30344"
@ -26,6 +28,15 @@
/* Maximum message size (2Mb) */ /* Maximum message size (2Mb) */
#define MESSAGE_SIZE_MAX (2 * 1024 * 1024) #define MESSAGE_SIZE_MAX (2 * 1024 * 1024)
struct peer_info {
const char *name;
#if defined(HAVE_STRUCT_IN6_ADDR)
char ipaddr[INET6_ADDRSTRLEN];
#else
char ipaddr[INET_ADDRSTRLEN];
#endif
};
struct connection_buffer { struct connection_buffer {
TAILQ_ENTRY(connection_buffer) entries; TAILQ_ENTRY(connection_buffer) entries;
uint8_t *data; uint8_t *data;

View File

@ -1,7 +1,7 @@
/* /*
* SPDX-License-Identifier: ISC * SPDX-License-Identifier: ISC
* *
* Copyright (c) 2019-2020 Todd C. Miller <Todd.Miller@sudo.ws> * Copyright (c) 2019-2021 Todd C. Miller <Todd.Miller@sudo.ws>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -80,12 +80,7 @@
TAILQ_HEAD(connection_list, client_closure); TAILQ_HEAD(connection_list, client_closure);
static struct connection_list connections = TAILQ_HEAD_INITIALIZER(connections); static struct connection_list connections = TAILQ_HEAD_INITIALIZER(connections);
static const char *server_name = "localhost"; static struct peer_info server_info = { "localhost" };
#if defined(HAVE_STRUCT_IN6_ADDR)
static char server_ip[INET6_ADDRSTRLEN];
#else
static char server_ip[INET_ADDRSTRLEN];
#endif
static char *iolog_dir; static char *iolog_dir;
static bool testrun = false; static bool testrun = false;
static int nr_of_conns = 1; static int nr_of_conns = 1;
@ -165,7 +160,7 @@ help(void)
* Returns open socket or -1 on error. * Returns open socket or -1 on error.
*/ */
static int static int
connect_server(const char *host, const char *port) connect_server(struct peer_info *server, const char *port)
{ {
struct addrinfo hints, *res, *res0; struct addrinfo hints, *res, *res0;
const char *addr, *cause = "getaddrinfo"; const char *addr, *cause = "getaddrinfo";
@ -175,9 +170,9 @@ connect_server(const char *host, const char *port)
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo(host, port, &hints, &res0); error = getaddrinfo(server->name, port, &hints, &res0);
if (error != 0) { if (error != 0) {
sudo_warnx(U_("unable to look up %s:%s: %s"), host, port, sudo_warnx(U_("unable to look up %s:%s: %s"), server->name, port,
gai_strerror(error)); gai_strerror(error));
debug_return_int(-1); debug_return_int(-1);
} }
@ -197,7 +192,7 @@ connect_server(const char *host, const char *port)
sock = -1; sock = -1;
continue; continue;
} }
if (*server_ip == '\0') { if (server->ipaddr[0] == '\0') {
switch (res->ai_family) { switch (res->ai_family) {
case AF_INET: case AF_INET:
addr = (char *)&((struct sockaddr_in *)res->ai_addr)->sin_addr; addr = (char *)&((struct sockaddr_in *)res->ai_addr)->sin_addr;
@ -213,8 +208,8 @@ connect_server(const char *host, const char *port)
sock = -1; sock = -1;
continue; continue;
} }
if (inet_ntop(res->ai_family, addr, server_ip, if (inet_ntop(res->ai_family, addr, server->ipaddr,
sizeof(server_ip)) == NULL) { sizeof(server->ipaddr)) == NULL) {
sudo_warnx("%s", U_("unable to get server IP addr")); sudo_warnx("%s", U_("unable to get server IP addr"));
} }
} }
@ -377,6 +372,18 @@ fmt_client_hello(struct client_closure *closure)
debug_return_bool(ret); debug_return_bool(ret);
} }
#if defined(HAVE_OPENSSL)
/* Wrapper for fmt_client_hello() called via tls_connect_cb() */
static bool
tls_start_fn(struct tls_client_closure *tls_client)
{
struct client_closure *closure =
__containerof(tls_client, struct client_closure, tls_client);
return fmt_client_hello(closure);
}
#endif /* HAVE_OPENSSL */
static void static void
free_info_messages(InfoMessage **info_msgs, size_t n_info_msgs) free_info_messages(InfoMessage **info_msgs, size_t n_info_msgs)
{ {
@ -1115,13 +1122,14 @@ server_msg_cb(int fd, int what, void *v)
#if defined(HAVE_OPENSSL) #if defined(HAVE_OPENSSL)
if (cert != NULL) { if (cert != NULL) {
SSL *ssl = closure->tls_client.ssl;
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: reading ServerMessage (TLS)", __func__); sudo_debug_printf(SUDO_DEBUG_INFO, "%s: reading ServerMessage (TLS)", __func__);
nread = SSL_read(closure->ssl, buf->data + buf->len, buf->size - buf->len); nread = SSL_read(ssl, buf->data + buf->len, buf->size - buf->len);
if (nread <= 0) { if (nread <= 0) {
const char *errstr; const char *errstr;
int err; int err;
switch (SSL_get_error(closure->ssl, nread)) { switch (SSL_get_error(ssl, nread)) {
case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_ZERO_RETURN:
/* ssl connection shutdown cleanly */ /* ssl connection shutdown cleanly */
nread = 0; nread = 0;
@ -1260,11 +1268,12 @@ client_msg_cb(int fd, int what, void *v)
#if defined(HAVE_OPENSSL) #if defined(HAVE_OPENSSL)
if (cert != NULL) { if (cert != NULL) {
nwritten = SSL_write(closure->ssl, buf->data + buf->off, buf->len - buf->off); SSL *ssl = closure->tls_client.ssl;
nwritten = SSL_write(ssl, buf->data + buf->off, buf->len - buf->off);
if (nwritten <= 0) { if (nwritten <= 0) {
const char *errstr; const char *errstr;
switch (SSL_get_error(closure->ssl, nwritten)) { switch (SSL_get_error(ssl, nwritten)) {
case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_ZERO_RETURN:
/* ssl connection shutdown */ /* ssl connection shutdown */
goto bad; goto bad;
@ -1351,166 +1360,6 @@ parse_timespec(struct timespec *ts, char *strval)
debug_return_bool(true); debug_return_bool(true);
} }
#if defined(HAVE_OPENSSL)
/*
* Check that the server's certificate is valid that it contains the
* server name or IP address.
* Returns 0 if the cert is invalid, else 1.
*/
static int
verify_peer_identity(int preverify_ok, X509_STORE_CTX *ctx)
{
X509 *current_cert;
X509 *peer_cert;
debug_decl(verify_peer_identity, SUDO_DEBUG_UTIL);
/* if pre-verification of the cert failed, just propagate that result back */
if (preverify_ok != 1) {
debug_return_int(0);
}
/* since this callback is called for each cert in the chain,
* check that current cert is the peer's certificate
*/
current_cert = X509_STORE_CTX_get_current_cert(ctx);
peer_cert = X509_STORE_CTX_get0_cert(ctx);
if (current_cert != peer_cert) {
debug_return_int(1);
}
if (validate_hostname(peer_cert, server_name, server_ip, 0) == MatchFound) {
debug_return_int(1);
}
debug_return_int(0);
}
static void
tls_connect_cb(int sock, int what, void *v)
{
struct client_closure *closure = v;
struct sudo_event_base *evbase = closure->evbase;
struct timespec timeo = { TLS_HANDSHAKE_TIMEO_SEC, 0 };
const char *errstr;
int con_stat;
debug_decl(tls_connect_cb, SUDO_DEBUG_UTIL);
if (what == SUDO_EV_TIMEOUT) {
sudo_warnx("%s", U_("TLS handshake timeout occurred"));
goto bad;
}
con_stat = SSL_connect(closure->ssl);
if (con_stat == 1) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"SSL_connect successful");
closure->tls_connect_state = true;
} else {
switch (SSL_get_error(closure->ssl, con_stat)) {
/* TLS handshake is not finished, reschedule event */
case SSL_ERROR_WANT_READ:
sudo_debug_printf(SUDO_DEBUG_NOTICE|SUDO_DEBUG_LINENO,
"SSL_connect returns SSL_ERROR_WANT_READ");
if (what != SUDO_EV_READ) {
if (sudo_ev_set(closure->tls_connect_ev, closure->sock,
SUDO_EV_READ, tls_connect_cb, closure) == -1) {
sudo_warnx("%s", U_("unable to set event"));
goto bad;
}
}
if (sudo_ev_add(evbase, closure->tls_connect_ev, &timeo, false) == -1) {
sudo_warnx("%s", U_("unable to add event to queue"));
goto bad;
}
break;
case SSL_ERROR_WANT_WRITE:
sudo_debug_printf(SUDO_DEBUG_NOTICE|SUDO_DEBUG_LINENO,
"SSL_connect returns SSL_ERROR_WANT_WRITE");
if (what != SUDO_EV_WRITE) {
if (sudo_ev_set(closure->tls_connect_ev, closure->sock,
SUDO_EV_WRITE, tls_connect_cb, closure) == -1) {
sudo_warnx("%s", U_("unable to set event"));
goto bad;
}
}
if (sudo_ev_add(evbase, closure->tls_connect_ev, &timeo, false) == -1) {
sudo_warnx("%s", U_("unable to add event to queue"));
goto bad;
}
break;
case SSL_ERROR_SYSCALL:
sudo_warnx(U_("TLS connection failed: %s"), strerror(errno));
goto bad;
default:
errstr = ERR_reason_error_string(ERR_get_error());
sudo_warnx(U_("TLS connection failed: %s"), errstr);
goto bad;
}
}
if (closure->tls_connect_state) {
if (!testrun) {
printf("Negotiated protocol version: %s\n", SSL_get_version(closure->ssl));
printf("Negotiated ciphersuite: %s\n", SSL_get_cipher(closure->ssl));
}
/* Done with TLS connect, send ClientHello */
sudo_ev_free(closure->tls_connect_ev);
closure->tls_connect_ev = NULL;
if (!fmt_client_hello(closure))
goto bad;
}
debug_return;
bad:
sudo_ev_loopbreak(evbase);
debug_return;
}
static bool
tls_setup(struct client_closure *closure)
{
const char *errstr;
debug_decl(tls_setup, SUDO_DEBUG_UTIL);
ssl_ctx = init_tls_context(ca_bundle, cert, key, NULL, NULL, NULL, false);
if (ssl_ctx == NULL) {
errstr = ERR_reason_error_string(ERR_get_error());
sudo_warnx(U_("Unable to initialize ssl context: %s"), errstr);
goto bad;
}
if (verify_server) {
/* verify server cert during the handshake */
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, verify_peer_identity);
}
if ((closure->ssl = SSL_new(ssl_ctx)) == NULL) {
errstr = ERR_reason_error_string(ERR_get_error());
sudo_warnx(U_("Unable to allocate ssl object: %s"), errstr);
goto bad;
}
if (SSL_set_fd(closure->ssl, closure->sock) <= 0) {
errstr = ERR_reason_error_string(ERR_get_error());
sudo_warnx(U_("Unable to attach socket to the ssl object: %s"),
errstr);
goto bad;
}
if (sudo_ev_add(closure->evbase, closure->tls_connect_ev, NULL, false) == -1) {
sudo_warnx("%s", U_("unable to add event to queue"));
goto bad;
}
debug_return_bool(true);
bad:
debug_return_bool(false);
}
#endif /* HAVE_OPENSSL */
/* /*
* Free client closure contents. * Free client closure contents.
*/ */
@ -1522,11 +1371,11 @@ client_closure_free(struct client_closure *closure)
if (closure != NULL) { if (closure != NULL) {
TAILQ_REMOVE(&connections, closure, entries); TAILQ_REMOVE(&connections, closure, entries);
#if defined(HAVE_OPENSSL) #if defined(HAVE_OPENSSL)
if (closure->ssl != NULL) { if (closure->tls_client.ssl != NULL) {
SSL_shutdown(closure->ssl); SSL_shutdown(closure->tls_client.ssl);
SSL_free(closure->ssl); SSL_free(closure->tls_client.ssl);
} }
sudo_ev_free(closure->tls_connect_ev); sudo_ev_free(closure->tls_client.tls_connect_ev);
#endif #endif
sudo_ev_free(closure->read_ev); sudo_ev_free(closure->read_ev);
sudo_ev_free(closure->write_ev); sudo_ev_free(closure->write_ev);
@ -1588,10 +1437,13 @@ client_closure_alloc(int sock, struct sudo_event_base *base,
#if defined(HAVE_OPENSSL) #if defined(HAVE_OPENSSL)
if (cert != NULL) { if (cert != NULL) {
closure->tls_connect_ev = sudo_ev_alloc(sock, SUDO_EV_WRITE, closure->tls_client.tls_connect_ev = sudo_ev_alloc(sock, SUDO_EV_WRITE,
tls_connect_cb, closure); tls_connect_cb, &closure->tls_client);
if (closure->tls_connect_ev == NULL) if (closure->tls_client.tls_connect_ev == NULL)
goto bad; goto bad;
closure->tls_client.evbase = base;
closure->tls_client.peer_name = &server_info;
closure->tls_client.start_fn = tls_start_fn;
} }
#endif #endif
@ -1673,7 +1525,7 @@ main(int argc, char *argv[])
accept_only = true; accept_only = true;
break; break;
case 'h': case 'h':
server_name = optarg; server_info.name = optarg;
break; break;
case 'i': case 'i':
iolog_id = optarg; iolog_id = optarg;
@ -1766,12 +1618,12 @@ main(int argc, char *argv[])
printf("connecting clients...\n"); printf("connecting clients...\n");
for (int i = 0; i < nr_of_conns; i++) { for (int i = 0; i < nr_of_conns; i++) {
sock = connect_server(server_name, port); sock = connect_server(&server_info, port);
if (sock == -1) if (sock == -1)
goto bad; goto bad;
if (!testrun) if (!testrun)
printf("Connected to %s:%s\n", server_name, port); printf("Connected to %s:%s\n", server_info.name, port);
closure = client_closure_alloc(sock, evbase, &elapsed, &restart, closure = client_closure_alloc(sock, evbase, &elapsed, &restart,
iolog_id, reject_reason, accept_only, evlog); iolog_id, reject_reason, accept_only, evlog);
@ -1789,7 +1641,8 @@ main(int argc, char *argv[])
#if defined(HAVE_OPENSSL) #if defined(HAVE_OPENSSL)
if (cert != NULL) { if (cert != NULL) {
if (!tls_setup(closure)) if (!tls_client_setup(closure->sock, ca_bundle, cert, key, NULL,
NULL, NULL, verify_server, false, &closure->tls_client))
goto bad; goto bad;
} else } else
#endif #endif

View File

@ -59,9 +59,7 @@ struct client_closure {
struct connection_buffer read_buf; struct connection_buffer read_buf;
struct connection_buffer write_buf; struct connection_buffer write_buf;
#if defined(HAVE_OPENSSL) #if defined(HAVE_OPENSSL)
SSL *ssl; struct tls_client_closure tls_client;
struct sudo_event *tls_connect_ev;
bool tls_connect_state;
#endif #endif
struct sudo_event *read_ev; struct sudo_event *read_ev;
struct sudo_event *write_ev; struct sudo_event *write_ev;

250
logsrvd/tls_client.c Normal file
View File

@ -0,0 +1,250 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2019-2021 Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <errno.h>
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif /* HAVE_STDBOOL_H */
#if defined(HAVE_STDINT_H)
# include <stdint.h>
#elif defined(HAVE_INTTYPES_H)
# include <inttypes.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#if defined(HAVE_OPENSSL)
# include <openssl/ssl.h>
# include <openssl/err.h>
#endif
#include "sudo_compat.h"
#include "sudo_debug.h"
#include "sudo_event.h"
#include "sudo_fatal.h"
#include "sudo_gettext.h"
#include "sudo_util.h"
#include "hostcheck.h"
#include "logsrv_util.h"
#include "tls_common.h"
#define TLS_HANDSHAKE_TIMEO_SEC 10
#if defined(HAVE_OPENSSL)
/*
* Check that the server's certificate is valid that it contains the
* server name or IP address.
* Returns 0 if the cert is invalid, else 1.
*/
static int
verify_peer_identity(int preverify_ok, X509_STORE_CTX *ctx)
{
HostnameValidationResult result;
struct peer_info *peer_info;
SSL *ssl;
X509 *current_cert;
X509 *peer_cert;
debug_decl(verify_peer_identity, SUDO_DEBUG_UTIL);
/* if pre-verification of the cert failed, just propagate that result back */
if (preverify_ok != 1) {
debug_return_int(0);
}
/*
* Since this callback is called for each cert in the chain,
* check that current cert is the peer's certificate
*/
current_cert = X509_STORE_CTX_get_current_cert(ctx);
peer_cert = X509_STORE_CTX_get0_cert(ctx);
if (current_cert != peer_cert) {
debug_return_int(1);
}
/* Fetch the attached peer_info from the ssl connection object. */
ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
peer_info = SSL_get_ex_data(ssl, 1);
/*
* Validate the cert based on the host name and IP address.
* If host name is not known, validate_hostname() can resolve it.
*/
result = validate_hostname(peer_cert,
peer_info->name ? peer_info->name : peer_info->ipaddr,
peer_info->ipaddr, peer_info->name ? 0 : 1);
debug_return_int(result == MatchFound);
}
void
tls_connect_cb(int sock, int what, void *v)
{
struct tls_client_closure *tls_client = v;
struct sudo_event_base *evbase = tls_client->evbase;
struct timespec timeo = { TLS_HANDSHAKE_TIMEO_SEC, 0 };
const char *errstr;
int con_stat;
debug_decl(tls_connect_cb, SUDO_DEBUG_UTIL);
if (what == SUDO_EV_TIMEOUT) {
sudo_warnx("%s", U_("TLS handshake timeout occurred"));
goto bad;
}
con_stat = SSL_connect(tls_client->ssl);
if (con_stat == 1) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"SSL_connect successful");
tls_client->tls_connect_state = true;
} else {
switch (SSL_get_error(tls_client->ssl, con_stat)) {
/* TLS handshake is not finished, reschedule event */
case SSL_ERROR_WANT_READ:
sudo_debug_printf(SUDO_DEBUG_NOTICE|SUDO_DEBUG_LINENO,
"SSL_connect returns SSL_ERROR_WANT_READ");
if (what != SUDO_EV_READ) {
if (sudo_ev_set(tls_client->tls_connect_ev,
SSL_get_fd(tls_client->ssl), SUDO_EV_READ,
tls_connect_cb, tls_client) == -1) {
sudo_warnx("%s", U_("unable to set event"));
goto bad;
}
}
if (sudo_ev_add(evbase, tls_client->tls_connect_ev, &timeo, false) == -1) {
sudo_warnx("%s", U_("unable to add event to queue"));
goto bad;
}
break;
case SSL_ERROR_WANT_WRITE:
sudo_debug_printf(SUDO_DEBUG_NOTICE|SUDO_DEBUG_LINENO,
"SSL_connect returns SSL_ERROR_WANT_WRITE");
if (what != SUDO_EV_WRITE) {
if (sudo_ev_set(tls_client->tls_connect_ev,
SSL_get_fd(tls_client->ssl), SUDO_EV_WRITE,
tls_connect_cb, tls_client) == -1) {
sudo_warnx("%s", U_("unable to set event"));
goto bad;
}
}
if (sudo_ev_add(evbase, tls_client->tls_connect_ev, &timeo, false) == -1) {
sudo_warnx("%s", U_("unable to add event to queue"));
goto bad;
}
break;
case SSL_ERROR_SYSCALL:
sudo_warnx(U_("TLS connection failed: %s"), strerror(errno));
goto bad;
default:
errstr = ERR_reason_error_string(ERR_get_error());
sudo_warnx(U_("TLS connection failed: %s"), errstr);
goto bad;
}
}
if (tls_client->tls_connect_state) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"TLS version: %s, negotiated cipher suite: %s",
SSL_get_version(tls_client->ssl), SSL_get_cipher(tls_client->ssl));
/* Done with TLS connect, send ClientHello */
sudo_ev_free(tls_client->tls_connect_ev);
tls_client->tls_connect_ev = NULL;
if (!tls_client->start_fn(tls_client))
goto bad;
}
debug_return;
bad:
sudo_ev_loopbreak(evbase);
debug_return;
}
bool
tls_ctx_client_setup(SSL_CTX *ssl_ctx, int sock,
struct tls_client_closure *closure)
{
const char *errstr;
bool ret = false;
debug_decl(tls_ctx_client_setup, SUDO_DEBUG_UTIL);
if ((closure->ssl = SSL_new(ssl_ctx)) == NULL) {
errstr = ERR_reason_error_string(ERR_get_error());
sudo_warnx(U_("unable to allocate ssl object: %s"), errstr);
goto done;
}
if (SSL_set_ex_data(closure->ssl, 1, closure->peer_name) <= 0) {
errstr = ERR_reason_error_string(ERR_get_error());
sudo_warnx(U_("Unable to attach user data to the ssl object: %s"),
errstr);
goto done;
}
if (SSL_set_fd(closure->ssl, sock) <= 0) {
errstr = ERR_reason_error_string(ERR_get_error());
sudo_warnx(U_("Unable to attach socket to the ssl object: %s"),
errstr);
goto done;
}
if (sudo_ev_add(closure->evbase, closure->tls_connect_ev, NULL, false) == -1) {
sudo_warnx("%s", U_("unable to add event to queue"));
goto done;
}
ret = true;
done:
debug_return_bool(ret);
}
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)
{
SSL_CTX *ssl_ctx;
debug_decl(tls_client_setup, SUDO_DEBUG_UTIL);
ssl_ctx = init_tls_context(ca_bundle_file, cert_file, key_file,
dhparam_file, ciphers_v12,ciphers_v13, verify_server);
if (ssl_ctx == NULL) {
sudo_warnx(U_("unable to initialize TLS context"));
debug_return_bool(false);
}
if (check_peer) {
/* Verify server cert during the handshake. */
SSL_CTX_set_verify(ssl_ctx,
SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
verify_peer_identity);
}
debug_return_bool(tls_ctx_client_setup(ssl_ctx, sock, closure));
}
#endif /* HAVE_OPENSSL */

View File

@ -24,6 +24,20 @@
#if defined(HAVE_OPENSSL) #if defined(HAVE_OPENSSL)
# include <openssl/ssl.h> # include <openssl/ssl.h>
struct tls_client_closure {
SSL *ssl;
struct sudo_event_base *evbase; /* duplicated */
struct sudo_event *tls_connect_ev;
struct peer_info *peer_name;
bool (*start_fn)(struct tls_client_closure *);
bool tls_connect_state;
};
/* tls_client.c */
void tls_connect_cb(int sock, int what, void *v);
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);
bool tls_ctx_client_setup(SSL_CTX *ssl_ctx, int sock, struct tls_client_closure *closure);
/* tls_init.c */ /* tls_init.c */
SSL_CTX *init_tls_context(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_cert); SSL_CTX *init_tls_context(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_cert);