curve_client.cpp 8.92 KB
Newer Older
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 7 8
    libzmq is free software; you can redistribute it and/or modify it under
    the terms of the GNU Lesser General Public License (LGPL) as published
    by the Free Software Foundation; either version 3 of the License, or
9 10
    (at your option) any later version.

11 12 13 14 15 16 17 18 19 20 21 22 23 24
    As a special exception, the Contributors give you permission to link
    this library with independent modules to produce an executable,
    regardless of the license terms of these independent modules, and to
    copy and distribute the resulting executable under terms of your choice,
    provided that you also meet, for each linked independent module, the
    terms and conditions of the license of that module. An independent
    module is a module which is not derived from or based on this library.
    If you modify this library, you must extend this exception to your
    version of the library.

    libzmq is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    License for more details.
25 26 27 28 29

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

30 31
#include "precompiled.hpp"
#include "macros.hpp"
32

33
#ifdef ZMQ_HAVE_CURVE
34 35 36 37 38 39

#include "msg.hpp"
#include "session_base.hpp"
#include "err.hpp"
#include "curve_client.hpp"
#include "wire.hpp"
40
#include "curve_client_tools.hpp"
41
#include "secure_allocator.hpp"
42

43 44
zmq::curve_client_t::curve_client_t (session_base_t *session_,
                                     const options_t &options_) :
45
    mechanism_base_t (session_, options_),
46 47
    curve_mechanism_base_t (
      session_, options_, "CurveZMQMESSAGEC", "CurveZMQMESSAGES"),
48 49 50 51
    _state (send_hello),
    _tools (options_.curve_public_key,
            options_.curve_secret_key,
            options_.curve_server_key)
52 53 54 55 56 57 58
{
}

zmq::curve_client_t::~curve_client_t ()
{
}

59
int zmq::curve_client_t::next_handshake_command (msg_t *msg_)
60 61 62
{
    int rc = 0;

63
    switch (_state) {
64
        case send_hello:
65
            rc = produce_hello (msg_);
66
            if (rc == 0)
67
                _state = expect_welcome;
68 69
            break;
        case send_initiate:
70
            rc = produce_initiate (msg_);
71
            if (rc == 0)
72
                _state = expect_ready;
73 74 75 76 77 78 79 80
            break;
        default:
            errno = EAGAIN;
            rc = -1;
    }
    return rc;
}

81
int 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 91
    else if (curve_client_tools_t::is_handshake_command_ready (msg_data,
                                                               msg_size))
92
        rc = process_ready (msg_data, msg_size);
93 94
    else if (curve_client_tools_t::is_handshake_command_error (msg_data,
                                                               msg_size))
95 96
        rc = process_error (msg_data, msg_size);
    else {
97
        session->get_socket ()->event_handshake_failed_protocol (
98
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
99 100
        errno = EPROTO;
        rc = -1;
101
    }
102

103 104 105 106 107 108
    if (rc == 0) {
        rc = msg_->close ();
        errno_assert (rc == 0);
        rc = msg_->init ();
        errno_assert (rc == 0);
    }
109

110 111 112
    return rc;
}

113 114
int zmq::curve_client_t::encode (msg_t *msg_)
{
115
    zmq_assert (_state == connected);
116
    return curve_mechanism_base_t::encode (msg_);
117 118 119 120
}

int zmq::curve_client_t::decode (msg_t *msg_)
{
121
    zmq_assert (_state == connected);
122
    return curve_mechanism_base_t::decode (msg_);
123 124
}

125
zmq::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
}

135
int zmq::curve_client_t::produce_hello (msg_t *msg_)
136
{
137 138
    int rc = msg_->init_size (200);
    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 144 145 146
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);

        // TODO this is somewhat inconsistent: we call init_size, but we may
        // 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 156 157

    return 0;
}

158 159
int zmq::curve_client_t::process_welcome (const uint8_t *msg_data_,
                                          size_t msg_size_)
160
{
161
    const 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 168 169 170
        errno = EPROTO;
        return -1;
    }

171
    _state = send_initiate;
172

173 174 175
    return 0;
}

176
int zmq::curve_client_t::produce_initiate (msg_t *msg_)
177
{
178
    const size_t metadata_length = basic_properties_len ();
179 180
    std::vector<unsigned char, secure_allocator_t<unsigned char> >
      metadata_plaintext (metadata_length);
181

182
    add_basic_properties (&metadata_plaintext[0], metadata_length);
183

184 185
    const size_t msg_size =
      113 + 128 + crypto_box_BOXZEROBYTES + metadata_length;
186
    int rc = msg_->init_size (msg_size);
187 188
    errno_assert (rc == 0);

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 197 198
        // TODO see comment in produce_hello
        return -1;
    }
199

200
    cn_nonce++;
201 202 203 204

    return 0;
}

205 206
int zmq::curve_client_t::process_ready (const uint8_t *msg_data_,
                                        size_t msg_size_)
207
{
208
    if (msg_size_ < 30) {
209 210 211
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY);
212 213 214 215
        errno = EPROTO;
        return -1;
    }

216
    const size_t clen = (msg_size_ - 14) + crypto_box_BOXZEROBYTES;
217

218
    uint8_t ready_nonce[crypto_box_NONCEBYTES];
219 220
    std::vector<uint8_t, secure_allocator_t<uint8_t> > ready_plaintext (
      crypto_box_ZEROBYTES + clen);
221 222 223 224 225
    std::vector<uint8_t> ready_box (crypto_box_BOXZEROBYTES + 16 + clen);

    std::fill (ready_box.begin (), ready_box.begin () + crypto_box_BOXZEROBYTES,
               0);
    memcpy (&ready_box[crypto_box_BOXZEROBYTES], msg_data_ + 14,
226
            clen - crypto_box_BOXZEROBYTES);
227 228

    memcpy (ready_nonce, "CurveZMQREADY---", 16);
229 230
    memcpy (ready_nonce + 16, msg_data_ + 6, 8);
    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 239 240 241
        errno = EPROTO;
        return -1;
    }

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 249 250 251
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA);
        errno = EPROTO;
    }
252

253 254 255
    return rc;
}

256 257
int zmq::curve_client_t::process_error (const uint8_t *msg_data_,
                                        size_t msg_size_)
258
{
259
    if (_state != expect_welcome && _state != expect_ready) {
260 261
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
262 263 264
        errno = EPROTO;
        return -1;
    }
265
    if (msg_size_ < 7) {
266
        session->get_socket ()->event_handshake_failed_protocol (
267 268
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR);
269 270 271
        errno = EPROTO;
        return -1;
    }
272 273
    const size_t error_reason_len = static_cast<size_t> (msg_data_[6]);
    if (error_reason_len > msg_size_ - 7) {
274
        session->get_socket ()->event_handshake_failed_protocol (
275 276
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR);
277 278 279
        errno = EPROTO;
        return -1;
    }
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 284 285
    return 0;
}

286
#endif