gssapi_client.cpp 6.24 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 43
#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_) :
44
    gssapi_mechanism_base_t (options_),
45
    state (call_next_init),
46 47 48
    token_ptr (GSS_C_NO_BUFFER),
    mechs (),
    security_context_established (false)
49
{
Chris Busbey's avatar
Chris Busbey committed
50
    const std::string::size_type service_size = options_.gss_service_principal.size();
Chris Busbey's avatar
Chris Busbey committed
51 52
    service_name = static_cast <char *>(malloc(service_size+1));
    assert(service_name);
Chris Busbey's avatar
Chris Busbey committed
53
    memcpy(service_name, options_.gss_service_principal.c_str(), service_size+1 );
54

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

Chris Busbey's avatar
Chris Busbey committed
63
        if (acquire_credentials (principal_name, &cred) != 0)
Chris Busbey's avatar
Chris Busbey committed
64 65 66
            maj_stat = GSS_S_FAILURE;
    }

67 68
    mechs.elements = NULL;
    mechs.count = 0;
69 70 71 72
}

zmq::gssapi_client_t::~gssapi_client_t ()
{
73 74 75 76
    if(service_name)
        free (service_name);
    if(cred)
        gss_release_cred(&min_stat, &cred);
77 78 79 80
}

int zmq::gssapi_client_t::next_handshake_command (msg_t *msg_)
{
81 82
    if (state == send_ready) {
        int rc = produce_ready(msg_);
83
        if (rc == 0)
84 85 86 87 88
            state = connected;

        return rc;
    }

89 90 91 92 93 94 95
    if (state != call_next_init) {
        errno = EAGAIN;
        return -1;
    }

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

97 98 99 100 101 102
    if (produce_next_token (msg_) < 0)
        return -1;

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

103
    if (maj_stat == GSS_S_COMPLETE) {
104
        security_context_established = true;
105 106
        state = recv_ready;
    }
107 108
    else
        state = recv_next_token;
109

110
    return 0;
111 112 113 114
}

int zmq::gssapi_client_t::process_handshake_command (msg_t *msg_)
{
115 116 117 118 119 120 121 122
    if (state == recv_ready) {
        int rc = process_ready(msg_);
        if (rc == 0)
            state = send_ready;

        return rc;
    }

123 124 125
    if (state != recv_next_token) {
        errno = EPROTO;
        return -1;
126
    }
127 128 129 130

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

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

134 135
    state = call_next_init;

136 137
    errno_assert (msg_->close () == 0);
    errno_assert (msg_->init () == 0);
138

139
    return 0;
140 141
}

142 143
int zmq::gssapi_client_t::encode (msg_t *msg_)
{
144
    zmq_assert (state == connected);
145 146 147 148 149

    if (do_encryption)
      return encode_message (msg_);

    return 0;
150 151 152 153
}

int zmq::gssapi_client_t::decode (msg_t *msg_)
{
154
    zmq_assert (state == connected);
155 156 157 158 159

    if (do_encryption)
      return decode_message (msg_);

    return 0;
160 161
}

Martin Hurton's avatar
Martin Hurton committed
162
zmq::mechanism_t::status_t zmq::gssapi_client_t::status () const
163
{
Martin Hurton's avatar
Martin Hurton committed
164
    return state == connected? mechanism_t::ready: mechanism_t::handshaking;
165 166
}

167
int zmq::gssapi_client_t::initialize_context ()
168
{
169 170 171 172 173
    // 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,
174 175
                                        GSS_C_NT_HOSTBASED_SERVICE,
                                        &target_name);
176

177
        if (maj != GSS_S_COMPLETE)
178 179
            return -1;
    }
180

181 182 183 184 185 186 187
    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);
188

189 190 191 192 193 194 195
    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) {
196 197 198 199 200 201
            gss_release_buffer(&min_stat, &send_tok);
            gss_release_name(&min_stat, &target_name);
            return -1;
        }
    }
    gss_release_buffer(&min_stat, &send_tok);
202

203 204 205 206
    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);
207 208
        return -1;
    }
209

210 211 212
    return 0;
}

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

    return 0;
}

226
#endif