1/*
2 Copyright (c) 2007-2017 Contributors as noted in the AUTHORS file
3
4 This file is part of libzmq, the ZeroMQ core engine in C++.
5
6 libzmq is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License (LGPL) as published
8 by the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 As a special exception, the Contributors give you permission to link
12 this library with independent modules to produce an executable,
13 regardless of the license terms of these independent modules, and to
14 copy and distribute the resulting executable under terms of your choice,
15 provided that you also meet, for each linked independent module, the
16 terms and conditions of the license of that module. An independent
17 module is a module which is not derived from or based on this library.
18 If you modify this library, you must extend this exception to your
19 version of the library.
20
21 libzmq is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
24 License for more details.
25
26 You should have received a copy of the GNU Lesser General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
28*/
29
30// TODO remove this workaround for handling libsodium/tweetnacl
31
32// To define SIZE_MAX with older compilers
33#define __STDC_LIMIT_MACROS
34
35#if defined ZMQ_CUSTOM_PLATFORM_HPP
36#include "platform.hpp"
37#else
38#include "../src/platform.hpp"
39#endif
40
41#ifndef ZMQ_USE_TWEETNACL
42#define ZMQ_USE_TWEETNACL
43#endif
44#ifdef ZMQ_USE_LIBSODIUM
45#undef ZMQ_USE_LIBSODIUM
46#endif
47
48#include "testutil.hpp"
49#include "testutil_security.hpp"
50#if defined(ZMQ_HAVE_WINDOWS)
51#include <winsock2.h>
52#include <ws2tcpip.h>
53#include <stdexcept>
54#define close closesocket
55#else
56#include <sys/socket.h>
57#include <netinet/in.h>
58#include <arpa/inet.h>
59#include <unistd.h>
60#endif
61#include <unity.h>
62
63#include "../src/tweetnacl.h"
64#include "../src/curve_client_tools.hpp"
65#include "../src/random.hpp"
66
67char error_message_buffer[256];
68
69void *handler;
70void *zap_thread;
71void *server;
72void *server_mon;
73char my_endpoint[MAX_SOCKET_STRING];
74
75void setUp ()
76{
77 setup_test_context ();
78 setup_context_and_server_side (&handler, &zap_thread, &server, &server_mon,
79 my_endpoint);
80}
81
82void tearDown ()
83{
84 shutdown_context_and_server_side (zap_thread, server, server_mon, handler);
85 teardown_test_context ();
86}
87
88const int timeout = 250;
89
90const char large_routing_id[] = "0123456789012345678901234567890123456789"
91 "0123456789012345678901234567890123456789"
92 "0123456789012345678901234567890123456789"
93 "0123456789012345678901234567890123456789"
94 "0123456789012345678901234567890123456789"
95 "0123456789012345678901234567890123456789"
96 "012345678901234";
97
98static void zap_handler_large_routing_id (void * /*unused_*/)
99{
100 zap_handler_generic (zap_ok, large_routing_id);
101}
102
103void expect_new_client_curve_bounce_fail (const char *server_public_,
104 const char *client_public_,
105 const char *client_secret_,
106 char *my_endpoint_,
107 void *server_,
108 void **client_mon_ = NULL,
109 int expected_client_event_ = 0,
110 int expected_client_value_ = 0)
111{
112 curve_client_data_t curve_client_data = {server_public_, client_public_,
113 client_secret_};
114 expect_new_client_bounce_fail (
115 my_endpoint_, server_, socket_config_curve_client, &curve_client_data,
116 client_mon_, expected_client_event_, expected_client_value_);
117}
118
119void test_null_key (void *server_,
120 void *server_mon_,
121 char *my_endpoint_,
122 char *server_public_,
123 char *client_public_,
124 char *client_secret_)
125{
126 expect_new_client_curve_bounce_fail (server_public_, client_public_,
127 client_secret_, my_endpoint_, server_);
128
129 int handshake_failed_encryption_event_count =
130 expect_monitor_event_multiple (server_mon_,
131 ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL,
132 ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
133
134 // handshake_failed_encryption_event_count should be at least two because
135 // expect_bounce_fail involves two exchanges
136 // however, with valgrind we see only one event (maybe the next one takes
137 // very long, or does not happen at all because something else takes very
138 // long)
139
140 fprintf (stderr,
141 "count of "
142 "ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL/"
143 "ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC events: %i\n",
144 handshake_failed_encryption_event_count);
145}
146
147void test_curve_security_with_valid_credentials ()
148{
149 curve_client_data_t curve_client_data = {
150 valid_server_public, valid_client_public, valid_client_secret};
151 void *client_mon;
152 void *client = create_and_connect_client (
153 my_endpoint, socket_config_curve_client, &curve_client_data, &client_mon);
154 bounce (server, client);
155 test_context_socket_close (client);
156
157 int event = get_monitor_event_with_timeout (server_mon, NULL, NULL, -1);
158 assert (event == ZMQ_EVENT_HANDSHAKE_SUCCEEDED);
159
160 assert_no_more_monitor_events_with_timeout (server_mon, timeout);
161
162 event = get_monitor_event_with_timeout (client_mon, NULL, NULL, -1);
163 assert (event == ZMQ_EVENT_HANDSHAKE_SUCCEEDED);
164
165 assert_no_more_monitor_events_with_timeout (client_mon, timeout);
166
167 test_context_socket_close (client_mon);
168}
169
170void test_curve_security_with_bogus_client_credentials ()
171{
172 // This must be caught by the ZAP handler
173 char bogus_public[41];
174 char bogus_secret[41];
175 zmq_curve_keypair (bogus_public, bogus_secret);
176
177 expect_new_client_curve_bounce_fail (
178 valid_server_public, bogus_public, bogus_secret, my_endpoint, server,
179 NULL, ZMQ_EVENT_HANDSHAKE_FAILED_AUTH, 400);
180
181 int server_event_count = 0;
182 server_event_count = expect_monitor_event_multiple (
183 server_mon, ZMQ_EVENT_HANDSHAKE_FAILED_AUTH, 400);
184 TEST_ASSERT_LESS_OR_EQUAL_INT (1, server_event_count);
185
186 // there may be more than one ZAP request due to repeated attempts by the client
187 TEST_ASSERT (0 == server_event_count
188 || 1 <= zmq_atomic_counter_value (zap_requests_handled));
189}
190
191void expect_zmtp_mechanism_mismatch (void *client_,
192 char *my_endpoint_,
193 void *server_,
194 void *server_mon_)
195{
196 // This must be caught by the curve_server class, not passed to ZAP
197 TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (client_, my_endpoint_));
198 expect_bounce_fail (server_, client_);
199 test_context_socket_close_zero_linger (client_);
200
201 expect_monitor_event_multiple (server_mon_,
202 ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL,
203 ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH);
204
205 TEST_ASSERT_EQUAL_INT (0, zmq_atomic_counter_value (zap_requests_handled));
206}
207
208void test_curve_security_with_null_client_credentials ()
209{
210 void *client = test_context_socket (ZMQ_DEALER);
211
212 expect_zmtp_mechanism_mismatch (client, my_endpoint, server, server_mon);
213}
214
215void test_curve_security_with_plain_client_credentials ()
216{
217 void *client = test_context_socket (ZMQ_DEALER);
218 TEST_ASSERT_SUCCESS_ERRNO (
219 zmq_setsockopt (client, ZMQ_PLAIN_USERNAME, "admin", 5));
220 TEST_ASSERT_SUCCESS_ERRNO (
221 zmq_setsockopt (client, ZMQ_PLAIN_PASSWORD, "password", 8));
222
223 expect_zmtp_mechanism_mismatch (client, my_endpoint, server, server_mon);
224}
225
226fd_t connect_vanilla_socket (char *my_endpoint_)
227{
228 fd_t s;
229 struct sockaddr_in ip4addr;
230
231 unsigned short int port;
232 int rc = sscanf (my_endpoint_, "tcp://127.0.0.1:%hu", &port);
233 TEST_ASSERT_EQUAL_INT (1, rc);
234
235 ip4addr.sin_family = AF_INET;
236 ip4addr.sin_port = htons (port);
237#if defined(ZMQ_HAVE_WINDOWS) && (_WIN32_WINNT < 0x0600)
238 ip4addr.sin_addr.s_addr = inet_addr ("127.0.0.1");
239#else
240 inet_pton (AF_INET, "127.0.0.1", &ip4addr.sin_addr);
241#endif
242
243 s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
244 rc = connect (s, reinterpret_cast<struct sockaddr *> (&ip4addr),
245 sizeof (ip4addr));
246 TEST_ASSERT_GREATER_THAN_INT (-1, rc);
247 return s;
248}
249
250void test_curve_security_unauthenticated_message ()
251{
252 // Unauthenticated messages from a vanilla socket shouldn't be received
253 fd_t s = connect_vanilla_socket (my_endpoint);
254 // send anonymous ZMTP/1.0 greeting
255 send (s, "\x01\x00", 2, 0);
256 // send sneaky message that shouldn't be received
257 send (s, "\x08\x00sneaky\0", 9, 0);
258
259 zmq_setsockopt (server, ZMQ_RCVTIMEO, &timeout, sizeof (timeout));
260 char *buf = s_recv (server);
261 TEST_ASSERT_NULL_MESSAGE (buf, "Received unauthenticated message");
262 close (s);
263}
264
265void send_all (fd_t fd_, const char *data_, socket_size_t size_)
266{
267 while (size_ > 0) {
268 int res = send (fd_, data_, size_, 0);
269 TEST_ASSERT_GREATER_THAN_INT (0, res);
270 size_ -= res;
271 data_ += res;
272 }
273}
274
275template <size_t N> void send (fd_t fd_, const char (&data_)[N])
276{
277 send_all (fd_, data_, N - 1);
278}
279
280void send_greeting (fd_t s_)
281{
282 send (s_, "\xff\0\0\0\0\0\0\0\0\x7f"); // signature
283 send (s_, "\x03\x00"); // version 3.0
284 send (s_, "CURVE\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); // mechanism CURVE
285 send (s_, "\0"); // as-server == false
286 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");
287}
288
289void test_curve_security_invalid_hello_wrong_length ()
290{
291 fd_t s = connect_vanilla_socket (my_endpoint);
292
293 // send GREETING
294 send_greeting (s);
295
296 // send CURVE HELLO of wrong size
297 send (s, "\x04\x06\x05HELLO");
298
299 expect_monitor_event_multiple (
300 server_mon, ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL,
301 ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO);
302
303 close (s);
304}
305
306const size_t hello_length = 200;
307const size_t welcome_length = 168;
308
309zmq::curve_client_tools_t make_curve_client_tools ()
310{
311 uint8_t valid_client_secret_decoded[32];
312 uint8_t valid_client_public_decoded[32];
313
314 zmq_z85_decode (valid_client_public_decoded, valid_client_public);
315 zmq_z85_decode (valid_client_secret_decoded, valid_client_secret);
316
317 uint8_t valid_server_public_decoded[32];
318 zmq_z85_decode (valid_server_public_decoded, valid_server_public);
319
320 return zmq::curve_client_tools_t (valid_client_public_decoded,
321 valid_client_secret_decoded,
322 valid_server_public_decoded);
323}
324
325// same as htonll, which is only available on few platforms (recent Windows, but not on Linux, e.g.(
326static uint64_t host_to_network (uint64_t value_)
327{
328 // The answer is 42
329 static const int num = 42;
330
331 // Check the endianness
332 if (*reinterpret_cast<const char *> (&num) == num) {
333 const uint32_t high_part = htonl (static_cast<uint32_t> (value_ >> 32));
334 const uint32_t low_part =
335 htonl (static_cast<uint32_t> (value_ & 0xFFFFFFFFLL));
336
337 return (static_cast<uint64_t> (low_part) << 32) | high_part;
338 }
339 return value_;
340}
341
342template <size_t N> void send_command (fd_t s_, char (&command_)[N])
343{
344 if (N < 256) {
345 send (s_, "\x04");
346 char len = (char) N;
347 send_all (s_, &len, 1);
348 } else {
349 send (s_, "\x06");
350 uint64_t len = host_to_network (N);
351 send_all (s_, reinterpret_cast<char *> (&len), 8);
352 }
353 send_all (s_, command_, N);
354}
355
356void test_curve_security_invalid_hello_command_name ()
357{
358 fd_t s = connect_vanilla_socket (my_endpoint);
359
360 send_greeting (s);
361
362 zmq::curve_client_tools_t tools = make_curve_client_tools ();
363
364 // send CURVE HELLO with a misspelled command name (but otherwise correct)
365 char hello[hello_length];
366 TEST_ASSERT_SUCCESS_ERRNO (tools.produce_hello (hello, 0));
367 hello[5] = 'X';
368
369 send_command (s, hello);
370
371 expect_monitor_event_multiple (server_mon,
372 ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL,
373 ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
374
375 close (s);
376}
377
378void test_curve_security_invalid_hello_version ()
379{
380 fd_t s = connect_vanilla_socket (my_endpoint);
381
382 send_greeting (s);
383
384 zmq::curve_client_tools_t tools = make_curve_client_tools ();
385
386 // send CURVE HELLO with a wrong version number (but otherwise correct)
387 char hello[hello_length];
388 TEST_ASSERT_SUCCESS_ERRNO (tools.produce_hello (hello, 0));
389 hello[6] = 2;
390
391 send_command (s, hello);
392
393 expect_monitor_event_multiple (
394 server_mon, ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL,
395 ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO);
396
397 close (s);
398}
399
400void flush_read (fd_t fd_)
401{
402 int res;
403 char buf[256];
404
405 while ((res = recv (fd_, buf, 256, 0)) == 256) {
406 }
407 TEST_ASSERT_NOT_EQUAL (-1, res);
408}
409
410void recv_all (fd_t fd_, uint8_t *data_, socket_size_t len_)
411{
412 socket_size_t received = 0;
413 while (received < len_) {
414 int res = recv (fd_, reinterpret_cast<char *> (data_), len_, 0);
415 TEST_ASSERT_GREATER_THAN_INT (0, res);
416
417 data_ += res;
418 received += res;
419 }
420}
421
422void recv_greeting (fd_t fd_)
423{
424 uint8_t greeting[64];
425 recv_all (fd_, greeting, 64);
426 // TODO assert anything about the greeting received from the server?
427}
428
429fd_t connect_exchange_greeting_and_send_hello (
430 char *my_endpoint_, zmq::curve_client_tools_t &tools_)
431{
432 fd_t s = connect_vanilla_socket (my_endpoint_);
433
434 send_greeting (s);
435 recv_greeting (s);
436
437 // send valid CURVE HELLO
438 char hello[hello_length];
439 TEST_ASSERT_SUCCESS_ERRNO (tools_.produce_hello (hello, 0));
440
441 send_command (s, hello);
442 return s;
443}
444
445void test_curve_security_invalid_initiate_wrong_length ()
446{
447 zmq::curve_client_tools_t tools = make_curve_client_tools ();
448
449 fd_t s = connect_exchange_greeting_and_send_hello (my_endpoint, tools);
450
451 // receive but ignore WELCOME
452 flush_read (s);
453
454 int res = get_monitor_event_with_timeout (server_mon, NULL, NULL, timeout);
455 TEST_ASSERT_EQUAL_INT (-1, res);
456
457 send (s, "\x04\x09\x08INITIATE");
458
459 expect_monitor_event_multiple (
460 server_mon, ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL,
461 ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE);
462
463 close (s);
464}
465
466fd_t connect_exchange_greeting_and_hello_welcome (
467 char *my_endpoint_,
468 void *server_mon_,
469 int timeout_,
470 zmq::curve_client_tools_t &tools_)
471{
472 fd_t s = connect_exchange_greeting_and_send_hello (my_endpoint_, tools_);
473
474 // receive but ignore WELCOME
475 uint8_t welcome[welcome_length + 2];
476 recv_all (s, welcome, welcome_length + 2);
477
478 uint8_t cn_precom[crypto_box_BEFORENMBYTES];
479 TEST_ASSERT_SUCCESS_ERRNO (
480 tools_.process_welcome (welcome + 2, welcome_length, cn_precom));
481
482 const int res =
483 get_monitor_event_with_timeout (server_mon_, NULL, NULL, timeout_);
484 TEST_ASSERT_EQUAL_INT (-1, res);
485
486 return s;
487}
488
489void test_curve_security_invalid_initiate_command_name ()
490{
491 zmq::curve_client_tools_t tools = make_curve_client_tools ();
492 fd_t s = connect_exchange_greeting_and_hello_welcome (
493 my_endpoint, server_mon, timeout, tools);
494
495 char initiate[257];
496 tools.produce_initiate (initiate, 257, 1, NULL, 0);
497 // modify command name
498 initiate[5] = 'X';
499
500 send_command (s, initiate);
501
502 expect_monitor_event_multiple (server_mon,
503 ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL,
504 ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
505
506 close (s);
507}
508
509void test_curve_security_invalid_initiate_command_encrypted_cookie ()
510{
511 zmq::curve_client_tools_t tools = make_curve_client_tools ();
512 fd_t s = connect_exchange_greeting_and_hello_welcome (
513 my_endpoint, server_mon, timeout, tools);
514
515 char initiate[257];
516 tools.produce_initiate (initiate, 257, 1, NULL, 0);
517 // make garbage from encrypted cookie
518 initiate[30] = !initiate[30];
519
520 send_command (s, initiate);
521
522 expect_monitor_event_multiple (server_mon,
523 ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL,
524 ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
525
526 close (s);
527}
528
529void test_curve_security_invalid_initiate_command_encrypted_content ()
530{
531 zmq::curve_client_tools_t tools = make_curve_client_tools ();
532 fd_t s = connect_exchange_greeting_and_hello_welcome (
533 my_endpoint, server_mon, timeout, tools);
534
535 char initiate[257];
536 tools.produce_initiate (initiate, 257, 1, NULL, 0);
537 // make garbage from encrypted content
538 initiate[150] = !initiate[150];
539
540 send_command (s, initiate);
541
542 expect_monitor_event_multiple (server_mon,
543 ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL,
544 ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
545
546 close (s);
547}
548
549void test_curve_security_invalid_keysize (void *ctx_)
550{
551 // Check return codes for invalid buffer sizes
552 void *client = zmq_socket (ctx_, ZMQ_DEALER);
553 TEST_ASSERT_NOT_NULL (client);
554 errno = 0;
555 int rc =
556 zmq_setsockopt (client, ZMQ_CURVE_SERVERKEY, valid_server_public, 123);
557 assert (rc == -1 && errno == EINVAL);
558 errno = 0;
559 rc = zmq_setsockopt (client, ZMQ_CURVE_PUBLICKEY, valid_client_public, 123);
560 assert (rc == -1 && errno == EINVAL);
561 errno = 0;
562 rc = zmq_setsockopt (client, ZMQ_CURVE_SECRETKEY, valid_client_secret, 123);
563 assert (rc == -1 && errno == EINVAL);
564 TEST_ASSERT_SUCCESS_ERRNO (zmq_close (client));
565}
566
567// TODO why isn't this const?
568char null_key[] = "0000000000000000000000000000000000000000";
569
570void test_null_server_key ()
571{
572 // Check CURVE security with a null server key
573 // This will be caught by the curve_server class, not passed to ZAP
574 test_null_key (server, server_mon, my_endpoint, null_key,
575 valid_client_public, valid_client_secret);
576}
577
578void test_null_client_public_key ()
579{
580 // Check CURVE security with a null client public key
581 // This will be caught by the curve_server class, not passed to ZAP
582 test_null_key (server, server_mon, my_endpoint, valid_server_public,
583 null_key, valid_client_secret);
584}
585
586void test_null_client_secret_key ()
587{
588 // Check CURVE security with a null client public key
589 // This will be caught by the curve_server class, not passed to ZAP
590 test_null_key (server, server_mon, my_endpoint, valid_server_public,
591 valid_client_public, null_key);
592}
593
594
595int main (void)
596{
597 if (!zmq_has ("curve")) {
598 printf ("CURVE encryption not installed, skipping test\n");
599 return 0;
600 }
601
602 zmq::random_open ();
603
604 setup_testutil_security_curve ();
605
606
607 setup_test_environment ();
608
609 UNITY_BEGIN ();
610 RUN_TEST (test_curve_security_with_valid_credentials);
611 RUN_TEST (test_null_server_key);
612 RUN_TEST (test_null_client_public_key);
613 RUN_TEST (test_null_client_secret_key);
614 RUN_TEST (test_curve_security_with_bogus_client_credentials);
615 RUN_TEST (test_curve_security_with_null_client_credentials);
616 RUN_TEST (test_curve_security_with_plain_client_credentials);
617 RUN_TEST (test_curve_security_unauthenticated_message);
618
619 // tests with misbehaving CURVE client
620 RUN_TEST (test_curve_security_invalid_hello_wrong_length);
621 RUN_TEST (test_curve_security_invalid_hello_command_name);
622 RUN_TEST (test_curve_security_invalid_hello_version);
623 RUN_TEST (test_curve_security_invalid_initiate_wrong_length);
624 RUN_TEST (test_curve_security_invalid_initiate_command_name);
625 RUN_TEST (test_curve_security_invalid_initiate_command_encrypted_cookie);
626 RUN_TEST (test_curve_security_invalid_initiate_command_encrypted_content);
627
628 // TODO this requires a deviating test setup, must be moved to a separate executable/fixture
629 // test with a large routing id (resulting in large metadata)
630 fprintf (stderr,
631 "test_curve_security_with_valid_credentials (large routing id)\n");
632 setup_test_context ();
633 setup_context_and_server_side (&handler, &zap_thread, &server, &server_mon,
634 my_endpoint, &zap_handler_large_routing_id,
635 &socket_config_curve_server,
636 &valid_server_secret, large_routing_id);
637 test_curve_security_with_valid_credentials ();
638 shutdown_context_and_server_side (zap_thread, server, server_mon, handler);
639 teardown_test_context ();
640
641 void *ctx = zmq_ctx_new ();
642 test_curve_security_invalid_keysize (ctx);
643 TEST_ASSERT_SUCCESS_ERRNO (zmq_ctx_term (ctx));
644
645 zmq::random_close ();
646
647 return UNITY_END ();
648}
649