gssapi_mechanism_base.cpp 12.3 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
#include "precompiled.hpp"
Chris Laws's avatar
Chris Laws committed
31 32 33

#ifdef HAVE_LIBGSSAPI_KRB5

34 35 36 37 38 39 40 41 42
#include <string.h>
#include <string>

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

43 44 45 46 47
zmq::gssapi_mechanism_base_t::gssapi_mechanism_base_t (
  session_base_t *session_,
  const std::string &peer_address_,
  const options_t &options_) :
    mechanism_base_t (session_, options_),
48 49
    send_tok (),
    recv_tok (),
50
    /// FIXME remove? in_buf (),
51
    target_name (GSS_C_NO_NAME),
Chris Busbey's avatar
Chris Busbey committed
52
    principal_name (NULL),
Chris Busbey's avatar
Chris Busbey committed
53
    maj_stat (GSS_S_COMPLETE),
54 55 56 57 58
    min_stat (0),
    init_sec_min_stat (0),
    ret_flags (0),
    gss_flags (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG),
    cred (GSS_C_NO_CREDENTIAL),
59 60
    context (GSS_C_NO_CONTEXT),
    do_encryption (!options_.gss_plaintext)
61 62 63 64 65
{
}

zmq::gssapi_mechanism_base_t::~gssapi_mechanism_base_t ()
{
66 67 68 69
    if(target_name)
        gss_release_name(&min_stat, &target_name);
    if(context)
        gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
70 71
}

72
int zmq::gssapi_mechanism_base_t::encode_message (msg_t *msg_)
73
{
74 75 76 77
    // Wrap the token value
    int state;
    gss_buffer_desc plaintext;
    gss_buffer_desc wrapped;
78 79 80 81

    uint8_t flags = 0;
    if (msg_->flags () & msg_t::more)
        flags |= 0x01;
82
    if (msg_->flags () & msg_t::command)
Jonathan Reams's avatar
Jonathan Reams committed
83
        flags |= 0x02;
84 85

    uint8_t *plaintext_buffer = static_cast <uint8_t *>(malloc(msg_->size ()+1));
86 87
    alloc_assert(plaintext_buffer);

88 89 90 91 92
    plaintext_buffer[0] = flags;
    memcpy (plaintext_buffer+1, msg_->data(), msg_->size());

    plaintext.value = plaintext_buffer;
    plaintext.length = msg_->size ()+1;
Chris Laws's avatar
Chris Laws committed
93

94 95
    maj_stat = gss_wrap(&min_stat, context, 1, GSS_C_QOP_DEFAULT,
                        &plaintext, &state, &wrapped);
Chris Laws's avatar
Chris Laws committed
96

97 98
    zmq_assert (maj_stat == GSS_S_COMPLETE);
    zmq_assert (state);
99

100 101 102
    // Re-initialize msg_ for wrapped text
    int rc = msg_->close ();
    zmq_assert (rc == 0);
103

104 105
    rc = msg_->init_size (8 + 4 + wrapped.length);
    zmq_assert (rc == 0);
106

107
    uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
Chris Laws's avatar
Chris Laws committed
108

109 110 111
    // Add command string
    memcpy (ptr, "\x07MESSAGE", 8);
    ptr += 8;
112 113

    // Add token length
114
    put_uint32 (ptr, static_cast <uint32_t> (wrapped.length));
115 116
    ptr += 4;

117 118 119
    // Add wrapped token value
    memcpy (ptr, wrapped.value, wrapped.length);
    ptr += wrapped.length;
120

121
    gss_release_buffer (&min_stat, &wrapped);
122 123 124 125

    return 0;
}

126
int zmq::gssapi_mechanism_base_t::decode_message (msg_t *msg_)
127
{
128
    const uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
129 130
    size_t bytes_left = msg_->size ();

131 132 133 134
    int rc = check_basic_command_structure (msg_);
    if (rc == -1)
        return rc;

135 136
    // Get command string
    if (bytes_left < 8 || memcmp (ptr, "\x07MESSAGE", 8)) {
137 138 139
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
140 141 142
        errno = EPROTO;
        return -1;
    }
143 144
    ptr += 8;
    bytes_left -= 8;
145 146 147

    // Get token length
    if (bytes_left < 4) {
148 149 150
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE);
151 152 153
        errno = EPROTO;
        return -1;
    }
154 155
    gss_buffer_desc wrapped;
    wrapped.length = get_uint32 (ptr);
156 157
    ptr += 4;
    bytes_left -= 4;
Chris Laws's avatar
Chris Laws committed
158

159 160
    // Get token value
    if (bytes_left < wrapped.length) {
161 162 163
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE);
164 165 166
        errno = EPROTO;
        return -1;
    }
167 168 169
    // TODO: instead of malloc/memcpy, can we just do: wrapped.value = ptr;
    const size_t alloc_length = wrapped.length? wrapped.length: 1;
    wrapped.value = static_cast <char *> (malloc (alloc_length));
170 171
    alloc_assert (wrapped.value);

172 173 174 175
    if (wrapped.length) {
        memcpy(wrapped.value, ptr, wrapped.length);
        ptr += wrapped.length;
        bytes_left -= wrapped.length;
176 177
    }

178 179 180 181 182 183
    // Unwrap the token value
    int state;
    gss_buffer_desc plaintext;
    maj_stat = gss_unwrap(&min_stat, context, &wrapped, &plaintext,
                          &state, (gss_qop_t *) NULL);

184 185 186
    if (maj_stat != GSS_S_COMPLETE)
    {
        gss_release_buffer (&min_stat, &plaintext);
187
        free (wrapped.value);
188 189 190 191 192 193
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
        errno = EPROTO;
        return -1;
    }
194 195 196
    zmq_assert(state);

    // Re-initialize msg_ for plaintext
197
    rc = msg_->close ();
198 199
    zmq_assert (rc == 0);

200
    rc = msg_->init_size (plaintext.length-1);
201
    zmq_assert (rc == 0);
202 203 204

    const uint8_t flags = static_cast <char *> (plaintext.value)[0];
    if (flags & 0x01)
205
        msg_->set_flags (msg_t::more);
Jonathan Reams's avatar
Jonathan Reams committed
206 207
    if (flags & 0x02)
        msg_->set_flags (msg_t::command);
Chris Laws's avatar
Chris Laws committed
208

209
    memcpy (msg_->data (), static_cast <char *> (plaintext.value)+1, plaintext.length-1);
Chris Laws's avatar
Chris Laws committed
210

211
    gss_release_buffer (&min_stat, &plaintext);
212
    free(wrapped.value);
213

214
    if (bytes_left > 0) {
215 216 217
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE);
218 219 220
        errno = EPROTO;
        return -1;
    }
221 222 223 224

    return 0;
}

225
int zmq::gssapi_mechanism_base_t::produce_initiate (msg_t *msg_, void *token_value_, size_t token_length_)
226
{
227 228
    zmq_assert (token_value_);
    zmq_assert (token_length_ <= 0xFFFFFFFFUL);
229

230
    const size_t command_size = 9 + 4 + token_length_;
Chris Laws's avatar
Chris Laws committed
231

232 233
    const int rc = msg_->init_size (command_size);
    errno_assert (rc == 0);
Chris Laws's avatar
Chris Laws committed
234

235
    uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
Chris Laws's avatar
Chris Laws committed
236

237 238 239 240 241 242 243 244 245 246 247 248
    // Add command string
    memcpy (ptr, "\x08INITIATE", 9);
    ptr += 9;

    // Add token length
    put_uint32 (ptr, static_cast <uint32_t> (token_length_));
    ptr += 4;

    // Add token value
    memcpy (ptr, token_value_, token_length_);
    ptr += token_length_;

249 250 251
    return 0;
}

252
int zmq::gssapi_mechanism_base_t::process_initiate (msg_t *msg_, void **token_value_, size_t &token_length_)
253
{
254
    zmq_assert (token_value_);
Chris Laws's avatar
Chris Laws committed
255

256 257
    const uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
    size_t bytes_left = msg_->size ();
258

259 260 261 262
    int rc = check_basic_command_structure (msg_);
    if (rc == -1)
        return rc;

263 264
    // Get command string
    if (bytes_left < 9 || memcmp (ptr, "\x08INITIATE", 9)) {
265 266 267
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
268 269 270 271 272
        errno = EPROTO;
        return -1;
    }
    ptr += 9;
    bytes_left -= 9;
273

274 275
    // Get token length
    if (bytes_left < 4) {
276 277 278
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE);
279 280 281 282 283 284
        errno = EPROTO;
        return -1;
    }
    token_length_ = get_uint32 (ptr);
    ptr += 4;
    bytes_left -= 4;
Chris Laws's avatar
Chris Laws committed
285

286 287
    // Get token value
    if (bytes_left < token_length_) {
288 289 290
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE);
291 292 293
        errno = EPROTO;
        return -1;
    }
294

295
    *token_value_ = static_cast <char *> (malloc (token_length_ ? token_length_ : 1));
296 297
    alloc_assert (*token_value_);

298 299 300 301 302 303 304
    if (token_length_) {
        memcpy(*token_value_, ptr, token_length_);
        ptr += token_length_;
        bytes_left -= token_length_;
    }

    if (bytes_left > 0) {
305 306 307
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE);
308 309 310
        errno = EPROTO;
        return -1;
    }
Chris Laws's avatar
Chris Laws committed
311

312 313 314
    return 0;
}

315
int zmq::gssapi_mechanism_base_t::produce_ready (msg_t *msg_)
316 317 318 319 320 321 322 323 324 325 326 327
{
    unsigned char * const command_buffer = (unsigned char *) malloc (512);
    alloc_assert (command_buffer);

    unsigned char *ptr = command_buffer;

    //  Add command name
    memcpy (ptr, "\x05READY", 6);
    ptr += 6;

    //  Add socket type property
    const char *socket_type = socket_type_string (options.type);
328 329
    ptr += add_property (ptr, ZMQ_MSG_PROPERTY_SOCKET_TYPE, socket_type,
                         strlen (socket_type));
330 331 332 333

    //  Add identity property
    if (options.type == ZMQ_REQ
    ||  options.type == ZMQ_DEALER
Pieter Hintjens's avatar
Pieter Hintjens committed
334
    ||  options.type == ZMQ_ROUTER)
335 336
        ptr += add_property (ptr, ZMQ_MSG_PROPERTY_IDENTITY, options.identity,
                             options.identity_size);
337 338 339 340 341 342 343

    const size_t command_size = ptr - command_buffer;
    const int rc = msg_->init_size (command_size);
    errno_assert (rc == 0);
    memcpy (msg_->data (), command_buffer, command_size);
    free (command_buffer);

344 345 346 347
    if (do_encryption)
        return encode_message (msg_);

    return 0;
348 349 350 351
}

int zmq::gssapi_mechanism_base_t::process_ready (msg_t *msg_)
{
Chris Laws's avatar
Chris Laws committed
352
    if (do_encryption) {
353 354 355 356
        const int rc = decode_message (msg_);
        if (rc != 0)
            return rc;
    }
357

358 359 360
    const unsigned char *ptr = static_cast <unsigned char *> (msg_->data ());
    size_t bytes_left = msg_->size ();

361 362 363 364
    int rc = check_basic_command_structure (msg_);
    if (rc == -1)
        return rc;

365
    if (bytes_left < 6 || memcmp (ptr, "\x05READY", 6)) {
366 367 368
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
369 370 371 372 373
        errno = EPROTO;
        return -1;
    }
    ptr += 6;
    bytes_left -= 6;
374 375 376 377 378 379 380
    rc = parse_metadata (ptr, bytes_left);
    if (rc == -1)
              session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA);

    return rc;
381
}
382

383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
const gss_OID zmq::gssapi_mechanism_base_t::convert_nametype (int zmq_nametype)
{
    switch (zmq_nametype) {
        case ZMQ_GSSAPI_NT_HOSTBASED:
            return GSS_C_NT_HOSTBASED_SERVICE;
        case ZMQ_GSSAPI_NT_USER_NAME:
            return GSS_C_NT_USER_NAME;
        case ZMQ_GSSAPI_NT_KRB5_PRINCIPAL:
#ifdef GSS_KRB5_NT_PRINCIPAL_NAME
            return (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME;
#else
            return GSS_C_NT_USER_NAME;
#endif
    }
    return NULL;
}

400
int zmq::gssapi_mechanism_base_t::acquire_credentials (char * service_name_, gss_cred_id_t * cred_, gss_OID name_type_)
401 402 403 404
{
    OM_uint32 maj_stat;
    OM_uint32 min_stat;
    gss_name_t server_name;
Chris Laws's avatar
Chris Laws committed
405

406 407 408
    gss_buffer_desc name_buf;
    name_buf.value = service_name_;
    name_buf.length = strlen ((char *) name_buf.value) + 1;
Chris Laws's avatar
Chris Laws committed
409

410
    maj_stat = gss_import_name (&min_stat, &name_buf,
411
                                name_type_, &server_name);
412 413 414 415 416

    if (maj_stat != GSS_S_COMPLETE)
        return -1;

    maj_stat = gss_acquire_cred (&min_stat, server_name, 0,
417
                                 GSS_C_NO_OID_SET, GSS_C_BOTH,
418 419 420 421 422 423 424 425 426 427
                                 cred_, NULL, NULL);

    if (maj_stat != GSS_S_COMPLETE)
        return -1;

    gss_release_name(&min_stat, &server_name);

    return 0;
}

Chris Laws's avatar
Chris Laws committed
428
#endif