Merge pull request #9 from sudo-project/audit-server-tls-support

Audit server tls support
This commit is contained in:
Todd C. Miller
2019-11-15 11:30:39 -07:00
committed by GitHub
7 changed files with 821 additions and 22 deletions

3
configure vendored
View File

@@ -744,6 +744,7 @@ LOCALEDIR_SUFFIX
SUDO_NLS
LIBPTHREAD
LIBMD
OPENSSL_LIBS
LIBINTL
LIBRT
LIBDL
@@ -3152,6 +3153,7 @@ PSMAN=0
SEMAN=0
LIBINTL=
LIBMD=
OPENSSL_LIBS=
ZLIB=
ZLIB_SRC=
AUTH_OBJS=
@@ -6428,6 +6430,7 @@ if test "${enable_openssl+set}" = set; then :
enableval=$enable_openssl; case $enableval in
no) ;;
*) LIBMD="-lcrypto"
OPENSSL_LIBS="-lcrypto -lssl"
DIGEST=digest_openssl.lo
$as_echo "#define HAVE_OPENSSL 1" >>confdefs.h

View File

@@ -17,6 +17,51 @@
# The default is to listen on all addresses.
#listen_address = *:30344
# Sets timeout for the socket. If this parameter is not set,
# the value will be 0 (no timeout)
#timeout = 30
# Sets audit server's communication over TLS on/off.
# Minimum negotiable TLS version is 1.2
#tls = true
# Path to the certificate authority bundle file
# It must be in PEM format!
#tls_cacert = /etc/sudo_logsrvd_ca.pem
# Path to the audit server's private key file
# It must be in PEM format!
#tls_key = /etc/sudo_logsrvd_key.pem
# Path to the audit server's certificate file
# It must be in PEM format!
#tls_cert = /etc/sudo_logsrvd_cert.pem
# Path to the Diffie-Hellman parameter file
# If this parameter is not set, the audit server
# will use the OpenSSL defaults for Diffie-Hellman key generation
# It must be in PEM format!
#tls_dhparams = /etc/sudo_logsrvd_dhparams.pem
# TLS cipher list (see "CIPHER LIST FORMAT" in the OpenSSL manual)
# NOTE that this setting is only effective if the negotiated protocol
# is TLSver1.2.
#tls_ciphers_v12 = HIGH:!aNULL
# TLS cipher list if negotiated protocol is TLSver1.3
# available ciphersuites are:
# TLS_AES_128_GCM_SHA256
# TLS_AES_256_GCM_SHA384
# TLS_CHACHA20_POLY1305_SHA256
# TLS_AES_128_CCM_SHA256
# TLS_AES_128_CCM_8_SHA256
# multiple ciphersuites can be set separated by ':'
# by default, all ciphersuites are enabled
#tls_ciphers_v13 = TLS_AES_256_GCM_SHA384
# Validate client certificates
#tls_checkpeer = false
[iolog]
# The top-level directory to use when constructing the path name for the
# I/O log directory. The session sequence number, if any, is stored here.

View File

@@ -72,6 +72,8 @@ PIE_LDFLAGS = @PIE_LDFLAGS@
SSP_CFLAGS = @SSP_CFLAGS@
SSP_LDFLAGS = @SSP_LDFLAGS@
OPENSSL_LIBS = @OPENSSL_LIBS@
# cppcheck options, usually set in the top-level Makefile
CPPCHECK_OPTS = -q --force --enable=warning,performance,portability --suppress=constStatement --error-exitcode=1 --inline-suppr -Dva_copy=va_copy -U__cplusplus -UQUAD_MAX -UQUAD_MIN -UUQUAD_MAX -U_POSIX_HOST_NAME_MAX -U_POSIX_PATH_MAX -U__NBBY -DNSIG=64
@@ -145,10 +147,10 @@ Makefile: $(srcdir)/Makefile.in
ifile=$<; rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $${ifile%i}c --i-file $< --output-file $@
sudo_logsrvd: $(LOGSRVD_OBJS) $(LT_LIBS)
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(LOGSRVD_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS)
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(LOGSRVD_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) $(OPENSSL_LIBS)
sudo_sendlog: $(SENDLOG_OBJS) $(LT_LIBS)
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(SENDLOG_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS)
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(SENDLOG_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) $(OPENSSL_LIBS)
GENERATED = log_server.pb-c.h log_server.pb-c.c

View File

@@ -40,6 +40,11 @@
#include <time.h>
#include <unistd.h>
#if defined(HAVE_OPENSSL)
# include <openssl/ssl.h>
# include <openssl/err.h>
#endif
#include "log_server.pb-c.h"
#include "sudo_gettext.h" /* must be included before sudo_compat.h */
#include "sudo_compat.h"
@@ -60,6 +65,11 @@
# include "compat/getopt.h"
#endif /* HAVE_GETOPT_LONG */
#if defined(HAVE_OPENSSL)
# define LOGSRVD_DEFAULT_CIPHER_LST12 "HIGH:!aNULL"
# define LOGSRVD_DEFAULT_CIPHER_LST13 "TLS_AES_256_GCM_SHA384"
#endif
/*
* Sudo I/O audit server.
*/
@@ -81,6 +91,13 @@ connection_closure_free(struct connection_closure *closure)
if (closure != NULL) {
bool shutting_down = closure->state == SHUTDOWN;
#if defined(HAVE_OPENSSL)
/* deallocate the connection's ssl object */
if (logsrvd_conf_get_tls_opt() == true) {
if (closure->ssl)
SSL_free(closure->ssl);
}
#endif
TAILQ_REMOVE(&connections, closure, entries);
close(closure->sock);
iolog_close_all(closure);
@@ -556,6 +573,12 @@ shutdown_cb(int unused, int what, void *v)
struct sudo_event_base *base = v;
debug_decl(shutdown_cb, SUDO_DEBUG_UTIL)
#if defined(HAVE_OPENSSL)
/* deallocate server's SSL context object */
if (logsrvd_conf_get_tls_opt() == true) {
SSL_CTX_free(logsrvd_get_tls_runtime()->ssl_ctx);
}
#endif
sudo_ev_loopbreak(base);
debug_return;
@@ -614,7 +637,16 @@ server_msg_cb(int fd, int what, void *v)
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: sending %u bytes to client",
__func__, buf->len - buf->off);
#if defined(HAVE_OPENSSL)
if (logsrvd_conf_get_tls_opt() == true) {
nwritten = SSL_write(closure->ssl, buf->data + buf->off, buf->len - buf->off);
} else {
nwritten = send(fd, buf->data + buf->off, buf->len - buf->off, 0);
}
#else
nwritten = send(fd, buf->data + buf->off, buf->len - buf->off, 0);
#endif
if (nwritten == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"unable to send %u bytes", buf->len - buf->off);
@@ -652,7 +684,16 @@ client_msg_cb(int fd, int what, void *v)
ssize_t nread;
debug_decl(client_msg_cb, SUDO_DEBUG_UTIL)
nread = recv(fd, buf->data + buf->len, buf->size - buf->len, 0);
#if defined(HAVE_OPENSSL)
if (logsrvd_conf_get_tls_opt() == true) {
nread = SSL_read(closure->ssl, buf->data + buf->len, buf->size);
} else {
nread = recv(fd, buf->data + buf->len, buf->size - buf->len, 0);
}
#else
nread = recv(fd, buf->data + buf->len, buf->size - buf->len, 0);
#endif
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: received %zd bytes from client",
__func__, nread);
switch (nread) {
@@ -784,6 +825,306 @@ signal_cb(int signo, int what, void *v)
debug_return;
}
#if defined(HAVE_OPENSSL)
static X509 *
load_cert(const char *file)
{
X509 *x509 = NULL;
BIO *cert = NULL;
debug_decl(load_cert, SUDO_DEBUG_UTIL)
if ((cert = BIO_new(BIO_s_file())) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to allocate new BIO object for certificate: %s",
ERR_error_string(ERR_get_error(), NULL));
goto exit;
}
if (BIO_read_filename(cert, file) <= 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to read certificate file: %s",
ERR_error_string(ERR_get_error(), NULL));
goto exit;
}
x509 = PEM_read_bio_X509_AUX(cert, NULL, NULL, NULL);
exit:
if (cert)
BIO_free(cert);
debug_return_ptr(x509);
}
static bool
check_cert(X509_STORE *ca_store_ctx, SSL_CTX *ctx, const char *cert_file)
{
bool ret = false;
X509_STORE_CTX *store_ctx = NULL;
X509 *x509 = NULL;
debug_decl(check_cert, SUDO_DEBUG_UTIL)
if ((x509 = load_cert(cert_file)) == NULL)
goto exit;
if ((store_ctx = X509_STORE_CTX_new()) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to allocate X509_STORE_CTX object: %s",
ERR_error_string(ERR_get_error(), NULL));
goto exit;
}
X509_STORE_set_flags(ca_store_ctx, X509_V_FLAG_X509_STRICT);
if (!X509_STORE_CTX_init(store_ctx, ca_store_ctx, x509, 0)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to initialize X509_STORE_CTX object: %s",
ERR_error_string(ERR_get_error(), NULL));
goto exit;
}
if (X509_verify_cert(store_ctx) <= 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to verify cert: %s",
ERR_error_string(ERR_get_error(), NULL));
goto exit;
}
/* everything is good, use this server certificate during TLS handshakes */
if (!SSL_CTX_use_certificate(ctx, x509)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to load cert to the ssl context: %s",
ERR_error_string(ERR_get_error(), NULL));
goto exit;
}
ret = true;
exit:
X509_STORE_CTX_free(store_ctx);
X509_free(x509);
debug_return_bool(ret);
}
static bool
verify_server_cert(SSL_CTX *ctx, const struct logsrvd_tls_config *tls_config)
{
bool ret = false;
X509_STORE *ca_store_ctx = NULL;
X509_LOOKUP *x509_lookup = NULL;
debug_decl(verify_server_cert, SUDO_DEBUG_UTIL)
if ((ca_store_ctx = X509_STORE_new()) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to allocate X509_STORE object for CA bundle: %s",
ERR_error_string(ERR_get_error(), NULL));
goto exit;
}
if ((x509_lookup = X509_STORE_add_lookup(ca_store_ctx, X509_LOOKUP_file())) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to set lookup method: %s",
ERR_error_string(ERR_get_error(), NULL));
goto exit;
}
if(!X509_LOOKUP_load_file(x509_lookup, tls_config->cacert_path, X509_FILETYPE_PEM)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to load CA bundle file: %s",
ERR_error_string(ERR_get_error(), NULL));
goto exit;
}
ret = check_cert(ca_store_ctx, ctx, tls_config->cert_path);
exit:
X509_STORE_free(ca_store_ctx);
debug_return_bool(ret);
}
static bool
init_tls_ciphersuites(SSL_CTX *ctx, const struct logsrvd_tls_config *tls_config)
{
debug_decl(init_tls_ciphersuites, SUDO_DEBUG_UTIL)
/* try to set TLS v1.2 ciphersuite list from config if given */
if (tls_config->ciphers_v12) {
if (SSL_CTX_set_cipher_list(ctx, tls_config->ciphers_v12)) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"TLS v1.2 ciphersuite list is set from config");
} else {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to set configured TLS v1.2 ciphersuite list (%s). Falling back to default...",
ERR_error_string(ERR_get_error(), NULL));
debug_return_bool(false);
}
/* fallback to default ciphersuites for TLS v1.2 */
} else {
if (SSL_CTX_set_cipher_list(ctx, LOGSRVD_DEFAULT_CIPHER_LST12) <= 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to load default TLS v1.2 ciphersuite list: %s",
ERR_error_string(ERR_get_error(), NULL));
debug_return_bool(false);
} else {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"TLS v1.2 ciphersuite list is set to default (%s)",
LOGSRVD_DEFAULT_CIPHER_LST12);
}
}
/* try to set TLSv1.3 ciphersuite list from config */
if (tls_config->ciphers_v13) {
if (SSL_CTX_set_ciphersuites(ctx, tls_config->ciphers_v13)) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"TLS v1.3 ciphersuite list is set from config");
} else {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to load configured TLS v1.3 ciphersuite list (%s). Falling back to default...",
ERR_error_string(ERR_get_error(), NULL));
debug_return_bool(false);
}
/* fallback to default ciphersuites for TLS v1.3 */
} else {
if (SSL_CTX_set_ciphersuites(ctx, LOGSRVD_DEFAULT_CIPHER_LST13) <= 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to load default TLS v1.3 ciphersuite list: %s",
ERR_error_string(ERR_get_error(), NULL));
debug_return_bool(false);
} else {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"TLS v1.3 ciphersuite list is set to default (%s)",
LOGSRVD_DEFAULT_CIPHER_LST13);
}
}
debug_return_bool(true);
}
/*
* Calls series of openssl initialization functions in order to
* be able to establish configured network connections over TLS
*/
static SSL_CTX *
init_tls_server_context(void)
{
const SSL_METHOD *method;
SSL_CTX *ctx = NULL;
const struct logsrvd_tls_config *tls_config = logsrvd_get_tls_config();
debug_decl(init_tls_server_context, SUDO_DEBUG_UTIL)
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
if ((method = TLS_server_method()) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"creation of SSL_METHOD failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
if ((ctx = SSL_CTX_new(method)) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"creation of new SSL_CTX object failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
/* verify server certification against the CA bundle file */
if (!verify_server_cert(ctx, tls_config)) {
goto bad;
}
/* if peer authentication is enabled, verify client cert during TLS handshake */
if (tls_config->check_peer) {
X509 *cacert = load_cert(tls_config->cacert_path);
/* server will send the name of the CA to the client during the handshake */
if (SSL_CTX_add_client_CA(ctx, cacert) <= 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"calling SSL_CTX_add_client_CA() failed: %s",
ERR_error_string(ERR_get_error(), NULL));
}
/* sets the location of the CA bundle file for verification purposes */
if (SSL_CTX_load_verify_locations(ctx, tls_config->cacert_path, NULL) <= 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"calling SSL_CTX_load_verify_locations() failed: %s",
ERR_error_string(ERR_get_error(), NULL));
}
/* server will send a client certificate request to the peer during the handshake */
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
}
if (!SSL_CTX_use_PrivateKey_file(ctx, tls_config->pkey_path, SSL_FILETYPE_PEM)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to load key file: %s",
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
if (!SSL_CTX_check_private_key(ctx)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to verify key file: %s",
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
/* initialize TLSv1.2 and TLSv1.3 ciphersuites */
if (!init_tls_ciphersuites(ctx, tls_config)) {
goto bad;
}
/* try to load and set diffie-hellman parameters */
FILE *dhparam_file = fopen(tls_config->dhparams_path, "r");
if (dhparam_file != NULL) {
DH* dhparams;
if ((dhparams = PEM_read_DHparams(dhparam_file, NULL, NULL, NULL)) != NULL) {
if (!SSL_CTX_set_tmp_dh(ctx, dhparams)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to set dh parameters: %s",
ERR_error_string(ERR_get_error(), NULL));
} else {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"diffie-hellman parameters are loaded");
}
} else {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"dhparam file can't be loaded: %s",
ERR_error_string(ERR_get_error(), NULL));
}
fclose(dhparam_file);
} else {
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
"dhparam file not found, will use default parameters");
}
/* audit server supports TLS ver1.2 or higher */
if (!SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to restrict min. protocol version: %s",
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
goto good;
bad:
if (ctx) {
SSL_CTX_free(ctx);
ctx = NULL;
}
good:
debug_return_ptr(ctx);
}
#endif
/*
* Allocate a new connection closure.
*/
@@ -796,6 +1137,33 @@ connection_closure_alloc(int sock)
if ((closure = calloc(1, sizeof(*closure))) == NULL)
debug_return_ptr(NULL);
TAILQ_INSERT_TAIL(&connections, closure, entries);
#if defined(HAVE_OPENSSL)
if (logsrvd_conf_get_tls_opt() == true) {
if ((closure->ssl = SSL_new(logsrvd_get_tls_runtime()->ssl_ctx)) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to create new ssl object: %s",
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
if (SSL_set_fd(closure->ssl, sock) != 1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to set fd for TLS: %s",
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
if (SSL_accept(closure->ssl) != 1 ) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"TLS handshake was unsuccessful: %s",
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
}
#endif
closure->iolog_dir_fd = -1;
closure->sock = sock;
@@ -819,7 +1187,6 @@ connection_closure_alloc(int sock)
if (closure->write_ev == NULL)
goto bad;
TAILQ_INSERT_TAIL(&connections, closure, entries);
debug_return_ptr(closure);
bad:
connection_closure_free(closure);
@@ -859,6 +1226,7 @@ static int
create_listener(struct listen_address *addr)
{
int flags, i, sock;
struct timeval timeout;
debug_decl(create_listener, SUDO_DEBUG_UTIL)
if ((sock = socket(addr->sa_un.sa.sa_family, SOCK_STREAM, 0)) == -1) {
@@ -868,6 +1236,12 @@ create_listener(struct listen_address *addr)
i = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) == -1)
sudo_warn("SO_REUSEADDR");
timeout.tv_sec = logsrvd_conf_get_sock_timeout();
timeout.tv_usec = 0;
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1)
sudo_warn("SO_RCVTIMEO");
if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) == -1)
sudo_warn("SO_SNDTIMEO");
if (bind(sock, &addr->sa_un.sa, addr->sa_len) == -1) {
sudo_warn("bind");
goto bad;
@@ -923,12 +1297,13 @@ register_listener(struct listen_address *addr, struct sudo_event_base *base)
debug_decl(register_listener, SUDO_DEBUG_UTIL)
sock = create_listener(addr);
if (sock != -1) {
ev = sudo_ev_alloc(sock, SUDO_EV_READ|SUDO_EV_PERSIST, listener_cb, base);
if (ev == NULL)
sudo_fatal(NULL);
if (sudo_ev_add(base, ev, NULL, false) == -1)
sudo_fatal(U_("unable to add event to queue"));
ev = sudo_ev_alloc(sock, SUDO_EV_READ|SUDO_EV_PERSIST, listener_cb, base);
if (ev == NULL)
sudo_fatal(NULL);
if (sudo_ev_add(base, ev, NULL, false) == -1)
sudo_fatal(U_("unable to add event to queue"));
}
debug_return;
@@ -938,7 +1313,7 @@ static void
register_signal(int signo, struct sudo_event_base *base)
{
struct sudo_event *ev;
debug_decl(register_listener, SUDO_DEBUG_UTIL)
debug_decl(register_signal, SUDO_DEBUG_UTIL)
ev = sudo_ev_alloc(signo, SUDO_EV_SIGNAL, signal_cb, base);
if (ev == NULL)
@@ -1104,6 +1479,14 @@ main(int argc, char *argv[])
TAILQ_FOREACH(addr, logsrvd_conf_listen_address(), entries)
register_listener(addr, evbase);
#if defined(HAVE_OPENSSL)
if (logsrvd_conf_get_tls_opt() == true) {
struct logsrvd_tls_runtime *tls_runtime = logsrvd_get_tls_runtime();
if ((tls_runtime->ssl_ctx = init_tls_server_context()) == NULL)
sudo_fatal(NULL);
}
#endif
register_signal(SIGHUP, evbase);
register_signal(SIGINT, evbase);
register_signal(SIGTERM, evbase);

View File

@@ -21,11 +21,20 @@
# error protobuf-c version 1.30 or higher required
#endif
#include "config.h"
#if defined(HAVE_OPENSSL)
# include <openssl/ssl.h>
#endif
#include "logsrv_util.h"
/* Default listen address (port 30344 on all interfaces). */
#define DEFAULT_LISTEN_ADDR "*:" DEFAULT_PORT_STR
/* Default timeout value for server socket */
#define DEFAULT_SOCKET_TIMEOUT_SEC 30
/* How often to send an ACK to the client (commit point) in seconds */
#define ACK_FREQUENCY 10
@@ -81,6 +90,9 @@ struct connection_closure {
struct sudo_event *commit_ev;
struct sudo_event *read_ev;
struct sudo_event *write_ev;
#if defined(HAVE_OPENSSL)
SSL *ssl;
#endif
const char *errstr;
struct iolog_file iolog_files[IOFD_MAX];
int iolog_dir_fd;
@@ -106,6 +118,23 @@ struct listen_address {
};
TAILQ_HEAD(listen_address_list, listen_address);
#if defined(HAVE_OPENSSL)
/* parameters to configure tls */
struct logsrvd_tls_config {
char *pkey_path;
char *cert_path;
char *cacert_path;
char *dhparams_path;
char *ciphers_v12;
char *ciphers_v13;
bool check_peer;
};
struct logsrvd_tls_runtime {
SSL_CTX *ssl_ctx;
};
#endif
/* Supported eventlog types */
enum logsrvd_eventlog_type {
EVLOG_NONE,
@@ -138,6 +167,12 @@ bool logsrvd_conf_read(const char *path);
const char *logsrvd_conf_iolog_dir(void);
const char *logsrvd_conf_iolog_file(void);
struct listen_address_list *logsrvd_conf_listen_address(void);
int logsrvd_conf_get_sock_timeout(void);
#if defined(HAVE_OPENSSL)
bool logsrvd_conf_get_tls_opt(void);
const struct logsrvd_tls_config *logsrvd_get_tls_config(void);
struct logsrvd_tls_runtime *logsrvd_get_tls_runtime(void);
#endif
enum logsrvd_eventlog_type logsrvd_conf_eventlog_type(void);
enum logsrvd_eventlog_format logsrvd_conf_eventlog_format(void);
unsigned int logsrvd_conf_syslog_maxlen(void);

View File

@@ -67,7 +67,13 @@ struct logsrvd_config_section {
static struct logsrvd_config {
struct logsrvd_config_server {
struct listen_address_list addresses;
struct listen_address_list addresses;
int timeout;
#if defined(HAVE_OPENSSL)
bool tls;
struct logsrvd_tls_config tls_config;
struct logsrvd_tls_runtime tls_runtime;
#endif
} server;
struct logsrvd_config_iolog {
bool compress;
@@ -123,6 +129,32 @@ logsrvd_conf_listen_address(void)
return &logsrvd_config->server.addresses;
}
int
logsrvd_conf_get_sock_timeout(void)
{
return logsrvd_config->server.timeout;
}
#if defined(HAVE_OPENSSL)
bool
logsrvd_conf_get_tls_opt(void)
{
return logsrvd_config->server.tls;
}
const struct logsrvd_tls_config *
logsrvd_get_tls_config(void)
{
return &logsrvd_config->server.tls_config;
}
struct logsrvd_tls_runtime *
logsrvd_get_tls_runtime(void)
{
return &logsrvd_config->server.tls_runtime;
}
#endif
/* eventlog getters */
enum logsrvd_eventlog_type
logsrvd_conf_eventlog_type(void)
@@ -358,6 +390,128 @@ done:
debug_return_bool(ret);
}
#if defined(HAVE_OPENSSL)
static bool
cb_tls_opt(struct logsrvd_config *config, const char *str)
{
int val;
debug_decl(cb_tls_opt, SUDO_DEBUG_UTIL)
if ((val = sudo_strtobool(str)) == -1)
debug_return_bool(false);
config->server.tls = val;
debug_return_bool(true);
}
static bool
cb_timeout(struct logsrvd_config *config, const char *str)
{
int timeout;
const char* errstr;
debug_decl(cb_timeout, SUDO_DEBUG_UTIL)
timeout = sudo_strtonum(str, 0, UINT_MAX, &errstr);
if (errstr != NULL)
debug_return_bool(false);
config->server.timeout = timeout;
debug_return_bool(true);
}
static bool
cb_tls_key(struct logsrvd_config *config, const char *path)
{
debug_decl(cb_tls_key, SUDO_DEBUG_UTIL)
free(config->server.tls_config.pkey_path);
if ((config->server.tls_config.pkey_path = strdup(path)) == NULL) {
sudo_warn(NULL);
debug_return_bool(false);
}
debug_return_bool(true);
}
static bool
cb_tls_cacert(struct logsrvd_config *config, const char *path)
{
debug_decl(cb_tls_cacert, SUDO_DEBUG_UTIL)
free(config->server.tls_config.cacert_path);
if ((config->server.tls_config.cacert_path = strdup(path)) == NULL) {
sudo_warn(NULL);
debug_return_bool(false);
}
debug_return_bool(true);
}
static bool
cb_tls_cert(struct logsrvd_config *config, const char *path)
{
debug_decl(cb_tls_cert, SUDO_DEBUG_UTIL)
free(config->server.tls_config.cert_path);
if ((config->server.tls_config.cert_path = strdup(path)) == NULL) {
sudo_warn(NULL);
debug_return_bool(false);
}
debug_return_bool(true);
}
static bool
cb_tls_dhparam(struct logsrvd_config *config, const char *path)
{
debug_decl(cb_tls_dhparam, SUDO_DEBUG_UTIL)
free(config->server.tls_config.dhparams_path);
if ((config->server.tls_config.dhparams_path = strdup(path)) == NULL) {
sudo_warn(NULL);
debug_return_bool(false);
}
debug_return_bool(true);
}
static bool
cb_tls_ciphers12(struct logsrvd_config *config, const char *str)
{
debug_decl(cb_tls_ciphers12, SUDO_DEBUG_UTIL)
free(config->server.tls_config.ciphers_v12);
if ((config->server.tls_config.ciphers_v12 = strdup(str)) == NULL) {
sudo_warn(NULL);
debug_return_bool(false);
}
debug_return_bool(true);
}
static bool
cb_tls_ciphers13(struct logsrvd_config *config, const char *str)
{
debug_decl(cb_tls_ciphers13, SUDO_DEBUG_UTIL)
free(config->server.tls_config.ciphers_v13);
if ((config->server.tls_config.ciphers_v13 = strdup(str)) == NULL) {
sudo_warn(NULL);
debug_return_bool(false);
}
debug_return_bool(true);
}
static bool
cb_tls_checkpeer(struct logsrvd_config *config, const char *str)
{
int val;
debug_decl(cb_tls_checkpeer, SUDO_DEBUG_UTIL)
if ((val = sudo_strtobool(str)) == -1)
debug_return_bool(false);
config->server.tls_config.check_peer = val;
debug_return_bool(true);
}
#endif
/* eventlog callbacks */
static bool
cb_eventlog_type(struct logsrvd_config *config, const char *str)
@@ -513,6 +667,17 @@ cb_logfile_time_format(struct logsrvd_config *config, const char *str)
static struct logsrvd_config_entry server_conf_entries[] = {
{ "listen_address", cb_listen_address },
{ "timeout", cb_timeout },
#if defined(HAVE_OPENSSL)
{ "tls", cb_tls_opt },
{ "tls_key", cb_tls_key },
{ "tls_cacert", cb_tls_cacert },
{ "tls_cert", cb_tls_cert },
{ "tls_dhparams", cb_tls_dhparam },
{ "tls_ciphers_v12", cb_tls_ciphers12 },
{ "tls_ciphers_v13", cb_tls_ciphers13 },
{ "tls_checkpeer", cb_tls_checkpeer },
#endif
{ NULL }
};
@@ -682,6 +847,7 @@ logsrvd_conf_alloc(void)
/* Server defaults */
TAILQ_INIT(&config->server.addresses);
config->server.timeout = DEFAULT_SOCKET_TIMEOUT_SEC;
/* I/O log defaults */
config->iolog.compress = false;

View File

@@ -53,6 +53,11 @@
#include "sudo_iolog.h"
#include "sendlog.h"
#if defined(HAVE_OPENSSL)
# include <openssl/ssl.h>
# include <openssl/err.h>
#endif
#ifndef HAVE_GETADDRINFO
# include "compat/getaddrinfo.h"
#endif
@@ -65,10 +70,20 @@
static struct iolog_file iolog_files[IOFD_MAX];
static char *iolog_dir;
#if defined(HAVE_OPENSSL)
static bool tls = false;
static SSL_CTX *ssl_ctx = NULL;
static SSL *ssl = NULL;
#endif
static void
usage(bool fatal)
{
#if defined(HAVE_OPENSSL)
fprintf(stderr, "usage: %s [-h host] [-i iolog-id] [-p port] [-t -b ca_bundle] [-c cert_file [-k key_file]] "
#else
fprintf(stderr, "usage: %s [-h host] [-i iolog-id] [-p port] "
#endif
"[-r restart-point] /path/to/iolog\n", getprogname());
exit(1);
}
@@ -85,10 +100,78 @@ help(void)
" -i, --iolog_id remote ID of I/O log to be resumed\n"
" -p, --port port to use when connecting to host\n"
" -r, --restart restart previous I/O log transfer\n"
#if defined(HAVE_OPENSSL)
" -t, --tls set the communication over TLS with the audit server\n"
" -b, --ca-bundle certificate bundle file to verify server's cert against\n"
" -c, --cert certificate file for TLS handshake\n"
" -k, --key private key file\n"
#endif
" -V, --version display version information and exit\n"));
exit(0);
}
#if defined(HAVE_OPENSSL)
static SSL_CTX *
init_tls_client_context(const char *ca_bundle_file, const char *cert_file, const char *key_file)
{
const SSL_METHOD *method;
SSL_CTX *ctx;
debug_decl(init_tls_client_context, SUDO_DEBUG_UTIL)
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
if ((method = TLS_client_method()) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"creation of SSL_METHOD failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
if ((ctx = SSL_CTX_new(method)) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"creation of new SSL_CTX object failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
if (cert_file) {
if (!SSL_CTX_use_certificate_chain_file(ctx, cert_file)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to load cert to the ssl context: %s",
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
if (!SSL_CTX_use_PrivateKey_file(ctx, key_file, X509_FILETYPE_PEM)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to load key to the ssl context: %s",
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
}
/* sets the location of the CA bundle file for verification purposes */
if (SSL_CTX_load_verify_locations(ctx, ca_bundle_file, NULL) <= 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"calling SSL_CTX_load_verify_locations() failed: %s",
ERR_error_string(ERR_get_error(), NULL));
}
/* set verify server cert during the handshake */
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
goto exit;
bad:
if (ctx)
SSL_CTX_free(ctx);
exit:
return ctx;
}
#endif
/*
* Connect to specified host:port
* If host has multiple addresses, the first one that connects is used.
@@ -129,16 +212,6 @@ connect_server(const char *host, const char *port)
}
break; /* success */
}
if (sock != -1) {
int flags = fcntl(sock, F_GETFL, 0);
if (flags == -1 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
cause = "fcntl(O_NONBLOCK)";
save_errno = errno;
close(sock);
errno = save_errno;
sock = -1;
}
}
if (sock == -1)
sudo_warn("%s", cause);
@@ -877,7 +950,15 @@ server_msg_cb(int fd, int what, void *v)
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: reading ServerMessage", __func__);
/* XXX - make common */
#if defined(HAVE_OPENSSL)
if (tls) {
nread = SSL_read(ssl, buf->data + buf->len, buf->size - buf->len);
} else {
nread = recv(fd, buf->data + buf->len, buf->size - buf->len, 0);
}
#else
nread = recv(fd, buf->data + buf->len, buf->size - buf->len, 0);
#endif
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: received %zd bytes from server",
__func__, nread);
switch (nread) {
@@ -942,7 +1023,15 @@ client_msg_cb(int fd, int what, void *v)
sudo_debug_printf(SUDO_DEBUG_INFO,
"%s: sending %u bytes to server", __func__, buf->len - buf->off);
#if defined(HAVE_OPENSSL)
if (tls) {
nwritten = SSL_write(ssl, buf->data + buf->off, buf->len - buf->off);
} else {
nwritten = send(fd, buf->data + buf->off, buf->len - buf->off, 0);
}
#else
nwritten = send(fd, buf->data + buf->off, buf->len - buf->off, 0);
#endif
if (nwritten == -1) {
sudo_warn("send");
goto bad;
@@ -1049,13 +1138,23 @@ parse_timespec(struct timespec *ts, const char *strval)
debug_return_bool(true);
}
#if defined(HAVE_OPENSSL)
static const char short_opts[] = "h:i:p:r:tb:c:k:V";
#else
static const char short_opts[] = "h:i:p:r:V";
#endif
static struct option long_opts[] = {
{ "help", no_argument, NULL, 1 },
{ "host", required_argument, NULL, 'h' },
{ "iolog-id", required_argument, NULL, 'i' },
{ "port", required_argument, NULL, 'p' },
{ "restart", required_argument, NULL, 'r' },
#if defined(HAVE_OPENSSL)
{ "tls", no_argument, NULL, 't' },
{ "ca-bundle", required_argument, NULL, 'b' },
{ "cert", required_argument, NULL, 'c' },
{ "key", required_argument, NULL, 'k' },
#endif
{ "version", no_argument, NULL, 'V' },
{ NULL, no_argument, NULL, 0 },
};
@@ -1068,6 +1167,11 @@ main(int argc, char *argv[])
struct client_closure closure;
struct sudo_event_base *evbase;
struct iolog_info *log_info;
#if defined(HAVE_OPENSSL)
const char *ca_bundle = NULL;
const char *cert = NULL;
const char *key = NULL;
#endif
const char *host = "localhost";
const char *port = DEFAULT_PORT_STR;
struct timespec restart = { 0, 0 };
@@ -1120,6 +1224,20 @@ main(int argc, char *argv[])
case 1:
help();
break;
#if defined(HAVE_OPENSSL)
case 'b':
ca_bundle = optarg;
break;
case 'c':
cert = optarg;
break;
case 'k':
key = optarg;
break;
case 't':
tls = true;
break;
#endif
case 'V':
(void)printf(_("%s version %s\n"), getprogname(),
PACKAGE_VERSION);
@@ -1131,6 +1249,21 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
#if defined(HAVE_OPENSSL)
/* if the protocol is tls, the CA bundle file is a required argument
* to be able to verify the server's certificate
*/
if (tls && !ca_bundle) {
sudo_warnx("%s", U_("with the tls protocol, the CA bundle file must be specified"));
usage(true);
}
/* if no key file is given explicitly, try to load the key from the cert */
if (cert && !key) {
key = cert;
}
#endif
if (sudo_timespecisset(&restart) != (iolog_id != NULL)) {
sudo_warnx("%s", U_("both restart point and iolog ID must be specified"));
usage(true);
@@ -1167,7 +1300,39 @@ main(int argc, char *argv[])
sock = connect_server(host, port);
if (sock == -1)
goto bad;
printf("connected to %s:%s\n", host, port);
#if defined(HAVE_OPENSSL)
if (tls) {
int ret;
if ((ssl_ctx = init_tls_client_context(ca_bundle, cert, key)) == NULL) {
sudo_warnx(U_("Unable to initialize ssl context: %s\n"),
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
if ((ssl = SSL_new(ssl_ctx)) == NULL) {
sudo_warnx(U_("Unable to allocate ssl object: %s\n"),
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
if (SSL_set_fd(ssl, sock) <= 0) {
sudo_warnx(U_("Unable to attach socket to the ssl object: %s\n"),
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
ret = SSL_ERROR_NONE;
if ((ret = SSL_connect(ssl)) != 1) {
sudo_warnx(U_("SSL_connect failed: ret=%d ssl_error=%d, stack=%s\n"),
ret,
SSL_get_error(ssl, ret),
ERR_error_string(ERR_get_error(), NULL));
goto bad;
}
printf("Protocol version: %s\n", SSL_get_version(ssl));
printf("Negotiated ciphersuite: %s\n", SSL_get_cipher(ssl));
}
#endif
printf("Connected to %s:%s\n", host, port);
if ((evbase = sudo_ev_base_alloc()) == NULL)
sudo_fatal(NULL);