Added support for initial response in SASL authentication.

This commit is contained in:
Vincent Richard 2014-02-13 22:23:59 +01:00
parent c655495025
commit c860c273d3
13 changed files with 128 additions and 3 deletions

View File

@ -355,7 +355,26 @@ void IMAPConnection::authenticateSASL()
saslSession->init(); 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 ; ) for (bool cont = true ; cont ; )
{ {

View File

@ -73,6 +73,17 @@ shared_ptr <POP3Command> POP3Command::AUTH(const string& mechName)
} }
// static
shared_ptr <POP3Command> 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 // static
shared_ptr <POP3Command> POP3Command::STLS() shared_ptr <POP3Command> POP3Command::STLS()
{ {

View File

@ -57,6 +57,7 @@ public:
static shared_ptr <POP3Command> CAPA(); static shared_ptr <POP3Command> CAPA();
static shared_ptr <POP3Command> NOOP(); static shared_ptr <POP3Command> NOOP();
static shared_ptr <POP3Command> AUTH(const string& mechName); static shared_ptr <POP3Command> AUTH(const string& mechName);
static shared_ptr <POP3Command> AUTH(const string& mechName, const string& initialResponse);
static shared_ptr <POP3Command> STLS(); static shared_ptr <POP3Command> STLS();
static shared_ptr <POP3Command> APOP(const string& username, const string& digest); static shared_ptr <POP3Command> APOP(const string& username, const string& digest);
static shared_ptr <POP3Command> USER(const string& username); static shared_ptr <POP3Command> USER(const string& username);

View File

@ -446,7 +446,29 @@ void POP3Connection::authenticateSASL()
saslSession->init(); saslSession->init();
POP3Command::AUTH(mech->getName())->send(dynamicCast <POP3Connection>(shared_from_this())); shared_ptr <POP3Command> 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 <POP3Connection>(shared_from_this()));
for (bool cont = true ; cont ; ) for (bool cont = true ; cont ; )
{ {

View File

@ -79,6 +79,17 @@ shared_ptr <SMTPCommand> SMTPCommand::AUTH(const string& mechName)
} }
// static
shared_ptr <SMTPCommand> 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 // static
shared_ptr <SMTPCommand> SMTPCommand::STARTTLS() shared_ptr <SMTPCommand> SMTPCommand::STARTTLS()
{ {

View File

@ -60,6 +60,7 @@ public:
static shared_ptr <SMTPCommand> HELO(const string& hostname); static shared_ptr <SMTPCommand> HELO(const string& hostname);
static shared_ptr <SMTPCommand> EHLO(const string& hostname); static shared_ptr <SMTPCommand> EHLO(const string& hostname);
static shared_ptr <SMTPCommand> AUTH(const string& mechName); static shared_ptr <SMTPCommand> AUTH(const string& mechName);
static shared_ptr <SMTPCommand> AUTH(const string& mechName, const std::string& initialResponse);
static shared_ptr <SMTPCommand> STARTTLS(); static shared_ptr <SMTPCommand> STARTTLS();
static shared_ptr <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8); static shared_ptr <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8);
static shared_ptr <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8, const size_t size); static shared_ptr <SMTPCommand> MAIL(const mailbox& mbox, const bool utf8, const size_t size);

View File

@ -367,7 +367,25 @@ void SMTPConnection::authenticateSASL()
saslSession->init(); 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())); sendRequest(SMTPCommand::AUTH(mech->getName()));
}
for (bool cont = true ; cont ; ) for (bool cont = true ; cont ; )
{ {

View File

@ -58,6 +58,9 @@ public:
* server (challenge), process it and return data to be returned * server (challenge), process it and return data to be returned
* in response to the server. * 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 sess SASL session
* @param challenge challenge sent from the server * @param challenge challenge sent from the server
* @param challengeLen length of challenge * @param challengeLen length of challenge
@ -84,6 +87,15 @@ public:
*/ */
virtual bool isComplete() const = 0; 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 /** Encode data according to negotiated SASL mechanism. This
* might mean that data is integrity or privacy protected. * might mean that data is integrity or privacy protected.
* *

View File

@ -96,6 +96,9 @@ public:
* server (challenge), process it and return data to be returned * server (challenge), process it and return data to be returned
* in response to the server. * 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 challenge challenge sent from the server
* @param challengeLen length of challenge * @param challengeLen length of challenge
* @param response response to send to the server (allocated by * @param response response to send to the server (allocated by

View File

@ -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 void builtinSASLMechanism::encode
(shared_ptr <SASLSession> sess, const byte_t* input, const size_t inputLen, (shared_ptr <SASLSession> sess, const byte_t* input, const size_t inputLen,
byte_t** output, size_t* outputLen) byte_t** output, size_t* outputLen)

View File

@ -61,6 +61,8 @@ public:
bool isComplete() const; bool isComplete() const;
bool hasInitialResponse() const;
void encode(shared_ptr <SASLSession> sess, void encode(shared_ptr <SASLSession> sess,
const byte_t* input, const size_t inputLen, const byte_t* input, const size_t inputLen,
byte_t** output, size_t* outputLen); byte_t** output, size_t* outputLen);

View File

@ -39,6 +39,7 @@ VMIME_TEST_SUITE_BEGIN(POP3CommandTest)
VMIME_TEST(testCAPA) VMIME_TEST(testCAPA)
VMIME_TEST(testNOOP) VMIME_TEST(testNOOP)
VMIME_TEST(testAUTH) VMIME_TEST(testAUTH)
VMIME_TEST(testAUTH_InitialResponse)
VMIME_TEST(testSTLS) VMIME_TEST(testSTLS)
VMIME_TEST(testAPOP) VMIME_TEST(testAPOP)
VMIME_TEST(testUSER) VMIME_TEST(testUSER)
@ -97,6 +98,14 @@ VMIME_TEST_SUITE_BEGIN(POP3CommandTest)
VASSERT_EQ("Text", "AUTH saslmechanism", cmd->getText()); VASSERT_EQ("Text", "AUTH saslmechanism", cmd->getText());
} }
void testAUTH_InitialResponse()
{
vmime::shared_ptr <POP3Command> cmd = POP3Command::AUTH("saslmechanism", "initial-response");
VASSERT_NOT_NULL("Not null", cmd);
VASSERT_EQ("Text", "AUTH saslmechanism initial-response", cmd->getText());
}
void testSTLS() void testSTLS()
{ {
vmime::shared_ptr <POP3Command> cmd = POP3Command::STLS(); vmime::shared_ptr <POP3Command> cmd = POP3Command::STLS();

View File

@ -37,6 +37,7 @@ VMIME_TEST_SUITE_BEGIN(SMTPCommandTest)
VMIME_TEST(testHELO) VMIME_TEST(testHELO)
VMIME_TEST(testEHLO) VMIME_TEST(testEHLO)
VMIME_TEST(testAUTH) VMIME_TEST(testAUTH)
VMIME_TEST(testAUTH_InitialResponse)
VMIME_TEST(testSTARTTLS) VMIME_TEST(testSTARTTLS)
VMIME_TEST(testMAIL) VMIME_TEST(testMAIL)
VMIME_TEST(testMAIL_Encoded) VMIME_TEST(testMAIL_Encoded)
@ -95,6 +96,14 @@ VMIME_TEST_SUITE_BEGIN(SMTPCommandTest)
VASSERT_EQ("Text", "AUTH saslmechanism", cmd->getText()); VASSERT_EQ("Text", "AUTH saslmechanism", cmd->getText());
} }
void testAUTH_InitialResponse()
{
vmime::shared_ptr <SMTPCommand> cmd = SMTPCommand::AUTH("saslmechanism", "initial-response");
VASSERT_NOT_NULL("Not null", cmd);
VASSERT_EQ("Text", "AUTH saslmechanism initial-response", cmd->getText());
}
void testSTARTTLS() void testSTARTTLS()
{ {
vmime::shared_ptr <SMTPCommand> cmd = SMTPCommand::STARTTLS(); vmime::shared_ptr <SMTPCommand> cmd = SMTPCommand::STARTTLS();