From 4b0a67b55cec24f99d4842fe8ac980327beed0cb Mon Sep 17 00:00:00 2001 From: Andrew Clemons Date: Wed, 2 Aug 2017 21:22:53 +1200 Subject: [PATCH] Add support for XOAUTH2 auth I have thus far only tested this with gmail and using node-xoauth2 for generating the tokens. Emailrelay still requires a client auth configuration file with four values. The id value here can be anything since it is ignored. I am using: client XOAUTH2 ignored --- src/gauth/gsaslclient.h | 5 +++++ src/gauth/gsaslclient_native.cpp | 32 +++++++++++++++++++++++++++++++- src/gsmtp/gclientprotocol.cpp | 18 ++++++++++++++++-- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/gauth/gsaslclient.h b/src/gauth/gsaslclient.h index ea12e05..d74b56d 100644 --- a/src/gauth/gsaslclient.h +++ b/src/gauth/gsaslclient.h @@ -67,6 +67,11 @@ class GAuth::SaslClient ///< Returns true if the constructor's secrets object ///< is valid. + std::string initial_response( const std::string & mechanism , + bool & done , bool & error , bool & sensitive ) const ; + ///< Returns an initial_response for authentication. + ///< Returns various boolean flags by reference. + std::string response( const std::string & mechanism , const std::string & challenge , bool & done , bool & error , bool & sensitive ) const ; ///< Returns a response to the given challenge. diff --git a/src/gauth/gsaslclient_native.cpp b/src/gauth/gsaslclient_native.cpp index d0bded2..924772a 100644 --- a/src/gauth/gsaslclient_native.cpp +++ b/src/gauth/gsaslclient_native.cpp @@ -101,6 +101,33 @@ bool GAuth::SaslClient::active() const return m_imp->m_secrets.valid() ; } +std::string GAuth::SaslClient::initial_response( const std::string & mechanism , bool & done , + bool & error , bool & sensitive ) const +{ + done = false ; + error = false ; + sensitive = false ; + + std::string auth("AUTH") ; + std::string sep(" ") ; + + std::string rsp ; + if( mechanism == "XOAUTH2" ) + { + std::string secret = m_imp->m_secrets.secret(mechanism) ; + rsp = auth + sep + mechanism + sep + secret ; + error = secret.empty() ; + done = true ; + sensitive = true ; + } + else + { + rsp = auth + sep + mechanism ; + } + + return rsp ; +} + std::string GAuth::SaslClient::response( const std::string & mechanism , const std::string & challenge , bool & done , bool & error , bool & sensitive ) const { @@ -175,6 +202,7 @@ std::string GAuth::SaslClient::preferred( const G::Strings & mechanism_list ) co const std::string login( "LOGIN" ) ; const std::string plain( "PLAIN" ) ; + const std::string xoauth2( "XOAUTH2" ) ; const std::string cram( "CRAM-MD5" ) ; // create a them set @@ -186,15 +214,17 @@ std::string GAuth::SaslClient::preferred( const G::Strings & mechanism_list ) co std::set us ; if( !m_imp->m_secrets.id(login).empty() ) us.insert(login) ; if( !m_imp->m_secrets.id(plain).empty() ) us.insert(plain) ; + if( !m_imp->m_secrets.id(xoauth2).empty() ) us.insert(xoauth2) ; if( !m_imp->m_secrets.id(cram).empty() ) us.insert(cram) ; // get the intersection std::set both ; std::set_intersection( them.begin() , them.end() , us.begin() , us.end() , std::inserter(both,both.end()) ) ; - // preferred order: cram, plain, login + // preferred order: cram, xoauth2, plain, login std::string m ; if( m.empty() && both.find(cram) != both.end() ) m = cram ; + if( m.empty() && both.find(xoauth2) != both.end() ) m = xoauth2 ; if( m.empty() && both.find(plain) != both.end() ) m = plain ; if( m.empty() && both.find(login) != both.end() ) m = login ; G_DEBUG( "GAuth::SaslClient::preferred: we prefer \"" << m << "\"" ) ; diff --git a/src/gsmtp/gclientprotocol.cpp b/src/gsmtp/gclientprotocol.cpp index 3ebc0c7..bbd8aca 100644 --- a/src/gsmtp/gclientprotocol.cpp +++ b/src/gsmtp/gclientprotocol.cpp @@ -303,8 +303,22 @@ bool GSmtp::ClientProtocol::applyEvent( const Reply & reply , bool is_start_even } else if( m_server_has_auth && m_sasl->active() ) { - m_state = sAuth1 ; - send( "AUTH " , m_auth_mechanism ) ; + bool done = true ; + bool error = false ; + bool sensitive = false ; + std::string rsp = m_sasl->initial_response( m_auth_mechanism , + done , error , sensitive ) ; + + if( error ) + { + m_state = sAuth2 ; + send( "*" ) ; // ie. cancel authentication + } + else + { + m_state = done ? sAuth2 : sAuth1 ; + send( rsp , false , sensitive ) ; + } } else if( !m_server_has_auth && m_sasl->active() && m_must_authenticate ) {