43class GAuth::SaslServerBasicImp
46 SaslServerBasicImp(
const SaslServerSecrets & ,
bool ,
47 const std::string & config ,
const std::string & challenge_domain ) ;
50 bool init(
bool ,
const std::string & mechanism ) ;
51 std::string mechanism()
const ;
52 std::string preferredMechanism(
bool )
const ;
53 std::string initialChallenge()
const ;
54 std::string apply(
const std::string & response ,
bool & done ) ;
55 bool trusted(
const G::StringArray & ,
const std::string & )
const ;
56 bool trustedCore(
const std::string & ,
const std::string & )
const ;
57 std::string id()
const ;
58 bool authenticated()
const ;
62 const SaslServerSecrets & m_secrets ;
65 std::string m_mechanism ;
66 std::string m_challenge ;
67 std::string m_challenge_domain ;
68 bool m_authenticated ;
70 std::string m_trustee ;
75G::string_view GAuth::SaslServerBasicImp::login_challenge_1 {
"Username:",9U} ;
76G::string_view GAuth::SaslServerBasicImp::login_challenge_2 {
"Password:",9U} ;
80GAuth::SaslServerBasicImp::SaslServerBasicImp(
const SaslServerSecrets & secrets ,
81 bool with_apop ,
const std::string & config ,
const std::string & challenge_domain ) :
84 m_challenge_domain(challenge_domain) ,
85 m_authenticated(false)
91 if( secrets.contains(
"PLAIN" , {} ) )
93 mechanisms = Cram::hashTypes(
"CRAM-" ) ;
94 mechanisms.push_back(
"PLAIN" ) ;
95 mechanisms.push_back(
"LOGIN" ) ;
100 for(
const auto & cram : Cram::hashTypes({},
true) )
102 if( secrets.contains( cram , {} ) )
103 mechanisms.push_back( std::string(
"CRAM-").append(cram) ) ;
107 mechanisms.push_back(
"APOP" ) ;
110 m_mechanisms_secure = mechanisms ;
111 m_mechanisms_insecure = mechanisms ;
114 if( m_mechanisms_secure.empty() && secrets.valid() )
116 m_mechanisms_secure.push_back(
"PLAIN" ) ;
143 if( have_a || have_d )
155void GAuth::SaslServerBasicImp::reset()
157 m_first_apply = true ;
158 m_authenticated = false ;
161 m_challenge.clear() ;
162 m_mechanism.clear() ;
165G::StringArray GAuth::SaslServerBasicImp::mechanisms(
bool secure )
const
167 return secure ? m_mechanisms_secure : m_mechanisms_insecure ;
170bool GAuth::SaslServerBasicImp::init(
bool secure ,
const std::string & mechanism_in )
175 const G::StringArray & mechanisms = secure ? m_mechanisms_secure : m_mechanisms_insecure ;
177 if( mechanism.empty() || std::find(mechanisms.begin(),mechanisms.end(),mechanism) == mechanisms.end() )
179 G_DEBUG(
"GAuth::SaslServerBasicImp::init: requested mechanism [" << mechanism <<
"] is not in our list" ) ;
182 else if( mechanism ==
"APOP" || mechanism.find(
"CRAM-") == 0U )
184 m_mechanism = mechanism ;
185 m_challenge = Cram::challenge(
G::Random::rand() , m_challenge_domain ) ;
190 m_mechanism = mechanism ;
195std::string GAuth::SaslServerBasicImp::preferredMechanism(
bool secure )
const
199 auto mechanism_list = mechanisms( secure ) ;
200 std::reverse( mechanism_list.begin() , mechanism_list.end() ) ;
201 for(
const auto & m : mechanism_list )
206 if( m_secrets.contains( type , m_id ) )
211 return std::string() ;
214std::string GAuth::SaslServerBasicImp::initialChallenge()
const
217 if( m_mechanism ==
"PLAIN" )
218 return std::string() ;
219 else if( m_mechanism ==
"LOGIN" )
220 return G::sv_to_string(login_challenge_1) ;
225std::string GAuth::SaslServerBasicImp::apply(
const std::string & response ,
bool & done )
227 G_DEBUG(
"GAuth::SaslServerBasic::apply: response: \"" <<
G::Str::printable(response) <<
"\"" ) ;
229 bool first_apply = m_first_apply ;
230 m_first_apply = false ;
234 Secret secret = Secret::none() ;
235 std::string next_challenge ;
237 if( m_mechanism.find(
"CRAM-") == 0U || m_mechanism ==
"APOP" )
239 id = Cram::id( response ) ;
240 secret = Secret::none() ;
243 if( m_mechanism ==
"APOP" )
245 secret = m_secrets.serverSecret(
"plain"_sv ,
id ) ;
249 std::string hash_type = m_mechanism.substr(5U) ;
250 secret = m_secrets.serverSecret( hash_type ,
id ) ;
251 if( !secret.valid() )
252 secret = m_secrets.serverSecret(
"plain"_sv ,
id ) ;
255 if( !secret.valid() )
257 m_authenticated = false ;
263 m_mechanism ==
"APOP" ?
264 Cram::validate(
"MD5" ,
false , secret , m_challenge , response ) :
265 Cram::validate( m_mechanism.substr(5U) , true , secret , m_challenge , response ) ;
269 else if( m_mechanism ==
"PLAIN" )
276 secret = m_secrets.serverSecret(
"plain"_sv ,
id ) ;
278 m_authenticated = secret.valid() && !
id.empty() && !pwd_in.empty() && pwd_in == secret.secret() ;
282 else if( first_apply )
285 G_ASSERT( m_mechanism ==
"LOGIN" ) ;
286 id = m_id = response ;
288 next_challenge = G::sv_to_string(login_challenge_2) ;
292 G_ASSERT( m_mechanism ==
"LOGIN" ) ;
294 secret = m_secrets.serverSecret(
"plain"_sv , m_id ) ;
295 m_authenticated = secret.valid() && !response.empty() && response == secret.secret() ;
301 std::ostringstream ss ;
303 << (m_authenticated?
"successful":
"failed") <<
" authentication of remote client using mechanism ["
304 <<
G::Str::lower(m_mechanism) <<
"] and " << secret.info(
id) ;
305 if( m_authenticated )
306 G_LOG(
"GAuth::SaslServerBasicImp::apply: " << ss.str() ) ;
308 G_WARNING(
"GAuth::SaslServerBasicImp::apply: " << ss.str() ) ;
311 return next_challenge ;
314bool GAuth::SaslServerBasicImp::trusted(
const G::StringArray & address_wildcards ,
const std::string & address_display )
const
316 return std::any_of( address_wildcards.cbegin() , address_wildcards.cend() ,
317 [&](
const std::string &wca){return trustedCore(wca,address_display);} ) ;
320bool GAuth::SaslServerBasicImp::trustedCore(
const std::string & address_wildcard ,
const std::string & address_display )
const
322 G_DEBUG(
"GAuth::SaslServerBasicImp::trustedCore: \"" << address_wildcard <<
"\", \"" << address_display <<
"\"" ) ;
323 std::pair<std::string,std::string> pair = m_secrets.serverTrust( address_wildcard ) ;
324 std::string & trustee = pair.first ;
325 if( !trustee.empty() )
327 G_LOG(
"GAuth::SaslServer::trusted: trusting [" << address_display <<
"]: "
328 <<
"matched [" << address_wildcard <<
"] from " << pair.second ) ;
329 const_cast<SaslServerBasicImp*
>(
this)->m_trustee = trustee ;
338std::string GAuth::SaslServerBasicImp::mechanism()
const
343std::string GAuth::SaslServerBasicImp::id()
const
345 return m_authenticated ? m_id : m_trustee ;
348bool GAuth::SaslServerBasicImp::authenticated()
const
350 return m_authenticated ;
356 const std::string & config ,
const std::string & challenge_domain ) :
357 m_imp(
std::make_unique<SaslServerBasicImp>(secrets,with_apop,config,challenge_domain))
361GAuth::SaslServerBasic::~SaslServerBasic()
364G::StringArray GAuth::SaslServerBasic::mechanisms(
bool secure )
const
366 return m_imp->mechanisms( secure ) ;
369std::string GAuth::SaslServerBasic::mechanism()
const
371 return m_imp->mechanism() ;
374std::string GAuth::SaslServerBasic::preferredMechanism(
bool secure )
const
376 return m_imp->preferredMechanism( secure ) ;
379bool GAuth::SaslServerBasic::trusted(
const G::StringArray & address_wildcards ,
380 const std::string & address_display )
const
382 return m_imp->trusted( address_wildcards , address_display ) ;
385void GAuth::SaslServerBasic::reset()
387 return m_imp->reset() ;
390bool GAuth::SaslServerBasic::init(
bool secure ,
const std::string & mechanism )
392 return m_imp->init( secure , mechanism ) ;
395bool GAuth::SaslServerBasic::mustChallenge()
const
397 const bool plain =
G::Str::imatch( m_imp->mechanism() ,
"PLAIN" ) ;
398 const bool login = !plain &&
G::Str::imatch( m_imp->mechanism() ,
"LOGIN" ) ;
399 return !plain && !login ;
402std::string GAuth::SaslServerBasic::initialChallenge()
const
404 return m_imp->initialChallenge() ;
407std::string GAuth::SaslServerBasic::apply(
const std::string & response ,
bool & done )
409 return m_imp->apply( response , done ) ;
412bool GAuth::SaslServerBasic::authenticated()
const
414 return m_imp->authenticated() ;
417std::string GAuth::SaslServerBasic::id()
const
SaslServerBasic(const SaslServerSecrets &, bool allow_pop, const std::string &config, const std::string &challenge_domain)
Constructor.
An interface used by GAuth::SaslServer to obtain authentication secrets.
static string_view tailView(string_view in, std::size_t pos, string_view default_={}) noexcept
Like tail() but returning a view into the input string.
static bool imatch(char, char) noexcept
Returns true if the two characters are the same, ignoring seven-bit case.
static void splitIntoTokens(const std::string &in, StringArray &out, string_view ws, char esc='\0')
Splits the string into 'ws'-delimited tokens.
static std::string lower(string_view)
Returns a copy of 's' in which all seven-bit upper-case characters have been replaced by lower-case c...
static std::string printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
static std::string upper(string_view)
Returns a copy of 's' in which all seven-bit lower-case characters have been replaced by upper-case c...
static std::string head(string_view in, std::size_t pos, string_view default_={})
Returns the first part of the string up to just before the given position.
static bool headMatch(const std::string &in, string_view head) noexcept
Returns true if the string has the given start (or head is empty).
A class like c++17's std::string_view.
unsigned int rand(unsigned int start=0U, unsigned int end=32767)
Returns a random value, uniformly distributed over the given range (including 'start' and 'end'),...
std::string headMatchResidue(const StringArray &list, string_view head)
Returns the unmatched part of the first string in the array that has the given start.
bool headMatch(const StringArray &list, string_view head)
Returns true if any string in the array has the given start (or 'head' is empty).
std::vector< std::string > StringArray
A std::vector of std::strings.
Filters a list of strings with allow and deny lists.