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

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

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

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

    uint8_t *plaintext_buffer = static_cast <uint8_t *>(malloc(msg_->size ()+1));
    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
88

89 90
    maj_stat = gss_wrap(&min_stat, context, 1, GSS_C_QOP_DEFAULT,
                        &plaintext, &state, &wrapped);
Chris Laws's avatar
Chris Laws committed
91

92 93
    zmq_assert (maj_stat == GSS_S_COMPLETE);
    zmq_assert (state);
94

95 96 97
    // Re-initialize msg_ for wrapped text
    int rc = msg_->close ();
    zmq_assert (rc == 0);
98

99 100
    rc = msg_->init_size (8 + 4 + wrapped.length);
    zmq_assert (rc == 0);
101

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

104 105 106
    // Add command string
    memcpy (ptr, "\x07MESSAGE", 8);
    ptr += 8;
107 108

    // Add token length
109
    put_uint32 (ptr, static_cast <uint32_t> (wrapped.length));
110 111
    ptr += 4;

112 113 114
    // Add wrapped token value
    memcpy (ptr, wrapped.value, wrapped.length);
    ptr += wrapped.length;
115

116
    gss_release_buffer (&min_stat, &wrapped);
117 118 119 120

    return 0;
}

121
int zmq::gssapi_mechanism_base_t::decode_message (msg_t *msg_)
122
{
123
    const uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
124 125
    size_t bytes_left = msg_->size ();

126 127
    // Get command string
    if (bytes_left < 8 || memcmp (ptr, "\x07MESSAGE", 8)) {
128 129 130
        errno = EPROTO;
        return -1;
    }
131 132
    ptr += 8;
    bytes_left -= 8;
133 134 135 136 137 138

    // Get token length
    if (bytes_left < 4) {
        errno = EPROTO;
        return -1;
    }
139 140
    gss_buffer_desc wrapped;
    wrapped.length = get_uint32 (ptr);
141 142
    ptr += 4;
    bytes_left -= 4;
Chris Laws's avatar
Chris Laws committed
143

144 145
    // Get token value
    if (bytes_left < wrapped.length) {
146 147 148
        errno = EPROTO;
        return -1;
    }
149 150 151 152 153 154 155 156
    // 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));
    if (wrapped.length) {
        alloc_assert (wrapped.value);
        memcpy(wrapped.value, ptr, wrapped.length);
        ptr += wrapped.length;
        bytes_left -= wrapped.length;
157 158
    }

159 160 161 162 163 164 165 166 167 168 169 170 171
    // Unwrap the token value
    int state;
    gss_buffer_desc plaintext;
    maj_stat = gss_unwrap(&min_stat, context, &wrapped, &plaintext,
                          &state, (gss_qop_t *) NULL);

    zmq_assert(maj_stat == GSS_S_COMPLETE);
    zmq_assert(state);

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

172
    rc = msg_->init_size (plaintext.length-1);
173
    zmq_assert (rc == 0);
174 175 176

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

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

183
    gss_release_buffer (&min_stat, &plaintext);
184
    free(wrapped.value);
185

186 187 188 189
    if (bytes_left > 0) {
        errno = EPROTO;
        return -1;
    }
190 191 192 193

    return 0;
}

194
int zmq::gssapi_mechanism_base_t::produce_initiate (msg_t *msg_, void *token_value_, size_t token_length_)
195
{
196 197
    zmq_assert (token_value_);
    zmq_assert (token_length_ <= 0xFFFFFFFFUL);
198

199
    const size_t command_size = 9 + 4 + token_length_;
Chris Laws's avatar
Chris Laws committed
200

201 202
    const int rc = msg_->init_size (command_size);
    errno_assert (rc == 0);
Chris Laws's avatar
Chris Laws committed
203

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

206 207 208 209 210 211 212 213 214 215 216 217
    // 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_;

218 219 220
    return 0;
}

221
int zmq::gssapi_mechanism_base_t::process_initiate (msg_t *msg_, void **token_value_, size_t &token_length_)
222
{
223
    zmq_assert (token_value_);
Chris Laws's avatar
Chris Laws committed
224

225 226
    const uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
    size_t bytes_left = msg_->size ();
227

228 229 230 231 232 233 234
    // Get command string
    if (bytes_left < 9 || memcmp (ptr, "\x08INITIATE", 9)) {
        errno = EPROTO;
        return -1;
    }
    ptr += 9;
    bytes_left -= 9;
235

236 237 238 239 240 241 242 243
    // Get token length
    if (bytes_left < 4) {
        errno = EPROTO;
        return -1;
    }
    token_length_ = get_uint32 (ptr);
    ptr += 4;
    bytes_left -= 4;
Chris Laws's avatar
Chris Laws committed
244

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
    // Get token value
    if (bytes_left < token_length_) {
        errno = EPROTO;
        return -1;
    }
    *token_value_ = static_cast <char *> (malloc (token_length_ ? token_length_ : 1));
    if (token_length_) {
        alloc_assert (*token_value_);
        memcpy(*token_value_, ptr, token_length_);
        ptr += token_length_;
        bytes_left -= token_length_;
    }

    if (bytes_left > 0) {
        errno = EPROTO;
        return -1;
    }
Chris Laws's avatar
Chris Laws committed
262

263 264 265
    return 0;
}

266
int zmq::gssapi_mechanism_base_t::produce_ready (msg_t *msg_)
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
{
    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);
    ptr += add_property (ptr, "Socket-Type", socket_type, strlen (socket_type));

    //  Add identity property
    if (options.type == ZMQ_REQ
    ||  options.type == ZMQ_DEALER
Pieter Hintjens's avatar
Pieter Hintjens committed
284 285
    ||  options.type == ZMQ_ROUTER)
        ptr += add_property (ptr, "Identity", options.identity, options.identity_size);
286 287 288 289 290 291 292

    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);

293 294 295 296
    if (do_encryption)
        return encode_message (msg_);

    return 0;
297 298 299 300
}

int zmq::gssapi_mechanism_base_t::process_ready (msg_t *msg_)
{
Chris Laws's avatar
Chris Laws committed
301
    if (do_encryption) {
302 303 304 305
        const int rc = decode_message (msg_);
        if (rc != 0)
            return rc;
    }
306

307 308 309 310 311 312 313 314 315 316 317 318
    const unsigned char *ptr = static_cast <unsigned char *> (msg_->data ());
    size_t bytes_left = msg_->size ();

    if (bytes_left < 6 || memcmp (ptr, "\x05READY", 6)) {
        errno = EPROTO;
        return -1;
    }
    ptr += 6;
    bytes_left -= 6;
    return parse_metadata (ptr, bytes_left);
}

319 320 321 322 323
int zmq::gssapi_mechanism_base_t::acquire_credentials (char * service_name_, gss_cred_id_t * cred_)
{
    OM_uint32 maj_stat;
    OM_uint32 min_stat;
    gss_name_t server_name;
Chris Laws's avatar
Chris Laws committed
324

325 326 327
    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
328

329
    maj_stat = gss_import_name (&min_stat, &name_buf,
330
                                GSS_C_NT_HOSTBASED_SERVICE, &server_name);
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346

    if (maj_stat != GSS_S_COMPLETE)
        return -1;

    maj_stat = gss_acquire_cred (&min_stat, server_name, 0,
                                 GSS_C_NO_OID_SET, GSS_C_ACCEPT,
                                 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
347
#endif