void IMAPConnection::authenticateSASL() { if (!dynamicCast <security::sasl::SASLAuthenticator>(getAuthenticator())) throw exceptions::authentication_error("No SASL authenticator available."); const std::vector <string> capa = getCapabilities(); std::vector <string> saslMechs; for (unsigned int i = 0 ; i < capa.size() ; ++i) { const string& x = capa[i]; if (x.length() > 5 && (x[0] == 'A' || x[0] == 'a') && (x[1] == 'U' || x[1] == 'u') && (x[2] == 'T' || x[2] == 't') && (x[3] == 'H' || x[3] == 'h') && x[4] == '=') { saslMechs.push_back(string(x.begin() + 5, x.end())); } } if (saslMechs.empty()) throw exceptions::authentication_error("No SASL mechanism available."); std::vector <shared_ptr <security::sasl::SASLMechanism> > mechList; shared_ptr <security::sasl::SASLContext> saslContext = make_shared <security::sasl::SASLContext>(); for (unsigned int i = 0 ; i < saslMechs.size() ; ++i) { try { mechList.push_back (saslContext->createMechanism(saslMechs[i])); } catch (exceptions::no_such_mechanism&) { // Ignore mechanism } } if (mechList.empty()) throw exceptions::authentication_error("No SASL mechanism available."); // Try to suggest a mechanism among all those supported shared_ptr <security::sasl::SASLMechanism> suggestedMech = saslContext->suggestMechanism(mechList); if (!suggestedMech) throw exceptions::authentication_error("Unable to suggest SASL mechanism."); // Allow application to choose which mechanisms to use mechList = dynamicCast <security::sasl::SASLAuthenticator>(getAuthenticator())-> getAcceptableMechanisms(mechList, suggestedMech); if (mechList.empty()) throw exceptions::authentication_error("No SASL mechanism available."); // Try each mechanism in the list in turn for (unsigned int i = 0 ; i < mechList.size() ; ++i) { shared_ptr <security::sasl::SASLMechanism> mech = mechList[i]; shared_ptr <security::sasl::SASLSession> saslSession = saslContext->createSession("imap", getAuthenticator(), mech); saslSession->init(); shared_ptr <IMAPCommand> authCmd; if (saslSession->getMechanism()->hasInitialResponse()) { byte_t* initialResp = 0; size_t initialRespLen = 0; saslSession->evaluateChallenge(NULL, 0, &initialResp, &initialRespLen); string encodedInitialResp(saslContext->encodeB64(initialResp, initialRespLen)); delete [] initialResp; if (encodedInitialResp.empty()) authCmd = IMAPCommand::AUTHENTICATE(mech->getName(), "="); else authCmd = IMAPCommand::AUTHENTICATE(mech->getName(), encodedInitialResp); } else { authCmd = IMAPCommand::AUTHENTICATE(mech->getName()); } authCmd->send(dynamicCast <IMAPConnection>(shared_from_this())); for (bool cont = true ; cont ; ) { std::auto_ptr <IMAPParser::response> resp(m_parser->readResponse()); if (resp->response_done() && resp->response_done()->response_tagged() && resp->response_done()->response_tagged()->resp_cond_state()-> status() == IMAPParser::resp_cond_state::OK) { m_socket = saslSession->getSecuredSocket(m_socket); return; } else { std::vector <IMAPParser::continue_req_or_response_data*> respDataList = resp->continue_req_or_response_data(); string response; bool hasResponse = false; for (unsigned int i = 0 ; i < respDataList.size() ; ++i) { if (respDataList[i]->continue_req()) { response = respDataList[i]->continue_req()->resp_text()->text(); hasResponse = true; break; } } if (!hasResponse) { cont = false; continue; } byte_t* challenge = 0; size_t challengeLen = 0; byte_t* resp = 0; size_t respLen = 0; try { // Extract challenge saslContext->decodeB64(response, &challenge, &challengeLen); // Prepare response saslSession->evaluateChallenge (challenge, challengeLen, &resp, &respLen); // Send response const string respB64 = saslContext->encodeB64(resp, respLen) + "\r\n"; sendRaw(utility::stringUtils::bytesFromString(respB64), respB64.length()); if (m_tracer) m_tracer->traceSendBytes(respB64.length() - 2, "SASL exchange"); // Server capabilities may change when logged in invalidateCapabilities(); } catch (exceptions::sasl_exception& e) { if (challenge) { delete [] challenge; challenge = NULL; } if (resp) { delete [] resp; resp = NULL; } // Cancel SASL exchange sendRaw(utility::stringUtils::bytesFromString("*\r\n"), 3); if (m_tracer) m_tracer->traceSend("*"); } catch (...) { if (challenge) delete [] challenge; if (resp) delete [] resp; throw; } if (challenge) delete [] challenge; if (resp) delete [] resp; } } } throw exceptions::authentication_error ("Could not authenticate using SASL: all mechanisms failed."); }
void SMTPConnection::authenticateSASL() { if (!dynamicCast <security::sasl::SASLAuthenticator>(getAuthenticator())) throw exceptions::authentication_error("No SASL authenticator available."); // Obtain SASL mechanisms supported by server from ESMTP extensions std::vector <string> saslMechs; hasExtension("AUTH", &saslMechs); if (saslMechs.empty()) throw exceptions::authentication_error("No SASL mechanism available."); std::vector <shared_ptr <security::sasl::SASLMechanism> > mechList; shared_ptr <security::sasl::SASLContext> saslContext = make_shared <security::sasl::SASLContext>(); for (unsigned int i = 0 ; i < saslMechs.size() ; ++i) { try { mechList.push_back (saslContext->createMechanism(saslMechs[i])); } catch (const exceptions::no_such_mechanism&) { // Ignore mechanism } } if (mechList.empty()) throw exceptions::authentication_error("No SASL mechanism available."); // Try to suggest a mechanism among all those supported shared_ptr <security::sasl::SASLMechanism> suggestedMech = saslContext->suggestMechanism(mechList); if (!suggestedMech) throw exceptions::authentication_error("Unable to suggest SASL mechanism."); // Allow application to choose which mechanisms to use mechList = dynamicCast <security::sasl::SASLAuthenticator>(getAuthenticator())-> getAcceptableMechanisms(mechList, suggestedMech); if (mechList.empty()) throw exceptions::authentication_error("No SASL mechanism available."); // Try each mechanism in the list in turn for (unsigned int i = 0 ; i < mechList.size() ; ++i) { shared_ptr <security::sasl::SASLMechanism> mech = mechList[i]; shared_ptr <security::sasl::SASLSession> saslSession = saslContext->createSession("smtp", getAuthenticator(), mech); saslSession->init(); if (saslSession->getMechanism()->hasInitialResponse()) { byte_t* initialResp = 0; size_t initialRespLen = 0; saslSession->evaluateChallenge(NULL, 0, &initialResp, &initialRespLen); string encodedInitialResp(saslContext->encodeB64(initialResp, initialRespLen)); delete [] initialResp; if (encodedInitialResp.empty()) sendRequest(SMTPCommand::AUTH(mech->getName(), "=")); else sendRequest(SMTPCommand::AUTH(mech->getName(), encodedInitialResp)); } else { sendRequest(SMTPCommand::AUTH(mech->getName())); } for (bool cont = true ; cont ; ) { shared_ptr <SMTPResponse> response = readResponse(); switch (response->getCode()) { case 235: { m_socket = saslSession->getSecuredSocket(m_socket); return; } case 334: { byte_t* challenge = nullptr; size_t challengeLen = 0; byte_t* resp = nullptr; size_t respLen = 0; try { // Extract challenge saslContext->decodeB64(response->getText(), &challenge, &challengeLen); // Prepare response saslSession->evaluateChallenge (challenge, challengeLen, &resp, &respLen); // Send response const string respB64 = saslContext->encodeB64(resp, respLen) + "\r\n"; m_socket->sendRaw(utility::stringUtils::bytesFromString(respB64), respB64.length()); if (m_tracer) m_tracer->traceSendBytes(respB64.length() - 2, "SASL exchange"); } catch (const exceptions::sasl_exception& e) { delete [] challenge; challenge = nullptr; delete [] resp; resp = nullptr; // Cancel SASL exchange m_socket->send("*\r\n"); if (m_tracer) m_tracer->traceSend("*"); } catch (...) { delete [] challenge; delete [] resp; throw; } delete [] challenge; delete [] resp; break; } default: cont = false; break; } } } throw exceptions::authentication_error ("Could not authenticate using SASL: all mechanisms failed."); }