Commit 3c414c4a authored by Mike Gatny's avatar Mike Gatny Committed by Chris Busbey

GSSAPI mechanism now fully working with encryption

parent eb286252
...@@ -69,6 +69,7 @@ McClain Looney <m@loonsoft.com> ...@@ -69,6 +69,7 @@ McClain Looney <m@loonsoft.com>
Michael Compton <michael.compton@littleedge.co.uk> Michael Compton <michael.compton@littleedge.co.uk>
Mika Fischer <mika.fischer@zoopnet.de> Mika Fischer <mika.fischer@zoopnet.de>
Mikael Helbo Kjaer <mhk@designtech.dk> Mikael Helbo Kjaer <mhk@designtech.dk>
Mike Gatny <mgatny@gmail.com>
Mikko Koppanen <mkoppanen@php.net> Mikko Koppanen <mkoppanen@php.net>
Min Ragan-Kelley <benjaminrk@gmail.com> Min Ragan-Kelley <benjaminrk@gmail.com>
Neale Ferguson <neale@sinenomine.net> Neale Ferguson <neale@sinenomine.net>
......
...@@ -34,13 +34,13 @@ ...@@ -34,13 +34,13 @@
zmq::gssapi_client_t::gssapi_client_t (const options_t &options_) : zmq::gssapi_client_t::gssapi_client_t (const options_t &options_) :
gssapi_mechanism_base_t (), gssapi_mechanism_base_t (),
mechanism_t (options_), mechanism_t (options_),
state (send_next_token), state (call_next_init),
token_ptr (GSS_C_NO_BUFFER), token_ptr (GSS_C_NO_BUFFER),
mechs (), mechs (),
security_context_established (false) security_context_established (false)
{ {
maj_stat = GSS_S_COMPLETE; maj_stat = GSS_S_COMPLETE;
service_name = strdup("host"); /// FIXME add service_name to options service_name = strdup("host"); // TODO: add service_name to options
mechs.elements = NULL; mechs.elements = NULL;
mechs.count = 0; mechs.count = 0;
} }
...@@ -49,80 +49,80 @@ zmq::gssapi_client_t::~gssapi_client_t () ...@@ -49,80 +49,80 @@ zmq::gssapi_client_t::~gssapi_client_t ()
{ {
if(service_name) if(service_name)
free (service_name); free (service_name);
/// FIXME release this or not?
if(cred) if(cred)
gss_release_cred(&min_stat, &cred); gss_release_cred(&min_stat, &cred);
} }
int zmq::gssapi_client_t::next_handshake_command (msg_t *msg_) int zmq::gssapi_client_t::next_handshake_command (msg_t *msg_)
{ {
int rc = 0; if (security_context_established) {
state = connected;
switch (state) { return 0;
case send_next_token:
rc = produce_next_token (msg_);
if (rc == 0)
state = recv_next_token;
break;
case almost_ready:
state = ready;
break;
default:
errno = EAGAIN;
rc = -1;
break;
} }
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;
else
state = recv_next_token;
return 0;
} }
int zmq::gssapi_client_t::process_handshake_command (msg_t *msg_) int zmq::gssapi_client_t::process_handshake_command (msg_t *msg_)
{ {
int rc = 0; if (state != recv_next_token) {
errno = EPROTO;
switch (state) { return -1;
case recv_next_token:
rc = process_next_token (msg_);
if (rc == 0)
state = security_context_established? almost_ready: send_next_token;
break;
case almost_ready:
state = ready;
break;
default:
errno = EPROTO;
rc = -1;
break;
}
if (rc == 0) {
rc = msg_->close ();
errno_assert (rc == 0);
rc = msg_->init ();
errno_assert (rc == 0);
} }
return rc;
if (process_next_token (msg_) < 0)
return -1;
if (maj_stat == GSS_S_COMPLETE)
security_context_established = true;
else if (maj_stat == GSS_S_CONTINUE_NEEDED)
state = call_next_init;
else
return -1;
errno_assert (msg_->close () == 0);
errno_assert (msg_->init () == 0);
return 0;
} }
int zmq::gssapi_client_t::encode (msg_t *msg_) int zmq::gssapi_client_t::encode (msg_t *msg_)
{ {
int rc = 0; zmq_assert (state == connected);
zmq_assert (state == ready); return encode_message (msg_);
return rc;
} }
int zmq::gssapi_client_t::decode (msg_t *msg_) int zmq::gssapi_client_t::decode (msg_t *msg_)
{ {
int rc = 0; zmq_assert (state == connected);
zmq_assert (state == ready); return decode_message (msg_);
return rc;
} }
bool zmq::gssapi_client_t::is_handshake_complete () const bool zmq::gssapi_client_t::is_handshake_complete () const
{ {
fprintf(stderr, "%s:%d: is_handshake_complete=%d, security_context_established=%d\n", __FILE__, __LINE__, (state==ready), security_context_established); /// FIXME remove return state == connected;
return state == ready;
} }
int zmq::gssapi_client_t::produce_next_token (msg_t *msg_) int zmq::gssapi_client_t::initialize_context ()
{ {
// First time through, import service_name into target_name // First time through, import service_name into target_name
if (target_name == GSS_C_NO_NAME) { if (target_name == GSS_C_NO_NAME) {
...@@ -131,10 +131,8 @@ int zmq::gssapi_client_t::produce_next_token (msg_t *msg_) ...@@ -131,10 +131,8 @@ int zmq::gssapi_client_t::produce_next_token (msg_t *msg_)
OM_uint32 maj = gss_import_name(&min_stat, &send_tok, OM_uint32 maj = gss_import_name(&min_stat, &send_tok,
gss_nt_service_name, &target_name); gss_nt_service_name, &target_name);
if (maj != GSS_S_COMPLETE) { if (maj != GSS_S_COMPLETE)
fprintf(stderr, "%s:%d: failed to import service name\n", __FILE__, __LINE__); /// FIXME die("creating context", maj_stat, init_sec_min_stat); /// FIXME die("parsing name", maj_stat, min_stat);
return -1; return -1;
}
} }
maj_stat = gss_init_sec_context(&init_sec_min_stat, cred, &context, maj_stat = gss_init_sec_context(&init_sec_min_stat, cred, &context,
...@@ -144,22 +142,22 @@ int zmq::gssapi_client_t::produce_next_token (msg_t *msg_) ...@@ -144,22 +142,22 @@ int zmq::gssapi_client_t::produce_next_token (msg_t *msg_)
if (token_ptr != GSS_C_NO_BUFFER) if (token_ptr != GSS_C_NO_BUFFER)
free(recv_tok.value); free(recv_tok.value);
if (send_tok.length != 0) { // server expects another token return 0;
fprintf(stderr, "%s:%d: producing token\n", __FILE__, __LINE__); /// FIXME die("creating context", maj_stat, init_sec_min_stat); /// FIXME die("parsing name", maj_stat, min_stat); }
if (produce_token (msg_, TOKEN_CONTEXT, send_tok.value, send_tok.length) < 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_buffer(&min_stat, &send_tok);
gss_release_name(&min_stat, &target_name); gss_release_name(&min_stat, &target_name);
return -1; return -1;
} }
} }
else
fprintf(stderr, "%s:%d: skip producing token\n", __FILE__, __LINE__); /// FIXME die("creating context", maj_stat, init_sec_min_stat); /// FIXME die("parsing name", maj_stat, min_stat);
gss_release_buffer(&min_stat, &send_tok); gss_release_buffer(&min_stat, &send_tok);
if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
fprintf(stderr, "%s:%d: failed to create GSSAPI security context\n", __FILE__, __LINE__); /// FIXME die("creating context", maj_stat, init_sec_min_stat);
gss_release_name(&min_stat, &target_name); gss_release_name(&min_stat, &target_name);
if (context != GSS_C_NO_CONTEXT) if (context != GSS_C_NO_CONTEXT)
gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
...@@ -172,18 +170,12 @@ int zmq::gssapi_client_t::produce_next_token (msg_t *msg_) ...@@ -172,18 +170,12 @@ int zmq::gssapi_client_t::produce_next_token (msg_t *msg_)
int zmq::gssapi_client_t::process_next_token (msg_t *msg_) int zmq::gssapi_client_t::process_next_token (msg_t *msg_)
{ {
if (maj_stat == GSS_S_CONTINUE_NEEDED) { if (maj_stat == GSS_S_CONTINUE_NEEDED) {
fprintf(stderr, "%s:%d: processing token\n", __FILE__, __LINE__); /// FIXME die("creating context", maj_stat, init_sec_min_stat); /// FIXME die("parsing name", maj_stat, min_stat); if (process_initiate(msg_, &recv_tok.value, recv_tok.length) < 0) {
if (process_token(msg_, token_flags, &recv_tok.value, recv_tok.length) < 0) {
gss_release_name(&min_stat, &target_name); gss_release_name(&min_stat, &target_name);
return -1; return -1;
} }
token_ptr = &recv_tok; token_ptr = &recv_tok;
} }
else
fprintf(stderr, "%s:%d: skip processing token\n", __FILE__, __LINE__); /// FIXME die("creating context", maj_stat, init_sec_min_stat); /// FIXME die("parsing name", maj_stat, min_stat);
if (maj_stat == GSS_S_COMPLETE)
security_context_established = true;
return 0; return 0;
} }
......
...@@ -48,17 +48,26 @@ namespace zmq ...@@ -48,17 +48,26 @@ namespace zmq
private: private:
enum state_t { enum state_t {
call_next_init,
send_next_token, send_next_token,
recv_next_token, recv_next_token,
almost_ready, connected
ready
}; };
// Current FSM state
state_t state; state_t state;
// Points to either send_tok or recv_tok
// during context initialization
gss_buffer_desc *token_ptr; gss_buffer_desc *token_ptr;
// The desired underlying mechanism
gss_OID_set_desc mechs; gss_OID_set_desc mechs;
// True iff client considers the server authenticated
bool security_context_established; bool security_context_established;
int initialize_context ();
int produce_next_token (msg_t *msg_); int produce_next_token (msg_t *msg_);
int process_next_token (msg_t *msg_); int process_next_token (msg_t *msg_);
}; };
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
zmq::gssapi_mechanism_base_t::gssapi_mechanism_base_t () : zmq::gssapi_mechanism_base_t::gssapi_mechanism_base_t () :
send_tok (), send_tok (),
recv_tok (), recv_tok (),
in_buf (), /// FIXME remove? in_buf (),
target_name (GSS_C_NO_NAME), target_name (GSS_C_NO_NAME),
service_name (NULL), service_name (NULL),
maj_stat (GSS_S_COMPLETE), maj_stat (GSS_S_COMPLETE),
...@@ -42,7 +42,6 @@ zmq::gssapi_mechanism_base_t::gssapi_mechanism_base_t () : ...@@ -42,7 +42,6 @@ zmq::gssapi_mechanism_base_t::gssapi_mechanism_base_t () :
init_sec_min_stat (0), init_sec_min_stat (0),
ret_flags (0), ret_flags (0),
gss_flags (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG), gss_flags (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG),
token_flags (0),
cred (GSS_C_NO_CREDENTIAL), cred (GSS_C_NO_CREDENTIAL),
context (GSS_C_NO_CONTEXT) context (GSS_C_NO_CONTEXT)
{ {
...@@ -56,87 +55,106 @@ zmq::gssapi_mechanism_base_t::~gssapi_mechanism_base_t () ...@@ -56,87 +55,106 @@ zmq::gssapi_mechanism_base_t::~gssapi_mechanism_base_t ()
gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
} }
int zmq::gssapi_mechanism_base_t::produce_token (msg_t *msg_, int flags_, void *token_value_, size_t token_length_) int zmq::gssapi_mechanism_base_t::encode_message (msg_t *msg_)
{ {
zmq_assert (token_value_); // Wrap the token value
zmq_assert (token_length_ <= 0xFFFFFFFFUL); int state;
gss_buffer_desc plaintext;
const size_t cmd_len = 6 + 1 + 4 + token_length_; gss_buffer_desc wrapped;
uint8_t *cmd_buf = static_cast <uint8_t *> (malloc (cmd_len)); plaintext.value = msg_->data ();
alloc_assert (cmd_buf); plaintext.length = msg_->size ();
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);
uint8_t *ptr = cmd_buf; // Re-initialize msg_ for wrapped text
int rc = msg_->close ();
zmq_assert (rc == 0);
// Add command name rc = msg_->init_size (8 + 4 + wrapped.length);
memcpy (ptr, "\x05TOKEN", 6); zmq_assert (rc == 0);
ptr += 6;
// Add gss flags uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
put_uint8 (ptr, static_cast <uint8_t> (flags_));
ptr += 1; // Add command string
memcpy (ptr, "\x07MESSAGE", 8);
ptr += 8;
// Add token length // Add token length
put_uint32 (ptr, static_cast <uint32_t> (token_length_)); put_uint32 (ptr, static_cast <uint32_t> (wrapped.length));
ptr += 4; ptr += 4;
// Add token value // Add wrapped token value
memcpy (ptr, token_value_, token_length_); memcpy (ptr, wrapped.value, wrapped.length);
ptr += token_length_; ptr += wrapped.length;
const int rc = msg_->init_size (cmd_len); gss_release_buffer (&min_stat, &wrapped);
errno_assert (rc == 0);
memcpy (msg_->data (), cmd_buf, cmd_len);
free (cmd_buf);
return 0; return 0;
} }
int zmq::gssapi_mechanism_base_t::process_token (msg_t *msg_, int &flags_, void **token_value_, size_t &token_length_) int zmq::gssapi_mechanism_base_t::decode_message (msg_t *msg_)
{ {
zmq_assert (token_value_); const uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
size_t bytes_left = msg_->size (); size_t bytes_left = msg_->size ();
// Get command name // Get command string
if (bytes_left < 6 || memcmp (ptr, "\x05TOKEN", 6)) { if (bytes_left < 8 || memcmp (ptr, "\x07MESSAGE", 8)) {
errno = EPROTO;
return -1;
}
ptr += 6;
bytes_left -= 6;
// Get flags
if (bytes_left < 1) {
errno = EPROTO; errno = EPROTO;
return -1; return -1;
} }
flags_ = static_cast <int> (get_uint8 (ptr)); ptr += 8;
ptr += 1; bytes_left -= 8;
bytes_left -= 1;
// Get token length // Get token length
if (bytes_left < 4) { if (bytes_left < 4) {
errno = EPROTO; errno = EPROTO;
return -1; return -1;
} }
token_length_ = get_uint32 (ptr); gss_buffer_desc wrapped;
wrapped.length = get_uint32 (ptr);
ptr += 4; ptr += 4;
bytes_left -= 4; bytes_left -= 4;
// Get token value. TODO do unwrap here to prevent this extra memcpy. // Get token value
if (bytes_left < token_length_) { if (bytes_left < wrapped.length) {
errno = EPROTO; errno = EPROTO;
return -1; return -1;
} }
*token_value_ = static_cast <char *> (malloc (token_length_ ? token_length_ : 1)); // TODO: instead of malloc/memcpy, can we just do: wrapped.value = ptr;
if (token_length_) { const size_t alloc_length = wrapped.length? wrapped.length: 1;
alloc_assert (*token_value_); wrapped.value = static_cast <char *> (malloc (alloc_length));
memcpy(*token_value_, ptr, token_length_); if (wrapped.length) {
ptr += token_length_; alloc_assert (wrapped.value);
bytes_left -= token_length_; 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);
zmq_assert (rc == 0);
memcpy (msg_->data (), plaintext.value, plaintext.length);
gss_release_buffer (&min_stat, &plaintext);
gss_release_buffer (&min_stat, &wrapped);
if (bytes_left > 0) { if (bytes_left > 0) {
errno = EPROTO; errno = EPROTO;
return -1; return -1;
...@@ -145,69 +163,75 @@ int zmq::gssapi_mechanism_base_t::process_token (msg_t *msg_, int &flags_, void ...@@ -145,69 +163,75 @@ int zmq::gssapi_mechanism_base_t::process_token (msg_t *msg_, int &flags_, void
return 0; return 0;
} }
/// TODO add support for TOKEN_SEND_MIC int zmq::gssapi_mechanism_base_t::produce_initiate (msg_t *msg_, void *token_value_, size_t token_length_)
/// TODO use gss_wrap_size_limit
int
zmq::gssapi_mechanism_base_t::produce_message (msg_t *msg_)
{ {
// wrap it zmq_assert (token_value_);
int state; zmq_assert (token_length_ <= 0xFFFFFFFFUL);
gss_buffer_desc plaintext;
gss_buffer_desc wrapped;
plaintext.value = msg_->data ();
plaintext.length = msg_->size ();
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);
// prepare msg_ for wrapped text const size_t command_size = 9 + 4 + token_length_;
int rc = msg_->close ();
zmq_assert (rc == 0);
// produce token const int rc = msg_->init_size (command_size);
const int flags = (TOKEN_DATA | TOKEN_WRAPPED | TOKEN_ENCRYPTED); errno_assert (rc == 0);
rc = produce_token (msg_, flags, wrapped.value, wrapped.length);
zmq_assert (rc == 0); uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
gss_release_buffer (&min_stat, &wrapped);
// 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; return 0;
} }
int int zmq::gssapi_mechanism_base_t::process_initiate (msg_t *msg_, void **token_value_, size_t &token_length_)
zmq::gssapi_mechanism_base_t::process_message (msg_t *msg_)
{ {
// process token zmq_assert (token_value_);
int flags;
gss_buffer_desc wrapped; const uint8_t *ptr = static_cast <uint8_t *> (msg_->data ());
int rc = process_token(msg_, flags, &wrapped.value, wrapped.length); size_t bytes_left = msg_->size ();
zmq_assert (rc == 0);
// ensure valid security context
zmq_assert (context != GSS_C_NO_CONTEXT);
zmq_assert (flags & TOKEN_WRAPPED);
zmq_assert (flags & TOKEN_ENCRYPTED);
// unwrap
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); // Get command string
zmq_assert(state); if (bytes_left < 9 || memcmp (ptr, "\x08INITIATE", 9)) {
errno = EPROTO;
return -1;
}
ptr += 9;
bytes_left -= 9;
// re-init msg_ with plaintext // Get token length
rc = msg_->close (); if (bytes_left < 4) {
zmq_assert (rc == 0); errno = EPROTO;
msg_->init_size (plaintext.length); return -1;
zmq_assert (rc == 0); }
token_length_ = get_uint32 (ptr);
ptr += 4;
bytes_left -= 4;
memcpy (msg_->data (), plaintext.value, plaintext.length); // Get token value
gss_release_buffer (&min_stat, &plaintext); if (bytes_left < token_length_) {
gss_release_buffer (&min_stat, &wrapped); 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; return 0;
} }
......
...@@ -29,8 +29,9 @@ namespace zmq ...@@ -29,8 +29,9 @@ namespace zmq
class msg_t; class msg_t;
/// Both gssapi_server and gssapi_client need to produce and process /// Commonalities between clients and servers are captured here.
/// GSSAPI tokens. Common implementation is captured here. /// For example, clients and server both need to produce and
/// process INITIATE and MESSAGE commands.
class gssapi_mechanism_base_t class gssapi_mechanism_base_t
{ {
public: public:
...@@ -38,42 +39,55 @@ namespace zmq ...@@ -38,42 +39,55 @@ namespace zmq
virtual ~gssapi_mechanism_base_t () = 0; virtual ~gssapi_mechanism_base_t () = 0;
protected: protected:
/// Produce a security context initialization token /// Produce an INITIATE during security context initialization
int produce_token (msg_t *msg_, int flags_, void *token_value_, size_t token_length_); int produce_initiate (msg_t *msg_, void *data_, size_t data_len_);
/// Process a security context initialization token /// Process an INITIATE during security context initialization
int process_token (msg_t *msg_, int &flags_, void **token_value_, size_t &token_length_); int process_initiate (msg_t *msg_, void **data_, size_t &data_len_);
/// Produce a wrapped message using the established security context /// Encode a MESSAGE using the established security context
int produce_message (msg_t *msg_); int encode_message (msg_t *msg_);
/// Process a wrapped message using the established security context /// Decode a MESSAGE using the established security context
int process_message (msg_t *msg_); int decode_message (msg_t *msg_);
/// Acquire security context credentials /// Acquire security context credentials
static int acquire_credentials (char * service_name_, gss_cred_id_t * cred_); static int acquire_credentials (char * service_name_,
gss_cred_id_t * cred_);
protected: protected:
const static int TOKEN_NOOP = (1<<0); /// Opaque GSSAPI token for outgoing data
const static int TOKEN_CONTEXT = (1<<1);
const static int TOKEN_DATA = (1<<2);
const static int TOKEN_MIC = (1<<3);
const static int TOKEN_CONTEXT_NEXT = (1<<4);
const static int TOKEN_WRAPPED = (1<<5);
const static int TOKEN_ENCRYPTED = (1<<6);
const static int TOKEN_SEND_MIC = (1<<7);
gss_buffer_desc send_tok; gss_buffer_desc send_tok;
/// Opaque GSSAPI token for incoming data
gss_buffer_desc recv_tok; gss_buffer_desc recv_tok;
gss_buffer_desc in_buf;
/// Opaque GSSAPI representation of service_name
gss_name_t target_name; gss_name_t target_name;
/// Human-readable service principal name
char * service_name; char * service_name;
/// Status code returned by GSSAPI functions
OM_uint32 maj_stat; OM_uint32 maj_stat;
/// Status code returned by the underlying mechanism
OM_uint32 min_stat; OM_uint32 min_stat;
/// Status code returned by the underlying mechanism
/// during context initialization
OM_uint32 init_sec_min_stat; OM_uint32 init_sec_min_stat;
/// Flags returned by GSSAPI (ignored)
OM_uint32 ret_flags; OM_uint32 ret_flags;
/// Flags returned by GSSAPI (ignored)
OM_uint32 gss_flags; OM_uint32 gss_flags;
int token_flags;
/// Credentials used to establish security context
gss_cred_id_t cred; gss_cred_id_t cred;
/// Opaque GSSAPI representation of the security context
gss_ctx_id_t context; gss_ctx_id_t context;
}; };
} }
#endif #endif
...@@ -41,16 +41,12 @@ zmq::gssapi_server_t::gssapi_server_t (session_base_t *session_, ...@@ -41,16 +41,12 @@ zmq::gssapi_server_t::gssapi_server_t (session_base_t *session_,
state (recv_next_token), state (recv_next_token),
security_context_established (false) security_context_established (false)
{ {
service_name = (char *) "host"; /// FIXME add service_name to options service_name = (char *) "host"; // TODO: add service_name to options
int rc = acquire_credentials (service_name, &cred); /// FIXME add creds to options too? int rc = acquire_credentials (service_name, &cred); // TODO: add creds to options too?
if (rc == 0) { if (rc == 0)
fprintf(stderr, "%s:%d: acquire creds successful\n", __FILE__, __LINE__); /// FIXME remove
maj_stat = GSS_S_CONTINUE_NEEDED; maj_stat = GSS_S_CONTINUE_NEEDED;
} else
else {
fprintf(stderr, "%s:%d: acquire creds failed\n", __FILE__, __LINE__); /// FIXME remove
maj_stat = GSS_S_FAILURE; maj_stat = GSS_S_FAILURE;
}
} }
zmq::gssapi_server_t::~gssapi_server_t () zmq::gssapi_server_t::~gssapi_server_t ()
...@@ -61,64 +57,58 @@ zmq::gssapi_server_t::~gssapi_server_t () ...@@ -61,64 +57,58 @@ zmq::gssapi_server_t::~gssapi_server_t ()
int zmq::gssapi_server_t::next_handshake_command (msg_t *msg_) int zmq::gssapi_server_t::next_handshake_command (msg_t *msg_)
{ {
int rc = 0; if (state != send_next_token) {
errno = EAGAIN;
switch (state) { return -1;
case send_next_token: }
rc = produce_next_token (msg_);
if (rc == 0) if (produce_next_token (msg_) < 0)
state = security_context_established? almost_ready: recv_next_token; return -1;
break;
case almost_ready: if (maj_stat != GSS_S_CONTINUE_NEEDED && maj_stat != GSS_S_COMPLETE)
state = ready; return -1;
break;
default: if (maj_stat == GSS_S_COMPLETE) {
errno = EPROTO; gss_release_name(&min_stat, &target_name);
rc = -1; security_context_established = true;
break; state = connected;
}
else {
state = recv_next_token;
} }
return rc;
return 0;
} }
int zmq::gssapi_server_t::process_handshake_command (msg_t *msg_) int zmq::gssapi_server_t::process_handshake_command (msg_t *msg_)
{ {
int rc = 0; if (state != recv_next_token) {
errno = EPROTO;
switch (state) { return -1;
case recv_next_token:
rc = process_next_token (msg_);
if (rc == 0)
state = send_next_token;
break;
case almost_ready:
state = ready;
break;
default:
errno = EAGAIN;
rc = -1;
break;
}
if (rc == 0) {
rc = msg_->close ();
errno_assert (rc == 0);
rc = msg_->init ();
errno_assert (rc == 0);
} }
return rc;
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;
} }
int zmq::gssapi_server_t::encode (msg_t *msg_) int zmq::gssapi_server_t::encode (msg_t *msg_)
{ {
int rc = 0; zmq_assert (state == connected);
zmq_assert (state == ready); return encode_message (msg_);
return rc;
} }
int zmq::gssapi_server_t::decode (msg_t *msg_) int zmq::gssapi_server_t::decode (msg_t *msg_)
{ {
int rc = 0; zmq_assert (state == connected);
zmq_assert (state == ready); return decode_message (msg_);
return rc;
} }
int zmq::gssapi_server_t::zap_msg_available () int zmq::gssapi_server_t::zap_msg_available ()
...@@ -128,53 +118,42 @@ int zmq::gssapi_server_t::zap_msg_available () ...@@ -128,53 +118,42 @@ int zmq::gssapi_server_t::zap_msg_available ()
bool zmq::gssapi_server_t::is_handshake_complete () const bool zmq::gssapi_server_t::is_handshake_complete () const
{ {
fprintf(stderr, "%s:%d: is_handshake_complete=%d\n", __FILE__, __LINE__, (state==ready)); /// FIXME remove return state == connected;
return state == ready;
} }
int zmq::gssapi_server_t::produce_next_token (msg_t *msg_) int zmq::gssapi_server_t::produce_next_token (msg_t *msg_)
{ {
if (send_tok.length != 0) { // client expects another token if (send_tok.length != 0) { // Client expects another token
fprintf(stderr, "%s:%d: producing token\n", __FILE__, __LINE__); /// FIXME die("creating context", maj_stat, init_sec_min_stat); /// FIXME die("creating context", maj_stat, init_sec_min_stat); if (produce_initiate(msg_, send_tok.value, send_tok.length) < 0)
if (produce_token(msg_, TOKEN_CONTEXT, send_tok.value, send_tok.length) < 0) {
fprintf(stderr, "%s:%d: failed to produce token!\n", __FILE__, __LINE__); /// FIXME die("creating context", maj_stat, init_sec_min_stat); /// FIXME die("creating context", maj_stat, init_sec_min_stat);
return -1; return -1;
}
gss_release_buffer(&min_stat, &send_tok); gss_release_buffer(&min_stat, &send_tok);
} }
else
fprintf(stderr, "%s:%d: skip producing token\n", __FILE__, __LINE__); /// FIXME die("creating context", maj_stat, init_sec_min_stat); /// FIXME die("creating context", maj_stat, init_sec_min_stat);
if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
fprintf(stderr, "%s:%d: failed to create GSSAPI security context\n", __FILE__, __LINE__); /// FIXME die("creating context", maj_stat, init_sec_min_stat); /// FIXME die("creating context", maj_stat, init_sec_min_stat);
gss_release_name(&min_stat, &target_name); gss_release_name(&min_stat, &target_name);
if (context != GSS_C_NO_CONTEXT) if (context != GSS_C_NO_CONTEXT)
gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
return -1; return -1;
} }
if (maj_stat == GSS_S_COMPLETE) {
gss_release_name(&min_stat, &target_name);
security_context_established = true;
}
return 0; return 0;
} }
int zmq::gssapi_server_t::process_next_token (msg_t *msg_) int zmq::gssapi_server_t::process_next_token (msg_t *msg_)
{ {
if (maj_stat == GSS_S_CONTINUE_NEEDED) { if (maj_stat == GSS_S_CONTINUE_NEEDED) {
fprintf(stderr, "%s:%d: processing token\n", __FILE__, __LINE__); /// FIXME die("creating context", maj_stat, init_sec_min_stat); /// FIXME die("creating context", maj_stat, init_sec_min_stat); if (process_initiate(msg_, &recv_tok.value, recv_tok.length) < 0) {
if (process_token(msg_, token_flags, &recv_tok.value, recv_tok.length) < 0) {
if (target_name != GSS_C_NO_NAME) if (target_name != GSS_C_NO_NAME)
gss_release_name(&min_stat, &target_name); gss_release_name(&min_stat, &target_name);
fprintf(stderr, "%s:%d: failed to process token!\n", __FILE__, __LINE__); /// FIXME die("creating context", maj_stat, init_sec_min_stat); /// FIXME die("creating context", maj_stat, init_sec_min_stat);
return -1; return -1;
} }
} }
else
fprintf(stderr, "%s:%d: skip processing token\n", __FILE__, __LINE__); /// FIXME die("creating context", maj_stat, init_sec_min_stat); /// FIXME die("creating context", maj_stat, init_sec_min_stat); return 0;
}
void zmq::gssapi_server_t::accept_context ()
{
maj_stat = gss_accept_sec_context(&init_sec_min_stat, &context, cred, maj_stat = gss_accept_sec_context(&init_sec_min_stat, &context, cred,
&recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS,
&target_name, &doid, &send_tok, &target_name, &doid, &send_tok,
...@@ -184,7 +163,5 @@ int zmq::gssapi_server_t::process_next_token (msg_t *msg_) ...@@ -184,7 +163,5 @@ int zmq::gssapi_server_t::process_next_token (msg_t *msg_)
free (recv_tok.value); free (recv_tok.value);
recv_tok.value = NULL; recv_tok.value = NULL;
} }
return 0;
} }
...@@ -54,19 +54,25 @@ namespace zmq ...@@ -54,19 +54,25 @@ namespace zmq
enum state_t { enum state_t {
send_next_token, send_next_token,
recv_next_token, recv_next_token,
waiting_for_zap_reply, connected
almost_ready,
ready
}; };
session_base_t * const session; session_base_t * const session;
const std::string peer_address; const std::string peer_address;
// Current FSM state
state_t state; state_t state;
// True iff server considers the client authenticated
bool security_context_established; bool security_context_established;
// The underlying mechanism type (ignored)
gss_OID doid; gss_OID doid;
int produce_next_token(msg_t *msg_); void accept_context ();
int process_next_token(msg_t *msg_); int produce_next_token (msg_t *msg_);
int process_next_token (msg_t *msg_);
}; };
} }
......
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