44 std::string initialResponse( std::size_t limit )
const ;
45 Response response(
const std::string & mechanism ,
const std::string & challenge )
const ;
47 std::string mechanism()
const ;
48 std::string id()
const ;
49 std::string info()
const ;
50 static bool match(
const G::StringArray & mechanisms ,
const std::string & ) ;
54 std::string m_config ;
56 mutable std::string m_info ;
57 mutable std::string m_id ;
60 static const char *
const login_challenge_1 ;
61 static const char *
const login_challenge_2 ;
64const char *
const GAuth::SaslClientImp::login_challenge_1 =
"Username:" ;
65const char *
const GAuth::SaslClientImp::login_challenge_2 =
"Password:" ;
69GAuth::SaslClientImp::SaslClientImp(
const SaslClientSecrets & secrets ,
const std::string & sasl_client_config ) :
71 m_config(sasl_client_config) ,
77std::string GAuth::SaslClientImp::mechanism(
const G::StringArray & server_mechanisms )
const
81 return std::string() ;
90 if( m_secrets.clientSecret(
"plain").valid() )
97 for(
auto p = our_list.begin() ; p != our_list.end() ; )
99 if( m_secrets.clientSecret((*p).substr(5U)).valid() )
102 p = our_list.erase( p ) ;
105 if( m_secrets.clientSecret(
"oauth").valid() )
107 our_list.push_back(
"XOAUTH2" ) ;
109 if( m_secrets.clientSecret(
"plain").valid() )
111 our_list.push_back( PLAIN ) ;
112 our_list.push_back( LOGIN ) ;
116 if( !m_config.empty() )
122 our_list.erase(
G::Str::keepMatch( our_list.begin() , our_list.end() , whitelist ,
true ) , our_list.end() ) ;
123 our_list.erase(
G::Str::removeMatch( our_list.begin() , our_list.end() , blocklist ,
true ) , our_list.end() ) ;
127 m_mechanisms.clear() ;
128 for(
auto & our_mechanism : our_list )
130 if( match(server_mechanisms,our_mechanism) )
132 m_mechanisms.push_back( our_mechanism ) ;
136 G_DEBUG(
"GAuth::SaslClientImp::mechanism: server mechanisms: [" <<
G::Str::join(
",",server_mechanisms) <<
"]" ) ;
137 G_DEBUG(
"GAuth::SaslClientImp::mechanism: our mechanisms: [" <<
G::Str::join(
",",our_list) <<
"]" ) ;
138 G_DEBUG(
"GAuth::SaslClientImp::mechanism: usable mechanisms: [" <<
G::Str::join(
",",m_mechanisms) <<
"]" ) ;
140 return m_mechanisms.empty() ? std::string() : m_mechanisms.at(0U) ;
143bool GAuth::SaslClientImp::next()
145 if( !m_mechanisms.empty() )
146 m_mechanisms.erase( m_mechanisms.begin() ) ;
147 return !m_mechanisms.empty() ;
150std::string GAuth::SaslClientImp::mechanism()
const
152 return m_mechanisms.empty() ? std::string() : m_mechanisms.at(0U) ;
155std::string GAuth::SaslClientImp::initialResponse( std::size_t limit )
const
161 const std::string m = mechanism() ;
162 if( m.empty() || m.find(
"CRAM-") == 0U )
163 return std::string() ;
165 Response rsp = response( m , m==
"LOGIN"?std::string(login_challenge_1):std::string() ) ;
166 if( rsp.error || rsp.data.size() > limit )
167 return std::string() ;
173 const std::string & challenge )
const
176 rsp.sensitive = true ;
181 if( mechanism.find(
"CRAM-") == 0U )
183 std::string hash_type = mechanism.substr( 5U ) ;
184 secret = m_secrets.clientSecret( hash_type ) ;
185 if( !secret.valid() )
186 secret = m_secrets.clientSecret(
"plain" ) ;
187 rsp.data =
Cram::response( hash_type ,
true , secret , challenge , secret.id() ) ;
188 rsp.error = rsp.data.empty() ;
191 else if( mechanism ==
"APOP" )
193 secret = m_secrets.clientSecret(
"MD5" ) ;
194 rsp.data =
Cram::response(
"MD5" ,
false , secret , challenge , secret.id() ) ;
195 rsp.error = rsp.data.empty() ;
198 else if( mechanism == PLAIN )
200 secret = m_secrets.clientSecret(
"plain" ) ;
201 const std::string sep( 1U ,
'\0' ) ;
202 rsp.data = sep + secret.id() + sep + secret.key() ;
203 rsp.error = !secret.valid() ;
206 else if( mechanism == LOGIN && challenge == login_challenge_1 )
208 secret = m_secrets.clientSecret(
"plain" ) ;
209 rsp.data = secret.id() ;
210 rsp.error = !secret.valid() ;
212 rsp.sensitive = false ;
214 else if( mechanism == LOGIN && challenge == login_challenge_2 )
216 secret = m_secrets.clientSecret(
"plain" ) ;
217 rsp.data = secret.key() ;
218 rsp.error = !secret.valid() ;
221 else if( mechanism ==
"XOAUTH2" && challenge.empty() )
223 secret = m_secrets.clientSecret(
"oauth" ) ;
224 rsp.data = secret.key() ;
225 rsp.error = !secret.valid() ;
228 else if( mechanism ==
"XOAUTH2" )
230 secret = m_secrets.clientSecret(
"oauth" ) ;
234 rsp.sensitive = false ;
239 std::ostringstream ss ;
240 ss <<
"using mechanism [" <<
G::Str::lower(mechanism) <<
"] and " << secret.info() ;
248std::string GAuth::SaslClientImp::id()
const
253std::string GAuth::SaslClientImp::info()
const
258bool GAuth::SaslClientImp::active()
const
260 return m_secrets.valid() ;
263bool GAuth::SaslClientImp::match(
const G::StringArray & mechanisms ,
const std::string & mechanism )
265 return std::find(mechanisms.begin(),mechanisms.end(),mechanism) != mechanisms.end() ;
280 return m_imp->active() ;
285 return m_imp->response( mechanism , challenge ) ;
290 return m_imp->initialResponse( limit ) ;
295 return m_imp->mechanism( server_mechanisms ) ;
300 return m_imp->next() ;
305 if( s.empty() )
return s ;
306 return m_imp->next() ? mechanism() : std::string() ;
311 return m_imp->mechanism() ;
321 return m_imp->info() ;
static std::string response(const std::string &hash_type, bool hmac, const Secret &secret, const std::string &challenge, const std::string &response_prefix)
Constructs a response to a challenge comprising the response-prefix, space, and digest-or-hmac of sec...
static G::StringArray hashTypes(const std::string &prefix=std::string(), bool require_state=false)
Returns a list of supported hash types, such as "MD5" and "SHA1", ordered with the strongest first.
A private pimple-pattern implementation class used by GAuth::SaslClient.
An interface used by GAuth::SaslClient to obtain a client id and its authentication secret.
Response response(const std::string &mechanism, const std::string &challenge) const
Returns a response to the given challenge.
std::string id() const
Returns the authentication id, valid after the last response().
std::string initialResponse(std::size_t limit=0U) const
Returns an optional initial response.
SaslClient(const SaslClientSecrets &secrets, const std::string &config)
Constructor. The secrets reference is kept.
bool next()
Moves to the next preferred mechanism.
std::string info() const
Returns logging and diagnostic information, valid after the last response().
std::string mechanism() const
Returns the name of the current mechanism once next() has returned true.
bool active() const
Returns true if the constructor's secrets object is valid.
static Secret none()
Factory function that returns a secret that is not valid() and has an empty id().
static std::string join(const std::string &sep, const StringArray &strings)
Concatenates an array of strings with separators.
static bool imatch(char, char)
Returns true if the two characters are the same, ignoring Latin-1 case.
static void splitIntoTokens(const std::string &in, StringArray &out, string_view ws, char esc='\0')
Splits the string into 'ws'-delimited tokens.
static StringArray::iterator removeMatch(StringArray::iterator begin, StringArray::iterator end, const StringArray &match_list, bool ignore_case=false)
Removes items in the begin/end list that match one of the elements in the match-list (blocklist).
static std::string upper(const std::string &s)
Returns a copy of 's' in which all Latin-1 lower-case characters have been replaced by upper-case cha...
static std::string headMatchResidue(const StringArray &in, const std::string &head)
Returns the unmatched part of the first string in the array that has the given start.
static StringArray::iterator keepMatch(StringArray::iterator begin, StringArray::iterator end, const StringArray &match_list, bool ignore_case=false)
Removes items in the begin/end list that do not match any of the elements in the match-list (whitelis...
static std::string lower(const std::string &s)
Returns a copy of 's' in which all Latin-1 upper-case characters have been replaced by lower-case cha...
An interface to an underlying TLS library.
std::vector< std::string > StringArray
A std::vector of std::strings.
Result structure returned from GAuth::SaslClient::response.