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 | |
58 | const char *name = "zmqtest2" ; |
59 | |
60 | static 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 | |
67 | static 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 | |
112 | static char my_endpoint[MAX_SOCKET_STRING]; |
113 | static void *zap_thread; |
114 | static void *server; |
115 | static void *server_mon; |
116 | |
117 | void 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 | |
124 | void 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 | |
167 | void 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. |
184 | static 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 | |
217 | void 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. |
239 | void 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 |
261 | void 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 |
276 | void 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 |
289 | void 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 | |
322 | int 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 | |