mirror of
https://github.com/sudo-project/sudo.git
synced 2025-08-22 09:57:41 +00:00
Move common TLS client code to tls_client.c and use it in sendlog.c.
This commit is contained in:
parent
71e5275a1c
commit
72c40ae0e1
1
MANIFEST
1
MANIFEST
@ -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
|
||||||
|
@ -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 \
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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
250
logsrvd/tls_client.c
Normal 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 */
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user