gssapi_client.cpp 6.79 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"
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_client.hpp"
#include "wire.hpp"

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

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

66 67
        gss_OID name_type = convert_nametype (options_.gss_principal_nt);
        if (acquire_credentials (principal_name, &cred, name_type) != 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
    if (state != recv_next_token) {
128 129 130
        session->get_socket ()->event_handshake_failed_protocol (
          session->get_endpoint (),
          ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
131 132
        errno = EPROTO;
        return -1;
133
    }
134 135 136 137

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

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

141 142
    state = call_next_init;

143 144
    errno_assert (msg_->close () == 0);
    errno_assert (msg_->init () == 0);
145

146
    return 0;
147 148
}

149 150
int zmq::gssapi_client_t::encode (msg_t *msg_)
{
151
    zmq_assert (state == connected);
152 153 154 155 156

    if (do_encryption)
      return encode_message (msg_);

    return 0;
157 158 159 160
}

int zmq::gssapi_client_t::decode (msg_t *msg_)
{
161
    zmq_assert (state == connected);
162 163 164 165 166

    if (do_encryption)
      return decode_message (msg_);

    return 0;
167 168
}

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

174
int zmq::gssapi_client_t::initialize_context ()
175
{
176 177 178 179
    // principal was specified but credentials could not be acquired
    if (principal_name != NULL && cred == NULL)
        return -1;

180 181 182
    // First time through, import service_name into target_name
    if (target_name == GSS_C_NO_NAME) {
        send_tok.value = service_name;
183
        send_tok.length = strlen(service_name) + 1;
184
        OM_uint32 maj = gss_import_name(&min_stat, &send_tok,
185
                                        service_name_type,
186
                                        &target_name);
187

188
        if (maj != GSS_S_COMPLETE)
189 190
            return -1;
    }
191

192 193 194 195 196 197 198
    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);
199

200 201 202 203 204 205 206
    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) {
207 208 209 210 211 212
            gss_release_buffer(&min_stat, &send_tok);
            gss_release_name(&min_stat, &target_name);
            return -1;
        }
    }
    gss_release_buffer(&min_stat, &send_tok);
213

214 215 216 217
    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);
218 219
        return -1;
    }
220

221 222 223
    return 0;
}

224
int zmq::gssapi_client_t::process_next_token (msg_t *msg_)
225
{
226
    if (maj_stat == GSS_S_CONTINUE_NEEDED) {
227
        if (process_initiate(msg_, &recv_tok.value, recv_tok.length) < 0) {
228 229 230 231
            gss_release_name(&min_stat, &target_name);
            return -1;
        }
        token_ptr = &recv_tok;
232 233 234 235 236
    }

    return 0;
}

237
#endif