Commit dd64f643 authored by Pieter Hintjens's avatar Pieter Hintjens

Merge pull request #978 from connamara/gssapi

GSSAPI/Kerberos Security Mechanism
parents d1bdd45e d1334d9d
......@@ -27,6 +27,7 @@ Brett Cameron <Brett.Cameron@hp.com>
Burak Arslan <burak-github@arskom.com.tr>
Carl Clemens <carlclemens1@yahoo.com>
Chia-liang Kao <clkao@clkao.org>
Chris Busbey <chris.busbey@gmail.com>
Chris Rempel <csrl@gmx.com>
Chris Wong <chris@chriswongstudio.com>
Christian Gudrian <christian.gudrian@fluidon.com>
......@@ -69,6 +70,7 @@ McClain Looney <m@loonsoft.com>
Michael Compton <michael.compton@littleedge.co.uk>
Mika Fischer <mika.fischer@zoopnet.de>
Mikael Helbo Kjaer <mhk@designtech.dk>
Mike Gatny <mgatny@gmail.com>
Mikko Koppanen <mkoppanen@php.net>
Min Ragan-Kelley <benjaminrk@gmail.com>
Neale Ferguson <neale@sinenomine.net>
......
......@@ -305,6 +305,7 @@ else
AC_CHECK_LIB([sodium], [sodium_init],,AC_MSG_WARN(libsodium is needed for CURVE security))
fi
AC_CHECK_LIB([gssapi_krb5], [gss_init_sec_context],,AC_MSG_WARN(libgssapi_krb5 is needed for GSSAPI security))
#
# Check if the compiler supports -fvisibility=hidden flag. MinGW32 uses __declspec
......
......@@ -296,6 +296,10 @@ ZMQ_EXPORT char *zmq_msg_gets (zmq_msg_t *msg, char *property);
#define ZMQ_IPC_FILTER_UID 59
#define ZMQ_IPC_FILTER_GID 60
#define ZMQ_CONNECT_RID 61
#define ZMQ_GSSAPI_SERVER 62
#define ZMQ_GSSAPI_PRINCIPAL 63
#define ZMQ_GSSAPI_SERVICE_PRINCIPAL 64
#define ZMQ_GSSAPI_PLAINTEXT 65
/* Message options */
#define ZMQ_MORE 1
......@@ -309,6 +313,7 @@ ZMQ_EXPORT char *zmq_msg_gets (zmq_msg_t *msg, char *property);
#define ZMQ_NULL 0
#define ZMQ_PLAIN 1
#define ZMQ_CURVE 2
#define ZMQ_GSSAPI 3
/* Deprecated options and aliases */
#define ZMQ_IPV4ONLY 31
......
......@@ -25,6 +25,9 @@ libzmq_la_SOURCES = \
err.hpp \
fd.hpp \
fq.hpp \
gssapi_mechanism_base.hpp \
gssapi_client.hpp \
gssapi_server.hpp \
i_encoder.hpp \
i_decoder.hpp \
i_engine.hpp \
......@@ -101,6 +104,9 @@ libzmq_la_SOURCES = \
epoll.cpp \
err.cpp \
fq.cpp \
gssapi_mechanism_base.cpp \
gssapi_client.cpp \
gssapi_server.cpp \
io_object.cpp \
io_thread.cpp \
ip.cpp \
......
/*
Copyright (c) 2007-2014 Contributors as noted in the AUTHORS file
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"
#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_) :
gssapi_mechanism_base_t (options_),
state (call_next_init),
token_ptr (GSS_C_NO_BUFFER),
mechs (),
security_context_established (false)
{
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 );
maj_stat = GSS_S_COMPLETE;
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 );
if (acquire_credentials (principal_name, &cred) != 0)
maj_stat = GSS_S_FAILURE;
}
mechs.elements = NULL;
mechs.count = 0;
}
zmq::gssapi_client_t::~gssapi_client_t ()
{
if(service_name)
free (service_name);
if(cred)
gss_release_cred(&min_stat, &cred);
}
int zmq::gssapi_client_t::next_handshake_command (msg_t *msg_)
{
if (state == send_ready) {
int rc = produce_ready(msg_);
if (rc == 0)
state = connected;
return rc;
}
if (state != call_next_init) {
errno = EAGAIN;
return -1;
}
if (initialize_context () < 0)
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;
state = recv_ready;
}
else
state = recv_next_token;
return 0;
}
int zmq::gssapi_client_t::process_handshake_command (msg_t *msg_)
{
if (state == recv_ready) {
int rc = process_ready(msg_);
if (rc == 0)
state = send_ready;
return rc;
}
if (state != recv_next_token) {
errno = EPROTO;
return -1;
}
if (process_next_token (msg_) < 0)
return -1;
if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
return -1;
state = call_next_init;
errno_assert (msg_->close () == 0);
errno_assert (msg_->init () == 0);
return 0;
}
int zmq::gssapi_client_t::encode (msg_t *msg_)
{
zmq_assert (state == connected);
if (do_encryption)
return encode_message (msg_);
return 0;
}
int zmq::gssapi_client_t::decode (msg_t *msg_)
{
zmq_assert (state == connected);
if (do_encryption)
return decode_message (msg_);
return 0;
}
bool zmq::gssapi_client_t::is_handshake_complete () const
{
return state == connected;
}
int zmq::gssapi_client_t::initialize_context ()
{
// 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,
gss_nt_service_name, &target_name);
if (maj != GSS_S_COMPLETE)
return -1;
}
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);
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) {
gss_release_buffer(&min_stat, &send_tok);
gss_release_name(&min_stat, &target_name);
return -1;
}
}
gss_release_buffer(&min_stat, &send_tok);
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);
return -1;
}
return 0;
}
int zmq::gssapi_client_t::process_next_token (msg_t *msg_)
{
if (maj_stat == GSS_S_CONTINUE_NEEDED) {
if (process_initiate(msg_, &recv_tok.value, recv_tok.length) < 0) {
gss_release_name(&min_stat, &target_name);
return -1;
}
token_ptr = &recv_tok;
}
return 0;
}
/*
Copyright (c) 2007-2014 Contributors as noted in the AUTHORS file
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/>.
*/
#ifndef __ZMQ_GSSAPI_CLIENT_HPP_INCLUDED__
#define __ZMQ_GSSAPI_CLIENT_HPP_INCLUDED__
#include "gssapi_mechanism_base.hpp"
namespace zmq
{
class msg_t;
class gssapi_client_t :
public gssapi_mechanism_base_t
{
public:
gssapi_client_t (const options_t &options_);
virtual ~gssapi_client_t ();
// mechanism implementation
virtual int next_handshake_command (msg_t *msg_);
virtual int process_handshake_command (msg_t *msg_);
virtual int encode (msg_t *msg_);
virtual int decode (msg_t *msg_);
virtual bool is_handshake_complete () const;
private:
enum state_t {
call_next_init,
send_next_token,
recv_next_token,
send_ready,
recv_ready,
connected
};
// Human-readable principal name of the service we are connecting to
char * service_name;
// Current FSM state
state_t state;
// Points to either send_tok or recv_tok
// during context initialization
gss_buffer_desc *token_ptr;
// The desired underlying mechanism
gss_OID_set_desc mechs;
// True iff client considers the server authenticated
bool security_context_established;
int initialize_context ();
int produce_next_token (msg_t *msg_);
int process_next_token (msg_t *msg_);
};
}
#endif
/*
Copyright (c) 2007-2014 Contributors as noted in the AUTHORS file
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"
#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_mechanism_base.hpp"
#include "wire.hpp"
zmq::gssapi_mechanism_base_t::gssapi_mechanism_base_t (const options_t & options_) :
mechanism_t(options_),
send_tok (),
recv_tok (),
/// FIXME remove? in_buf (),
target_name (GSS_C_NO_NAME),
principal_name (NULL),
maj_stat (GSS_S_COMPLETE),
min_stat (0),
init_sec_min_stat (0),
ret_flags (0),
gss_flags (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG),
cred (GSS_C_NO_CREDENTIAL),
context (GSS_C_NO_CONTEXT),
do_encryption (!options_.gss_plaintext)
{
}
zmq::gssapi_mechanism_base_t::~gssapi_mechanism_base_t ()
{
if(target_name)
gss_release_name(&min_stat, &target_name);
if(context)
gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
}
int zmq::gssapi_mechanism_base_t::encode_message (msg_t *msg_)
{
// Wrap the token value
int state;
gss_buffer_desc plaintext;
gss_buffer_desc wrapped;
uint8_t flags = 0;
if (msg_->flags () & msg_t::more)
flags |= 0x01;
uint8_t *plaintext_buffer = static_cast <uint8_t *>(malloc(msg_->size ()+1));
plaintext_buffer[0] = flags;
memcpy (plaintext_buffer+1, msg_->data(), msg_->size());
plaintext.value = plaintext_buffer;
plaintext.length = msg_->size ()+1;
maj_stat = gss_wrap(&min_stat, context, 1, GSS_C_QOP_DEFAULT,
&plaintext, &state, &wrapped);
zmq_assert (maj_stat == GSS_S_COMPLETE);
zmq_assert (state);
// Re-initialize msg_ for wrapped text
int rc = msg_->close ();
zmq_assert (rc == 0);
rc = msg_->init_size (8 + 4 + wrapped.length);
zmq_assert (rc == 0);
uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
// Add command string
memcpy (ptr, "\x07MESSAGE", 8);
ptr += 8;
// Add token length
put_uint32 (ptr, static_cast <uint32_t> (wrapped.length));
ptr += 4;
// Add wrapped token value
memcpy (ptr, wrapped.value, wrapped.length);
ptr += wrapped.length;
gss_release_buffer (&min_stat, &wrapped);
return 0;
}
int zmq::gssapi_mechanism_base_t::decode_message (msg_t *msg_)
{
const uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
size_t bytes_left = msg_->size ();
// Get command string
if (bytes_left < 8 || memcmp (ptr, "\x07MESSAGE", 8)) {
errno = EPROTO;
return -1;
}
ptr += 8;
bytes_left -= 8;
// Get token length
if (bytes_left < 4) {
errno = EPROTO;
return -1;
}
gss_buffer_desc wrapped;
wrapped.length = get_uint32 (ptr);
ptr += 4;
bytes_left -= 4;
// Get token value
if (bytes_left < wrapped.length) {
errno = EPROTO;
return -1;
}
// TODO: instead of malloc/memcpy, can we just do: wrapped.value = ptr;
const size_t alloc_length = wrapped.length? wrapped.length: 1;
wrapped.value = static_cast <char *> (malloc (alloc_length));
if (wrapped.length) {
alloc_assert (wrapped.value);
memcpy(wrapped.value, ptr, wrapped.length);
ptr += wrapped.length;
bytes_left -= wrapped.length;
}
// Unwrap the token value
int state;
gss_buffer_desc plaintext;
maj_stat = gss_unwrap(&min_stat, context, &wrapped, &plaintext,
&state, (gss_qop_t *) NULL);
zmq_assert(maj_stat == GSS_S_COMPLETE);
zmq_assert(state);
// Re-initialize msg_ for plaintext
int rc = msg_->close ();
zmq_assert (rc == 0);
rc = msg_->init_size (plaintext.length-1);
zmq_assert (rc == 0);
const uint8_t flags = static_cast <char *> (plaintext.value)[0];
if (flags & 0x01)
msg_->set_flags (msg_t::more);
memcpy (msg_->data (), static_cast <char *> (plaintext.value)+1, plaintext.length-1);
gss_release_buffer (&min_stat, &plaintext);
gss_release_buffer (&min_stat, &wrapped);
if (bytes_left > 0) {
errno = EPROTO;
return -1;
}
return 0;
}
int zmq::gssapi_mechanism_base_t::produce_initiate (msg_t *msg_, void *token_value_, size_t token_length_)
{
zmq_assert (token_value_);
zmq_assert (token_length_ <= 0xFFFFFFFFUL);
const size_t command_size = 9 + 4 + token_length_;
const int rc = msg_->init_size (command_size);
errno_assert (rc == 0);
uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
// Add command string
memcpy (ptr, "\x08INITIATE", 9);
ptr += 9;
// Add token length
put_uint32 (ptr, static_cast <uint32_t> (token_length_));
ptr += 4;
// Add token value
memcpy (ptr, token_value_, token_length_);
ptr += token_length_;
return 0;
}
int zmq::gssapi_mechanism_base_t::process_initiate (msg_t *msg_, void **token_value_, size_t &token_length_)
{
zmq_assert (token_value_);
const uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
size_t bytes_left = msg_->size ();
// Get command string
if (bytes_left < 9 || memcmp (ptr, "\x08INITIATE", 9)) {
errno = EPROTO;
return -1;
}
ptr += 9;
bytes_left -= 9;
// Get token length
if (bytes_left < 4) {
errno = EPROTO;
return -1;
}
token_length_ = get_uint32 (ptr);
ptr += 4;
bytes_left -= 4;
// Get token value
if (bytes_left < token_length_) {
errno = EPROTO;
return -1;
}
*token_value_ = static_cast <char *> (malloc (token_length_ ? token_length_ : 1));
if (token_length_) {
alloc_assert (*token_value_);
memcpy(*token_value_, ptr, token_length_);
ptr += token_length_;
bytes_left -= token_length_;
}
if (bytes_left > 0) {
errno = EPROTO;
return -1;
}
return 0;
}
int zmq::gssapi_mechanism_base_t::produce_ready (msg_t *msg_)
{
unsigned char * const command_buffer = (unsigned char *) malloc (512);
alloc_assert (command_buffer);
unsigned char *ptr = command_buffer;
// Add command name
memcpy (ptr, "\x05READY", 6);
ptr += 6;
// Add socket type property
const char *socket_type = socket_type_string (options.type);
ptr += add_property (ptr, "Socket-Type", socket_type, strlen (socket_type));
// Add identity property
if (options.type == ZMQ_REQ
|| options.type == ZMQ_DEALER
|| options.type == ZMQ_ROUTER) {
ptr += add_property (ptr, "Identity",
options.identity, options.identity_size);
}
const size_t command_size = ptr - command_buffer;
const int rc = msg_->init_size (command_size);
errno_assert (rc == 0);
memcpy (msg_->data (), command_buffer, command_size);
free (command_buffer);
if (do_encryption)
return encode_message (msg_);
return 0;
}
int zmq::gssapi_mechanism_base_t::process_ready (msg_t *msg_)
{
if (do_encryption) {
const int rc = decode_message (msg_);
if (rc != 0)
return rc;
}
const unsigned char *ptr = static_cast <unsigned char *> (msg_->data ());
size_t bytes_left = msg_->size ();
if (bytes_left < 6 || memcmp (ptr, "\x05READY", 6)) {
errno = EPROTO;
return -1;
}
ptr += 6;
bytes_left -= 6;
return parse_metadata (ptr, bytes_left);
}
int zmq::gssapi_mechanism_base_t::acquire_credentials (char * service_name_, gss_cred_id_t * cred_)
{
OM_uint32 maj_stat;
OM_uint32 min_stat;
gss_name_t server_name;
gss_buffer_desc name_buf;
name_buf.value = service_name_;
name_buf.length = strlen ((char *) name_buf.value) + 1;
maj_stat = gss_import_name (&min_stat, &name_buf,
gss_nt_service_name, &server_name);
if (maj_stat != GSS_S_COMPLETE)
return -1;
maj_stat = gss_acquire_cred (&min_stat, server_name, 0,
GSS_C_NO_OID_SET, GSS_C_ACCEPT,
cred_, NULL, NULL);
if (maj_stat != GSS_S_COMPLETE)
return -1;
gss_release_name(&min_stat, &server_name);
return 0;
}
/*
Copyright (c) 2007-2014 Contributors as noted in the AUTHORS file
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/>.
*/
#ifndef __ZMQ_GSSAPI_MECHANISM_BASE_HPP_INCLUDED__
#define __ZMQ_GSSAPI_MECHANISM_BASE_HPP_INCLUDED__
#include <gssapi/gssapi_generic.h>
#include <gssapi/gssapi_krb5.h>
#include "mechanism.hpp"
#include "options.hpp"
namespace zmq
{
class msg_t;
/// Commonalities between clients and servers are captured here.
/// For example, clients and servers both need to produce and
/// process context-level GSSAPI tokens (via INITIATE commands)
/// and per-message GSSAPI tokens (via MESSAGE commands).
class gssapi_mechanism_base_t:
public mechanism_t
{
public:
gssapi_mechanism_base_t (const options_t &options_);
virtual ~gssapi_mechanism_base_t () = 0;
protected:
// Produce a context-level GSSAPI token (INITIATE command)
// during security context initialization.
int produce_initiate (msg_t *msg_, void *data_, size_t data_len_);
// Process a context-level GSSAPI token (INITIATE command)
// during security context initialization.
int process_initiate (msg_t *msg_, void **data_, size_t &data_len_);
// Produce a metadata ready msg (READY) to conclude handshake
int produce_ready (msg_t *msg_);
// Process a metadata ready msg (READY)
int process_ready (msg_t *msg_);
// Encode a per-message GSSAPI token (MESSAGE command) using
// the established security context.
int encode_message (msg_t *msg_);
// Decode a per-message GSSAPI token (MESSAGE command) using
// the established security context.
int decode_message (msg_t *msg_);
// Acquire security context credentials from the
// underlying mechanism.
static int acquire_credentials (char * principal_name_,
gss_cred_id_t * cred_);
protected:
// Opaque GSSAPI token for outgoing data
gss_buffer_desc send_tok;
// Opaque GSSAPI token for incoming data
gss_buffer_desc recv_tok;
// Opaque GSSAPI representation of principal
gss_name_t target_name;
// Human-readable principal name
char * principal_name;
// Status code returned by GSSAPI functions
OM_uint32 maj_stat;
// Status code returned by the underlying mechanism
OM_uint32 min_stat;
// Status code returned by the underlying mechanism
// during context initialization
OM_uint32 init_sec_min_stat;
// Flags returned by GSSAPI (ignored)
OM_uint32 ret_flags;
// Flags returned by GSSAPI (ignored)
OM_uint32 gss_flags;
// Credentials used to establish security context
gss_cred_id_t cred;
// Opaque GSSAPI representation of the security context
gss_ctx_id_t context;
// If true, use gss to encrypt messages. If false, only utilize gss for auth.
bool do_encryption;
};
}
#endif
/*
Copyright (c) 2007-2014 Contributors as noted in the AUTHORS file
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"
#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_server.hpp"
#include "wire.hpp"
#include <gssapi/gssapi.h>
zmq::gssapi_server_t::gssapi_server_t (session_base_t *session_,
const std::string &peer_address_,
const options_t &options_) :
gssapi_mechanism_base_t (options_),
session (session_),
peer_address (peer_address_),
state (recv_next_token),
security_context_established (false)
{
maj_stat = GSS_S_CONTINUE_NEEDED;
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 );
if (acquire_credentials (principal_name, &cred) != 0)
maj_stat = GSS_S_FAILURE;
}
}
zmq::gssapi_server_t::~gssapi_server_t ()
{
if(cred)
gss_release_cred(&min_stat, &cred);
if(target_name)
gss_release_name(&min_stat, &target_name);
}
int zmq::gssapi_server_t::next_handshake_command (msg_t *msg_)
{
if (state == send_ready) {
int rc = produce_ready(msg_);
if (rc == 0)
state = recv_ready;
return rc;
}
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;
}
state = recv_next_token;
return 0;
}
int zmq::gssapi_server_t::process_handshake_command (msg_t *msg_)
{
if (state == recv_ready) {
int rc = process_ready(msg_);
if (rc == 0)
state = connected;
return rc;
}
if (state != recv_next_token) {
errno = EPROTO;
return -1;
}
if (security_context_established) {
// 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;
return 0;
}
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;
}
void zmq::gssapi_server_t::send_zap_request ()
{
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);
// Principal frame
gss_buffer_desc principal;
gss_display_name(&min_stat, target_name, &principal, NULL);
rc = msg.init_size (principal.length);
errno_assert (rc == 0);
memcpy (msg.data (), principal.value, principal.length);
rc = session->write_zap_msg (&msg);
errno_assert (rc == 0);
gss_release_buffer(&min_stat, &principal);
}
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 ()),
msg [6].size ());
error:
for (int i = 0; i < 7; i++) {
const int rc2 = msg [i].close ();
errno_assert (rc2 == 0);
}
return rc;
}
int zmq::gssapi_server_t::encode (msg_t *msg_)
{
zmq_assert (state == connected);
if (do_encryption)
return encode_message (msg_);
return 0;
}
int zmq::gssapi_server_t::decode (msg_t *msg_)
{
zmq_assert (state == connected);
if (do_encryption)
return decode_message (msg_);
return 0;
}
int zmq::gssapi_server_t::zap_msg_available ()
{
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;
}
bool zmq::gssapi_server_t::is_handshake_complete () const
{
return state == connected;
}
int zmq::gssapi_server_t::produce_next_token (msg_t *msg_)
{
if (send_tok.length != 0) { // Client expects another token
if (produce_initiate(msg_, send_tok.value, send_tok.length) < 0)
return -1;
gss_release_buffer(&min_stat, &send_tok);
}
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);
return -1;
}
return 0;
}
int zmq::gssapi_server_t::process_next_token (msg_t *msg_)
{
if (maj_stat == GSS_S_CONTINUE_NEEDED) {
if (process_initiate(msg_, &recv_tok.value, recv_tok.length) < 0) {
if (target_name != GSS_C_NO_NAME)
gss_release_name(&min_stat, &target_name);
return -1;
}
}
return 0;
}
void zmq::gssapi_server_t::accept_context ()
{
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);
if (recv_tok.value) {
free (recv_tok.value);
recv_tok.value = NULL;
}
}
/*
Copyright (c) 2007-2014 Contributors as noted in the AUTHORS file
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/>.
*/
#ifndef __ZMQ_GSSAPI_SERVER_HPP_INCLUDED__
#define __ZMQ_GSSAPI_SERVER_HPP_INCLUDED__
#include "gssapi_mechanism_base.hpp"
namespace zmq
{
class msg_t;
class session_base_t;
class gssapi_server_t :
public gssapi_mechanism_base_t
{
public:
gssapi_server_t (session_base_t *session_,
const std::string &peer_address,
const options_t &options_);
virtual ~gssapi_server_t ();
// mechanism implementation
virtual int next_handshake_command (msg_t *msg_);
virtual int process_handshake_command (msg_t *msg_);
virtual int encode (msg_t *msg_);
virtual int decode (msg_t *msg_);
virtual int zap_msg_available ();
virtual bool is_handshake_complete () const;
private:
enum state_t {
send_next_token,
recv_next_token,
expect_zap_reply,
send_ready,
recv_ready,
connected
};
session_base_t * const session;
const std::string peer_address;
// Current FSM state
state_t state;
// True iff server considers the client authenticated
bool security_context_established;
// The underlying mechanism type (ignored)
gss_OID doid;
void accept_context ();
int produce_next_token (msg_t *msg_);
int process_next_token (msg_t *msg_);
void send_zap_request ();
int receive_and_process_zap_reply();
};
}
#endif
......@@ -53,6 +53,7 @@ zmq::options_t::options_t () :
tcp_keepalive_intvl (-1),
mechanism (ZMQ_NULL),
as_server (0),
gss_plaintext (false),
socket_id (0),
conflate (false)
{
......@@ -394,13 +395,46 @@ int zmq::options_t::setsockopt (int option_, const void *optval_,
}
break;
# endif
case ZMQ_CONFLATE:
if (is_int && (value == 0 || value == 1)) {
conflate = (value != 0);
return 0;
}
break;
case ZMQ_GSSAPI_SERVER:
if (is_int && (value == 0 || value == 1)) {
as_server = value;
mechanism = ZMQ_GSSAPI;
return 0;
}
break;
case ZMQ_GSSAPI_PRINCIPAL:
if (optvallen_ > 0 && optvallen_ < 256 && optval_ != NULL) {
gss_principal.assign ((const char *) optval_, optvallen_);
mechanism = ZMQ_GSSAPI;
return 0;
}
break;
case ZMQ_GSSAPI_SERVICE_PRINCIPAL:
if (optvallen_ > 0 && optvallen_ < 256 && optval_ != NULL) {
gss_service_principal.assign ((const char *) optval_, optvallen_);
mechanism = ZMQ_GSSAPI;
as_server = 0;
return 0;
}
break;
case ZMQ_GSSAPI_PLAINTEXT:
if (is_int && (value == 0 || value == 1)) {
gss_plaintext = (value != 0);
return 0;
}
break;
default:
break;
......@@ -681,8 +715,39 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_)
return 0;
}
break;
case ZMQ_GSSAPI_SERVER:
if (is_int) {
*value = as_server && mechanism == ZMQ_GSSAPI;
return 0;
}
break;
}
case ZMQ_GSSAPI_PRINCIPAL:
if (*optvallen_ >= gss_principal.size () + 1) {
memcpy (optval_, gss_principal.c_str (), gss_principal.size () + 1);
*optvallen_ = gss_principal.size () + 1;
return 0;
}
break;
case ZMQ_GSSAPI_SERVICE_PRINCIPAL:
if (*optvallen_ >= gss_service_principal.size () + 1) {
memcpy (optval_, gss_service_principal.c_str (), gss_service_principal.size () + 1);
*optvallen_ = gss_service_principal.size () + 1;
return 0;
}
break;
case ZMQ_GSSAPI_PLAINTEXT:
if (is_int) {
*value = gss_plaintext;
return 0;
}
break;
}
errno = EINVAL;
return -1;
}
......@@ -156,6 +156,13 @@ namespace zmq
uint8_t curve_secret_key [CURVE_KEYSIZE];
uint8_t curve_server_key [CURVE_KEYSIZE];
// Principals for GSSAPI mechanism
std::string gss_principal;
std::string gss_service_principal;
// If true, gss encryption will be disabled
bool gss_plaintext;
// ID of the socket.
int socket_id;
......
......@@ -43,6 +43,8 @@
#include "v2_decoder.hpp"
#include "null_mechanism.hpp"
#include "plain_mechanism.hpp"
#include "gssapi_client.hpp"
#include "gssapi_server.hpp"
#include "curve_client.hpp"
#include "curve_server.hpp"
#include "raw_decoder.hpp"
......@@ -477,13 +479,17 @@ bool zmq::stream_engine_t::handshake ()
zmq_assert (options.mechanism == ZMQ_NULL
|| options.mechanism == ZMQ_PLAIN
|| options.mechanism == ZMQ_CURVE);
|| options.mechanism == ZMQ_CURVE
|| options.mechanism == ZMQ_GSSAPI);
if (options.mechanism == ZMQ_NULL)
memcpy (outpos + outsize, "NULL", 4);
else
if (options.mechanism == ZMQ_PLAIN)
memcpy (outpos + outsize, "PLAIN", 5);
else
if (options.mechanism == ZMQ_GSSAPI)
memcpy (outpos + outsize, "GSSAPI", 6);
else
memcpy (outpos + outsize, "CURVE", 5);
outsize += 20;
......@@ -589,6 +595,15 @@ bool zmq::stream_engine_t::handshake ()
alloc_assert (mechanism);
}
#endif
else
if (memcmp (greeting_recv + 12, "GSSAPI\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) == 0) {
if (options.as_server)
mechanism = new (std::nothrow)
gssapi_server_t (session, peer_address, options);
else
mechanism = new (std::nothrow) gssapi_client_t (options);
alloc_assert (mechanism);
}
else {
error ();
return false;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment