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#include "testutil.hpp"
31#include "testutil_unity.hpp"
32#if defined(ZMQ_HAVE_WINDOWS)
33#include <winsock2.h>
34#include <ws2tcpip.h>
35#include <stdexcept>
36#define close closesocket
37#else
38#include <sys/socket.h>
39#include <netinet/in.h>
40#include <arpa/inet.h>
41#include <unistd.h>
42#endif
43
44#include <stdlib.h>
45#include <string.h>
46
47// This test requires a KRB5 environment with the following
48// service principal (substitute your host.domain and REALM):
49//
50// zmqtest2/host.domain@REALM (host.domain should be host running test)
51//
52// Export keys for this principal to a keytab file and set the environment
53// variables KRB5_KTNAME and KRB5_CLIENT_KTNAME to FILE:/path/to/your/keytab.
54// The test will use it both for client and server roles.
55//
56// The test is derived in large part from test_security_curve.cpp
57
58const char *name = "zmqtest2";
59
60static volatile int zap_deny_all = 0;
61
62// --------------------------------------------------------------------------
63// This methods receives and validates ZAP requestes (allowing or denying
64// each client connection).
65// N.B. on failure, each crypto type in keytab will be tried
66
67static void zap_handler (void *handler_)
68{
69 // Process ZAP requests forever
70 while (true) {
71 char *version = s_recv (handler_);
72 if (!version)
73 break; // Terminating
74
75 char *sequence = s_recv (handler_);
76 char *domain = s_recv (handler_);
77 char *address = s_recv (handler_);
78 char *routing_id = s_recv (handler_);
79 char *mechanism = s_recv (handler_);
80 char *principal = s_recv (handler_);
81
82 TEST_ASSERT_EQUAL_STRING ("1.0", version);
83 TEST_ASSERT_EQUAL_STRING ("GSSAPI", mechanism);
84
85 send_string_expect_success (handler_, version, ZMQ_SNDMORE);
86 send_string_expect_success (handler_, sequence, ZMQ_SNDMORE);
87
88 if (!zap_deny_all) {
89 send_string_expect_success (handler_, "200", ZMQ_SNDMORE);
90 send_string_expect_success (handler_, "OK", ZMQ_SNDMORE);
91 send_string_expect_success (handler_, "anonymous", ZMQ_SNDMORE);
92 send_string_expect_success (handler_, "", 0);
93 //fprintf (stderr, "ALLOW %s\n", principal);
94 } else {
95 send_string_expect_success (handler_, "400", ZMQ_SNDMORE);
96 send_string_expect_success (handler_, "Denied", ZMQ_SNDMORE);
97 send_string_expect_success (handler_, "", ZMQ_SNDMORE);
98 send_string_expect_success (handler_, "", 0);
99 //fprintf (stderr, "DENY %s\n", principal);
100 }
101 free (version);
102 free (sequence);
103 free (domain);
104 free (address);
105 free (routing_id);
106 free (mechanism);
107 free (principal);
108 }
109 zmq_close (handler_);
110}
111
112static char my_endpoint[MAX_SOCKET_STRING];
113static void *zap_thread;
114static void *server;
115static void *server_mon;
116
117void check_krb_available ()
118{
119 if (!getenv ("KRB5_KTNAME") || !getenv ("KRB5_CLIENT_KTNAME")) {
120 TEST_IGNORE_MESSAGE ("KRB5 environment unavailable, skipping test");
121 }
122}
123
124void setUp ()
125{
126 setup_test_context ();
127
128 zap_thread = 0;
129 server = NULL;
130 server_mon = NULL;
131
132 check_krb_available ();
133
134 // Spawn ZAP handler
135 // We create and bind ZAP socket in main thread to avoid case
136 // where child thread does not start up fast enough.
137 void *handler = zmq_socket (get_test_context (), ZMQ_REP);
138 TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (handler, "inproc://zeromq.zap.01"));
139 zap_thread = zmq_threadstart (&zap_handler, handler);
140
141 // Server socket will accept connections
142 server = test_context_socket (ZMQ_DEALER);
143 int as_server = 1;
144 TEST_ASSERT_SUCCESS_ERRNO (
145 zmq_setsockopt (server, ZMQ_GSSAPI_SERVER, &as_server, sizeof (int)));
146 TEST_ASSERT_SUCCESS_ERRNO (
147 zmq_setsockopt (server, ZMQ_GSSAPI_PRINCIPAL, name, strlen (name) + 1));
148 int name_type = ZMQ_GSSAPI_NT_HOSTBASED;
149 TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
150 server, ZMQ_GSSAPI_PRINCIPAL_NAMETYPE, &name_type, sizeof (name_type)));
151 bind_loopback_ipv4 (server, my_endpoint, sizeof my_endpoint);
152
153 // Monitor handshake events on the server
154 TEST_ASSERT_SUCCESS_ERRNO (zmq_socket_monitor (
155 server, "inproc://monitor-server",
156 ZMQ_EVENT_HANDSHAKE_SUCCEEDED | ZMQ_EVENT_HANDSHAKE_FAILED_AUTH
157 | ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL));
158
159 // Create socket for collecting monitor events
160 server_mon = test_context_socket (ZMQ_PAIR);
161
162 // Connect it to the inproc endpoints so they'll get events
163 TEST_ASSERT_SUCCESS_ERRNO (
164 zmq_connect (server_mon, "inproc://monitor-server"));
165}
166
167void tearDown ()
168{
169 // Shutdown
170 if (server_mon)
171 test_context_socket_close_zero_linger (server_mon);
172 if (server)
173 test_context_socket_close (server);
174 teardown_test_context ();
175
176 // Wait until ZAP handler terminates
177 if (zap_thread)
178 zmq_threadclose (zap_thread);
179}
180
181// Read one event off the monitor socket; return value and address
182// by reference, if not null, and event number by value. Returns -1
183// in case of error.
184static int get_monitor_event (void *monitor_, int *value_, char **address_)
185{
186 // First frame in message contains event number and value
187 zmq_msg_t msg;
188 zmq_msg_init (&msg);
189 if (zmq_msg_recv (&msg, monitor_, 0) == -1)
190 return -1; // Interruped, presumably
191 TEST_ASSERT_TRUE (zmq_msg_more (&msg));
192
193 uint8_t *data = static_cast<uint8_t *> (zmq_msg_data (&msg));
194 uint16_t event = *reinterpret_cast<uint16_t *> (data);
195 if (value_)
196 *value_ = *reinterpret_cast<uint32_t *> (data + 2);
197 zmq_msg_close (&msg);
198
199 // Second frame in message contains event address
200 zmq_msg_init (&msg);
201 if (zmq_msg_recv (&msg, monitor_, 0) == -1)
202 return -1; // Interruped, presumably
203 TEST_ASSERT_FALSE (zmq_msg_more (&msg));
204
205 if (address_) {
206 uint8_t *data = static_cast<uint8_t *> (zmq_msg_data (&msg));
207 size_t size = zmq_msg_size (&msg);
208 *address_ = static_cast<char *> (malloc (size + 1));
209 memcpy (*address_, data, size);
210 *address_[size] = 0;
211 }
212 zmq_msg_close (&msg);
213
214 return event;
215}
216
217void test_valid_creds ()
218{
219 void *client = test_context_socket (ZMQ_DEALER);
220 TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
221 client, ZMQ_GSSAPI_SERVICE_PRINCIPAL, name, strlen (name) + 1));
222 TEST_ASSERT_SUCCESS_ERRNO (
223 zmq_setsockopt (client, ZMQ_GSSAPI_PRINCIPAL, name, strlen (name) + 1));
224 int name_type = ZMQ_GSSAPI_NT_HOSTBASED;
225 TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
226 client, ZMQ_GSSAPI_PRINCIPAL_NAMETYPE, &name_type, sizeof (name_type)));
227 TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (client, my_endpoint));
228
229 bounce (server, client);
230 test_context_socket_close (client);
231
232 int event = get_monitor_event (server_mon, NULL, NULL);
233 TEST_ASSERT_EQUAL_INT (ZMQ_EVENT_HANDSHAKE_SUCCEEDED, event);
234}
235
236// Check security with valid but unauthorized credentials
237// Note: ZAP may see multiple requests - after a failure, client will
238// fall back to other crypto types for principal, if available.
239void test_unauth_creds ()
240{
241 void *client = test_context_socket (ZMQ_DEALER);
242 TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
243 client, ZMQ_GSSAPI_SERVICE_PRINCIPAL, name, strlen (name) + 1));
244 TEST_ASSERT_SUCCESS_ERRNO (
245 zmq_setsockopt (client, ZMQ_GSSAPI_PRINCIPAL, name, strlen (name) + 1));
246 int name_type = ZMQ_GSSAPI_NT_HOSTBASED;
247 TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
248 client, ZMQ_GSSAPI_PRINCIPAL_NAMETYPE, &name_type, sizeof (name_type)));
249 zap_deny_all = 1;
250 TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (client, my_endpoint));
251
252 expect_bounce_fail (server, client);
253 test_context_socket_close_zero_linger (client);
254
255 int event = get_monitor_event (server_mon, NULL, NULL);
256 TEST_ASSERT_EQUAL_INT (ZMQ_EVENT_HANDSHAKE_FAILED_AUTH, event);
257}
258
259// Check GSSAPI security with NULL client credentials
260// This must be caught by the gssapi_server class, not passed to ZAP
261void test_null_creds ()
262{
263 void *client = test_context_socket (ZMQ_DEALER);
264 TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (client, my_endpoint));
265 expect_bounce_fail (server, client);
266 test_context_socket_close_zero_linger (client);
267
268 int error;
269 int event = get_monitor_event (server_mon, &error, NULL);
270 TEST_ASSERT_EQUAL_INT (ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL, event);
271 TEST_ASSERT_EQUAL_INT (ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH, error);
272}
273
274// Check GSSAPI security with PLAIN client credentials
275// This must be caught by the curve_server class, not passed to ZAP
276void test_plain_creds ()
277{
278 void *client = test_context_socket (ZMQ_DEALER);
279 TEST_ASSERT_SUCCESS_ERRNO (
280 zmq_setsockopt (client, ZMQ_PLAIN_USERNAME, "admin", 5));
281 TEST_ASSERT_SUCCESS_ERRNO (
282 zmq_setsockopt (client, ZMQ_PLAIN_PASSWORD, "password", 8));
283 TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (client, my_endpoint));
284 expect_bounce_fail (server, client);
285 test_context_socket_close_zero_linger (client);
286}
287
288// Unauthenticated messages from a vanilla socket shouldn't be received
289void test_vanilla_socket ()
290{
291 struct sockaddr_in ip4addr;
292 int s;
293 unsigned short int port;
294 int rc = sscanf (my_endpoint, "tcp://127.0.0.1:%hu", &port);
295 TEST_ASSERT_EQUAL_INT (1, rc);
296 ip4addr.sin_family = AF_INET;
297 ip4addr.sin_port = htons (port);
298#if defined(ZMQ_HAVE_WINDOWS) && (_WIN32_WINNT < 0x0600)
299 ip4addr.sin_addr.s_addr = inet_addr ("127.0.0.1");
300#else
301 inet_pton (AF_INET, "127.0.0.1", &ip4addr.sin_addr);
302#endif
303
304 s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
305 rc = connect (s, reinterpret_cast<struct sockaddr *> (&ip4addr),
306 sizeof (ip4addr));
307 TEST_ASSERT_GREATER_THAN (-1, rc);
308 // send anonymous ZMTP/1.0 greeting
309 send (s, "\x01\x00", 2, 0);
310 // send sneaky message that shouldn't be received
311 send (s, "\x08\x00sneaky\0", 9, 0);
312 int timeout = 250;
313 zmq_setsockopt (server, ZMQ_RCVTIMEO, &timeout, sizeof (timeout));
314 char *buf = s_recv (server);
315 if (buf != NULL) {
316 printf ("Received unauthenticated message: %s\n", buf);
317 TEST_ASSERT_NULL (buf);
318 }
319 close (s);
320}
321
322int main (void)
323{
324 // Avoid entanglements with user's credential cache
325 setenv ("KRB5CCNAME", "MEMORY", 1);
326
327 setup_test_environment ();
328
329 UNITY_BEGIN ();
330 RUN_TEST (test_valid_creds);
331 RUN_TEST (test_null_creds);
332 RUN_TEST (test_plain_creds);
333 RUN_TEST (test_vanilla_socket);
334 RUN_TEST (test_unauth_creds);
335 return UNITY_END ();
336 return 0;
337}
338