[WIP, do not merge] Problem: insufficient tests for ZMTP-CURVE protocol errors (#2680)

* Extracted connect_vanilla_socket function

* Problem: no tests for ZMTP-CURVE protocol errors

Solution: added two test cases with erroneous HELLO commands

* Problem: insufficient tests for ZMTP-CURVE protocol errors

Solution: added two test cases with erroneous HELLO command version

* Problem: test HELLO message is invalid apart from deliberate errors

Solution: create cryptographically correct HELLO message
add tweetnacl.c to test_security_curve

* Problem: nonce is incorrect, build fails with GCC

Solution: use correct non prefix

* Problem: make builds are failing

Solution: transfer CMake changes to (auto)make files

* Problem: nonce is incorrect, build fails with GCC

Solution: use correct non prefix

* Problem: make builds are failing

Solution: transfer CMake changes to (auto)make files

* Problem: no test with INITIATE command with invalid length

Solution: added test case

* Problem: code duplication between test_security_curve.cpp and curve_client.cpp

Solution: extracted parts of zmq::curve_client_t::produce_hello into reusable function

* Problem: code duplication between test_security_curve.cpp and curve_client.cpp

Solution: extracted further parts of zmq::curve_client_t into reusable functions
added missing file

* Problem: mechanism_t::add_property can be declared static

Solution: declare mechanism_t::add_property static

* Problem: intermediate crypto data needs to be passed between static function calls to curve_client_tools_t

Solution: add non-static member functions

* Problem: msg_t instance may be closed twice

Solution: remove offending close

* Problem: prepare_hello uses static curve_client_tools_t::produce_hello

Solution: Use non-static curve_client_tools_t::produce_hello

* Problem: no test with invalid command name where INITIATE command is expected

Solution: added test case

* Problem: make builds are failing due to curve_client_tools.hpp not being found

Solution: add curve_client_tools.hpp to list of source files

* Problem: wrong initializer order in zmq::curve_client_t

Solution: reorder

* Problem: under non-Windows systems, test fails because random_open was not called

Solution: call random_open/random_close within test

* Problem: conflict between custom function htonll and macro definition on Darwin

Solution: define htonll function only if not defined as a macro

* Problem: nullptr not defined on all platforms

Solution: replace nullptr by NULL

* Problem: libsodium builds not working

Solution: adapt compile and link file sets for libsodium builds

* Problem: Makefile.am broken

Solution: Fix syntax

* Problem: no tests for garbage encrypted cookie or content in INITIATE

Solution: added test cases

* Problem: test cases accidentally excluded from build

Solution: remove #if/#endif

* Solution: some error cases are unreachable

Problem: for the time being, added some comments without changing the code

* Added comments on hard-to-test cases
This commit is contained in:
Simon Giesecke
2017-08-15 16:28:24 +02:00
committed by Luca Boccassi
parent a927ecc0ed
commit d5e4319edc
10 changed files with 829 additions and 184 deletions

View File

@@ -40,6 +40,10 @@
# include <unistd.h>
#endif
#include "../src/tweetnacl.h"
#include "../src/curve_client_tools.hpp"
#include "../src/random.hpp"
// We'll generate random test keys at startup
static char valid_client_public [41];
static char valid_client_secret [41];
@@ -550,7 +554,6 @@ void expect_zmtp_failure (void *client, char *my_endpoint, void *server, void *s
expect_bounce_fail (server, client);
close_zero_linger (client);
#ifdef ZMQ_BUILD_DRAFT_API
expect_monitor_event_multiple (server_mon, ZMQ_EVENT_HANDSHAKE_FAILED_ZMTP);
#endif
@@ -584,13 +587,10 @@ void test_curve_security_with_plain_client_credentials (void *ctx,
expect_zmtp_failure (client, my_endpoint, server, server_mon);
}
void test_curve_security_unauthenticated_message (char *my_endpoint,
void *server,
int timeout)
int connect_vanilla_socket (char *my_endpoint)
{
// Unauthenticated messages from a vanilla socket shouldn't be received
struct sockaddr_in ip4addr;
int s;
struct sockaddr_in ip4addr;
unsigned short int port;
int rc = sscanf (my_endpoint, "tcp://127.0.0.1:%hu", &port);
@@ -607,6 +607,15 @@ void test_curve_security_unauthenticated_message (char *my_endpoint,
s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
rc = connect (s, (struct sockaddr *) &ip4addr, sizeof (ip4addr));
assert (rc > -1);
return s;
}
void test_curve_security_unauthenticated_message (char *my_endpoint,
void *server,
int timeout)
{
// Unauthenticated messages from a vanilla socket shouldn't be received
int s = connect_vanilla_socket(my_endpoint);
// send anonymous ZMTP/1.0 greeting
send (s, "\x01\x00", 2, 0);
// send sneaky message that shouldn't be received
@@ -621,6 +630,323 @@ void test_curve_security_unauthenticated_message (char *my_endpoint,
close (s);
}
void send_all (int fd, const char *data, size_t size)
{
while (size > 0) {
int res = send (fd, data, size, 0);
assert (res > 0);
size -= res;
data += res;
}
}
template <size_t N> void send (int fd, const char (&data) [N])
{
send_all (fd, data, N - 1);
}
void send_greeting(int s)
{
send (s, "\xff\0\0\0\0\0\0\0\0\x7f"); // signature
send (s, "\x03\x00"); // version 3.0
send (s, "CURVE\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); // mechanism CURVE
send (s, "\0"); // as-server == false
send (s, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
}
void test_curve_security_invalid_hello_wrong_length (char *my_endpoint,
void *server,
void *server_mon,
int timeout)
{
int s = connect_vanilla_socket (my_endpoint);
// send GREETING
send_greeting (s);
// send CURVE HELLO of wrong size
send(s, "\x04\x05HELLO");
#ifdef ZMQ_BUILD_DRAFT_API
expect_monitor_event_multiple (server_mon, ZMQ_EVENT_HANDSHAKE_FAILED_ZMTP,
EPROTO);
#endif
close (s);
}
const size_t hello_length = 200;
const size_t welcome_length = 168;
zmq::curve_client_tools_t make_curve_client_tools ()
{
uint8_t valid_client_secret_decoded[32];
uint8_t valid_client_public_decoded[32];
zmq_z85_decode (valid_client_public_decoded, valid_client_public);
zmq_z85_decode (valid_client_secret_decoded, valid_client_secret);
uint8_t valid_server_public_decoded[32];
zmq_z85_decode (valid_server_public_decoded, valid_server_public);
return zmq::curve_client_tools_t (valid_client_public_decoded,
valid_client_secret_decoded,
valid_server_public_decoded);
}
#ifndef htonll
uint64_t htonll (uint64_t value)
{
// The answer is 42
static const int num = 42;
// Check the endianness
if (*reinterpret_cast<const char *> (&num) == num) {
const uint32_t high_part = htonl (static_cast<uint32_t> (value >> 32));
const uint32_t low_part =
htonl (static_cast<uint32_t> (value & 0xFFFFFFFFLL));
return (static_cast<uint64_t> (low_part) << 32) | high_part;
} else {
return value;
}
}
#endif
template <size_t N> void send_command (int s, char (&command)[N])
{
if (N < 256) {
send(s, "\x04");
char len = (char)N;
send_all(s, &len, 1);
} else {
send(s, "\x06");
uint64_t len = htonll (N);
send_all (s, (char*)&len, 8);
}
send_all (s, command, N);
}
void test_curve_security_invalid_hello_command_name (char *my_endpoint,
void *server,
void *server_mon,
int timeout)
{
int s = connect_vanilla_socket (my_endpoint);
send_greeting (s);
zmq::curve_client_tools_t tools = make_curve_client_tools ();
// send CURVE HELLO with a misspelled command name (but otherwise correct)
char hello[hello_length];
int rc = tools.produce_hello (hello, 0);
assert (rc == 0);
hello[5] = 'X';
send_command(s, hello);
#ifdef ZMQ_BUILD_DRAFT_API
expect_monitor_event_multiple (server_mon, ZMQ_EVENT_HANDSHAKE_FAILED_ZMTP,
EPROTO);
#endif
close (s);
}
void test_curve_security_invalid_hello_version (char *my_endpoint,
void *server,
void *server_mon,
int timeout)
{
int s = connect_vanilla_socket (my_endpoint);
send_greeting (s);
zmq::curve_client_tools_t tools = make_curve_client_tools ();
// send CURVE HELLO with a wrong version number (but otherwise correct)
char hello[hello_length];
int rc = tools.produce_hello (hello, 0);
assert (rc == 0);
hello[6] = 2;
send_command (s, hello);
#ifdef ZMQ_BUILD_DRAFT_API
expect_monitor_event_multiple (server_mon, ZMQ_EVENT_HANDSHAKE_FAILED_ZMTP,
EPROTO);
#endif
close (s);
}
void flush_read(int fd)
{
int res;
char buf[256];
while ((res = recv (fd, buf, 256, 0)) == 256) {
}
assert (res != -1);
}
void recv_all(int fd, uint8_t *data, size_t len)
{
size_t received = 0;
while (received < len)
{
int res = recv(fd, (char*)data, len, 0);
assert(res > 0);
data += res;
received += res;
}
}
void recv_greeting (int fd)
{
uint8_t greeting[64];
recv_all (fd, greeting, 64);
// TODO assert anything about the greeting received from the server?
}
int connect_exchange_greeting_and_send_hello (char *my_endpoint,
zmq::curve_client_tools_t &tools)
{
int s = connect_vanilla_socket (my_endpoint);
send_greeting (s);
recv_greeting (s);
// send valid CURVE HELLO
char hello[hello_length];
int rc = tools.produce_hello (hello, 0);
assert (rc == 0);
send_command (s, hello);
return s;
}
void test_curve_security_invalid_initiate_length (char *my_endpoint,
void *server,
void *server_mon,
int timeout)
{
zmq::curve_client_tools_t tools = make_curve_client_tools ();
int s = connect_exchange_greeting_and_send_hello (my_endpoint, tools);
// receive but ignore WELCOME
flush_read (s);
#ifdef ZMQ_BUILD_DRAFT_API
int res = get_monitor_event_with_timeout (server_mon, NULL, NULL, timeout);
assert (res == -1);
#endif
send(s, "\x04\x08INITIATE");
#ifdef ZMQ_BUILD_DRAFT_API
expect_monitor_event_multiple (server_mon, ZMQ_EVENT_HANDSHAKE_FAILED_ZMTP,
EPROTO);
#endif
close (s);
}
int connect_exchange_greeting_and_hello_welcome (
char *my_endpoint,
void *server_mon,
int timeout,
zmq::curve_client_tools_t &tools)
{
int s = connect_exchange_greeting_and_send_hello (
my_endpoint, tools);
// receive but ignore WELCOME
uint8_t welcome[welcome_length + 2];
recv_all (s, welcome, welcome_length + 2);
int res = tools.process_welcome (welcome + 2, welcome_length);
assert (res == 0);
#ifdef ZMQ_BUILD_DRAFT_API
res = get_monitor_event_with_timeout (server_mon, NULL, NULL, timeout);
assert (res == -1);
#endif
return s;
}
void test_curve_security_invalid_initiate_command_name (char *my_endpoint,
void *server,
void *server_mon,
int timeout)
{
zmq::curve_client_tools_t tools = make_curve_client_tools ();
int s = connect_exchange_greeting_and_hello_welcome (
my_endpoint, server_mon, timeout, tools);
char initiate [257];
tools.produce_initiate (initiate, 257, 1, NULL, 0);
// modify command name
initiate[5] = 'X';
send_command (s, initiate);
#ifdef ZMQ_BUILD_DRAFT_API
expect_monitor_event_multiple (server_mon, ZMQ_EVENT_HANDSHAKE_FAILED_ZMTP,
EPROTO);
#endif
close (s);
}
void test_curve_security_invalid_initiate_command_encrypted_cookie (
char *my_endpoint, void *server, void *server_mon, int timeout)
{
zmq::curve_client_tools_t tools = make_curve_client_tools ();
int s = connect_exchange_greeting_and_hello_welcome (
my_endpoint, server_mon, timeout, tools);
char initiate [257];
tools.produce_initiate (initiate, 257, 1, NULL, 0);
// make garbage from encrypted cookie
initiate[30] = !initiate[30];
send_command (s, initiate);
#ifdef ZMQ_BUILD_DRAFT_API
expect_monitor_event_multiple (
server_mon, ZMQ_EVENT_HANDSHAKE_FAILED_ENCRYPTION, EPROTO);
#endif
close (s);
}
void test_curve_security_invalid_initiate_command_encrypted_content (
char *my_endpoint, void *server, void *server_mon, int timeout)
{
zmq::curve_client_tools_t tools = make_curve_client_tools ();
int s = connect_exchange_greeting_and_hello_welcome (
my_endpoint, server_mon, timeout, tools);
char initiate [257];
tools.produce_initiate (initiate, 257, 1, NULL, 0);
// make garbage from encrypted content
initiate[150] = !initiate[150];
send_command (s, initiate);
#ifdef ZMQ_BUILD_DRAFT_API
expect_monitor_event_multiple (
server_mon, ZMQ_EVENT_HANDSHAKE_FAILED_ENCRYPTION, EPROTO);
#endif
close (s);
}
void test_curve_security_zap_unsuccessful (void *ctx,
char *my_endpoint,
void *server,
@@ -682,6 +1008,8 @@ int main (void)
return 0;
}
zmq::random_open ();
// Generate new keypairs for these tests
int rc = zmq_curve_keypair (valid_client_public, valid_client_secret);
assert (rc == 0);
@@ -849,10 +1177,67 @@ int main (void)
shutdown_context_and_server_side (ctx, zap_thread, server, server_mon,
handler);
fprintf (stderr, "test_curve_security_invalid_hello_wrong_length\n");
setup_context_and_server_side (&ctx, &handler, &zap_thread, &server,
&server_mon, my_endpoint);
test_curve_security_invalid_hello_wrong_length (my_endpoint, server,
server_mon, timeout);
shutdown_context_and_server_side (ctx, zap_thread, server, server_mon,
handler);
fprintf (stderr, "test_curve_security_invalid_hello_command_name\n");
setup_context_and_server_side (&ctx, &handler, &zap_thread, &server,
&server_mon, my_endpoint);
test_curve_security_invalid_hello_command_name (my_endpoint, server,
server_mon, timeout);
shutdown_context_and_server_side (ctx, zap_thread, server, server_mon,
handler);
fprintf (stderr, "test_curve_security_invalid_hello_command_version\n");
setup_context_and_server_side (&ctx, &handler, &zap_thread, &server,
&server_mon, my_endpoint);
test_curve_security_invalid_hello_version (my_endpoint, server, server_mon,
timeout);
shutdown_context_and_server_side (ctx, zap_thread, server, server_mon,
handler);
fprintf (stderr, "test_curve_security_invalid_initiate_command_length\n");
setup_context_and_server_side (&ctx, &handler, &zap_thread, &server,
&server_mon, my_endpoint);
test_curve_security_invalid_initiate_length (my_endpoint, server,
server_mon, timeout);
shutdown_context_and_server_side (ctx, zap_thread, server, server_mon,
handler);
fprintf (stderr, "test_curve_security_invalid_initiate_command_name\n");
setup_context_and_server_side (&ctx, &handler, &zap_thread, &server,
&server_mon, my_endpoint);
test_curve_security_invalid_initiate_command_name (my_endpoint, server,
server_mon, timeout);
shutdown_context_and_server_side (ctx, zap_thread, server, server_mon,
handler);
fprintf (stderr, "test_curve_security_invalid_initiate_command_encrypted_cookie\n");
setup_context_and_server_side (&ctx, &handler, &zap_thread, &server,
&server_mon, my_endpoint);
test_curve_security_invalid_initiate_command_encrypted_cookie (
my_endpoint, server, server_mon, timeout);
shutdown_context_and_server_side (ctx, zap_thread, server, server_mon,
handler);
fprintf (stderr, "test_curve_security_invalid_initiate_command_encrypted_content\n");
setup_context_and_server_side (&ctx, &handler, &zap_thread, &server,
&server_mon, my_endpoint);
test_curve_security_invalid_initiate_command_encrypted_content (
my_endpoint, server, server_mon, timeout);
shutdown_context_and_server_side (ctx, zap_thread, server, server_mon,
handler);
ctx = zmq_ctx_new ();
test_curve_security_invalid_keysize (ctx);
rc = zmq_ctx_term (ctx);
assert (rc == 0);
zmq::random_close ();
return 0;
}