diff options
Diffstat (limited to '')
-rw-r--r-- | src/net/imap/IMAPConnection.cpp | 312 | ||||
-rw-r--r-- | src/net/imap/IMAPFolder.cpp | 2 | ||||
-rw-r--r-- | src/net/imap/IMAPStore.cpp | 80 |
3 files changed, 312 insertions, 82 deletions
diff --git a/src/net/imap/IMAPConnection.cpp b/src/net/imap/IMAPConnection.cpp index 5191dc94..02b6c607 100644 --- a/src/net/imap/IMAPConnection.cpp +++ b/src/net/imap/IMAPConnection.cpp @@ -25,6 +25,10 @@ #include "vmime/exception.hpp" #include "vmime/platformDependant.hpp" +#if VMIME_HAVE_SASL_SUPPORT + #include "vmime/security/sasl/SASLContext.hpp" +#endif // VMIME_HAVE_SASL_SUPPORT + #include <sstream> @@ -42,7 +46,7 @@ namespace net { namespace imap { -IMAPConnection::IMAPConnection(weak_ref <IMAPStore> store, ref <authenticator> auth) +IMAPConnection::IMAPConnection(weak_ref <IMAPStore> store, ref <security::authenticator> auth) : m_store(store), m_auth(auth), m_socket(NULL), m_parser(NULL), m_tag(NULL), m_hierarchySeparator('\0'), m_state(STATE_NONE), m_timeoutHandler(NULL) { @@ -51,10 +55,17 @@ IMAPConnection::IMAPConnection(weak_ref <IMAPStore> store, ref <authenticator> a IMAPConnection::~IMAPConnection() { - if (isConnected()) - disconnect(); - else if (m_socket) - internalDisconnect(); + try + { + if (isConnected()) + disconnect(); + else if (m_socket) + internalDisconnect(); + } + catch (vmime::exception&) + { + // Ignore + } } @@ -107,33 +118,294 @@ void IMAPConnection::connect() } else if (greet->resp_cond_auth()->condition() != IMAPParser::resp_cond_auth::PREAUTH) { - const authenticationInfos auth = m_auth->requestAuthInfos(); + try + { + authenticate(); + } + catch (...) + { + m_state = STATE_NONE; + throw; + } + } + + // Get the hierarchy separator character + initHierarchySeparator(); - // TODO: other authentication methods + // Switch to state "Authenticated" + setState(STATE_AUTHENTICATED); +} - send(true, "LOGIN " + IMAPUtils::quoteString(auth.getUsername()) - + " " + IMAPUtils::quoteString(auth.getPassword()), true); - utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse()); +void IMAPConnection::authenticate() +{ + getAuthenticator()->setService(thisRef().dynamicCast <service>()); - if (resp->isBad()) +#if VMIME_HAVE_SASL_SUPPORT + // First, try SASL authentication + if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL)) + { + try { - internalDisconnect(); - throw exceptions::command_error("LOGIN", m_parser->lastLine()); + authenticateSASL(); + return; + } + catch (exceptions::authentication_error& e) + { + if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK)) + { + // Can't fallback on normal authentication + internalDisconnect(); + throw e; + } + else + { + // Ignore, will try normal authentication + } } - else if (resp->response_done()->response_tagged()-> - resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + catch (exception& e) { internalDisconnect(); - throw exceptions::authentication_error(m_parser->lastLine()); + throw e; } } +#endif // VMIME_HAVE_SASL_SUPPORT - // Get the hierarchy separator character - initHierarchySeparator(); + // Normal authentication + const string username = getAuthenticator()->getUsername(); + const string password = getAuthenticator()->getPassword(); - // Switch to state "Authenticated" - setState(STATE_AUTHENTICATED); + send(true, "LOGIN " + IMAPUtils::quoteString(username) + + " " + IMAPUtils::quoteString(password), true); + + utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse()); + + if (resp->isBad()) + { + internalDisconnect(); + throw exceptions::command_error("LOGIN", m_parser->lastLine()); + } + else if (resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + internalDisconnect(); + throw exceptions::authentication_error(m_parser->lastLine()); + } +} + + +#if VMIME_HAVE_SASL_SUPPORT + +void IMAPConnection::authenticateSASL() +{ + if (!getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>()) + 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 <ref <security::sasl::SASLMechanism> > mechList; + + ref <security::sasl::SASLContext> saslContext = + vmime::create <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 + ref <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 = getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>()-> + 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) + { + ref <security::sasl::SASLMechanism> mech = mechList[i]; + + ref <security::sasl::SASLSession> saslSession = + saslContext->createSession("imap", getAuthenticator(), mech); + + saslSession->init(); + + send(true, "AUTHENTICATE " + mech->getName(), true); + + for (bool cont = true ; cont ; ) + { + utility::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; + + for (unsigned int i = 0 ; i < respDataList.size() ; ++i) + { + if (respDataList[i]->continue_req()) + { + response = respDataList[i]->continue_req()->resp_text()->text(); + break; + } + } + + if (response.empty()) + { + cont = false; + continue; + } + + byte* challenge = 0; + int challengeLen = 0; + + byte* resp = 0; + int respLen = 0; + + try + { + // Extract challenge + saslContext->decodeB64(response, &challenge, &challengeLen); + + // Prepare response + saslSession->evaluateChallenge + (challenge, challengeLen, &resp, &respLen); + + // Send response + send(false, saslContext->encodeB64(resp, respLen), true); + } + catch (exceptions::sasl_exception& e) + { + if (challenge) + { + delete [] challenge; + challenge = NULL; + } + + if (resp) + { + delete [] resp; + resp = NULL; + } + + // Cancel SASL exchange + send(false, "*", true); + } + 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."); +} + +#endif // VMIME_HAVE_SASL_SUPPORT + + +const std::vector <string> IMAPConnection::getCapabilities() +{ + send(true, "CAPABILITY", true); + + utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse()); + + std::vector <string> res; + + if (resp->response_done()->response_tagged()-> + resp_cond_state()->status() == IMAPParser::resp_cond_state::OK) + { + const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList = + resp->continue_req_or_response_data(); + + for (unsigned int i = 0 ; i < respDataList.size() ; ++i) + { + if (respDataList[i]->response_data() == NULL) + continue; + + const IMAPParser::capability_data* capaData = + respDataList[i]->response_data()->capability_data(); + + std::vector <IMAPParser::capability*> caps = capaData->capabilities(); + + for (unsigned int j = 0 ; j < caps.size() ; ++j) + { + if (caps[j]->auth_type()) + res.push_back("AUTH=" + caps[j]->auth_type()->name()); + else + res.push_back(caps[j]->atom()->value()); + } + } + } + + return res; +} + + +ref <security::authenticator> IMAPConnection::getAuthenticator() +{ + return m_auth; } diff --git a/src/net/imap/IMAPFolder.cpp b/src/net/imap/IMAPFolder.cpp index ea0ddfd9..a0df8f6b 100644 --- a/src/net/imap/IMAPFolder.cpp +++ b/src/net/imap/IMAPFolder.cpp @@ -133,7 +133,7 @@ void IMAPFolder::open(const int mode, bool failIfModeIsNotAvailable) // Open a connection for this folder ref <IMAPConnection> connection = - vmime::create <IMAPConnection>(m_store, m_store->oneTimeAuthenticator()); + vmime::create <IMAPConnection>(m_store, m_store->getAuthenticator()); try { diff --git a/src/net/imap/IMAPStore.cpp b/src/net/imap/IMAPStore.cpp index c8dab178..b7a9cbed 100644 --- a/src/net/imap/IMAPStore.cpp +++ b/src/net/imap/IMAPStore.cpp @@ -32,67 +32,23 @@ namespace net { namespace imap { -#ifndef VMIME_BUILDING_DOC - -// -// IMAPauthenticator: private class used internally -// -// Used to request user credentials only in the first authentication -// and reuse this information the next times -// - -class IMAPauthenticator : public authenticator +IMAPStore::IMAPStore(ref <session> sess, ref <security::authenticator> auth) + : store(sess, getInfosInstance(), auth), m_connection(NULL) { -public: +} - IMAPauthenticator(ref <authenticator> auth) - : m_auth(auth), m_infos(NULL) - { - } - ~IMAPauthenticator() +IMAPStore::~IMAPStore() +{ + try { + if (isConnected()) + disconnect(); } - - const authenticationInfos requestAuthInfos() const + catch (vmime::exception&) { - if (m_infos == NULL) - m_infos = vmime::create <authenticationInfos>(m_auth->requestAuthInfos()); - - return (*m_infos); + // Ignore } - -private: - - ref <authenticator> m_auth; - mutable ref <authenticationInfos> m_infos; -}; - -#endif // VMIME_BUILDING_DOC - - - -// -// IMAPStore -// - -IMAPStore::IMAPStore(ref <session> sess, ref <authenticator> auth) - : store(sess, getInfosInstance(), auth), - m_connection(NULL), m_oneTimeAuth(NULL) -{ -} - - -IMAPStore::~IMAPStore() -{ - if (isConnected()) - disconnect(); -} - - -ref <authenticator> IMAPStore::oneTimeAuthenticator() -{ - return (m_oneTimeAuth); } @@ -140,10 +96,8 @@ void IMAPStore::connect() if (isConnected()) throw exceptions::already_connected(); - m_oneTimeAuth = vmime::create <IMAPauthenticator>(getAuthenticator()); - m_connection = vmime::create <IMAPConnection> - (thisWeakRef().dynamicCast <IMAPStore>(), m_oneTimeAuth); + (thisWeakRef().dynamicCast <IMAPStore>(), getAuthenticator()); try { @@ -179,8 +133,6 @@ void IMAPStore::disconnect() m_connection->disconnect(); - m_oneTimeAuth = NULL; - m_connection = NULL; } @@ -263,7 +215,10 @@ const IMAPStore::_infos::props& IMAPStore::_infos::getProperties() const static props p = { // IMAP-specific options - // (none) +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "true"), +#endif // VMIME_HAVE_SASL_SUPPORT // Common properties property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), @@ -286,7 +241,10 @@ const std::vector <serviceInfos::property> IMAPStore::_infos::getAvailableProper const props& p = getProperties(); // IMAP-specific options - // (none) +#if VMIME_HAVE_SASL_SUPPORT + list.push_back(p.PROPERTY_OPTIONS_SASL); + list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK); +#endif // VMIME_HAVE_SASL_SUPPORT // Common properties list.push_back(p.PROPERTY_AUTH_USERNAME); |