1 | /* |
2 | Copyright (c) 2007-2016 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 "precompiled.hpp" |
31 | #include "macros.hpp" |
32 | |
33 | #ifdef ZMQ_HAVE_CURVE |
34 | |
35 | #include "msg.hpp" |
36 | #include "session_base.hpp" |
37 | #include "err.hpp" |
38 | #include "curve_server.hpp" |
39 | #include "wire.hpp" |
40 | #include "secure_allocator.hpp" |
41 | |
42 | zmq::curve_server_t::curve_server_t (session_base_t *session_, |
43 | const std::string &peer_address_, |
44 | const options_t &options_) : |
45 | mechanism_base_t (session_, options_), |
46 | zap_client_common_handshake_t ( |
47 | session_, peer_address_, options_, sending_ready), |
48 | curve_mechanism_base_t ( |
49 | session_, options_, "CurveZMQMESSAGES" , "CurveZMQMESSAGEC" ) |
50 | { |
51 | int rc; |
52 | // Fetch our secret key from socket options |
53 | memcpy (_secret_key, options_.curve_secret_key, crypto_box_SECRETKEYBYTES); |
54 | |
55 | // Generate short-term key pair |
56 | rc = crypto_box_keypair (_cn_public, _cn_secret); |
57 | zmq_assert (rc == 0); |
58 | } |
59 | |
60 | zmq::curve_server_t::~curve_server_t () |
61 | { |
62 | } |
63 | |
64 | int zmq::curve_server_t::next_handshake_command (msg_t *msg_) |
65 | { |
66 | int rc = 0; |
67 | |
68 | switch (state) { |
69 | case sending_welcome: |
70 | rc = produce_welcome (msg_); |
71 | if (rc == 0) |
72 | state = waiting_for_initiate; |
73 | break; |
74 | case sending_ready: |
75 | rc = produce_ready (msg_); |
76 | if (rc == 0) |
77 | state = ready; |
78 | break; |
79 | case sending_error: |
80 | rc = produce_error (msg_); |
81 | if (rc == 0) |
82 | state = error_sent; |
83 | break; |
84 | default: |
85 | errno = EAGAIN; |
86 | rc = -1; |
87 | break; |
88 | } |
89 | return rc; |
90 | } |
91 | |
92 | int zmq::curve_server_t::process_handshake_command (msg_t *msg_) |
93 | { |
94 | int rc = 0; |
95 | |
96 | switch (state) { |
97 | case waiting_for_hello: |
98 | rc = process_hello (msg_); |
99 | break; |
100 | case waiting_for_initiate: |
101 | rc = process_initiate (msg_); |
102 | break; |
103 | default: |
104 | // TODO I think this is not a case reachable with a misbehaving |
105 | // client. It is not an "invalid handshake command", but would be |
106 | // trying to process a handshake command in an invalid state, |
107 | // which is purely under control of this peer. |
108 | // Therefore, it should be changed to zmq_assert (false); |
109 | |
110 | // CURVE I: invalid handshake command |
111 | session->get_socket ()->event_handshake_failed_protocol ( |
112 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED); |
113 | errno = EPROTO; |
114 | rc = -1; |
115 | break; |
116 | } |
117 | if (rc == 0) { |
118 | rc = msg_->close (); |
119 | errno_assert (rc == 0); |
120 | rc = msg_->init (); |
121 | errno_assert (rc == 0); |
122 | } |
123 | return rc; |
124 | } |
125 | |
126 | int zmq::curve_server_t::encode (msg_t *msg_) |
127 | { |
128 | zmq_assert (state == ready); |
129 | return curve_mechanism_base_t::encode (msg_); |
130 | } |
131 | |
132 | int zmq::curve_server_t::decode (msg_t *msg_) |
133 | { |
134 | zmq_assert (state == ready); |
135 | return curve_mechanism_base_t::decode (msg_); |
136 | } |
137 | |
138 | int zmq::curve_server_t::process_hello (msg_t *msg_) |
139 | { |
140 | int rc = check_basic_command_structure (msg_); |
141 | if (rc == -1) |
142 | return -1; |
143 | |
144 | const size_t size = msg_->size (); |
145 | const uint8_t *const hello = static_cast<uint8_t *> (msg_->data ()); |
146 | |
147 | if (size < 6 || memcmp (hello, "\x05HELLO" , 6)) { |
148 | session->get_socket ()->event_handshake_failed_protocol ( |
149 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); |
150 | errno = EPROTO; |
151 | return -1; |
152 | } |
153 | |
154 | if (size != 200) { |
155 | session->get_socket ()->event_handshake_failed_protocol ( |
156 | session->get_endpoint (), |
157 | ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO); |
158 | errno = EPROTO; |
159 | return -1; |
160 | } |
161 | |
162 | const uint8_t major = hello[6]; |
163 | const uint8_t minor = hello[7]; |
164 | |
165 | if (major != 1 || minor != 0) { |
166 | // CURVE I: client HELLO has unknown version number |
167 | session->get_socket ()->event_handshake_failed_protocol ( |
168 | session->get_endpoint (), |
169 | ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO); |
170 | errno = EPROTO; |
171 | return -1; |
172 | } |
173 | |
174 | // Save client's short-term public key (C') |
175 | memcpy (_cn_client, hello + 80, 32); |
176 | |
177 | uint8_t hello_nonce[crypto_box_NONCEBYTES]; |
178 | std::vector<uint8_t, secure_allocator_t<uint8_t> > hello_plaintext ( |
179 | crypto_box_ZEROBYTES + 64); |
180 | uint8_t hello_box[crypto_box_BOXZEROBYTES + 80]; |
181 | |
182 | memcpy (hello_nonce, "CurveZMQHELLO---" , 16); |
183 | memcpy (hello_nonce + 16, hello + 112, 8); |
184 | cn_peer_nonce = get_uint64 (hello + 112); |
185 | |
186 | memset (hello_box, 0, crypto_box_BOXZEROBYTES); |
187 | memcpy (hello_box + crypto_box_BOXZEROBYTES, hello + 120, 80); |
188 | |
189 | // Open Box [64 * %x0](C'->S) |
190 | rc = crypto_box_open (&hello_plaintext[0], hello_box, sizeof hello_box, |
191 | hello_nonce, _cn_client, _secret_key); |
192 | if (rc != 0) { |
193 | // CURVE I: cannot open client HELLO -- wrong server key? |
194 | session->get_socket ()->event_handshake_failed_protocol ( |
195 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); |
196 | errno = EPROTO; |
197 | return -1; |
198 | } |
199 | |
200 | state = sending_welcome; |
201 | return rc; |
202 | } |
203 | |
204 | int zmq::curve_server_t::produce_welcome (msg_t *msg_) |
205 | { |
206 | uint8_t cookie_nonce[crypto_secretbox_NONCEBYTES]; |
207 | std::vector<uint8_t, secure_allocator_t<uint8_t> > cookie_plaintext ( |
208 | crypto_secretbox_ZEROBYTES + 64); |
209 | uint8_t cookie_ciphertext[crypto_secretbox_BOXZEROBYTES + 80]; |
210 | |
211 | // Create full nonce for encryption |
212 | // 8-byte prefix plus 16-byte random nonce |
213 | memcpy (cookie_nonce, "COOKIE--" , 8); |
214 | randombytes (cookie_nonce + 8, 16); |
215 | |
216 | // Generate cookie = Box [C' + s'](t) |
217 | std::fill (cookie_plaintext.begin (), |
218 | cookie_plaintext.begin () + crypto_secretbox_ZEROBYTES, 0); |
219 | memcpy (&cookie_plaintext[crypto_secretbox_ZEROBYTES], _cn_client, 32); |
220 | memcpy (&cookie_plaintext[crypto_secretbox_ZEROBYTES + 32], _cn_secret, 32); |
221 | |
222 | // Generate fresh cookie key |
223 | randombytes (_cookie_key, crypto_secretbox_KEYBYTES); |
224 | |
225 | // Encrypt using symmetric cookie key |
226 | int rc = |
227 | crypto_secretbox (cookie_ciphertext, &cookie_plaintext[0], |
228 | cookie_plaintext.size (), cookie_nonce, _cookie_key); |
229 | zmq_assert (rc == 0); |
230 | |
231 | uint8_t welcome_nonce[crypto_box_NONCEBYTES]; |
232 | std::vector<uint8_t, secure_allocator_t<uint8_t> > welcome_plaintext ( |
233 | crypto_box_ZEROBYTES + 128); |
234 | uint8_t welcome_ciphertext[crypto_box_BOXZEROBYTES + 144]; |
235 | |
236 | // Create full nonce for encryption |
237 | // 8-byte prefix plus 16-byte random nonce |
238 | memcpy (welcome_nonce, "WELCOME-" , 8); |
239 | randombytes (welcome_nonce + 8, crypto_box_NONCEBYTES - 8); |
240 | |
241 | // Create 144-byte Box [S' + cookie](S->C') |
242 | std::fill (welcome_plaintext.begin (), |
243 | welcome_plaintext.begin () + crypto_box_ZEROBYTES, 0); |
244 | memcpy (&welcome_plaintext[crypto_box_ZEROBYTES], _cn_public, 32); |
245 | memcpy (&welcome_plaintext[crypto_box_ZEROBYTES + 32], cookie_nonce + 8, |
246 | 16); |
247 | memcpy (&welcome_plaintext[crypto_box_ZEROBYTES + 48], |
248 | cookie_ciphertext + crypto_secretbox_BOXZEROBYTES, 80); |
249 | |
250 | rc = crypto_box (welcome_ciphertext, &welcome_plaintext[0], |
251 | welcome_plaintext.size (), welcome_nonce, _cn_client, |
252 | _secret_key); |
253 | |
254 | // TODO I think we should change this back to zmq_assert (rc == 0); |
255 | // as it was before https://github.com/zeromq/libzmq/pull/1832 |
256 | // The reason given there was that secret_key might be 0ed. |
257 | // But if it were, we would never get this far, since we could |
258 | // not have opened the client's hello box with a 0ed key. |
259 | |
260 | if (rc == -1) |
261 | return -1; |
262 | |
263 | rc = msg_->init_size (168); |
264 | errno_assert (rc == 0); |
265 | |
266 | uint8_t *const welcome = static_cast<uint8_t *> (msg_->data ()); |
267 | memcpy (welcome, "\x07WELCOME" , 8); |
268 | memcpy (welcome + 8, welcome_nonce + 8, 16); |
269 | memcpy (welcome + 24, welcome_ciphertext + crypto_box_BOXZEROBYTES, 144); |
270 | |
271 | return 0; |
272 | } |
273 | |
274 | int zmq::curve_server_t::process_initiate (msg_t *msg_) |
275 | { |
276 | int rc = check_basic_command_structure (msg_); |
277 | if (rc == -1) |
278 | return -1; |
279 | |
280 | const size_t size = msg_->size (); |
281 | const uint8_t *initiate = static_cast<uint8_t *> (msg_->data ()); |
282 | |
283 | if (size < 9 || memcmp (initiate, "\x08INITIATE" , 9)) { |
284 | session->get_socket ()->event_handshake_failed_protocol ( |
285 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); |
286 | errno = EPROTO; |
287 | return -1; |
288 | } |
289 | |
290 | if (size < 257) { |
291 | session->get_socket ()->event_handshake_failed_protocol ( |
292 | session->get_endpoint (), |
293 | ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE); |
294 | errno = EPROTO; |
295 | return -1; |
296 | } |
297 | |
298 | uint8_t cookie_nonce[crypto_secretbox_NONCEBYTES]; |
299 | uint8_t cookie_plaintext[crypto_secretbox_ZEROBYTES + 64]; |
300 | uint8_t cookie_box[crypto_secretbox_BOXZEROBYTES + 80]; |
301 | |
302 | // Open Box [C' + s'](t) |
303 | memset (cookie_box, 0, crypto_secretbox_BOXZEROBYTES); |
304 | memcpy (cookie_box + crypto_secretbox_BOXZEROBYTES, initiate + 25, 80); |
305 | |
306 | memcpy (cookie_nonce, "COOKIE--" , 8); |
307 | memcpy (cookie_nonce + 8, initiate + 9, 16); |
308 | |
309 | rc = crypto_secretbox_open (cookie_plaintext, cookie_box, sizeof cookie_box, |
310 | cookie_nonce, _cookie_key); |
311 | if (rc != 0) { |
312 | // CURVE I: cannot open client INITIATE cookie |
313 | session->get_socket ()->event_handshake_failed_protocol ( |
314 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); |
315 | errno = EPROTO; |
316 | return -1; |
317 | } |
318 | |
319 | // Check cookie plain text is as expected [C' + s'] |
320 | if (memcmp (cookie_plaintext + crypto_secretbox_ZEROBYTES, _cn_client, 32) |
321 | || memcmp (cookie_plaintext + crypto_secretbox_ZEROBYTES + 32, |
322 | _cn_secret, 32)) { |
323 | // TODO this case is very hard to test, as it would require a modified |
324 | // client that knows the server's secret temporary cookie key |
325 | |
326 | // CURVE I: client INITIATE cookie is not valid |
327 | session->get_socket ()->event_handshake_failed_protocol ( |
328 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); |
329 | errno = EPROTO; |
330 | return -1; |
331 | } |
332 | |
333 | const size_t clen = (size - 113) + crypto_box_BOXZEROBYTES; |
334 | |
335 | uint8_t initiate_nonce[crypto_box_NONCEBYTES]; |
336 | std::vector<uint8_t, secure_allocator_t<uint8_t> > initiate_plaintext ( |
337 | crypto_box_ZEROBYTES + clen); |
338 | std::vector<uint8_t> initiate_box (crypto_box_BOXZEROBYTES + clen); |
339 | |
340 | // Open Box [C + vouch + metadata](C'->S') |
341 | std::fill (initiate_box.begin (), |
342 | initiate_box.begin () + crypto_box_BOXZEROBYTES, 0); |
343 | memcpy (&initiate_box[crypto_box_BOXZEROBYTES], initiate + 113, |
344 | clen - crypto_box_BOXZEROBYTES); |
345 | |
346 | memcpy (initiate_nonce, "CurveZMQINITIATE" , 16); |
347 | memcpy (initiate_nonce + 16, initiate + 105, 8); |
348 | cn_peer_nonce = get_uint64 (initiate + 105); |
349 | |
350 | const uint8_t *client_key = &initiate_plaintext[crypto_box_ZEROBYTES]; |
351 | |
352 | rc = crypto_box_open (&initiate_plaintext[0], &initiate_box[0], clen, |
353 | initiate_nonce, _cn_client, _cn_secret); |
354 | if (rc != 0) { |
355 | // CURVE I: cannot open client INITIATE |
356 | session->get_socket ()->event_handshake_failed_protocol ( |
357 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); |
358 | errno = EPROTO; |
359 | return -1; |
360 | } |
361 | |
362 | uint8_t vouch_nonce[crypto_box_NONCEBYTES]; |
363 | std::vector<uint8_t, secure_allocator_t<uint8_t> > vouch_plaintext ( |
364 | crypto_box_ZEROBYTES + 64); |
365 | uint8_t vouch_box[crypto_box_BOXZEROBYTES + 80]; |
366 | |
367 | // Open Box Box [C',S](C->S') and check contents |
368 | memset (vouch_box, 0, crypto_box_BOXZEROBYTES); |
369 | memcpy (vouch_box + crypto_box_BOXZEROBYTES, |
370 | &initiate_plaintext[crypto_box_ZEROBYTES + 48], 80); |
371 | |
372 | memcpy (vouch_nonce, "VOUCH---" , 8); |
373 | memcpy (vouch_nonce + 8, &initiate_plaintext[crypto_box_ZEROBYTES + 32], |
374 | 16); |
375 | |
376 | rc = crypto_box_open (&vouch_plaintext[0], vouch_box, sizeof vouch_box, |
377 | vouch_nonce, client_key, _cn_secret); |
378 | if (rc != 0) { |
379 | // CURVE I: cannot open client INITIATE vouch |
380 | session->get_socket ()->event_handshake_failed_protocol ( |
381 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); |
382 | errno = EPROTO; |
383 | return -1; |
384 | } |
385 | |
386 | // What we decrypted must be the client's short-term public key |
387 | if (memcmp (&vouch_plaintext[crypto_box_ZEROBYTES], _cn_client, 32)) { |
388 | // TODO this case is very hard to test, as it would require a modified |
389 | // client that knows the server's secret short-term key |
390 | |
391 | // CURVE I: invalid handshake from client (public key) |
392 | session->get_socket ()->event_handshake_failed_protocol ( |
393 | session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE); |
394 | errno = EPROTO; |
395 | return -1; |
396 | } |
397 | |
398 | // Precompute connection secret from client key |
399 | rc = crypto_box_beforenm (cn_precom, _cn_client, _cn_secret); |
400 | zmq_assert (rc == 0); |
401 | |
402 | // Given this is a backward-incompatible change, it's behind a socket |
403 | // option disabled by default. |
404 | if (zap_required () || !options.zap_enforce_domain) { |
405 | // Use ZAP protocol (RFC 27) to authenticate the user. |
406 | rc = session->zap_connect (); |
407 | if (rc == 0) { |
408 | send_zap_request (client_key); |
409 | state = waiting_for_zap_reply; |
410 | |
411 | // TODO actually, it is quite unlikely that we can read the ZAP |
412 | // reply already, but removing this has some strange side-effect |
413 | // (probably because the pipe's in_active flag is true until a read |
414 | // is attempted) |
415 | if (-1 == receive_and_process_zap_reply ()) |
416 | return -1; |
417 | } else if (!options.zap_enforce_domain) { |
418 | // This supports the Stonehouse pattern (encryption without |
419 | // authentication) in legacy mode (domain set but no handler). |
420 | state = sending_ready; |
421 | } else { |
422 | session->get_socket ()->event_handshake_failed_no_detail ( |
423 | session->get_endpoint (), EFAULT); |
424 | return -1; |
425 | } |
426 | } else { |
427 | // This supports the Stonehouse pattern (encryption without authentication). |
428 | state = sending_ready; |
429 | } |
430 | |
431 | return parse_metadata (&initiate_plaintext[crypto_box_ZEROBYTES + 128], |
432 | clen - crypto_box_ZEROBYTES - 128); |
433 | } |
434 | |
435 | int zmq::curve_server_t::produce_ready (msg_t *msg_) |
436 | { |
437 | const size_t metadata_length = basic_properties_len (); |
438 | uint8_t ready_nonce[crypto_box_NONCEBYTES]; |
439 | |
440 | std::vector<uint8_t, secure_allocator_t<uint8_t> > ready_plaintext ( |
441 | crypto_box_ZEROBYTES + metadata_length); |
442 | |
443 | // Create Box [metadata](S'->C') |
444 | std::fill (ready_plaintext.begin (), |
445 | ready_plaintext.begin () + crypto_box_ZEROBYTES, 0); |
446 | uint8_t *ptr = &ready_plaintext[crypto_box_ZEROBYTES]; |
447 | |
448 | ptr += add_basic_properties (ptr, metadata_length); |
449 | const size_t mlen = ptr - &ready_plaintext[0]; |
450 | |
451 | memcpy (ready_nonce, "CurveZMQREADY---" , 16); |
452 | put_uint64 (ready_nonce + 16, cn_nonce); |
453 | |
454 | std::vector<uint8_t> ready_box (crypto_box_BOXZEROBYTES + 16 |
455 | + metadata_length); |
456 | |
457 | int rc = crypto_box_afternm (&ready_box[0], &ready_plaintext[0], mlen, |
458 | ready_nonce, cn_precom); |
459 | zmq_assert (rc == 0); |
460 | |
461 | rc = msg_->init_size (14 + mlen - crypto_box_BOXZEROBYTES); |
462 | errno_assert (rc == 0); |
463 | |
464 | uint8_t *ready = static_cast<uint8_t *> (msg_->data ()); |
465 | |
466 | memcpy (ready, "\x05READY" , 6); |
467 | // Short nonce, prefixed by "CurveZMQREADY---" |
468 | memcpy (ready + 6, ready_nonce + 16, 8); |
469 | // Box [metadata](S'->C') |
470 | memcpy (ready + 14, &ready_box[crypto_box_BOXZEROBYTES], |
471 | mlen - crypto_box_BOXZEROBYTES); |
472 | |
473 | cn_nonce++; |
474 | |
475 | return 0; |
476 | } |
477 | |
478 | int zmq::curve_server_t::produce_error (msg_t *msg_) const |
479 | { |
480 | const size_t expected_status_code_length = 3; |
481 | zmq_assert (status_code.length () == 3); |
482 | const int rc = msg_->init_size (6 + 1 + expected_status_code_length); |
483 | zmq_assert (rc == 0); |
484 | char *msg_data = static_cast<char *> (msg_->data ()); |
485 | memcpy (msg_data, "\5ERROR" , 6); |
486 | msg_data[6] = expected_status_code_length; |
487 | memcpy (msg_data + 7, status_code.c_str (), expected_status_code_length); |
488 | return 0; |
489 | } |
490 | |
491 | void zmq::curve_server_t::send_zap_request (const uint8_t *key_) |
492 | { |
493 | zap_client_t::send_zap_request ("CURVE" , 5, key_, |
494 | crypto_box_PUBLICKEYBYTES); |
495 | } |
496 | |
497 | #endif |
498 | |