diff --git a/src/vmime/net/imap/IMAPConnection.cpp b/src/vmime/net/imap/IMAPConnection.cpp index 5e6f6f8f..04705f6c 100644 --- a/src/vmime/net/imap/IMAPConnection.cpp +++ b/src/vmime/net/imap/IMAPConnection.cpp @@ -355,7 +355,26 @@ void IMAPConnection::authenticateSASL() saslSession->init(); - send(true, "AUTHENTICATE " + mech->getName(), true); + std::ostringstream cmd; + cmd << "AUTHENTICATE " << mech->getName(); + + 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()) + cmd << " ="; + else + cmd << " " << encodedInitialResp; + } + + send(true, cmd.str(), true); for (bool cont = true ; cont ; ) { diff --git a/src/vmime/net/pop3/POP3Command.cpp b/src/vmime/net/pop3/POP3Command.cpp index 6fe301ce..3c544fc0 100644 --- a/src/vmime/net/pop3/POP3Command.cpp +++ b/src/vmime/net/pop3/POP3Command.cpp @@ -73,6 +73,17 @@ shared_ptr POP3Command::AUTH(const string& mechName) } +// static +shared_ptr POP3Command::AUTH(const string& mechName, const string& initialResponse) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "AUTH " << mechName << " " << initialResponse; + + return createCommand(cmd.str()); +} + + // static shared_ptr POP3Command::STLS() { diff --git a/src/vmime/net/pop3/POP3Command.hpp b/src/vmime/net/pop3/POP3Command.hpp index cc3c4fd5..e34e4e2b 100644 --- a/src/vmime/net/pop3/POP3Command.hpp +++ b/src/vmime/net/pop3/POP3Command.hpp @@ -57,6 +57,7 @@ public: static shared_ptr CAPA(); static shared_ptr NOOP(); static shared_ptr AUTH(const string& mechName); + static shared_ptr AUTH(const string& mechName, const string& initialResponse); static shared_ptr STLS(); static shared_ptr APOP(const string& username, const string& digest); static shared_ptr USER(const string& username); diff --git a/src/vmime/net/pop3/POP3Connection.cpp b/src/vmime/net/pop3/POP3Connection.cpp index f5a1a448..45c668f5 100644 --- a/src/vmime/net/pop3/POP3Connection.cpp +++ b/src/vmime/net/pop3/POP3Connection.cpp @@ -446,7 +446,29 @@ void POP3Connection::authenticateSASL() saslSession->init(); - POP3Command::AUTH(mech->getName())->send(dynamicCast (shared_from_this())); + shared_ptr 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 = POP3Command::AUTH(mech->getName(), "="); + else + authCmd = POP3Command::AUTH(mech->getName(), encodedInitialResp); + } + else + { + authCmd = POP3Command::AUTH(mech->getName()); + } + + authCmd->send(dynamicCast (shared_from_this())); for (bool cont = true ; cont ; ) { diff --git a/src/vmime/net/smtp/SMTPCommand.cpp b/src/vmime/net/smtp/SMTPCommand.cpp index 949ab0c1..2120caf5 100644 --- a/src/vmime/net/smtp/SMTPCommand.cpp +++ b/src/vmime/net/smtp/SMTPCommand.cpp @@ -79,6 +79,17 @@ shared_ptr SMTPCommand::AUTH(const string& mechName) } +// static +shared_ptr SMTPCommand::AUTH(const string& mechName, const std::string& initialResponse) +{ + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + cmd << "AUTH " << mechName << " " << initialResponse; + + return createCommand(cmd.str()); +} + + // static shared_ptr SMTPCommand::STARTTLS() { diff --git a/src/vmime/net/smtp/SMTPCommand.hpp b/src/vmime/net/smtp/SMTPCommand.hpp index dbb0888b..a5b8cca5 100644 --- a/src/vmime/net/smtp/SMTPCommand.hpp +++ b/src/vmime/net/smtp/SMTPCommand.hpp @@ -60,6 +60,7 @@ public: static shared_ptr HELO(const string& hostname); static shared_ptr EHLO(const string& hostname); static shared_ptr AUTH(const string& mechName); + static shared_ptr AUTH(const string& mechName, const std::string& initialResponse); static shared_ptr STARTTLS(); static shared_ptr MAIL(const mailbox& mbox, const bool utf8); static shared_ptr MAIL(const mailbox& mbox, const bool utf8, const size_t size); diff --git a/src/vmime/net/smtp/SMTPConnection.cpp b/src/vmime/net/smtp/SMTPConnection.cpp index a45f9149..4985b563 100644 --- a/src/vmime/net/smtp/SMTPConnection.cpp +++ b/src/vmime/net/smtp/SMTPConnection.cpp @@ -367,7 +367,25 @@ void SMTPConnection::authenticateSASL() saslSession->init(); - sendRequest(SMTPCommand::AUTH(mech->getName())); + 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 ; ) { diff --git a/src/vmime/security/sasl/SASLMechanism.hpp b/src/vmime/security/sasl/SASLMechanism.hpp index 5492e48c..8756471f 100644 --- a/src/vmime/security/sasl/SASLMechanism.hpp +++ b/src/vmime/security/sasl/SASLMechanism.hpp @@ -58,6 +58,9 @@ public: * server (challenge), process it and return data to be returned * in response to the server. * + * If the challenge is empty (challengeLen == 0), the initial + * response is returned, if this mechanism has one. + * * @param sess SASL session * @param challenge challenge sent from the server * @param challengeLen length of challenge @@ -84,6 +87,15 @@ public: */ virtual bool isComplete() const = 0; + /** Determine if this mechanism has an optional initial response. + * If true, caller should call step() with an empty challenge to + * get the initial response. + * + * @return true if this mechanism has an initial response, or + * false otherwise + */ + virtual bool hasInitialResponse() const = 0; + /** Encode data according to negotiated SASL mechanism. This * might mean that data is integrity or privacy protected. * diff --git a/src/vmime/security/sasl/SASLSession.hpp b/src/vmime/security/sasl/SASLSession.hpp index ccf181cb..21ccc420 100644 --- a/src/vmime/security/sasl/SASLSession.hpp +++ b/src/vmime/security/sasl/SASLSession.hpp @@ -96,6 +96,9 @@ public: * server (challenge), process it and return data to be returned * in response to the server. * + * If the challenge is empty (challengeLen == 0), the initial + * response is returned, if the mechanism has one. + * * @param challenge challenge sent from the server * @param challengeLen length of challenge * @param response response to send to the server (allocated by diff --git a/src/vmime/security/sasl/builtinSASLMechanism.cpp b/src/vmime/security/sasl/builtinSASLMechanism.cpp index e179e715..9e352334 100644 --- a/src/vmime/security/sasl/builtinSASLMechanism.cpp +++ b/src/vmime/security/sasl/builtinSASLMechanism.cpp @@ -120,6 +120,13 @@ bool builtinSASLMechanism::isComplete() const } +bool builtinSASLMechanism::hasInitialResponse() const +{ + // It seems GNU SASL does not support initial response + return false; +} + + void builtinSASLMechanism::encode (shared_ptr sess, const byte_t* input, const size_t inputLen, byte_t** output, size_t* outputLen) diff --git a/src/vmime/security/sasl/builtinSASLMechanism.hpp b/src/vmime/security/sasl/builtinSASLMechanism.hpp index 09b46f00..6cecd1b9 100644 --- a/src/vmime/security/sasl/builtinSASLMechanism.hpp +++ b/src/vmime/security/sasl/builtinSASLMechanism.hpp @@ -61,6 +61,8 @@ public: bool isComplete() const; + bool hasInitialResponse() const; + void encode(shared_ptr sess, const byte_t* input, const size_t inputLen, byte_t** output, size_t* outputLen); diff --git a/tests/net/pop3/POP3CommandTest.cpp b/tests/net/pop3/POP3CommandTest.cpp index 4733c96b..139e948b 100644 --- a/tests/net/pop3/POP3CommandTest.cpp +++ b/tests/net/pop3/POP3CommandTest.cpp @@ -39,6 +39,7 @@ VMIME_TEST_SUITE_BEGIN(POP3CommandTest) VMIME_TEST(testCAPA) VMIME_TEST(testNOOP) VMIME_TEST(testAUTH) + VMIME_TEST(testAUTH_InitialResponse) VMIME_TEST(testSTLS) VMIME_TEST(testAPOP) VMIME_TEST(testUSER) @@ -97,6 +98,14 @@ VMIME_TEST_SUITE_BEGIN(POP3CommandTest) VASSERT_EQ("Text", "AUTH saslmechanism", cmd->getText()); } + void testAUTH_InitialResponse() + { + vmime::shared_ptr cmd = POP3Command::AUTH("saslmechanism", "initial-response"); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "AUTH saslmechanism initial-response", cmd->getText()); + } + void testSTLS() { vmime::shared_ptr cmd = POP3Command::STLS(); diff --git a/tests/net/smtp/SMTPCommandTest.cpp b/tests/net/smtp/SMTPCommandTest.cpp index 6d466865..9480948c 100644 --- a/tests/net/smtp/SMTPCommandTest.cpp +++ b/tests/net/smtp/SMTPCommandTest.cpp @@ -37,6 +37,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPCommandTest) VMIME_TEST(testHELO) VMIME_TEST(testEHLO) VMIME_TEST(testAUTH) + VMIME_TEST(testAUTH_InitialResponse) VMIME_TEST(testSTARTTLS) VMIME_TEST(testMAIL) VMIME_TEST(testMAIL_Encoded) @@ -95,6 +96,14 @@ VMIME_TEST_SUITE_BEGIN(SMTPCommandTest) VASSERT_EQ("Text", "AUTH saslmechanism", cmd->getText()); } + void testAUTH_InitialResponse() + { + vmime::shared_ptr cmd = SMTPCommand::AUTH("saslmechanism", "initial-response"); + + VASSERT_NOT_NULL("Not null", cmd); + VASSERT_EQ("Text", "AUTH saslmechanism initial-response", cmd->getText()); + } + void testSTARTTLS() { vmime::shared_ptr cmd = SMTPCommand::STARTTLS();