Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 53 additions & 55 deletions src/tls/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1556,12 +1556,34 @@ static const char *tls_session_alpn_get(void *session_)
return backend_session->alpn;
}

static void tls_log_io_error(int ssl_error, int saved_errno)
{
unsigned long err_code;
char err_buf[256];

err_code = ERR_get_error();

if (err_code != 0) {
ERR_error_string_n(err_code, err_buf, sizeof(err_buf) - 1);
flb_error("[tls] error: %s", err_buf);
}
else if (ssl_error == SSL_ERROR_SYSCALL && saved_errno != 0) {
flb_error("[tls] syscall error: %s", strerror(saved_errno));
}
else if (ssl_error == SSL_ERROR_SYSCALL) {
flb_error("[tls] error: unexpected EOF");
}
else {
flb_error("[tls] unknown error (ssl_error=%d)", ssl_error);
}
}

static int tls_net_read(struct flb_tls_session *session,
void *buf, size_t len)
{
int ret;
unsigned long err_code;
char err_buf[256];
int ssl_ret;
int saved_errno;
struct tls_context *ctx;
struct tls_session *backend_session;

Expand All @@ -1579,40 +1601,37 @@ static int tls_net_read(struct flb_tls_session *session,

ERR_clear_error();

errno = 0;
ret = SSL_read(backend_session->ssl, buf, len);
saved_errno = errno;

if (ret <= 0) {
ret = SSL_get_error(backend_session->ssl, ret);
ssl_ret = SSL_get_error(backend_session->ssl, ret);

if (ret == SSL_ERROR_WANT_READ) {
if (ssl_ret == SSL_ERROR_WANT_READ) {
ret = FLB_TLS_WANT_READ;
}
else if (ret == SSL_ERROR_WANT_WRITE) {
else if (ssl_ret == SSL_ERROR_WANT_WRITE) {
ret = FLB_TLS_WANT_WRITE;
}
else if (ret == SSL_ERROR_SYSCALL) {
flb_errno();

err_code = ERR_get_error();

if (err_code != 0) {
ERR_error_string_n(err_code, err_buf, sizeof(err_buf)-1);
flb_error("[tls] syscall error: %s", err_buf);
}
else {
flb_error("[tls] syscall error: %s", strerror(errno));
}
else if (ssl_ret == SSL_ERROR_SYSCALL) {
tls_log_io_error(ssl_ret, saved_errno);

/* According to the documentation these are non-recoverable
* errors so we don't need to screen them before saving them
* to the net_error field.
*/

session->connection->net_error = errno;
if (saved_errno != 0) {
session->connection->net_error = saved_errno;
}
else {
session->connection->net_error = ECONNRESET;
}

ret = -1;
}
else if (ret == SSL_ERROR_ZERO_RETURN) {
else if (ssl_ret == SSL_ERROR_ZERO_RETURN) {
flb_debug("[tls] connection closed by the remote peer "
"(close_notify)");

Expand All @@ -1629,18 +1648,10 @@ static int tls_net_read(struct flb_tls_session *session,

ret = -1;
}
else if (ret < 0) {
err_code = ERR_get_error();

if (err_code != 0) {
ERR_error_string_n(err_code, err_buf, sizeof(err_buf)-1);
flb_error("[tls] error: %s", err_buf);
}
else {
flb_error("[tls] error: %s", strerror(errno));
}
}
else {
tls_log_io_error(ssl_ret, saved_errno);
session->connection->net_error = ECONNRESET;

ret = -1;
}
}
Expand All @@ -1654,8 +1665,7 @@ static int tls_net_write(struct flb_tls_session *session,
{
int ret;
int ssl_ret;
unsigned long err_code;
char err_buf[256];
int saved_errno;
size_t total = 0;
struct tls_context *ctx;
struct tls_session *backend_session;
Expand All @@ -1673,9 +1683,11 @@ static int tls_net_write(struct flb_tls_session *session,

ERR_clear_error();

errno = 0;
ret = SSL_write(backend_session->ssl,
(unsigned char *) data + total,
len - total);
saved_errno = errno;

if (ret <= 0) {
ssl_ret = SSL_get_error(backend_session->ssl, ret);
Expand All @@ -1687,27 +1699,19 @@ static int tls_net_write(struct flb_tls_session *session,
ret = FLB_TLS_WANT_READ;
}
else if (ssl_ret == SSL_ERROR_SYSCALL) {
err_code = ERR_get_error();

if (err_code == 0) {
if (ret == 0) {
flb_debug("[tls] connection closed");
}
else {
flb_error("[tls] syscall error: %s", strerror(errno));
}
}
else {
ERR_error_string_n(err_code, err_buf, sizeof(err_buf) - 1);
flb_error("[tls] syscall error: %s", err_buf);
}
tls_log_io_error(ssl_ret, saved_errno);

/* According to the documentation these are non-recoverable
* errors so we don't need to screen them before saving them
* to the net_error field.
*/

session->connection->net_error = errno;
if (saved_errno != 0) {
session->connection->net_error = saved_errno;
}
else {
session->connection->net_error = ECONNRESET;
}

ret = -1;
}
Expand All @@ -1729,14 +1733,8 @@ static int tls_net_write(struct flb_tls_session *session,
ret = -1;
}
else {
err_code = ERR_get_error();
if (err_code == 0) {
flb_error("[tls] unknown error");
}
else {
ERR_error_string_n(err_code, err_buf, sizeof(err_buf) - 1);
flb_error("[tls] error: %s", err_buf);
}
tls_log_io_error(ssl_ret, saved_errno);
session->connection->net_error = ECONNRESET;

ret = -1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import shutil
import socket
import ssl
import struct
import subprocess
import sys
import tempfile
Expand Down Expand Up @@ -709,6 +710,33 @@ def _create_tls_memory_bio_client(port, cafile):
return raw_sock, tls, outgoing


def _reset_tls_connection(port, cafile):
raw_sock, tls, outgoing = _create_tls_memory_bio_client(port, cafile)

tls.write(b"\x91")
wire_payload = outgoing.read()
raw_sock.sendall(wire_payload[:-1])
time.sleep(1)

raw_sock.setsockopt(
socket.SOL_SOCKET,
socket.SO_LINGER,
struct.pack("ii", 1, 0),
)
raw_sock.close()


def _send_corrupted_tls_record(port, cafile):
raw_sock, tls, outgoing = _create_tls_memory_bio_client(port, cafile)

tls.write(b"\x91")
wire_payload = bytearray(outgoing.read())
wire_payload[-1] ^= 1
raw_sock.sendall(wire_payload)

return raw_sock


def _create_partial_forward_client(service, payload, use_tls):
if use_tls:
sock, tls, outgoing = _create_tls_memory_bio_client(
Expand Down Expand Up @@ -1412,6 +1440,65 @@ def test_in_forward_downstream_coro_eof_and_error_keep_listener_responsive(
assert records[0]["message"] == "after-io-errors"


def test_in_forward_tls_syscall_error_preserves_errno():
service = Service("in_forward_tls.yaml")
service.start()

try:
_reset_tls_connection(
service.flb_listener_port,
service.tls_crt_file,
)
log_text = service.wait_for_log_contains("[tls] syscall error:", timeout=10)

payload = _message_mode_payload(
TEST_TAG,
{"message": "after-tls-reset"},
)
_send_forward_payload(service, payload, True)
records = service.wait_for_record_count(1, timeout=10)
finally:
service.stop()

assert "Connection reset by peer" in log_text
assert "Inappropriate ioctl for device" not in log_text
assert not any(
"openssl.c:" in line and "errno=" in line
for line in log_text.splitlines()
)
assert records[0]["message"] == "after-tls-reset"


def test_in_forward_tls_protocol_error_is_reported():
service = Service("in_forward_tls.yaml")
protocol_sock = None
service.start()

try:
protocol_sock = _send_corrupted_tls_record(
service.flb_listener_port,
service.tls_crt_file,
)
log_text = service.wait_for_log_contains("[tls] error:", timeout=10)
protocol_sock.close()
protocol_sock = None

payload = _message_mode_payload(
TEST_TAG,
{"message": "after-tls-protocol-error"},
)
_send_forward_payload(service, payload, True)
records = service.wait_for_record_count(1, timeout=10)
finally:
if protocol_sock is not None:
protocol_sock.close()
service.stop()

assert "bad record mac" in log_text.lower()
assert "unknown error" not in log_text
assert records[0]["message"] == "after-tls-protocol-error"


@pytest.mark.parametrize(
"config_file,use_tls",
[
Expand Down
Loading