gssapi_mechanism_base.cpp 11.6 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
zmq::gssapi_mechanism_base_t::gssapi_mechanism_base_t (
44
  session_base_t *session_, const options_t &options_) :
45
    mechanism_base_t (session_, options_),
46 47
    send_tok (),
    recv_tok (),
48
    /// FIXME remove? in_buf (),
49
    target_name (GSS_C_NO_NAME),
Chris Busbey's avatar
Chris Busbey committed
50
    principal_name (NULL),
Chris Busbey's avatar
Chris Busbey committed
51
    maj_stat (GSS_S_COMPLETE),
52 53 54 55 56
    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),
57 58
    context (GSS_C_NO_CONTEXT),
    do_encryption (!options_.gss_plaintext)
59 60 61 62 63
{
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return 0;
}

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

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

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

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

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

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

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

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

    // Re-initialize msg_ for plaintext
193
    rc = msg_->close ();
194 195
    zmq_assert (rc == 0);

196
    rc = msg_->init_size (plaintext.length - 1);
197
    zmq_assert (rc == 0);
198

199
    const uint8_t flags = static_cast<char *> (plaintext.value)[0];
200
    if (flags & 0x01)
201
        msg_->set_flags (msg_t::more);
Jonathan Reams's avatar
Jonathan Reams committed
202 203
    if (flags & 0x02)
        msg_->set_flags (msg_t::command);
Chris Laws's avatar
Chris Laws committed
204

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

208
    gss_release_buffer (&min_stat, &plaintext);
209
    free (wrapped.value);
210

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

    return 0;
}

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

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

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

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

236 237 238 239 240
    // Add command string
    memcpy (ptr, "\x08INITIATE", 9);
    ptr += 9;

    // Add token length
241
    put_uint32 (ptr, static_cast<uint32_t> (token_length_));
242 243 244 245 246 247
    ptr += 4;

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

248 249 250
    return 0;
}

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

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

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

264 265
    // Get command string
    if (bytes_left < 9 || memcmp (ptr, "\x08INITIATE", 9)) {
266
        session->get_socket ()->event_handshake_failed_protocol (
267
          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 296
    *token_value_ =
      static_cast<char *> (malloc (token_length_ ? token_length_ : 1));
297 298
    alloc_assert (*token_value_);

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

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

313 314 315
    return 0;
}

316
int zmq::gssapi_mechanism_base_t::produce_ready (msg_t *msg_)
317
{
318
    make_command_with_basic_properties (msg_, "\5READY", 6);
319

320 321 322 323
    if (do_encryption)
        return encode_message (msg_);

    return 0;
324 325 326 327
}

int zmq::gssapi_mechanism_base_t::process_ready (msg_t *msg_)
{
Chris Laws's avatar
Chris Laws committed
328
    if (do_encryption) {
329 330 331 332
        const int rc = decode_message (msg_);
        if (rc != 0)
            return rc;
    }
333

334
    const unsigned char *ptr = static_cast<unsigned char *> (msg_->data ());
335 336
    size_t bytes_left = msg_->size ();

337 338 339 340
    int rc = check_basic_command_structure (msg_);
    if (rc == -1)
        return rc;

341
    if (bytes_left < 6 || memcmp (ptr, "\x05READY", 6)) {
342
        session->get_socket ()->event_handshake_failed_protocol (
343
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
344 345 346 347 348
        errno = EPROTO;
        return -1;
    }
    ptr += 6;
    bytes_left -= 6;
349 350
    rc = parse_metadata (ptr, bytes_left);
    if (rc == -1)
351 352
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA);
353 354

    return rc;
355
}
356

357 358 359 360 361 362 363 364 365
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
366
            return (gss_OID) GSS_KRB5_NT_PRINCIPAL_NAME;
367 368 369 370 371 372 373
#else
            return GSS_C_NT_USER_NAME;
#endif
    }
    return NULL;
}

374 375 376
int zmq::gssapi_mechanism_base_t::acquire_credentials (char *service_name_,
                                                       gss_cred_id_t *cred_,
                                                       gss_OID name_type_)
377 378 379 380
{
    OM_uint32 maj_stat;
    OM_uint32 min_stat;
    gss_name_t server_name;
Chris Laws's avatar
Chris Laws committed
381

382 383 384
    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
385

386
    maj_stat = gss_import_name (&min_stat, &name_buf, name_type_, &server_name);
387 388 389 390

    if (maj_stat != GSS_S_COMPLETE)
        return -1;

391 392
    maj_stat = gss_acquire_cred (&min_stat, server_name, 0, GSS_C_NO_OID_SET,
                                 GSS_C_BOTH, cred_, NULL, NULL);
393 394 395 396

    if (maj_stat != GSS_S_COMPLETE)
        return -1;

397
    gss_release_name (&min_stat, &server_name);
398 399 400 401

    return 0;
}

Chris Laws's avatar
Chris Laws committed
402
#endif