mirror of
https://github.com/zeromq/libzmq.git
synced 2025-10-05 10:54:55 +02:00
[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:

committed by
Luca Boccassi

parent
a927ecc0ed
commit
d5e4319edc
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user