Skip to content

Commit b6e3e0f

Browse files
committed
Merge pull request #101 from hintjens/master
Problem: issue #1273, protocol downgrade attack
2 parents f6968ec + 77ef79e commit b6e3e0f

7 files changed

+197
-57
lines changed

Diff for: NEWS

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
0MQ version 4.0.6 stable, released on 2015/xx/xx
1+
0MQ version 4.0.6 stable, released on 2015/12/xx
22
================================================
33

4+
* Fixed #1273 - V3 protocol handler vulnerable to downgrade attacks.
5+
46

57
0MQ version 4.0.5 stable, released on 2014/10/14
68
================================================

Diff for: src/session_base.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,14 @@ int zmq::session_base_t::zap_connect ()
323323
return 0;
324324
}
325325

326+
bool zmq::session_base_t::zap_enabled ()
327+
{
328+
return (
329+
options.mechanism != ZMQ_NULL ||
330+
(options.mechanism == ZMQ_NULL && options.zap_domain.length() > 0)
331+
);
332+
}
333+
326334
void zmq::session_base_t::process_attach (i_engine *engine_)
327335
{
328336
zmq_assert (engine_ != NULL);

Diff for: src/session_base.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ namespace zmq
6868
int push_msg (msg_t *msg_);
6969

7070
int zap_connect ();
71-
71+
bool zap_enabled ();
72+
7273
// Fetches a message. Returns 0 if successful; -1 otherwise.
7374
// The caller is responsible for freeing the message when no
7475
// longer used.

Diff for: src/stream_engine.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,11 @@ bool zmq::stream_engine_t::handshake ()
464464
// Is the peer using ZMTP/1.0 with no revision number?
465465
// If so, we send and receive rest of identity message
466466
if (greeting_recv [0] != 0xff || !(greeting_recv [9] & 0x01)) {
467+
if (session->zap_enabled ()) {
468+
// Reject ZMTP 1.0 connections if ZAP is enabled
469+
error ();
470+
return false;
471+
}
467472
encoder = new (std::nothrow) v1_encoder_t (out_batch_size);
468473
alloc_assert (encoder);
469474

@@ -505,6 +510,11 @@ bool zmq::stream_engine_t::handshake ()
505510
}
506511
else
507512
if (greeting_recv [revision_pos] == ZMTP_1_0) {
513+
if (session->zap_enabled ()) {
514+
// Reject ZMTP 1.0 connections if ZAP is enabled
515+
error ();
516+
return false;
517+
}
508518
encoder = new (std::nothrow) v1_encoder_t (
509519
out_batch_size);
510520
alloc_assert (encoder);
@@ -515,6 +525,11 @@ bool zmq::stream_engine_t::handshake ()
515525
}
516526
else
517527
if (greeting_recv [revision_pos] == ZMTP_2_0) {
528+
if (session->zap_enabled ()) {
529+
// Reject ZMTP 1.0 connections if ZAP is enabled
530+
error ();
531+
return false;
532+
}
518533
encoder = new (std::nothrow) v2_encoder_t (out_batch_size);
519534
alloc_assert (encoder);
520535

Diff for: tests/test_security_curve.cpp

+58-8
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,23 @@
1818
*/
1919

2020
#include "testutil.hpp"
21+
#if defined (ZMQ_HAVE_WINDOWS)
22+
# include <winsock2.h>
23+
# include <ws2tcpip.h>
24+
# include <stdexcept>
25+
# define close closesocket
26+
#else
27+
# include <sys/socket.h>
28+
# include <netinet/in.h>
29+
# include <arpa/inet.h>
30+
# include <unistd.h>
31+
#endif
2132

2233
// We'll generate random test keys at startup
23-
static char client_public [40];
24-
static char client_secret [40];
25-
static char server_public [40];
26-
static char server_secret [40];
34+
static char client_public [41];
35+
static char client_secret [41];
36+
static char server_public [41];
37+
static char server_secret [41];
2738

2839
// --------------------------------------------------------------------------
2940
// This methods receives and validates ZAP requestes (allowing or denying
@@ -46,7 +57,7 @@ static void zap_handler (void *handler)
4657
int size = zmq_recv (handler, client_key, 32, 0);
4758
assert (size == 32);
4859

49-
char client_key_text [40];
60+
char client_key_text [41];
5061
zmq_z85_encode (client_key_text, client_key, 32);
5162

5263
assert (streq (version, "1.0"));
@@ -181,8 +192,8 @@ int main (void)
181192

182193
// Check CURVE security with bogus client credentials
183194
// This must be caught by the ZAP handler
184-
char bogus_public [40];
185-
char bogus_secret [40];
195+
char bogus_public [41];
196+
char bogus_secret [41];
186197
zmq_curve_keypair (bogus_public, bogus_secret);
187198

188199
client = zmq_socket (ctx, ZMQ_DEALER);
@@ -217,7 +228,46 @@ int main (void)
217228
assert (rc == 0);
218229
expect_bounce_fail (server, client);
219230
close_zero_linger (client);
220-
231+
232+
// Unauthenticated messages from a vanilla socket shouldn't be received
233+
struct sockaddr_in ip4addr;
234+
int s;
235+
236+
ip4addr.sin_family = AF_INET;
237+
ip4addr.sin_port = htons (9998);
238+
inet_pton (AF_INET, "127.0.0.1", &ip4addr.sin_addr);
239+
240+
s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
241+
rc = connect (s, (struct sockaddr*) &ip4addr, sizeof (ip4addr));
242+
assert (rc > -1);
243+
// send anonymous ZMTP/1.0 greeting
244+
send (s, "\x01\x00", 2, 0);
245+
// send sneaky message that shouldn't be received
246+
send (s, "\x08\x00sneaky\0", 9, 0);
247+
int timeout = 150;
248+
zmq_setsockopt (server, ZMQ_RCVTIMEO, &timeout, sizeof (timeout));
249+
char *buf = s_recv (server);
250+
if (buf != NULL) {
251+
printf ("Received unauthenticated message: %s\n", buf);
252+
assert (buf == NULL);
253+
}
254+
close (s);
255+
256+
// Check return codes for invalid buffer sizes
257+
client = zmq_socket (ctx, ZMQ_DEALER);
258+
assert (client);
259+
errno = 0;
260+
rc = zmq_setsockopt (client, ZMQ_CURVE_SERVERKEY, server_public, 123);
261+
assert (rc == -1 && errno == EINVAL);
262+
errno = 0;
263+
rc = zmq_setsockopt (client, ZMQ_CURVE_PUBLICKEY, client_public, 123);
264+
assert (rc == -1 && errno == EINVAL);
265+
errno = 0;
266+
rc = zmq_setsockopt (client, ZMQ_CURVE_SECRETKEY, client_secret, 123);
267+
assert (rc == -1 && errno == EINVAL);
268+
rc = zmq_close (client);
269+
assert (rc == 0);
270+
221271
// Shutdown
222272
rc = zmq_close (server);
223273
assert (rc == 0);

Diff for: tests/test_security_null.cpp

+75-46
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2007-2013 Contributors as noted in the AUTHORS file
2+
Copyright (c) 2007-2014 Contributors as noted in the AUTHORS file
33
44
This file is part of 0MQ.
55
@@ -18,6 +18,17 @@
1818
*/
1919

2020
#include "testutil.hpp"
21+
#if defined (ZMQ_HAVE_WINDOWS)
22+
# include <winsock2.h>
23+
# include <ws2tcpip.h>
24+
# include <stdexcept>
25+
# define close closesocket
26+
#else
27+
# include <sys/socket.h>
28+
# include <netinet/in.h>
29+
# include <arpa/inet.h>
30+
# include <unistd.h>
31+
#endif
2132

2233
static void
2334
zap_handler (void *handler)
@@ -27,6 +38,7 @@ zap_handler (void *handler)
2738
char *version = s_recv (handler);
2839
if (!version)
2940
break; // Terminating
41+
3042
char *sequence = s_recv (handler);
3143
char *domain = s_recv (handler);
3244
char *address = s_recv (handler);
@@ -57,7 +69,7 @@ zap_handler (void *handler)
5769
free (identity);
5870
free (mechanism);
5971
}
60-
zmq_close (handler);
72+
close_zero_linger (handler);
6173
}
6274

6375
int main (void)
@@ -76,72 +88,89 @@ int main (void)
7688
void *zap_thread = zmq_threadstart (&zap_handler, handler);
7789

7890
// We bounce between a binding server and a connecting client
91+
92+
// We first test client/server with no ZAP domain
93+
// Libzmq does not call our ZAP handler, the connect must succeed
7994
void *server = zmq_socket (ctx, ZMQ_DEALER);
8095
assert (server);
8196
void *client = zmq_socket (ctx, ZMQ_DEALER);
8297
assert (client);
83-
84-
// We first test client/server with no ZAP domain
85-
// Libzmq does not call our ZAP handler, the connect must succeed
8698
rc = zmq_bind (server, "tcp://127.0.0.1:9000");
8799
assert (rc == 0);
88-
rc = zmq_connect (client, "tcp://localhost:9000");
100+
rc = zmq_connect (client, "tcp://127.0.0.1:9000");
89101
assert (rc == 0);
90102
bounce (server, client);
91-
zmq_unbind (server, "tcp://127.0.0.1:9000");
92-
zmq_disconnect (client, "tcp://localhost:9000");
93-
103+
close_zero_linger (client);
104+
close_zero_linger (server);
105+
94106
// Now define a ZAP domain for the server; this enables
95107
// authentication. We're using the wrong domain so this test
96108
// must fail.
97-
// **************************************************************
98-
// PH: the following causes libzmq to get confused, so that the
99-
// next step fails. To reproduce, uncomment this block. Note that
100-
// even creating a new client/server socket pair, the behaviour
101-
// does not change.
102-
// **************************************************************
103-
// Destroying the old sockets and creating new ones isn't needed,
104-
// but it shows that the problem isn't related to specific sockets.
105-
//close_zero_linger (client);
106-
//close_zero_linger (server);
107-
//server = zmq_socket (ctx, ZMQ_DEALER);
108-
//assert (server);
109-
//client = zmq_socket (ctx, ZMQ_DEALER);
110-
//assert (client);
111-
//// The above code should not be required
112-
//rc = zmq_setsockopt (server, ZMQ_ZAP_DOMAIN, "WRONG", 5);
113-
//assert (rc == 0);
114-
//rc = zmq_bind (server, "tcp://127.0.0.1:9001");
115-
//assert (rc == 0);
116-
//rc = zmq_connect (client, "tcp://localhost:9001");
117-
//assert (rc == 0);
118-
//expect_bounce_fail (server, client);
119-
//zmq_unbind (server, "tcp://127.0.0.1:9001");
120-
//zmq_disconnect (client, "tcp://localhost:9001");
121-
109+
server = zmq_socket (ctx, ZMQ_DEALER);
110+
assert (server);
111+
client = zmq_socket (ctx, ZMQ_DEALER);
112+
assert (client);
113+
rc = zmq_setsockopt (server, ZMQ_ZAP_DOMAIN, "WRONG", 5);
114+
assert (rc == 0);
115+
rc = zmq_bind (server, "tcp://127.0.0.1:9001");
116+
assert (rc == 0);
117+
rc = zmq_connect (client, "tcp://127.0.0.1:9001");
118+
assert (rc == 0);
119+
expect_bounce_fail (server, client);
120+
close_zero_linger (client);
121+
close_zero_linger (server);
122+
122123
// Now use the right domain, the test must pass
124+
server = zmq_socket (ctx, ZMQ_DEALER);
125+
assert (server);
126+
client = zmq_socket (ctx, ZMQ_DEALER);
127+
assert (client);
123128
rc = zmq_setsockopt (server, ZMQ_ZAP_DOMAIN, "TEST", 4);
124129
assert (rc == 0);
125130
rc = zmq_bind (server, "tcp://127.0.0.1:9002");
126131
assert (rc == 0);
127-
rc = zmq_connect (client, "tcp://localhost:9002");
132+
rc = zmq_connect (client, "tcp://127.0.0.1:9002");
128133
assert (rc == 0);
129-
// **************************************************************
130-
// PH: it fails here; though the ZAP reply is 200 OK, and
131-
// null_mechanism.cpp correctly parses that, the connection
132-
// never succeeds and the test hangs.
133-
// **************************************************************
134134
bounce (server, client);
135-
zmq_unbind (server, "tcp://127.0.0.1:9002");
136-
zmq_disconnect (client, "tcp://localhost:9002");
137-
138-
// Shutdown
139135
close_zero_linger (client);
140136
close_zero_linger (server);
141-
rc = zmq_ctx_term (ctx);
137+
138+
// Unauthenticated messages from a vanilla socket shouldn't be received
139+
server = zmq_socket (ctx, ZMQ_DEALER);
140+
assert (server);
141+
rc = zmq_setsockopt (server, ZMQ_ZAP_DOMAIN, "WRONG", 5);
142142
assert (rc == 0);
143+
rc = zmq_bind (server, "tcp://127.0.0.1:9003");
144+
assert (rc == 0);
145+
146+
struct sockaddr_in ip4addr;
147+
int s;
148+
149+
ip4addr.sin_family = AF_INET;
150+
ip4addr.sin_port = htons(9003);
151+
inet_pton(AF_INET, "127.0.0.1", &ip4addr.sin_addr);
143152

144-
// Wait until ZAP handler terminates.
153+
s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
154+
rc = connect (s, (struct sockaddr*) &ip4addr, sizeof ip4addr);
155+
assert (rc > -1);
156+
// send anonymous ZMTP/1.0 greeting
157+
send (s, "\x01\x00", 2, 0);
158+
// send sneaky message that shouldn't be received
159+
send (s, "\x08\x00sneaky\0", 9, 0);
160+
int timeout = 150;
161+
zmq_setsockopt (server, ZMQ_RCVTIMEO, &timeout, sizeof (timeout));
162+
char *buf = s_recv (server);
163+
if (buf != NULL) {
164+
printf ("Received unauthenticated message: %s\n", buf);
165+
assert (buf == NULL);
166+
}
167+
close (s);
168+
close_zero_linger (server);
169+
170+
// Shutdown
171+
rc = zmq_ctx_term (ctx);
172+
assert (rc == 0);
173+
// Wait until ZAP handler terminates
145174
zmq_threadclose (zap_thread);
146175

147176
return 0;

Diff for: tests/test_security_plain.cpp

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2007-2013 Contributors as noted in the AUTHORS file
2+
Copyright (c) 2007-2014 Contributors as noted in the AUTHORS file
33
44
This file is part of 0MQ.
55
@@ -18,6 +18,17 @@
1818
*/
1919

2020
#include "testutil.hpp"
21+
#if defined (ZMQ_HAVE_WINDOWS)
22+
# include <winsock2.h>
23+
# include <ws2tcpip.h>
24+
# include <stdexcept>
25+
# define close closesocket
26+
#else
27+
# include <sys/socket.h>
28+
# include <netinet/in.h>
29+
# include <arpa/inet.h>
30+
# include <unistd.h>
31+
#endif
2132

2233
static void
2334
zap_handler (void *ctx)
@@ -137,6 +148,30 @@ int main (void)
137148
expect_bounce_fail (server, client);
138149
close_zero_linger (client);
139150

151+
// Unauthenticated messages from a vanilla socket shouldn't be received
152+
struct sockaddr_in ip4addr;
153+
int s;
154+
155+
ip4addr.sin_family = AF_INET;
156+
ip4addr.sin_port = htons (9998);
157+
inet_pton (AF_INET, "127.0.0.1", &ip4addr.sin_addr);
158+
159+
s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
160+
rc = connect (s, (struct sockaddr*) &ip4addr, sizeof (ip4addr));
161+
assert (rc > -1);
162+
// send anonymous ZMTP/1.0 greeting
163+
send (s, "\x01\x00", 2, 0);
164+
// send sneaky message that shouldn't be received
165+
send (s, "\x08\x00sneaky\0", 9, 0);
166+
int timeout = 150;
167+
zmq_setsockopt (server, ZMQ_RCVTIMEO, &timeout, sizeof (timeout));
168+
char *buf = s_recv (server);
169+
if (buf != NULL) {
170+
printf ("Received unauthenticated message: %s\n", buf);
171+
assert (buf == NULL);
172+
}
173+
close (s);
174+
140175
// Shutdown
141176
rc = zmq_close (server);
142177
assert (rc == 0);

0 commit comments

Comments
 (0)