curve_server.cpp 17.2 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 40 41

#include "msg.hpp"
#include "session_base.hpp"
#include "err.hpp"
#include "curve_server.hpp"
#include "wire.hpp"

zmq::curve_server_t::curve_server_t (session_base_t *session_,
42
                                     const std::string &peer_address_,
43
                                     const options_t &options_) :
44
    mechanism_base_t (session_, options_),
45 46
    zap_client_common_handshake_t (
      session_, peer_address_, options_, sending_ready),
47 48
    curve_mechanism_base_t (
      session_, options_, "CurveZMQMESSAGES", "CurveZMQMESSAGEC")
49
{
50
    int rc;
51
    //  Fetch our secret key from socket options
52
    memcpy (_secret_key, options_.curve_secret_key, crypto_box_SECRETKEYBYTES);
53

54
    //  Generate short-term key pair
55
    rc = crypto_box_keypair (_cn_public, _cn_secret);
56 57 58 59 60 61 62
    zmq_assert (rc == 0);
}

zmq::curve_server_t::~curve_server_t ()
{
}

63
int zmq::curve_server_t::next_handshake_command (msg_t *msg_)
64 65 66 67
{
    int rc = 0;

    switch (state) {
68
        case sending_welcome:
69
            rc = produce_welcome (msg_);
70
            if (rc == 0)
71
                state = waiting_for_initiate;
72
            break;
73
        case sending_ready:
74
            rc = produce_ready (msg_);
75
            if (rc == 0)
76
                state = ready;
77
            break;
78
        case sending_error:
79 80 81 82
            rc = produce_error (msg_);
            if (rc == 0)
                state = error_sent;
            break;
83 84 85 86 87 88 89 90
        default:
            errno = EAGAIN;
            rc = -1;
            break;
    }
    return rc;
}

91
int zmq::curve_server_t::process_handshake_command (msg_t *msg_)
92 93 94 95
{
    int rc = 0;

    switch (state) {
96
        case waiting_for_hello:
97 98
            rc = process_hello (msg_);
            break;
99
        case waiting_for_initiate:
100 101 102
            rc = process_initiate (msg_);
            break;
        default:
103 104 105 106
            // TODO I think this is not a case reachable with a misbehaving
            // client. It is not an "invalid handshake command", but would be
            // trying to process a handshake command in an invalid state,
            // which is purely under control of this peer.
107 108
            // Therefore, it should be changed to zmq_assert (false);

109
            // CURVE I: invalid handshake command
110 111
            session->get_socket ()->event_handshake_failed_protocol (
              session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED);
112
            errno = EPROTO;
113 114 115 116 117 118 119 120 121 122 123 124
            rc = -1;
            break;
    }
    if (rc == 0) {
        rc = msg_->close ();
        errno_assert (rc == 0);
        rc = msg_->init ();
        errno_assert (rc == 0);
    }
    return rc;
}

125 126
int zmq::curve_server_t::encode (msg_t *msg_)
{
127
    zmq_assert (state == ready);
128
    return curve_mechanism_base_t::encode (msg_);
129 130 131 132
}

int zmq::curve_server_t::decode (msg_t *msg_)
{
133
    zmq_assert (state == ready);
134
    return curve_mechanism_base_t::decode (msg_);
135 136
}

137 138
int zmq::curve_server_t::process_hello (msg_t *msg_)
{
139 140
    int rc = check_basic_command_structure (msg_);
    if (rc == -1)
141
        return -1;
142

143
    const size_t size = msg_->size ();
144
    const uint8_t *const hello = static_cast<uint8_t *> (msg_->data ());
145 146

    if (size < 6 || memcmp (hello, "\x05HELLO", 6)) {
147 148
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
149 150 151 152
        errno = EPROTO;
        return -1;
    }

153
    if (size != 200) {
154
        session->get_socket ()->event_handshake_failed_protocol (
155 156
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO);
157 158 159 160
        errno = EPROTO;
        return -1;
    }

161 162
    const uint8_t major = hello[6];
    const uint8_t minor = hello[7];
163 164

    if (major != 1 || minor != 0) {
165
        // CURVE I: client HELLO has unknown version number
166
        session->get_socket ()->event_handshake_failed_protocol (
167 168
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO);
169 170 171 172
        errno = EPROTO;
        return -1;
    }

173
    //  Save client's short-term public key (C')
174
    memcpy (_cn_client, hello + 80, 32);
175

176 177 178
    uint8_t hello_nonce[crypto_box_NONCEBYTES];
    uint8_t hello_plaintext[crypto_box_ZEROBYTES + 64];
    uint8_t hello_box[crypto_box_BOXZEROBYTES + 80];
179 180 181

    memcpy (hello_nonce, "CurveZMQHELLO---", 16);
    memcpy (hello_nonce + 16, hello + 112, 8);
182
    cn_peer_nonce = get_uint64 (hello + 112);
183 184 185 186 187

    memset (hello_box, 0, crypto_box_BOXZEROBYTES);
    memcpy (hello_box + crypto_box_BOXZEROBYTES, hello + 120, 80);

    //  Open Box [64 * %x0](C'->S)
188
    rc = crypto_box_open (hello_plaintext, hello_box, sizeof hello_box,
189
                          hello_nonce, _cn_client, _secret_key);
190
    if (rc != 0) {
191
        // CURVE I: cannot open client HELLO -- wrong server key?
192 193
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
194
        errno = EPROTO;
195
        return -1;
196
    }
197

198
    state = sending_welcome;
199 200 201
    return rc;
}

202
int zmq::curve_server_t::produce_welcome (msg_t *msg_)
203
{
204 205 206
    uint8_t cookie_nonce[crypto_secretbox_NONCEBYTES];
    uint8_t cookie_plaintext[crypto_secretbox_ZEROBYTES + 64];
    uint8_t cookie_ciphertext[crypto_secretbox_BOXZEROBYTES + 80];
207 208 209 210 211 212 213 214

    //  Create full nonce for encryption
    //  8-byte prefix plus 16-byte random nonce
    memcpy (cookie_nonce, "COOKIE--", 8);
    randombytes (cookie_nonce + 8, 16);

    //  Generate cookie = Box [C' + s'](t)
    memset (cookie_plaintext, 0, crypto_secretbox_ZEROBYTES);
215 216
    memcpy (cookie_plaintext + crypto_secretbox_ZEROBYTES, _cn_client, 32);
    memcpy (cookie_plaintext + crypto_secretbox_ZEROBYTES + 32, _cn_secret, 32);
217 218

    //  Generate fresh cookie key
219
    randombytes (_cookie_key, crypto_secretbox_KEYBYTES);
220 221

    //  Encrypt using symmetric cookie key
222 223
    int rc =
      crypto_secretbox (cookie_ciphertext, cookie_plaintext,
224
                        sizeof cookie_plaintext, cookie_nonce, _cookie_key);
225 226
    zmq_assert (rc == 0);

227 228 229
    uint8_t welcome_nonce[crypto_box_NONCEBYTES];
    uint8_t welcome_plaintext[crypto_box_ZEROBYTES + 128];
    uint8_t welcome_ciphertext[crypto_box_BOXZEROBYTES + 144];
230 231 232 233 234 235 236 237

    //  Create full nonce for encryption
    //  8-byte prefix plus 16-byte random nonce
    memcpy (welcome_nonce, "WELCOME-", 8);
    randombytes (welcome_nonce + 8, crypto_box_NONCEBYTES - 8);

    //  Create 144-byte Box [S' + cookie](S->C')
    memset (welcome_plaintext, 0, crypto_box_ZEROBYTES);
238
    memcpy (welcome_plaintext + crypto_box_ZEROBYTES, _cn_public, 32);
239 240
    memcpy (welcome_plaintext + crypto_box_ZEROBYTES + 32, cookie_nonce + 8,
            16);
241 242 243 244
    memcpy (welcome_plaintext + crypto_box_ZEROBYTES + 48,
            cookie_ciphertext + crypto_secretbox_BOXZEROBYTES, 80);

    rc = crypto_box (welcome_ciphertext, welcome_plaintext,
245 246
                     sizeof welcome_plaintext, welcome_nonce, _cn_client,
                     _secret_key);
247 248 249 250 251 252 253

    //  TODO I think we should change this back to zmq_assert (rc == 0);
    //  as it was before https://github.com/zeromq/libzmq/pull/1832
    //  The reason given there was that secret_key might be 0ed.
    //  But if it were, we would never get this far, since we could
    //  not have opened the client's hello box with a 0ed key.

254 255
    if (rc == -1)
        return -1;
256 257 258 259

    rc = msg_->init_size (168);
    errno_assert (rc == 0);

260
    uint8_t *const welcome = static_cast<uint8_t *> (msg_->data ());
261
    memcpy (welcome, "\x07WELCOME", 8);
262 263 264 265 266 267 268 269
    memcpy (welcome + 8, welcome_nonce + 8, 16);
    memcpy (welcome + 24, welcome_ciphertext + crypto_box_BOXZEROBYTES, 144);

    return 0;
}

int zmq::curve_server_t::process_initiate (msg_t *msg_)
{
270 271
    int rc = check_basic_command_structure (msg_);
    if (rc == -1)
272
        return -1;
273

274
    const size_t size = msg_->size ();
275
    const uint8_t *initiate = static_cast<uint8_t *> (msg_->data ());
276 277

    if (size < 9 || memcmp (initiate, "\x08INITIATE", 9)) {
278 279
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
280 281 282 283
        errno = EPROTO;
        return -1;
    }

284
    if (size < 257) {
285 286 287
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE);
288 289 290 291
        errno = EPROTO;
        return -1;
    }

292 293 294
    uint8_t cookie_nonce[crypto_secretbox_NONCEBYTES];
    uint8_t cookie_plaintext[crypto_secretbox_ZEROBYTES + 64];
    uint8_t cookie_box[crypto_secretbox_BOXZEROBYTES + 80];
295 296 297

    //  Open Box [C' + s'](t)
    memset (cookie_box, 0, crypto_secretbox_BOXZEROBYTES);
298
    memcpy (cookie_box + crypto_secretbox_BOXZEROBYTES, initiate + 25, 80);
299 300

    memcpy (cookie_nonce, "COOKIE--", 8);
301
    memcpy (cookie_nonce + 8, initiate + 9, 16);
302

303
    rc = crypto_secretbox_open (cookie_plaintext, cookie_box, sizeof cookie_box,
304
                                cookie_nonce, _cookie_key);
305
    if (rc != 0) {
306
        // CURVE I: cannot open client INITIATE cookie
307 308
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
309 310 311 312 313
        errno = EPROTO;
        return -1;
    }

    //  Check cookie plain text is as expected [C' + s']
314
    if (memcmp (cookie_plaintext + crypto_secretbox_ZEROBYTES, _cn_client, 32)
315
        || memcmp (cookie_plaintext + crypto_secretbox_ZEROBYTES + 32,
316
                   _cn_secret, 32)) {
317 318 319
        // TODO this case is very hard to test, as it would require a modified
        //  client that knows the server's secret temporary cookie key

320
        // CURVE I: client INITIATE cookie is not valid
321 322
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
323
        errno = EPROTO;
324 325 326
        return -1;
    }

327
    const size_t clen = (size - 113) + crypto_box_BOXZEROBYTES;
328

329 330 331
    uint8_t initiate_nonce[crypto_box_NONCEBYTES];
    uint8_t initiate_plaintext[crypto_box_ZEROBYTES + 128 + 256];
    uint8_t initiate_box[crypto_box_BOXZEROBYTES + 144 + 256];
332 333 334

    //  Open Box [C + vouch + metadata](C'->S')
    memset (initiate_box, 0, crypto_box_BOXZEROBYTES);
335 336
    memcpy (initiate_box + crypto_box_BOXZEROBYTES, initiate + 113,
            clen - crypto_box_BOXZEROBYTES);
337 338

    memcpy (initiate_nonce, "CurveZMQINITIATE", 16);
339
    memcpy (initiate_nonce + 16, initiate + 105, 8);
340
    cn_peer_nonce = get_uint64 (initiate + 105);
341

342
    rc = crypto_box_open (initiate_plaintext, initiate_box, clen,
343
                          initiate_nonce, _cn_client, _cn_secret);
344
    if (rc != 0) {
345
        // CURVE I: cannot open client INITIATE
346 347
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
348 349 350 351 352 353
        errno = EPROTO;
        return -1;
    }

    const uint8_t *client_key = initiate_plaintext + crypto_box_ZEROBYTES;

354 355 356
    uint8_t vouch_nonce[crypto_box_NONCEBYTES];
    uint8_t vouch_plaintext[crypto_box_ZEROBYTES + 64];
    uint8_t vouch_box[crypto_box_BOXZEROBYTES + 80];
357

358
    //  Open Box Box [C',S](C->S') and check contents
359 360
    memset (vouch_box, 0, crypto_box_BOXZEROBYTES);
    memcpy (vouch_box + crypto_box_BOXZEROBYTES,
361
            initiate_plaintext + crypto_box_ZEROBYTES + 48, 80);
362 363

    memcpy (vouch_nonce, "VOUCH---", 8);
364 365
    memcpy (vouch_nonce + 8, initiate_plaintext + crypto_box_ZEROBYTES + 32,
            16);
366

367
    rc = crypto_box_open (vouch_plaintext, vouch_box, sizeof vouch_box,
368
                          vouch_nonce, client_key, _cn_secret);
369
    if (rc != 0) {
370
        // CURVE I: cannot open client INITIATE vouch
371 372
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
373 374 375 376
        errno = EPROTO;
        return -1;
    }

377
    //  What we decrypted must be the client's short-term public key
378
    if (memcmp (vouch_plaintext + crypto_box_ZEROBYTES, _cn_client, 32)) {
379 380 381
        // TODO this case is very hard to test, as it would require a modified
        //  client that knows the server's secret short-term key

382
        // CURVE I: invalid handshake from client (public key)
383 384
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE);
385 386 387 388 389
        errno = EPROTO;
        return -1;
    }

    //  Precompute connection secret from client key
390
    rc = crypto_box_beforenm (cn_precom, _cn_client, _cn_secret);
391 392
    zmq_assert (rc == 0);

393 394 395
    //  Given this is a backward-incompatible change, it's behind a socket
    //  option disabled by default.
    if (zap_required () || !options.zap_enforce_domain) {
396
        //  Use ZAP protocol (RFC 27) to authenticate the user.
397 398 399
        rc = session->zap_connect ();
        if (rc == 0) {
            send_zap_request (client_key);
400 401
            state = waiting_for_zap_reply;

402
            //  TODO actually, it is quite unlikely that we can read the ZAP
403
            //  reply already, but removing this has some strange side-effect
404
            //  (probably because the pipe's in_active flag is true until a read
405
            //  is attempted)
406 407 408
            rc = receive_and_process_zap_reply ();
            if (rc == -1)
                return -1;
409 410 411 412
        } else if (!options.zap_enforce_domain) {
            //  This supports the Stonehouse pattern (encryption without
            //  authentication) in legacy mode (domain set but no handler).
            state = sending_ready;
413 414 415
        } else {
            session->get_socket ()->event_handshake_failed_no_detail (
              session->get_endpoint (), EFAULT);
416
            return -1;
417
        }
418 419
    } else {
        //  This supports the Stonehouse pattern (encryption without authentication).
420
        state = sending_ready;
421
    }
422

423 424
    return parse_metadata (initiate_plaintext + crypto_box_ZEROBYTES + 128,
                           clen - crypto_box_ZEROBYTES - 128);
425 426
}

427
int zmq::curve_server_t::produce_ready (msg_t *msg_)
428
{
429
    const size_t metadata_length = basic_properties_len ();
430
    uint8_t ready_nonce[crypto_box_NONCEBYTES];
431 432

    uint8_t *ready_plaintext =
433
      static_cast<uint8_t *> (malloc (crypto_box_ZEROBYTES + metadata_length));
434
    alloc_assert (ready_plaintext);
435 436 437 438 439

    //  Create Box [metadata](S'->C')
    memset (ready_plaintext, 0, crypto_box_ZEROBYTES);
    uint8_t *ptr = ready_plaintext + crypto_box_ZEROBYTES;

440
    ptr += add_basic_properties (ptr, metadata_length);
441 442 443
    const size_t mlen = ptr - ready_plaintext;

    memcpy (ready_nonce, "CurveZMQREADY---", 16);
444
    put_uint64 (ready_nonce + 16, cn_nonce);
445

446 447
    uint8_t *ready_box = static_cast<uint8_t *> (
      malloc (crypto_box_BOXZEROBYTES + 16 + metadata_length));
448 449 450 451
    alloc_assert (ready_box);

    int rc = crypto_box_afternm (ready_box, ready_plaintext, mlen, ready_nonce,
                                 cn_precom);
452 453
    zmq_assert (rc == 0);

454 455
    free (ready_plaintext);

456
    rc = msg_->init_size (14 + mlen - crypto_box_BOXZEROBYTES);
457 458
    errno_assert (rc == 0);

459
    uint8_t *ready = static_cast<uint8_t *> (msg_->data ());
460

461
    memcpy (ready, "\x05READY", 6);
462
    //  Short nonce, prefixed by "CurveZMQREADY---"
463
    memcpy (ready + 6, ready_nonce + 16, 8);
464
    //  Box [metadata](S'->C')
465
    memcpy (ready + 14, ready_box + crypto_box_BOXZEROBYTES,
466
            mlen - crypto_box_BOXZEROBYTES);
467
    free (ready_box);
468

469
    cn_nonce++;
470 471 472 473

    return 0;
}

474 475
int zmq::curve_server_t::produce_error (msg_t *msg_) const
{
476
    const size_t expected_status_code_length = 3;
477
    zmq_assert (status_code.length () == 3);
478
    const int rc = msg_->init_size (6 + 1 + expected_status_code_length);
479
    zmq_assert (rc == 0);
480
    char *msg_data = static_cast<char *> (msg_->data ());
481
    memcpy (msg_data, "\5ERROR", 6);
482
    msg_data[6] = expected_status_code_length;
483
    memcpy (msg_data + 7, status_code.c_str (), expected_status_code_length);
484 485 486
    return 0;
}

487
void zmq::curve_server_t::send_zap_request (const uint8_t *key_)
488
{
489 490
    zap_client_t::send_zap_request ("CURVE", 5, key_,
                                    crypto_box_PUBLICKEYBYTES);
491 492 493
}

#endif