in src/saslclientio.c [646:936]
static void on_sasl_frame_received_callback(void* context, AMQP_VALUE sasl_frame)
{
SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)context;
/* Codes_SRS_SASLCLIENTIO_01_067: [The SASL frame exchange shall be started as soon as the SASL header handshake is done.] */
switch (sasl_client_io_instance->io_state)
{
default:
LogError("SASL frame received while in state %d", (int)sasl_client_io_instance->io_state);
break;
case IO_STATE_OPEN:
case IO_STATE_OPENING_UNDERLYING_IO:
case IO_STATE_CLOSING:
/* Codes_SRS_SASLCLIENTIO_01_117: [If `on_sasl_frame_received_callback` is called when the state of the IO is OPEN then the `on_io_error` callback shall be triggered.]*/
handle_error(sasl_client_io_instance);
break;
case IO_STATE_SASL_HANDSHAKE:
if (sasl_client_io_instance->sasl_header_exchange_state != SASL_HEADER_EXCHANGE_HEADER_EXCH)
{
/* Codes_SRS_SASLCLIENTIO_01_118: [If `on_sasl_frame_received_callback` is called in the OPENING state but the header exchange has not yet been completed, then the `on_io_error` callback shall be triggered.]*/
handle_error(sasl_client_io_instance);
}
else
{
AMQP_VALUE descriptor = amqpvalue_get_inplace_descriptor(sasl_frame);
if (descriptor == NULL)
{
/* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
LogError("Could not obtain SASL frame descriptor");
handle_error(sasl_client_io_instance);
}
else
{
if (sasl_client_io_instance->is_trace_on != 0)
{
log_incoming_frame(sasl_frame);
}
/* Codes_SRS_SASLCLIENTIO_01_032: [The peer acting as the SASL server MUST announce supported authentication mechanisms using the sasl-mechanisms frame.] */
/* Codes_SRS_SASLCLIENTIO_01_040: [The peer playing the role of the SASL client and the peer playing the role of the SASL server MUST correspond to the TCP client and server respectively.] */
/* Codes_SRS_SASLCLIENTIO_01_034: [<-- SASL-MECHANISMS] */
if (is_sasl_mechanisms_type_by_descriptor(descriptor))
{
switch (sasl_client_io_instance->sasl_client_negotiation_state)
{
default:
LogError("SASL mechanisms frame received in %" PRI_MU_ENUM " state", MU_ENUM_VALUE(SASL_CLIENT_NEGOTIATION_STATE, sasl_client_io_instance->sasl_client_negotiation_state));
handle_error(sasl_client_io_instance);
break;
case SASL_CLIENT_NEGOTIATION_NOT_STARTED:
{
SASL_MECHANISMS_HANDLE sasl_mechanisms_handle;
if (amqpvalue_get_sasl_mechanisms(sasl_frame, &sasl_mechanisms_handle) != 0)
{
/* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
LogError("Could not get SASL mechanisms");
handle_error(sasl_client_io_instance);
}
else
{
AMQP_VALUE sasl_server_mechanisms;
uint32_t mechanisms_count;
if ((sasl_mechanisms_get_sasl_server_mechanisms(sasl_mechanisms_handle, &sasl_server_mechanisms) != 0) ||
(amqpvalue_get_array_item_count(sasl_server_mechanisms, &mechanisms_count) != 0) ||
(mechanisms_count == 0))
{
/* Codes_SRS_SASLCLIENTIO_01_042: [It is invalid for this list to be null or empty.] */
LogError("Invalid SASL mechanisms list");
handle_error(sasl_client_io_instance);
}
else
{
const char* sasl_mechanism_name = saslmechanism_get_mechanism_name(sasl_client_io_instance->sasl_mechanism);
if (sasl_mechanism_name == NULL)
{
/* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
LogError("Cannot get the mechanism name");
handle_error(sasl_client_io_instance);
}
else
{
uint32_t i;
for (i = 0; i < mechanisms_count; i++)
{
AMQP_VALUE sasl_server_mechanism;
sasl_server_mechanism = amqpvalue_get_array_item(sasl_server_mechanisms, i);
if (sasl_server_mechanism == NULL)
{
LogError("Cannot get SASL mechanisms array item for index %u", (unsigned int)i);
i = mechanisms_count;
}
else
{
const char* sasl_server_mechanism_name;
if (amqpvalue_get_symbol(sasl_server_mechanism, &sasl_server_mechanism_name) != 0)
{
LogError("Error getting server SASL mechanism from array item");
i = mechanisms_count;
}
else
{
if (strcmp(sasl_mechanism_name, sasl_server_mechanism_name) == 0)
{
amqpvalue_destroy(sasl_server_mechanism);
break;
}
}
amqpvalue_destroy(sasl_server_mechanism);
}
}
if (i == mechanisms_count)
{
/* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
LogError("Could not find desired SASL mechanism in the list presented by server");
handle_error(sasl_client_io_instance);
}
else
{
sasl_client_io_instance->sasl_client_negotiation_state = SASL_CLIENT_NEGOTIATION_MECH_RCVD;
/* Codes_SRS_SASLCLIENTIO_01_035: [SASL-INIT -->] */
/* Codes_SRS_SASLCLIENTIO_01_033: [The partner MUST then choose one of the supported mechanisms and initiate a sasl exchange.] */
/* Codes_SRS_SASLCLIENTIO_01_054: [Selects the sasl mechanism and provides the initial response if needed.] */
if (send_sasl_init(sasl_client_io_instance, sasl_mechanism_name) != 0)
{
/* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
LogError("Could not send SASL init");
handle_error(sasl_client_io_instance);
}
else
{
sasl_client_io_instance->sasl_client_negotiation_state = SASL_CLIENT_NEGOTIATION_INIT_SENT;
}
}
}
}
sasl_mechanisms_destroy(sasl_mechanisms_handle);
}
break;
}
}
}
/* Codes_SRS_SASLCLIENTIO_01_052: [Send the SASL challenge data as defined by the SASL specification.] */
/* Codes_SRS_SASLCLIENTIO_01_036: [<-- SASL-CHALLENGE *] */
/* Codes_SRS_SASLCLIENTIO_01_039: [the SASL challenge/response step can occur zero or more times depending on the details of the SASL mechanism chosen.] */
else if (is_sasl_challenge_type_by_descriptor(descriptor))
{
/* Codes_SRS_SASLCLIENTIO_01_032: [The peer acting as the SASL server MUST announce supported authentication mechanisms using the sasl-mechanisms frame.] */
if ((sasl_client_io_instance->sasl_client_negotiation_state != SASL_CLIENT_NEGOTIATION_INIT_SENT) &&
(sasl_client_io_instance->sasl_client_negotiation_state != SASL_CLIENT_NEGOTIATION_RESPONSE_SENT))
{
LogError("SASL challenge received in a bad state: %" PRI_MU_ENUM "", MU_ENUM_VALUE(SASL_CLIENT_NEGOTIATION_STATE, sasl_client_io_instance->sasl_client_negotiation_state));
handle_error(sasl_client_io_instance);
}
else
{
SASL_CHALLENGE_HANDLE sasl_challenge_handle;
if (amqpvalue_get_sasl_challenge(sasl_frame, &sasl_challenge_handle) != 0)
{
/* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
LogError("Cannot get SASL challenge values");
handle_error(sasl_client_io_instance);
}
else
{
amqp_binary challenge_binary_value;
challenge_binary_value.bytes = NULL;
challenge_binary_value.length = 0;
/* Codes_SRS_SASLCLIENTIO_01_053: [Challenge information, a block of opaque binary data passed to the security mechanism.] */
if (sasl_challenge_get_challenge(sasl_challenge_handle, &challenge_binary_value) != 0)
{
/* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
LogError("Cannot get SASL challenge binary value");
handle_error(sasl_client_io_instance);
}
else
{
SASL_MECHANISM_BYTES challenge;
SASL_MECHANISM_BYTES response_bytes;
challenge.bytes = challenge_binary_value.bytes;
challenge.length = challenge_binary_value.length;
response_bytes.bytes = NULL;
response_bytes.length = 0;
/* Codes_SRS_SASLCLIENTIO_01_057: [The contents of this data are defined by the SASL security mechanism.] */
/* Codes_SRS_SASLCLIENTIO_01_037: [SASL-RESPONSE -->] */
if (saslmechanism_challenge(sasl_client_io_instance->sasl_mechanism, &challenge, &response_bytes) != 0)
{
/* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
LogError("SASL Challenge failed");
handle_error(sasl_client_io_instance);
}
else if (send_sasl_response(sasl_client_io_instance, response_bytes) != 0)
{
/* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
LogError("Cannot send SASL response");
handle_error(sasl_client_io_instance);
}
}
sasl_challenge_destroy(sasl_challenge_handle);
}
}
}
/* Codes_SRS_SASLCLIENTIO_01_058: [This frame indicates the outcome of the SASL dialog.] */
/* Codes_SRS_SASLCLIENTIO_01_038: [<-- SASL-OUTCOME] */
else if (is_sasl_outcome_type_by_descriptor(descriptor))
{
/* Codes_SRS_SASLCLIENTIO_01_032: [The peer acting as the SASL server MUST announce supported authentication mechanisms using the sasl-mechanisms frame.] */
if ((sasl_client_io_instance->sasl_client_negotiation_state != SASL_CLIENT_NEGOTIATION_INIT_SENT) &&
(sasl_client_io_instance->sasl_client_negotiation_state != SASL_CLIENT_NEGOTIATION_RESPONSE_SENT))
{
LogError("SASL outcome received in a bad state: %" PRI_MU_ENUM "", MU_ENUM_VALUE(SASL_CLIENT_NEGOTIATION_STATE, sasl_client_io_instance->sasl_client_negotiation_state));
handle_error(sasl_client_io_instance);
}
else
{
SASL_OUTCOME_HANDLE sasl_outcome;
sasl_client_io_instance->sasl_client_negotiation_state = SASL_CLIENT_NEGOTIATION_OUTCOME_RCVD;
if (amqpvalue_get_sasl_outcome(sasl_frame, &sasl_outcome) != 0)
{
LogError("Cannot get SASL outcome");
handle_error(sasl_client_io_instance);
}
else
{
sasl_code sasl_code;
/* Codes_SRS_SASLCLIENTIO_01_060: [A reply-code indicating the outcome of the SASL dialog.] */
if (sasl_outcome_get_code(sasl_outcome, &sasl_code) != 0)
{
LogError("Cannot get SASL outcome code");
handle_error(sasl_client_io_instance);
}
else
{
switch (sasl_code)
{
default:
case sasl_code_auth:
/* Codes_SRS_SASLCLIENTIO_01_063: [1 Connection authentication failed due to an unspecified problem with the supplied credentials.] */
case sasl_code_sys:
/* Codes_SRS_SASLCLIENTIO_01_064: [2 Connection authentication failed due to a system error.] */
case sasl_code_sys_perm:
/* Codes_SRS_SASLCLIENTIO_01_065: [3 Connection authentication failed due to a system error that is unlikely to be corrected without intervention.] */
case sasl_code_sys_temp:
/* Codes_SRS_SASLCLIENTIO_01_066: [4 Connection authentication failed due to a transient system error.] */
LogError("SASL handshake failed with code %02X", (unsigned char)sasl_code);
handle_error(sasl_client_io_instance);
break;
case sasl_code_ok:
/* Codes_SRS_SASLCLIENTIO_01_059: [Upon successful completion of the SASL dialog the security layer has been established] */
/* Codes_SRS_SASLCLIENTIO_01_062: [0 Connection authentication succeeded.] */
sasl_client_io_instance->io_state = IO_STATE_OPEN;
/* Codes_SRS_SASLCLIENTIO_01_072: [When the SASL handshake is complete, if the handshake is successful, the SASL client IO state shall be switched to `IO_STATE_OPEN` and the `on_io_open_complete` callback shall be called with `IO_OPEN_OK`.]*/
indicate_open_complete(sasl_client_io_instance, IO_OPEN_OK);
break;
}
}
sasl_outcome_destroy(sasl_outcome);
}
}
}
else
{
LogError("Bad SASL frame");
}
}
}
break;
}
}