gssapi_mechanism_base.cpp 9.54 KB
Newer Older
1
/*
2
    Copyright (c) 2007-2015 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 30

    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/>.
*/

#include "platform.hpp"
Chris Laws's avatar
Chris Laws committed
31 32 33

#ifdef HAVE_LIBGSSAPI_KRB5

34 35 36 37 38 39 40 41 42 43 44 45 46
#ifdef ZMQ_HAVE_WINDOWS
#include "windows.hpp"
#endif

#include <string.h>
#include <string>

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

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

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

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

    uint8_t flags = 0;
    if (msg_->flags () & msg_t::more)
        flags |= 0x01;
Jonathan Reams's avatar
Jonathan Reams committed
83 84
    if (msg ->flags () & msg_t::command)
        flags |= 0x02;
85 86 87 88 89 90 91

    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
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
    // Get command string
    if (bytes_left < 8 || memcmp (ptr, "\x07MESSAGE", 8)) {
132 133 134
        errno = EPROTO;
        return -1;
    }
135 136
    ptr += 8;
    bytes_left -= 8;
137 138 139 140 141 142

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

148 149
    // Get token value
    if (bytes_left < wrapped.length) {
150 151 152
        errno = EPROTO;
        return -1;
    }
153 154 155 156 157 158 159 160
    // 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;
161 162
    }

163 164 165 166 167 168 169 170 171 172 173 174 175
    // 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);

176
    rc = msg_->init_size (plaintext.length-1);
177
    zmq_assert (rc == 0);
178 179 180 181

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

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

187 188 189
    gss_release_buffer (&min_stat, &plaintext);
    gss_release_buffer (&min_stat, &wrapped);

190 191 192 193
    if (bytes_left > 0) {
        errno = EPROTO;
        return -1;
    }
194 195 196 197

    return 0;
}

198
int zmq::gssapi_mechanism_base_t::produce_initiate (msg_t *msg_, void *token_value_, size_t token_length_)
199
{
200 201
    zmq_assert (token_value_);
    zmq_assert (token_length_ <= 0xFFFFFFFFUL);
202

203
    const size_t command_size = 9 + 4 + token_length_;
Chris Laws's avatar
Chris Laws committed
204

205 206
    const int rc = msg_->init_size (command_size);
    errno_assert (rc == 0);
Chris Laws's avatar
Chris Laws committed
207

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

210 211 212 213 214 215 216 217 218 219 220 221
    // 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_;

222 223 224
    return 0;
}

225
int zmq::gssapi_mechanism_base_t::process_initiate (msg_t *msg_, void **token_value_, size_t &token_length_)
226
{
227
    zmq_assert (token_value_);
Chris Laws's avatar
Chris Laws committed
228

229 230
    const uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
    size_t bytes_left = msg_->size ();
231

232 233 234 235 236 237 238
    // Get command string
    if (bytes_left < 9 || memcmp (ptr, "\x08INITIATE", 9)) {
        errno = EPROTO;
        return -1;
    }
    ptr += 9;
    bytes_left -= 9;
239

240 241 242 243 244 245 246 247
    // 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
248

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
    // 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
266

267 268 269
    return 0;
}

270
int zmq::gssapi_mechanism_base_t::produce_ready (msg_t *msg_)
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
{
    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
288 289
    ||  options.type == ZMQ_ROUTER)
        ptr += add_property (ptr, "Identity", options.identity, options.identity_size);
290 291 292 293 294 295 296

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

297 298 299 300
    if (do_encryption)
        return encode_message (msg_);

    return 0;
301 302 303 304
}

int zmq::gssapi_mechanism_base_t::process_ready (msg_t *msg_)
{
Chris Laws's avatar
Chris Laws committed
305
    if (do_encryption) {
306 307 308 309
        const int rc = decode_message (msg_);
        if (rc != 0)
            return rc;
    }
310

311 312 313 314 315 316 317 318 319 320 321 322
    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);
}

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

329 330 331
    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
332

333
    maj_stat = gss_import_name (&min_stat, &name_buf,
334
                                GSS_C_NT_HOSTBASED_SERVICE, &server_name);
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350

    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
351
#endif