gssapi_client.cpp 6.29 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"
31 32 33

#ifdef HAVE_LIBGSSAPI_KRB5

34 35 36 37 38 39 40 41 42 43 44 45 46 47
#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_client.hpp"
#include "wire.hpp"

zmq::gssapi_client_t::gssapi_client_t (const options_t &options_) :
48
    gssapi_mechanism_base_t (options_),
49
    state (call_next_init),
50 51 52
    token_ptr (GSS_C_NO_BUFFER),
    mechs (),
    security_context_established (false)
53
{
Chris Busbey's avatar
Chris Busbey committed
54
    const std::string::size_type service_size = options_.gss_service_principal.size();
Chris Busbey's avatar
Chris Busbey committed
55 56
    service_name = static_cast <char *>(malloc(service_size+1));
    assert(service_name);
Chris Busbey's avatar
Chris Busbey committed
57
    memcpy(service_name, options_.gss_service_principal.c_str(), service_size+1 );
58

Chris Busbey's avatar
Chris Busbey committed
59
    maj_stat = GSS_S_COMPLETE;
Chris Busbey's avatar
Chris Busbey committed
60
    if(!options_.gss_principal.empty())
Chris Busbey's avatar
Chris Busbey committed
61
    {
Chris Busbey's avatar
Chris Busbey committed
62 63 64 65
        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
66

Chris Busbey's avatar
Chris Busbey committed
67
        if (acquire_credentials (principal_name, &cred) != 0)
Chris Busbey's avatar
Chris Busbey committed
68 69 70
            maj_stat = GSS_S_FAILURE;
    }

71 72
    mechs.elements = NULL;
    mechs.count = 0;
73 74 75 76
}

zmq::gssapi_client_t::~gssapi_client_t ()
{
77 78 79 80
    if(service_name)
        free (service_name);
    if(cred)
        gss_release_cred(&min_stat, &cred);
81 82 83 84
}

int zmq::gssapi_client_t::next_handshake_command (msg_t *msg_)
{
85 86
    if (state == send_ready) {
        int rc = produce_ready(msg_);
87
        if (rc == 0)
88 89 90 91 92
            state = connected;

        return rc;
    }

93 94 95 96 97 98 99
    if (state != call_next_init) {
        errno = EAGAIN;
        return -1;
    }

    if (initialize_context () < 0)
        return -1;
100

101 102 103 104 105 106
    if (produce_next_token (msg_) < 0)
        return -1;

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

107
    if (maj_stat == GSS_S_COMPLETE) {
108
        security_context_established = true;
109 110
        state = recv_ready;
    }
111 112
    else
        state = recv_next_token;
113

114
    return 0;
115 116 117 118
}

int zmq::gssapi_client_t::process_handshake_command (msg_t *msg_)
{
119 120 121 122 123 124 125 126
    if (state == recv_ready) {
        int rc = process_ready(msg_);
        if (rc == 0)
            state = send_ready;

        return rc;
    }

127 128 129
    if (state != recv_next_token) {
        errno = EPROTO;
        return -1;
130
    }
131 132 133 134

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

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

138 139
    state = call_next_init;

140 141
    errno_assert (msg_->close () == 0);
    errno_assert (msg_->init () == 0);
142

143
    return 0;
144 145
}

146 147
int zmq::gssapi_client_t::encode (msg_t *msg_)
{
148
    zmq_assert (state == connected);
149 150 151 152 153

    if (do_encryption)
      return encode_message (msg_);

    return 0;
154 155 156 157
}

int zmq::gssapi_client_t::decode (msg_t *msg_)
{
158
    zmq_assert (state == connected);
159 160 161 162 163

    if (do_encryption)
      return decode_message (msg_);

    return 0;
164 165
}

Martin Hurton's avatar
Martin Hurton committed
166
zmq::mechanism_t::status_t zmq::gssapi_client_t::status () const
167
{
Martin Hurton's avatar
Martin Hurton committed
168
    return state == connected? mechanism_t::ready: mechanism_t::handshaking;
169 170
}

171
int zmq::gssapi_client_t::initialize_context ()
172
{
173 174 175 176 177
    // First time through, import service_name into target_name
    if (target_name == GSS_C_NO_NAME) {
        send_tok.value = service_name;
        send_tok.length = strlen(service_name);
        OM_uint32 maj = gss_import_name(&min_stat, &send_tok,
178 179
                                        GSS_C_NT_HOSTBASED_SERVICE,
                                        &target_name);
180

181
        if (maj != GSS_S_COMPLETE)
182 183
            return -1;
    }
184

185 186 187 188 189 190 191
    maj_stat = gss_init_sec_context(&init_sec_min_stat, cred, &context,
                                    target_name, mechs.elements,
                                    gss_flags, 0, NULL, token_ptr, NULL,
                                    &send_tok, &ret_flags, NULL);

    if (token_ptr != GSS_C_NO_BUFFER)
        free(recv_tok.value);
192

193 194 195 196 197 198 199
    return 0;
}

int zmq::gssapi_client_t::produce_next_token (msg_t *msg_)
{
    if (send_tok.length != 0) { // Server expects another token
        if (produce_initiate(msg_, send_tok.value, send_tok.length) < 0) {
200 201 202 203 204 205
            gss_release_buffer(&min_stat, &send_tok);
            gss_release_name(&min_stat, &target_name);
            return -1;
        }
    }
    gss_release_buffer(&min_stat, &send_tok);
206

207 208 209 210
    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);
211 212
        return -1;
    }
213

214 215 216
    return 0;
}

217
int zmq::gssapi_client_t::process_next_token (msg_t *msg_)
218
{
219
    if (maj_stat == GSS_S_CONTINUE_NEEDED) {
220
        if (process_initiate(msg_, &recv_tok.value, recv_tok.length) < 0) {
221 222 223 224
            gss_release_name(&min_stat, &target_name);
            return -1;
        }
        token_ptr = &recv_tok;
225 226 227 228 229
    }

    return 0;
}

230
#endif