Replace console output by monitoring events for curve security issues (#2645)

* Fixing #2002 one way of doing it

 * Mechanisms can implement a new method `error_detail()`
 * This error detail have three values for the moment: no_detail
 (default), protocol, encryption.
    + generic enough to make sense for all mechanisms.
    - low granularity level on information.

* Fixing #2002: implementation of the error details

The ZMQ_EVENT_HANDSHAKE_FAILED event carries the error details
as value.

* Removed Microsoft extenstion for enum member access

This was leading to compilation error under linux.

* Adaptation of CURVE test cases

* Monitoring event: changed API for detailed events

Removed ZMQ_EVENT_HANDSHAKE_FAILED and replaced it by:
- ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL,
- ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL,
- ZMQ_EVENT_HANDSHAKE_FAILED_ENCRYPTION

Adaptation of text case `security_curve`

* Removed event value comparison

This was introduced for the previous API model adaptation

* Removed the prints in std output and added missing details

`current_error_detail` was not set in every protocol error cases

* Fixed initialization of current_error_detail

* Fixed error in greeting test case

The handshake failure due to mechanism mismatch in greeting is actually
a protocol error. The error handling method consider it like so and
send a protocol handshake failure monitoring event instead of no_detail.

Fixed the test_security_curve expectation as well.

* Upgraded tests of monitoring events

The tests check the number of monitoring events received

* Problem: does not build under Linux or without ZMQ_DRAFT_API

Solution:
- properly use ZMQ_DRAFT_API conditional compilation
- use receive timeouts instead of Sleep

* Problem: duplicate definition of variable 'timeout'

Solution: merged definitions

* Problem: inconsistent timing dependencies

Solution: reduce timing dependency by using timeouts at more places

* Problem: assertion failure under Linux due to unexpected monitor event

Solution: output event type to aid debugging

* Problem: erroneous assertion code

* Problem: assertion failure with a garbage server key due to an extra third event

Solution: changed assertion to expect three events (needs to be checked)

* Problem: extra include directive to non-existent file

Solution: removed include directive

* Problem: assertion failure on appveyor for unknown reason

Solution: improve debug output

* Problem: no build with libsodium and draft api

Solution: add build configurations with libsodium and draft api

* Problem: assertion failure on CI

Solution: change assertion to reflect actual behaviour on CI (at least temporarily)

* Problem: error in condition in assertion code

* Problem: assertion failure on CI

Solution: generalize assertion to match behavior on CI

* Problem: assertion failures on CI

Solution: removed inconsistent assertion on no monitor events before flushing
improved debuggability by converting function into macro

* Problem: diverging test code for three analogous test cases with garbage key

Solution: extract common code into function

* Problem: does not build without ZMQ_BUILD_DRAFT_API

Solution: introduce dummy variable

* Attempt to remove workaround regarding ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL again

* Problem: EAGAIN error after handshake complete if there is no more data in inbuffer

Solution: Skip tcp_read attempt in that case

* Problem: handshaking event emitted after handshaking failed

Solution: use stream_engine_t::handshaking instead of mechanism_t::status() to determine whether still handshaking

* Include error code in debug output

* Improve debugging output: output flushed events

* Split up ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL into ZMQ_EVENT_HANDSHAKE_FAILED_ZMTP and ZMQ_EVENT_HANDSHAKE_FAILED_ZAP

* Fixed compilation without ZMQ_BUILD_DRAFT_API

* Renamed ZMQ_EVENT_HANDSHAKE_SUCCEED to ZMQ_EVENT_HANDSHAKE_SUCCEEDED for language consistency

* Renamed ZMQ_EVENT_HANDSHAKE_SUCCEED to ZMQ_EVENT_HANDSHAKE_SUCCEEDED for language consistency

* Renamed ZMQ_EVENT_HANDSHAKE_SUCCEED to ZMQ_EVENT_HANDSHAKE_SUCCEEDED for language consistency

* Fixed assert_monitor_event (require event instead of allowing no event)
Reverted erroneous change to handshaking condition
Renamed test_wrong_key to test_garbage_key
Generalized assumption in test_garbage_key to allow for ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL with error == EPIPE

* Better isolate test cases from each other by providing a fresh context & server for each

* Added diagnostic output

* Changed assertion to reflect actual behavior on CI

* Fixed formatting, observe maximum line length

* Fixed formatting, observe maximum line length

* Increase timeout to check if this fixes valgrind run

* Close server with close_zero_linger

* Increase timeout to check if this fixes valgrind run

* Increase timeout to check if this fixes valgrind run

* Generalize assertion to also work with valgrind

* Fixed formatting

* Add more diagnostic output

* Generalize assertion to also work with valgrind
This commit is contained in:
Simon Giesecke
2017-08-03 15:15:56 +02:00
committed by Luca Boccassi
parent fda9daa200
commit 5d4e30eb13
11 changed files with 486 additions and 202 deletions

View File

@@ -303,7 +303,7 @@ void zmq::stream_engine_t::in_event ()
return;
}
// If there's no data to process in the buffer...
// If there's no data to process in the buffer...
if (!insize) {
// Retrieve the buffer and read as much data as possible.
@@ -316,6 +316,8 @@ void zmq::stream_engine_t::in_event ()
const int rc = tcp_read (s, inpos, bufsize);
if (rc == 0) {
// connection closed by peer
errno = EPIPE;
error (connection_error);
return;
}
@@ -495,6 +497,7 @@ bool zmq::stream_engine_t::handshake ()
const int n = tcp_read (s, greeting_recv + greeting_bytes_read,
greeting_size - greeting_bytes_read);
if (n == 0) {
errno = EPIPE;
error (connection_error);
return false;
}
@@ -782,8 +785,17 @@ int zmq::stream_engine_t::next_handshake_command (msg_t *msg_)
if (rc == 0)
msg_->set_flags (msg_t::command);
#ifdef ZMQ_BUILD_DRAFT_API
if(mechanism->status() == mechanism_t::error)
socket->event_handshake_failed(endpoint, 0);
if (mechanism->status () == mechanism_t::error) {
int err = errno;
if (mechanism->error_detail () == mechanism_t::zmtp)
socket->event_handshake_failed_zmtp (endpoint, err);
else if (mechanism->error_detail () == mechanism_t::zap)
socket->event_handshake_failed_zap (endpoint, err);
else if (mechanism->error_detail () == mechanism_t::encryption)
socket->event_handshake_failed_encryption (endpoint, err);
else
socket->event_handshake_failed_no_detail (endpoint, err);
}
#endif
return rc;
@@ -868,7 +880,7 @@ void zmq::stream_engine_t::mechanism_ready ()
}
#ifdef ZMQ_BUILD_DRAFT_API
socket->event_handshake_succeed(endpoint, 0);
socket->event_handshake_succeeded (endpoint, 0);
#endif
}
@@ -975,8 +987,22 @@ void zmq::stream_engine_t::error (error_reason_t reason)
}
zmq_assert (session);
#ifdef ZMQ_BUILD_DRAFT_API
if(mechanism == NULL || mechanism->status() == mechanism_t::handshaking)
socket->event_handshake_failed(endpoint, (int) s);
int err = errno;
if (mechanism == NULL) {
if (reason == protocol_error)
socket->event_handshake_failed_zmtp (endpoint, err);
else
socket->event_handshake_failed_no_detail (endpoint, err);
} else if (mechanism->status () == mechanism_t::handshaking) {
if (mechanism->error_detail () == mechanism_t::zmtp)
socket->event_handshake_failed_zmtp (endpoint, err);
else if (mechanism->error_detail () == mechanism_t::zap)
socket->event_handshake_failed_zap (endpoint, err);
else if (mechanism->error_detail () == mechanism_t::encryption)
socket->event_handshake_failed_encryption (endpoint, err);
else
socket->event_handshake_failed_no_detail (endpoint, err);
}
#endif
socket->event_disconnected (endpoint, (int) s);
session->flush ();