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_client.hpp"
39#include "wire.hpp"
40#include "curve_client_tools.hpp"
41#include "secure_allocator.hpp"
42
43zmq::curve_client_t::curve_client_t (session_base_t *session_,
44 const options_t &options_) :
45 mechanism_base_t (session_, options_),
46 curve_mechanism_base_t (
47 session_, options_, "CurveZMQMESSAGEC", "CurveZMQMESSAGES"),
48 _state (send_hello),
49 _tools (options_.curve_public_key,
50 options_.curve_secret_key,
51 options_.curve_server_key)
52{
53}
54
55zmq::curve_client_t::~curve_client_t ()
56{
57}
58
59int zmq::curve_client_t::next_handshake_command (msg_t *msg_)
60{
61 int rc = 0;
62
63 switch (_state) {
64 case send_hello:
65 rc = produce_hello (msg_);
66 if (rc == 0)
67 _state = expect_welcome;
68 break;
69 case send_initiate:
70 rc = produce_initiate (msg_);
71 if (rc == 0)
72 _state = expect_ready;
73 break;
74 default:
75 errno = EAGAIN;
76 rc = -1;
77 }
78 return rc;
79}
80
81int zmq::curve_client_t::process_handshake_command (msg_t *msg_)
82{
83 const unsigned char *msg_data =
84 static_cast<unsigned char *> (msg_->data ());
85 const size_t msg_size = msg_->size ();
86
87 int rc = 0;
88 if (curve_client_tools_t::is_handshake_command_welcome (msg_data, msg_size))
89 rc = process_welcome (msg_data, msg_size);
90 else if (curve_client_tools_t::is_handshake_command_ready (msg_data,
91 msg_size))
92 rc = process_ready (msg_data, msg_size);
93 else if (curve_client_tools_t::is_handshake_command_error (msg_data,
94 msg_size))
95 rc = process_error (msg_data, msg_size);
96 else {
97 session->get_socket ()->event_handshake_failed_protocol (
98 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
99 errno = EPROTO;
100 rc = -1;
101 }
102
103 if (rc == 0) {
104 rc = msg_->close ();
105 errno_assert (rc == 0);
106 rc = msg_->init ();
107 errno_assert (rc == 0);
108 }
109
110 return rc;
111}
112
113int zmq::curve_client_t::encode (msg_t *msg_)
114{
115 zmq_assert (_state == connected);
116 return curve_mechanism_base_t::encode (msg_);
117}
118
119int zmq::curve_client_t::decode (msg_t *msg_)
120{
121 zmq_assert (_state == connected);
122 return curve_mechanism_base_t::decode (msg_);
123}
124
125zmq::mechanism_t::status_t zmq::curve_client_t::status () const
126{
127 if (_state == connected)
128 return mechanism_t::ready;
129 if (_state == error_received)
130 return mechanism_t::error;
131
132 return mechanism_t::handshaking;
133}
134
135int zmq::curve_client_t::produce_hello (msg_t *msg_)
136{
137 int rc = msg_->init_size (200);
138 errno_assert (rc == 0);
139
140 rc = _tools.produce_hello (msg_->data (), cn_nonce);
141 if (rc == -1) {
142 session->get_socket ()->event_handshake_failed_protocol (
143 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
144
145 // TODO this is somewhat inconsistent: we call init_size, but we may
146 // not close msg_; i.e. we assume that msg_ is initialized but empty
147 // (if it were non-empty, calling init_size might cause a leak!)
148
149 // msg_->close ();
150 return -1;
151 }
152
153 cn_nonce++;
154
155 return 0;
156}
157
158int zmq::curve_client_t::process_welcome (const uint8_t *msg_data_,
159 size_t msg_size_)
160{
161 int rc = _tools.process_welcome (msg_data_, msg_size_, cn_precom);
162
163 if (rc == -1) {
164 session->get_socket ()->event_handshake_failed_protocol (
165 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
166
167 errno = EPROTO;
168 return -1;
169 }
170
171 _state = send_initiate;
172
173 return 0;
174}
175
176int zmq::curve_client_t::produce_initiate (msg_t *msg_)
177{
178 const size_t metadata_length = basic_properties_len ();
179 std::vector<unsigned char, secure_allocator_t<unsigned char> >
180 metadata_plaintext (metadata_length);
181
182 add_basic_properties (&metadata_plaintext[0], metadata_length);
183
184 const size_t msg_size =
185 113 + 128 + crypto_box_BOXZEROBYTES + metadata_length;
186 int rc = msg_->init_size (msg_size);
187 errno_assert (rc == 0);
188
189 rc = _tools.produce_initiate (msg_->data (), msg_size, cn_nonce,
190 &metadata_plaintext[0], metadata_length);
191
192 if (-1 == rc) {
193 session->get_socket ()->event_handshake_failed_protocol (
194 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
195
196 // TODO see comment in produce_hello
197 return -1;
198 }
199
200 cn_nonce++;
201
202 return 0;
203}
204
205int zmq::curve_client_t::process_ready (const uint8_t *msg_data_,
206 size_t msg_size_)
207{
208 if (msg_size_ < 30) {
209 session->get_socket ()->event_handshake_failed_protocol (
210 session->get_endpoint (),
211 ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY);
212 errno = EPROTO;
213 return -1;
214 }
215
216 const size_t clen = (msg_size_ - 14) + crypto_box_BOXZEROBYTES;
217
218 uint8_t ready_nonce[crypto_box_NONCEBYTES];
219 std::vector<uint8_t, secure_allocator_t<uint8_t> > ready_plaintext (
220 crypto_box_ZEROBYTES + clen);
221 std::vector<uint8_t> ready_box (crypto_box_BOXZEROBYTES + 16 + clen);
222
223 std::fill (ready_box.begin (), ready_box.begin () + crypto_box_BOXZEROBYTES,
224 0);
225 memcpy (&ready_box[crypto_box_BOXZEROBYTES], msg_data_ + 14,
226 clen - crypto_box_BOXZEROBYTES);
227
228 memcpy (ready_nonce, "CurveZMQREADY---", 16);
229 memcpy (ready_nonce + 16, msg_data_ + 6, 8);
230 cn_peer_nonce = get_uint64 (msg_data_ + 6);
231
232 int rc = crypto_box_open_afternm (&ready_plaintext[0], &ready_box[0], clen,
233 ready_nonce, cn_precom);
234
235 if (rc != 0) {
236 session->get_socket ()->event_handshake_failed_protocol (
237 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
238 errno = EPROTO;
239 return -1;
240 }
241
242 rc = parse_metadata (&ready_plaintext[crypto_box_ZEROBYTES],
243 clen - crypto_box_ZEROBYTES);
244
245 if (rc == 0)
246 _state = connected;
247 else {
248 session->get_socket ()->event_handshake_failed_protocol (
249 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA);
250 errno = EPROTO;
251 }
252
253 return rc;
254}
255
256int zmq::curve_client_t::process_error (const uint8_t *msg_data_,
257 size_t msg_size_)
258{
259 if (_state != expect_welcome && _state != expect_ready) {
260 session->get_socket ()->event_handshake_failed_protocol (
261 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
262 errno = EPROTO;
263 return -1;
264 }
265 if (msg_size_ < 7) {
266 session->get_socket ()->event_handshake_failed_protocol (
267 session->get_endpoint (),
268 ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR);
269 errno = EPROTO;
270 return -1;
271 }
272 const size_t error_reason_len = static_cast<size_t> (msg_data_[6]);
273 if (error_reason_len > msg_size_ - 7) {
274 session->get_socket ()->event_handshake_failed_protocol (
275 session->get_endpoint (),
276 ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR);
277 errno = EPROTO;
278 return -1;
279 }
280 const char *error_reason = reinterpret_cast<const char *> (msg_data_) + 7;
281 handle_error_reason (error_reason, error_reason_len);
282 _state = error_received;
283 return 0;
284}
285
286#endif
287