static void pni_process_server_result(pn_transport_t *transport, int result) { pni_sasl_t *sasl = transport->sasl; sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context; switch (result) { case SASL_OK: // Authenticated sasl->outcome = PN_SASL_OK; transport->authenticated = true; // Get username from SASL const void* value; sasl_getprop(cyrus_conn, SASL_USERNAME, &value); sasl->username = (const char*) value; if (transport->trace & PN_TRACE_DRV) pn_transport_logf(transport, "Authenticated user: %s with mechanism %s", sasl->username, sasl->selected_mechanism); pni_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME); break; case SASL_CONTINUE: // Need to send a challenge pni_sasl_set_desired_state(transport, SASL_POSTED_CHALLENGE); break; default: pni_check_sasl_result(cyrus_conn, result, transport); // Failed to authenticate sasl->outcome = PN_SASL_AUTH; pni_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME); break; } }
// This is a hack to tell us that // no actual negotiation is going to happen and we can go // straight to the AMQP layer; it can only work on the client side // As the server doesn't know if SASL is even active until it sees // the SASL header from the client first. static void pni_sasl_force_anonymous(pn_transport_t *transport) { pni_sasl_t *sasl = transport->sasl; if (sasl->client) { // Pretend we got sasl mechanisms frame with just ANONYMOUS if (pni_init_client(transport) && pni_process_mechanisms(transport, "ANONYMOUS")) { pni_sasl_set_desired_state(transport, SASL_PRETEND_OUTCOME); } else { sasl->outcome = PN_SASL_PERM; pni_sasl_set_desired_state(transport, SASL_RECVED_OUTCOME_FAIL); } } }
static void pni_sasl_start_server_if_needed(pn_transport_t *transport) { pni_sasl_t *sasl = transport->sasl; if (!sasl->client && sasl->desired_state<SASL_POSTED_MECHANISMS) { if (!pni_init_server(transport)) return; // Setup to send SASL mechanisms frame pni_sasl_set_desired_state(transport, SASL_POSTED_MECHANISMS); } }
void pni_process_challenge(pn_transport_t *transport, const pn_bytes_t *recv) { pni_sasl_t *sasl = transport->sasl; sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context; int result = pni_wrap_client_step(sasl, recv); switch (result) { case SASL_OK: // Authenticated // TODO: Documented that we need to call sasl_client_step() again to be sure!; case SASL_CONTINUE: // Need to send a response pni_sasl_set_desired_state(transport, SASL_POSTED_RESPONSE); break; default: pni_check_sasl_result(cyrus_conn, result, transport); // Failed somehow - equivalent to failing authentication sasl->outcome = PN_SASL_AUTH; pni_sasl_set_desired_state(transport, SASL_RECVED_OUTCOME); break; } }
// Received client side int pn_do_outcome(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) { uint8_t outcome; int err = pn_data_scan(args, "D.[B]", &outcome); if (err) return err; pni_sasl_t *sasl = transport->sasl; sasl->outcome = (pn_sasl_outcome_t) outcome; bool authenticated = sasl->outcome==PN_SASL_OK; transport->authenticated = authenticated; pni_sasl_set_desired_state(transport, authenticated ? SASL_RECVED_OUTCOME_SUCCEED : SASL_RECVED_OUTCOME_FAIL); return 0; }
// Received client side int pn_do_mechanisms(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) { pni_sasl_t *sasl = transport->sasl; // If we already pretended we got the ANONYMOUS mech then ignore if (sasl->last_state==SASL_PRETEND_OUTCOME) return 0; // This scanning relies on pn_data_scan leaving the pn_data_t cursors // where they are after finishing the scan int err = pn_data_scan(args, "D.[@["); if (err) return err; pn_string_t *mechs = pn_string(""); // Now keep checking for end of array and pull a symbol while(pn_data_next(args)) { pn_bytes_t s = pn_data_get_symbol(args); if (pni_included_mech(transport->sasl->included_mechanisms, s)) { pn_string_addf(mechs, "%*s ", (int)s.size, s.start); } } if (pn_string_size(mechs)) { pn_string_buffer(mechs)[pn_string_size(mechs)-1] = 0; } if (pni_init_client(transport) && pni_process_mechanisms(transport, pn_string_get(mechs))) { pni_sasl_set_desired_state(transport, SASL_POSTED_INIT); } else { sasl->outcome = PN_SASL_PERM; pni_sasl_set_desired_state(transport, SASL_RECVED_OUTCOME_FAIL); } pn_free(mechs); return 0; }
static void pn_error_sasl(pn_transport_t* transport) { transport->close_sent = true; pni_sasl_set_desired_state(transport, SASL_ERROR); }