XOAUTH2 authentication mechanism (for GMail).

This commit is contained in:
Vincent Richard 2015-08-08 08:06:00 +02:00
parent 3dd5975422
commit 7e36a74645
11 changed files with 447 additions and 0 deletions

View File

@ -50,6 +50,9 @@ const serviceInfos::property serviceInfos::property::AUTH_USERNAME
const serviceInfos::property serviceInfos::property::AUTH_PASSWORD
("auth.password", serviceInfos::property::TYPE_STRING);
const serviceInfos::property serviceInfos::property::AUTH_ACCESS_TOKEN
("auth.accesstoken", serviceInfos::property::TYPE_STRING);
#if VMIME_HAVE_TLS_SUPPORT
const serviceInfos::property serviceInfos::property::CONNECTION_TLS

View File

@ -90,6 +90,10 @@ public:
* password used to authenticate with the server. */
static const property AUTH_PASSWORD;
/** The common property 'auth.accesstoken' which is the
* access token used to authenticate with the server. */
static const property AUTH_ACCESS_TOKEN;
#if VMIME_HAVE_TLS_SUPPORT
/** The common property 'connection.tls': this is used to

View File

@ -81,6 +81,15 @@ public:
*/
virtual const string getPassword() const = 0;
/** Return the optional access token for authentication. This is
* used for example with XOAuth2 SASL authentication.
*
* @return access token
* @throw exceptions::no_auth_information if the information
* could not be provided
*/
virtual const string getAccessToken() const = 0;
/** Return the local host name of the machine.
*
* @return hostname

View File

@ -76,6 +76,20 @@ const string defaultAuthenticator::getPassword() const
}
const string defaultAuthenticator::getAccessToken() const
{
shared_ptr <const net::service> service = m_service.lock();
const string prefix = service->getInfos().getPropertyPrefix();
const propertySet& props = service->getSession()->getProperties();
if (props.hasProperty(prefix + net::serviceInfos::property::AUTH_ACCESS_TOKEN.getName()))
return props[prefix + net::serviceInfos::property::AUTH_ACCESS_TOKEN.getName()];
throw exceptions::no_auth_information();
}
const string defaultAuthenticator::getHostname() const
{
return platform::getHandler()->getHostName();

View File

@ -53,6 +53,7 @@ public:
const string getHostname() const;
const string getAnonymousToken() const;
const string getServiceName() const;
const string getAccessToken() const;
void setService(shared_ptr <net::service> serv);
weak_ptr <net::service> getService() const;

View File

@ -0,0 +1,98 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 3 of
// the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// Linking this library statically or dynamically with other modules is making
// a combined work based on this library. Thus, the terms and conditions of
// the GNU General Public License cover the whole combination.
//
#include "vmime/config.hpp"
#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_SASL_SUPPORT
#include "vmime/security/sasl/XOAuth2SASLAuthenticator.hpp"
#include "vmime/security/sasl/SASLMechanism.hpp"
#include "vmime/security/sasl/SASLSession.hpp"
#include "vmime/security/sasl/SASLContext.hpp"
namespace vmime {
namespace security {
namespace sasl {
XOAuth2SASLAuthenticator::XOAuth2SASLAuthenticator(const Mode mode)
: m_mode(mode)
{
}
XOAuth2SASLAuthenticator::~XOAuth2SASLAuthenticator()
{
}
const std::vector <shared_ptr <SASLMechanism> >
XOAuth2SASLAuthenticator::getAcceptableMechanisms
(const std::vector <shared_ptr <SASLMechanism> >& available,
shared_ptr <SASLMechanism> suggested) const
{
if (m_mode == MODE_EXCLUSIVE)
{
std::vector <shared_ptr <SASLMechanism> > mechs;
for (size_t i = available.size() ; i != 0 ; --i)
{
shared_ptr <SASLMechanism> mech = available[i - 1];
if ("XOAUTH2" == mech->getName())
{
// Only allow XOAuth2
mechs.push_back(mech);
}
}
return mechs;
}
else
{
for (size_t i = available.size() ; i != 0 ; --i)
{
shared_ptr <SASLMechanism> mech = available[i - 1];
if ("XOAUTH2" == mech->getName())
{
// Suggest using XOAuth2
suggested = mech;
}
}
return defaultSASLAuthenticator::getAcceptableMechanisms(available, suggested);
}
}
} // sasl
} // security
} // vmime
#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_SASL_SUPPORT

View File

@ -0,0 +1,78 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 3 of
// the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// Linking this library statically or dynamically with other modules is making
// a combined work based on this library. Thus, the terms and conditions of
// the GNU General Public License cover the whole combination.
//
#ifndef VMIME_SECURITY_SASL_XOAUTH2SASLAUTHENTICATOR_HPP_INCLUDED
#define VMIME_SECURITY_SASL_XOAUTH2SASLAUTHENTICATOR_HPP_INCLUDED
#include "vmime/config.hpp"
#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_SASL_SUPPORT
#include "vmime/security/sasl/defaultSASLAuthenticator.hpp"
namespace vmime {
namespace security {
namespace sasl {
/** An authenticator that is capable of providing information
* for XOAuth2 authentication mechanisms (username and access token).
* This authenticator force using the XOAUTH2 mechanism.
*/
class VMIME_EXPORT XOAuth2SASLAuthenticator : public defaultSASLAuthenticator
{
public:
enum Mode
{
MODE_SUGGEST, /**< Try XOAUTH2 before other mechanisms. */
MODE_EXCLUSIVE /**< Use XOAUTH2 and nothing else. */
};
XOAuth2SASLAuthenticator(const Mode mode);
~XOAuth2SASLAuthenticator();
const std::vector <shared_ptr <SASLMechanism> > getAcceptableMechanisms
(const std::vector <shared_ptr <SASLMechanism> >& available,
shared_ptr <SASLMechanism> suggested) const;
private:
Mode m_mode;
};
} // sasl
} // security
} // vmime
#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_SASL_SUPPORT
#endif // VMIME_SECURITY_SASL_XOAUTH2SASLAUTHENTICATOR_HPP_INCLUDED

View File

@ -0,0 +1,142 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 3 of
// the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// Linking this library statically or dynamically with other modules is making
// a combined work based on this library. Thus, the terms and conditions of
// the GNU General Public License cover the whole combination.
//
#include "vmime/config.hpp"
#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_SASL_SUPPORT
#include "vmime/security/sasl/XOAuth2SASLMechanism.hpp"
#include "vmime/security/sasl/SASLContext.hpp"
#include "vmime/security/sasl/SASLSession.hpp"
#include "vmime/exception.hpp"
#include <sstream>
namespace vmime {
namespace security {
namespace sasl {
XOAuth2SASLMechanism::XOAuth2SASLMechanism(shared_ptr <SASLContext> ctx, const string& /* name */)
: m_context(ctx), m_complete(false)
{
}
XOAuth2SASLMechanism::~XOAuth2SASLMechanism()
{
}
const string XOAuth2SASLMechanism::getName() const
{
return "XOAUTH2";
}
bool XOAuth2SASLMechanism::step
(shared_ptr <SASLSession> sess,
const byte_t* /* challenge */, const size_t /* challengeLen */,
byte_t** response, size_t* responseLen)
{
// Build initial response
//
// The SASL XOAUTH2 initial client response has the following format:
// base64("user=" {User} "^Aauth=Bearer " {Access Token} "^A^A")
const std::string user(sess->getAuthenticator()->getUsername());
const std::string accessToken(sess->getAuthenticator()->getAccessToken());
std::ostringstream initRespBytes;
initRespBytes.write("user=", 5);
initRespBytes.write(user.c_str(), user.length());
initRespBytes.write("\x01", 1);
initRespBytes.write("auth=Bearer ", 12);
initRespBytes.write(accessToken.c_str(), accessToken.length());
initRespBytes.write("\x01\x01", 2);
const std::string initResp = initRespBytes.str();
// Set initial response
byte_t* res = new byte_t[initResp.length()];
std::copy(initResp.c_str(), initResp.c_str() + initResp.length(), res);
*response = res;
*responseLen = initResp.length();
m_complete = true;
return true;
}
bool XOAuth2SASLMechanism::isComplete() const
{
return m_complete;
}
bool XOAuth2SASLMechanism::hasInitialResponse() const
{
return true;
}
void XOAuth2SASLMechanism::encode
(shared_ptr <SASLSession> /* sess */,
const byte_t* input, const size_t inputLen,
byte_t** output, size_t* outputLen)
{
// No encoding performed, just copy input bytes
byte_t* res = new byte_t[inputLen];
std::copy(input, input + inputLen, res);
*outputLen = inputLen;
*output = res;
}
void XOAuth2SASLMechanism::decode
(shared_ptr <SASLSession> /* sess */,
const byte_t* input, const size_t inputLen,
byte_t** output, size_t* outputLen)
{
// No decoding performed, just copy input bytes
byte_t* res = new byte_t[inputLen];
std::copy(input, input + inputLen, res);
*outputLen = inputLen;
*output = res;
}
} // sasl
} // security
} // vmime
#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_SASL_SUPPORT

View File

@ -0,0 +1,91 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 3 of
// the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// Linking this library statically or dynamically with other modules is making
// a combined work based on this library. Thus, the terms and conditions of
// the GNU General Public License cover the whole combination.
//
#ifndef VMIME_SECURITY_SASL_XOAUTH2SASLMECHANISM_HPP_INCLUDED
#define VMIME_SECURITY_SASL_XOAUTH2SASLMECHANISM_HPP_INCLUDED
#include "vmime/config.hpp"
#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_SASL_SUPPORT
#include "vmime/security/sasl/SASLMechanism.hpp"
namespace vmime {
namespace security {
namespace sasl {
class SASLContext;
/** SASL XOAUTH2 mechanism, used by GMail.
*/
class VMIME_EXPORT XOAuth2SASLMechanism : public SASLMechanism
{
public:
XOAuth2SASLMechanism(shared_ptr <SASLContext> ctx, const string& name);
~XOAuth2SASLMechanism();
const string getName() const;
bool step(shared_ptr <SASLSession> sess,
const byte_t* challenge, const size_t challengeLen,
byte_t** response, size_t* responseLen);
bool isComplete() const;
bool hasInitialResponse() const;
void encode(shared_ptr <SASLSession> sess,
const byte_t* input, const size_t inputLen,
byte_t** output, size_t* outputLen);
void decode(shared_ptr <SASLSession> sess,
const byte_t* input, const size_t inputLen,
byte_t** output, size_t* outputLen);
private:
/** SASL context */
shared_ptr <SASLContext> m_context;
/** Authentication process status. */
bool m_complete;
};
} // sasl
} // security
} // vmime
#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_SASL_SUPPORT
#endif // VMIME_SECURITY_SASL_XOAUTH2SASLMECHANISM_HPP_INCLUDED

View File

@ -89,6 +89,12 @@ const string defaultSASLAuthenticator::getPassword() const
}
const string defaultSASLAuthenticator::getAccessToken() const
{
return m_default.getAccessToken();
}
const string defaultSASLAuthenticator::getHostname() const
{
return m_default.getHostname();

View File

@ -59,6 +59,7 @@ public:
const string getHostname() const;
const string getAnonymousToken() const;
const string getServiceName() const;
const string getAccessToken() const;
void setService(shared_ptr <net::service> serv);
weak_ptr <net::service> getService() const;