gssapi_server.cpp 9.14 KB
Newer Older
1
/*
2
    Copyright (c) 2007-2014 Contributors as noted in the AUTHORS file
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

    This file is part of 0MQ.

    0MQ is free software; you can redistribute it and/or modify it under
    the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    0MQ 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.

    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
21 22 23

#ifdef HAVE_LIBGSSAPI_KRB5

24 25 26 27 28 29 30 31 32 33
#ifdef ZMQ_HAVE_WINDOWS
#include "windows.hpp"
#endif

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

#include "msg.hpp"
#include "session_base.hpp"
#include "err.hpp"
34
#include "gssapi_server.hpp"
35 36
#include "wire.hpp"

37 38
#include <gssapi/gssapi.h>

39
zmq::gssapi_server_t::gssapi_server_t (session_base_t *session_,
40 41
                                       const std::string &peer_address_,
                                       const options_t &options_) :
42
    gssapi_mechanism_base_t (options_),
43 44
    session (session_),
    peer_address (peer_address_),
45 46
    state (recv_next_token),
    security_context_established (false)
47
{
Chris Busbey's avatar
Chris Busbey committed
48
    maj_stat = GSS_S_CONTINUE_NEEDED;
Chris Busbey's avatar
Chris Busbey committed
49
    if(!options_.gss_principal.empty())
Chris Busbey's avatar
Chris Busbey committed
50
    {
Chris Busbey's avatar
Chris Busbey committed
51 52 53 54
        const std::string::size_type principal_size = options_.gss_principal.size();
        principal_name = static_cast <char *>(malloc(principal_size+1));
        assert(principal_name);
        memcpy(principal_name, options_.gss_principal.c_str(), principal_size+1 );
Chris Busbey's avatar
Chris Busbey committed
55

Chris Busbey's avatar
Chris Busbey committed
56
        if (acquire_credentials (principal_name, &cred) != 0)
Chris Busbey's avatar
Chris Busbey committed
57 58
            maj_stat = GSS_S_FAILURE;
    }
59 60
}

61
zmq::gssapi_server_t::~gssapi_server_t ()
62
{
63 64
    if(cred)
        gss_release_cred(&min_stat, &cred);
Chris Busbey's avatar
Chris Busbey committed
65 66 67

    if(target_name)
        gss_release_name(&min_stat, &target_name);
68 69
}

70
int zmq::gssapi_server_t::next_handshake_command (msg_t *msg_)
71
{
72 73 74 75 76 77 78 79
    if (state == send_ready) {
        int rc = produce_ready(msg_);
        if (rc == 0)
            state = recv_ready;

        return rc;
    }

80 81 82 83 84 85 86 87 88 89 90 91 92
    if (state != send_next_token) {
        errno = EAGAIN;
        return -1;
    }

    if (produce_next_token (msg_) < 0)
        return -1;

    if (maj_stat != GSS_S_CONTINUE_NEEDED && maj_stat != GSS_S_COMPLETE)
        return -1;

    if (maj_stat == GSS_S_COMPLETE) {
        security_context_established = true;
93
    }
94

95 96
    state = recv_next_token;

97
    return 0;
98 99
}

100
int zmq::gssapi_server_t::process_handshake_command (msg_t *msg_)
101
{
102 103
    if (state == recv_ready) {
        int rc = process_ready(msg_);
Chris Laws's avatar
Chris Laws committed
104
        if (rc == 0)
105 106 107 108 109
            state = connected;

        return rc;
    }

110 111 112
    if (state != recv_next_token) {
        errno = EPROTO;
        return -1;
113
    }
114

115
    if (security_context_established) {
116 117 118 119 120 121 122 123 124 125 126 127 128
     	//  Use ZAP protocol (RFC 27) to authenticate the user.
        bool expecting_zap_reply = false;
        int rc = session->zap_connect ();
        if (rc == 0) {
            send_zap_request();
            rc = receive_and_process_zap_reply ();
            if (rc != 0) {
                if (errno != EAGAIN)
                    return -1;
                expecting_zap_reply = true;
            }
        }
        state = expecting_zap_reply? expect_zap_reply: send_ready;
129 130 131
        return 0;
    }

132 133 134 135 136 137 138 139 140 141
    if (process_next_token (msg_) < 0)
        return -1;

    accept_context ();
    state = send_next_token;

    errno_assert (msg_->close () == 0);
    errno_assert (msg_->init () == 0);

    return 0;
142 143
}

Chris Laws's avatar
Chris Laws committed
144
void zmq::gssapi_server_t::send_zap_request ()
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
{
    int rc;
    msg_t msg;

    //  Address delimiter frame
    rc = msg.init ();
    errno_assert (rc == 0);
    msg.set_flags (msg_t::more);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);

    //  Version frame
    rc = msg.init_size (3);
    errno_assert (rc == 0);
    memcpy (msg.data (), "1.0", 3);
    msg.set_flags (msg_t::more);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);

    //  Request ID frame
    rc = msg.init_size (1);
    errno_assert (rc == 0);
    memcpy (msg.data (), "1", 1);
    msg.set_flags (msg_t::more);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);

    //  Domain frame
    rc = msg.init_size (options.zap_domain.length ());
    errno_assert (rc == 0);
    memcpy (msg.data (), options.zap_domain.c_str (), options.zap_domain.length ());
    msg.set_flags (msg_t::more);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);

    //  Address frame
    rc = msg.init_size (peer_address.length ());
    errno_assert (rc == 0);
    memcpy (msg.data (), peer_address.c_str (), peer_address.length ());
    msg.set_flags (msg_t::more);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);

    //  Identity frame
    rc = msg.init_size (options.identity_size);
    errno_assert (rc == 0);
    memcpy (msg.data (), options.identity, options.identity_size);
    msg.set_flags (msg_t::more);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);

    //  Mechanism frame
    rc = msg.init_size (6);
    errno_assert (rc == 0);
    memcpy (msg.data (), "GSSAPI", 6);
    msg.set_flags (msg_t::more);
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);

Chris Busbey's avatar
Chris Busbey committed
204 205 206
    //  Principal frame
    gss_buffer_desc principal;
    gss_display_name(&min_stat, target_name, &principal, NULL);
207

Chris Busbey's avatar
Chris Busbey committed
208
    rc = msg.init_size (principal.length);
209
    errno_assert (rc == 0);
Chris Busbey's avatar
Chris Busbey committed
210
    memcpy (msg.data (), principal.value, principal.length);
211 212
    rc = session->write_zap_msg (&msg);
    errno_assert (rc == 0);
Chris Busbey's avatar
Chris Busbey committed
213
    gss_release_buffer(&min_stat, &principal);
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
}

int zmq::gssapi_server_t::receive_and_process_zap_reply ()
{
    int rc = 0;
    msg_t msg [7];  //  ZAP reply consists of 7 frames

    //  Initialize all reply frames
    for (int i = 0; i < 7; i++) {
        rc = msg [i].init ();
        errno_assert (rc == 0);
    }

    for (int i = 0; i < 7; i++) {
        rc = session->read_zap_msg (&msg [i]);
        if (rc == -1)
            break;
        if ((msg [i].flags () & msg_t::more) == (i < 6? 0: msg_t::more)) {
            errno = EPROTO;
            rc = -1;
            break;
        }
    }

    if (rc != 0)
        goto error;

    //  Address delimiter frame
    if (msg [0].size () > 0) {
        rc = -1;
        errno = EPROTO;
        goto error;
    }

    //  Version frame
    if (msg [1].size () != 3 || memcmp (msg [1].data (), "1.0", 3)) {
        rc = -1;
        errno = EPROTO;
        goto error;
    }

    //  Request id frame
    if (msg [2].size () != 1 || memcmp (msg [2].data (), "1", 1)) {
        rc = -1;
        errno = EPROTO;
        goto error;
    }

    //  Status code frame
    if (msg [3].size () != 3 || memcmp (msg [3].data (), "200", 3)) {
        rc = -1;
        errno = EACCES;
        goto error;
    }

    //  Process metadata frame
    rc = parse_metadata (static_cast <const unsigned char*> (msg [6].data ()),
271
                         msg [6].size (), true);
272 273 274 275 276 277 278 279 280 281 282

error:
    for (int i = 0; i < 7; i++) {
        const int rc2 = msg [i].close ();
        errno_assert (rc2 == 0);
    }

    return rc;
}


283 284
int zmq::gssapi_server_t::encode (msg_t *msg_)
{
285
    zmq_assert (state == connected);
286 287 288 289 290

    if (do_encryption)
      return encode_message (msg_);

    return 0;
291 292 293 294
}

int zmq::gssapi_server_t::decode (msg_t *msg_)
{
295
    zmq_assert (state == connected);
296 297 298 299 300

    if (do_encryption)
      return decode_message (msg_);

    return 0;
301 302
}

303
int zmq::gssapi_server_t::zap_msg_available ()
304
{
305 306 307 308 309 310 311 312
    if (state != expect_zap_reply) {
        errno = EFSM;
        return -1;
    }
    const int rc = receive_and_process_zap_reply ();
    if (rc == 0)
        state = send_ready;
    return rc;
313 314
}

315
bool zmq::gssapi_server_t::is_handshake_complete () const
316
{
317
    return state == connected;
318 319
}

320
int zmq::gssapi_server_t::produce_next_token (msg_t *msg_)
321
{
322 323
    if (send_tok.length != 0) { // Client expects another token
        if (produce_initiate(msg_, send_tok.value, send_tok.length) < 0)
324 325 326
            return -1;
        gss_release_buffer(&min_stat, &send_tok);
    }
327

328 329 330 331
    if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
        gss_release_name(&min_stat, &target_name);
        if (context != GSS_C_NO_CONTEXT)
            gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
332 333 334 335 336 337
        return -1;
    }

    return 0;
}

338
int zmq::gssapi_server_t::process_next_token (msg_t *msg_)
339
{
340
    if (maj_stat == GSS_S_CONTINUE_NEEDED) {
341
        if (process_initiate(msg_, &recv_tok.value, recv_tok.length) < 0) {
342 343 344
            if (target_name != GSS_C_NO_NAME)
                gss_release_name(&min_stat, &target_name);
            return -1;
345 346
        }
    }
347 348 349 350 351 352

    return 0;
}

void zmq::gssapi_server_t::accept_context ()
{
353 354 355 356
    maj_stat = gss_accept_sec_context(&init_sec_min_stat, &context, cred,
                                      &recv_tok, GSS_C_NO_CHANNEL_BINDINGS,
                                      &target_name, &doid, &send_tok,
                                      &ret_flags, NULL, NULL);
357

358 359 360
    if (recv_tok.value) {
        free (recv_tok.value);
        recv_tok.value = NULL;
361 362
    }
}
363

Chris Laws's avatar
Chris Laws committed
364
#endif