mirror of
https://github.com/sudo-project/sudo.git
synced 2025-08-22 01:49:11 +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/sendlog.c
|
||||
logsrvd/sendlog.h
|
||||
logsrvd/tls_client.c
|
||||
logsrvd/tls_common.h
|
||||
logsrvd/tls_init.c
|
||||
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 \
|
||||
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)
|
||||
|
||||
@ -407,6 +407,24 @@ sendlog.i: $(srcdir)/sendlog.c $(incdir)/compat/getaddrinfo.h \
|
||||
$(CC) -E -o $@ $(CPPFLAGS) $<
|
||||
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 $@
|
||||
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 \
|
||||
$(incdir)/hostcheck.h $(incdir)/sudo_compat.h \
|
||||
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
|
||||
|
@ -19,6 +19,8 @@
|
||||
#ifndef SUDO_LOGSRV_UTIL_H
|
||||
#define SUDO_LOGSRV_UTIL_H
|
||||
|
||||
#include <netinet/in.h> /* for INET_ADDRSTRLEN and INET6_ADDRSTRLEN */
|
||||
|
||||
/* Default ports to listen on */
|
||||
#define DEFAULT_PORT "30343"
|
||||
#define DEFAULT_PORT_TLS "30344"
|
||||
@ -26,6 +28,15 @@
|
||||
/* Maximum message size (2Mb) */
|
||||
#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 {
|
||||
TAILQ_ENTRY(connection_buffer) entries;
|
||||
uint8_t *data;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@ -80,12 +80,7 @@
|
||||
TAILQ_HEAD(connection_list, client_closure);
|
||||
static struct connection_list connections = TAILQ_HEAD_INITIALIZER(connections);
|
||||
|
||||
static const char *server_name = "localhost";
|
||||
#if defined(HAVE_STRUCT_IN6_ADDR)
|
||||
static char server_ip[INET6_ADDRSTRLEN];
|
||||
#else
|
||||
static char server_ip[INET_ADDRSTRLEN];
|
||||
#endif
|
||||
static struct peer_info server_info = { "localhost" };
|
||||
static char *iolog_dir;
|
||||
static bool testrun = false;
|
||||
static int nr_of_conns = 1;
|
||||
@ -165,7 +160,7 @@ help(void)
|
||||
* Returns open socket or -1 on error.
|
||||
*/
|
||||
static int
|
||||
connect_server(const char *host, const char *port)
|
||||
connect_server(struct peer_info *server, const char *port)
|
||||
{
|
||||
struct addrinfo hints, *res, *res0;
|
||||
const char *addr, *cause = "getaddrinfo";
|
||||
@ -175,9 +170,9 @@ connect_server(const char *host, const char *port)
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
error = getaddrinfo(host, port, &hints, &res0);
|
||||
error = getaddrinfo(server->name, port, &hints, &res0);
|
||||
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));
|
||||
debug_return_int(-1);
|
||||
}
|
||||
@ -197,7 +192,7 @@ connect_server(const char *host, const char *port)
|
||||
sock = -1;
|
||||
continue;
|
||||
}
|
||||
if (*server_ip == '\0') {
|
||||
if (server->ipaddr[0] == '\0') {
|
||||
switch (res->ai_family) {
|
||||
case AF_INET:
|
||||
addr = (char *)&((struct sockaddr_in *)res->ai_addr)->sin_addr;
|
||||
@ -213,8 +208,8 @@ connect_server(const char *host, const char *port)
|
||||
sock = -1;
|
||||
continue;
|
||||
}
|
||||
if (inet_ntop(res->ai_family, addr, server_ip,
|
||||
sizeof(server_ip)) == NULL) {
|
||||
if (inet_ntop(res->ai_family, addr, server->ipaddr,
|
||||
sizeof(server->ipaddr)) == NULL) {
|
||||
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);
|
||||
}
|
||||
|
||||
#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
|
||||
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 (cert != NULL) {
|
||||
SSL *ssl = closure->tls_client.ssl;
|
||||
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) {
|
||||
const char *errstr;
|
||||
int err;
|
||||
|
||||
switch (SSL_get_error(closure->ssl, nread)) {
|
||||
switch (SSL_get_error(ssl, nread)) {
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
/* ssl connection shutdown cleanly */
|
||||
nread = 0;
|
||||
@ -1260,11 +1268,12 @@ client_msg_cb(int fd, int what, void *v)
|
||||
|
||||
#if defined(HAVE_OPENSSL)
|
||||
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) {
|
||||
const char *errstr;
|
||||
|
||||
switch (SSL_get_error(closure->ssl, nwritten)) {
|
||||
switch (SSL_get_error(ssl, nwritten)) {
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
/* ssl connection shutdown */
|
||||
goto bad;
|
||||
@ -1351,166 +1360,6 @@ parse_timespec(struct timespec *ts, char *strval)
|
||||
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.
|
||||
*/
|
||||
@ -1522,11 +1371,11 @@ client_closure_free(struct client_closure *closure)
|
||||
if (closure != NULL) {
|
||||
TAILQ_REMOVE(&connections, closure, entries);
|
||||
#if defined(HAVE_OPENSSL)
|
||||
if (closure->ssl != NULL) {
|
||||
SSL_shutdown(closure->ssl);
|
||||
SSL_free(closure->ssl);
|
||||
if (closure->tls_client.ssl != NULL) {
|
||||
SSL_shutdown(closure->tls_client.ssl);
|
||||
SSL_free(closure->tls_client.ssl);
|
||||
}
|
||||
sudo_ev_free(closure->tls_connect_ev);
|
||||
sudo_ev_free(closure->tls_client.tls_connect_ev);
|
||||
#endif
|
||||
sudo_ev_free(closure->read_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 (cert != NULL) {
|
||||
closure->tls_connect_ev = sudo_ev_alloc(sock, SUDO_EV_WRITE,
|
||||
tls_connect_cb, closure);
|
||||
if (closure->tls_connect_ev == NULL)
|
||||
closure->tls_client.tls_connect_ev = sudo_ev_alloc(sock, SUDO_EV_WRITE,
|
||||
tls_connect_cb, &closure->tls_client);
|
||||
if (closure->tls_client.tls_connect_ev == NULL)
|
||||
goto bad;
|
||||
closure->tls_client.evbase = base;
|
||||
closure->tls_client.peer_name = &server_info;
|
||||
closure->tls_client.start_fn = tls_start_fn;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1673,7 +1525,7 @@ main(int argc, char *argv[])
|
||||
accept_only = true;
|
||||
break;
|
||||
case 'h':
|
||||
server_name = optarg;
|
||||
server_info.name = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
iolog_id = optarg;
|
||||
@ -1766,12 +1618,12 @@ main(int argc, char *argv[])
|
||||
printf("connecting clients...\n");
|
||||
|
||||
for (int i = 0; i < nr_of_conns; i++) {
|
||||
sock = connect_server(server_name, port);
|
||||
sock = connect_server(&server_info, port);
|
||||
if (sock == -1)
|
||||
goto bad;
|
||||
|
||||
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,
|
||||
iolog_id, reject_reason, accept_only, evlog);
|
||||
@ -1789,7 +1641,8 @@ main(int argc, char *argv[])
|
||||
|
||||
#if defined(HAVE_OPENSSL)
|
||||
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;
|
||||
} else
|
||||
#endif
|
||||
|
@ -59,9 +59,7 @@ struct client_closure {
|
||||
struct connection_buffer read_buf;
|
||||
struct connection_buffer write_buf;
|
||||
#if defined(HAVE_OPENSSL)
|
||||
SSL *ssl;
|
||||
struct sudo_event *tls_connect_ev;
|
||||
bool tls_connect_state;
|
||||
struct tls_client_closure tls_client;
|
||||
#endif
|
||||
struct sudo_event *read_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)
|
||||
# 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 */
|
||||
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