void SMTPConnection::authenticate()
{
	if (!m_extendedSMTP)
	{
		internalDisconnect();
		throw exceptions::command_error("AUTH", "ESMTP not supported.");
	}

	getAuthenticator()->setService(m_transport.lock());

#if VMIME_HAVE_SASL_SUPPORT
	// Try SASL authentication
	if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL))
	{
		try
		{
			authenticateSASL();

			m_authenticated = true;
			return;
		}
		catch (const vmime::exception&)
		{
			internalDisconnect();
			throw;
		}
	}
#endif // VMIME_HAVE_SASL_SUPPORT

	// No other authentication method is possible
	throw exceptions::authentication_error("All authentication methods failed");
}
Beispiel #2
0
void POP3Store::connect()
{
	if (isConnected())
		throw exceptions::already_connected();

	m_connection = make_shared <POP3Connection>
		(dynamicCast <POP3Store>(shared_from_this()), getAuthenticator());

	m_connection->connect();
}
Beispiel #3
0
void IMAPStore::connect()
{
	if (isConnected())
		throw exceptions::already_connected();

	m_connection = vmime::create <IMAPConnection>
		(thisRef().dynamicCast <IMAPStore>(), getAuthenticator());

	try
	{
		m_connection->connect();
	}
	catch (std::exception&)
	{
		m_connection = NULL;
		throw;
	}
}
Beispiel #4
0
			}
		}
		catch (exception& e)
		{
			internalDisconnect();
			throw e;
		}
	}
#endif // VMIME_HAVE_SASL_SUPPORT

	// Secured authentication with APOP (if requested and if available)
	//
	// eg:  C: APOP vincent <digest>
	// ---  S: +OK vincent is a valid mailbox

	const string username = getAuthenticator()->getUsername();
	const string password = getAuthenticator()->getPassword();

	ref <POP3Connection> conn = thisRef().dynamicCast <POP3Connection>();
	ref <POP3Response> response;

	if (GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP))
	{
		if (randomMID.getLeft().length() != 0 &&
		    randomMID.getRight().length() != 0)
		{
			// <digest> is the result of MD5 applied to "<message-id>password"
			ref <security::digest::messageDigest> md5 =
				security::digest::messageDigestFactory::getInstance()->create("md5");

			md5->update(randomMID.generate() + password);
Beispiel #5
0
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.");
}