gssapi_client.cpp 6.73 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
{
52 53 54 55 56 57
    const std::string::size_type service_size =
      options_.gss_service_principal.size ();
    service_name = static_cast<char *> (malloc (service_size + 1));
    assert (service_name);
    memcpy (service_name, options_.gss_service_principal.c_str (),
            service_size + 1);
58

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

69 70
        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
71 72 73
            maj_stat = GSS_S_FAILURE;
    }

74 75
    mechs.elements = NULL;
    mechs.count = 0;
76 77 78 79
}

zmq::gssapi_client_t::~gssapi_client_t ()
{
80
    if (service_name)
81
        free (service_name);
82 83
    if (cred)
        gss_release_cred (&min_stat, &cred);
84 85 86 87
}

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

        return rc;
    }

96 97 98 99 100 101 102
    if (state != call_next_init) {
        errno = EAGAIN;
        return -1;
    }

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

104 105 106 107 108 109
    if (produce_next_token (msg_) < 0)
        return -1;

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

110
    if (maj_stat == GSS_S_COMPLETE) {
111
        security_context_established = true;
112
        state = recv_ready;
113
    } else
114
        state = recv_next_token;
115

116
    return 0;
117 118 119 120
}

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

        return rc;
    }

129
    if (state != recv_next_token) {
130
        session->get_socket ()->event_handshake_failed_protocol (
131
          session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
132 133
        errno = EPROTO;
        return -1;
134
    }
135 136 137 138

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

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

142 143
    state = call_next_init;

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

147
    return 0;
148 149
}

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

    if (do_encryption)
155
        return encode_message (msg_);
156 157

    return 0;
158 159 160 161
}

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

    if (do_encryption)
165
        return decode_message (msg_);
166 167

    return 0;
168 169
}

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

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

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

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

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

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

199 200 201 202 203 204
    return 0;
}

int zmq::gssapi_client_t::produce_next_token (msg_t *msg_)
{
    if (send_tok.length != 0) { // Server expects another token
205 206 207
        if (produce_initiate (msg_, send_tok.value, send_tok.length) < 0) {
            gss_release_buffer (&min_stat, &send_tok);
            gss_release_name (&min_stat, &target_name);
208 209 210
            return -1;
        }
    }
211
    gss_release_buffer (&min_stat, &send_tok);
212

213
    if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
214
        gss_release_name (&min_stat, &target_name);
215
        if (context != GSS_C_NO_CONTEXT)
216
            gss_delete_sec_context (&min_stat, &context, GSS_C_NO_BUFFER);
217 218
        return -1;
    }
219

220 221 222
    return 0;
}

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

    return 0;
}

236
#endif