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 ;
61 ~SaslServerBasicImp() = default ;
62 SaslServerBasicImp(
const SaslServerBasicImp & ) = delete ;
63 SaslServerBasicImp( SaslServerBasicImp && ) = delete ;
64 SaslServerBasicImp & operator=(
const SaslServerBasicImp & ) = delete ;
65 SaslServerBasicImp & operator=( SaslServerBasicImp && ) = delete ;
68 bool m_first_apply {
true} ;
69 const SaslServerSecrets & m_secrets ;
72 std::string m_mechanism ;
73 std::string m_challenge ;
74 std::string m_challenge_domain ;
75 bool m_authenticated {
false} ;
77 std::string m_trustee ;
78 static std::string_view login_challenge_1 ;
79 static std::string_view login_challenge_2 ;
82std::string_view GAuth::SaslServerBasicImp::login_challenge_1 {
"Username:",9U} ;
83std::string_view GAuth::SaslServerBasicImp::login_challenge_2 {
"Password:",9U} ;
87GAuth::SaslServerBasicImp::SaslServerBasicImp(
const SaslServerSecrets & secrets ,
88 bool with_apop ,
const std::string & config ,
const std::string & challenge_domain ) :
90 m_challenge_domain(challenge_domain)
96 if( secrets.contains(
"PLAIN" , {} ) )
98 mechanisms = Cram::hashTypes(
"CRAM-" ) ;
99 mechanisms.emplace_back(
"PLAIN" ) ;
100 mechanisms.emplace_back(
"LOGIN" ) ;
105 for(
const auto & cram : Cram::hashTypes({},
true) )
107 if( secrets.contains( cram , {} ) )
108 mechanisms.push_back( std::string(
"CRAM-").append(cram) ) ;
112 mechanisms.emplace_back(
"APOP" ) ;
115 m_mechanisms_secure = mechanisms ;
116 m_mechanisms_insecure = mechanisms ;
119 if( m_mechanisms_secure.empty() && secrets.valid() )
121 m_mechanisms_secure.emplace_back(
"PLAIN" ) ;
148 if( have_a || have_d )
160void GAuth::SaslServerBasicImp::reset()
162 m_first_apply = true ;
163 m_authenticated = false ;
166 m_challenge.clear() ;
167 m_mechanism.clear() ;
170G::StringArray GAuth::SaslServerBasicImp::mechanisms(
bool secure )
const
172 return secure ? m_mechanisms_secure : m_mechanisms_insecure ;
175bool GAuth::SaslServerBasicImp::init(
bool secure ,
const std::string & mechanism_in )
180 const G::StringArray & mechanisms = secure ? m_mechanisms_secure : m_mechanisms_insecure ;
182 if( mechanism.empty() || std::find(mechanisms.begin(),mechanisms.end(),mechanism) == mechanisms.end() )
184 G_DEBUG(
"GAuth::SaslServerBasicImp::init: requested mechanism [" << mechanism <<
"] is not in our list" ) ;
187 else if( mechanism ==
"APOP" || mechanism.find(
"CRAM-") == 0U )
189 m_mechanism = mechanism ;
190 m_challenge = Cram::challenge(
G::Random::rand() , m_challenge_domain ) ;
195 m_mechanism = mechanism ;
200std::string GAuth::SaslServerBasicImp::preferredMechanism(
bool secure )
const
204 auto mechanism_list = mechanisms( secure ) ;
205 std::reverse( mechanism_list.begin() , mechanism_list.end() ) ;
206 for(
const auto & m : mechanism_list )
211 if( m_secrets.contains( type , m_id ) )
219std::string GAuth::SaslServerBasicImp::initialChallenge()
const
222 if( m_mechanism ==
"PLAIN" )
224 else if( m_mechanism ==
"LOGIN" )
225 return G::sv_to_string(login_challenge_1) ;
230std::string GAuth::SaslServerBasicImp::apply(
const std::string & response ,
bool & done )
232 G_DEBUG(
"GAuth::SaslServerBasic::apply: response: \"" <<
G::Str::printable(response) <<
"\"" ) ;
234 bool first_apply = m_first_apply ;
235 m_first_apply = false ;
239 Secret secret = Secret::none() ;
240 std::string next_challenge ;
242 if( m_mechanism.find(
"CRAM-") == 0U || m_mechanism ==
"APOP" )
244 id = Cram::id( response ) ;
245 secret = Secret::none() ;
248 if( m_mechanism ==
"APOP" )
250 secret = m_secrets.serverSecret(
"plain"_sv ,
id ) ;
254 std::string hash_type = m_mechanism.substr(5U) ;
255 secret = m_secrets.serverSecret( hash_type ,
id ) ;
256 if( !secret.valid() )
257 secret = m_secrets.serverSecret(
"plain"_sv ,
id ) ;
260 if( !secret.valid() )
262 m_authenticated = false ;
268 m_mechanism ==
"APOP" ?
269 Cram::validate(
"MD5" ,
false , secret , m_challenge , response ) :
270 Cram::validate( m_mechanism.substr(5U) , true , secret , m_challenge , response ) ;
274 else if( m_mechanism ==
"PLAIN" )
277 std::string_view sep(
"\0" , 1U ) ;
281 secret = m_secrets.serverSecret(
"plain"_sv ,
id ) ;
283 m_authenticated = secret.valid() && !
id.empty() && !pwd_in.empty() && pwd_in == secret.secret() ;
287 else if( first_apply )
290 G_ASSERT( m_mechanism ==
"LOGIN" ) ;
291 id = m_id = response ;
293 next_challenge = G::sv_to_string(login_challenge_2) ;
297 G_ASSERT( m_mechanism ==
"LOGIN" ) ;
299 secret = m_secrets.serverSecret(
"plain"_sv , m_id ) ;
300 m_authenticated = secret.valid() && !response.empty() && response == secret.secret() ;
306 std::ostringstream ss ;
308 << (m_authenticated?
"successful":
"failed") <<
" authentication of remote client using mechanism ["
309 <<
G::Str::lower(m_mechanism) <<
"] and " << secret.info(
id) ;
310 if( m_authenticated )
311 G_LOG(
"GAuth::SaslServerBasicImp::apply: " << ss.str() ) ;
313 G_WARNING(
"GAuth::SaslServerBasicImp::apply: " << ss.str() ) ;
316 return next_challenge ;
319bool GAuth::SaslServerBasicImp::trusted(
const G::StringArray & address_wildcards ,
const std::string & address_display )
const
321 return std::any_of( address_wildcards.cbegin() , address_wildcards.cend() ,
322 [&](
const std::string &wca){return trustedCore(wca,address_display);} ) ;
325bool GAuth::SaslServerBasicImp::trustedCore(
const std::string & address_wildcard ,
const std::string & address_display )
const
327 G_DEBUG(
"GAuth::SaslServerBasicImp::trustedCore: \"" << address_wildcard <<
"\", \"" << address_display <<
"\"" ) ;
328 std::pair<std::string,std::string> pair = m_secrets.serverTrust( address_wildcard ) ;
329 std::string & trustee = pair.first ;
330 if( !trustee.empty() )
332 G_LOG(
"GAuth::SaslServer::trusted: trusting [" << address_display <<
"]: "
333 <<
"matched [" << address_wildcard <<
"] from " << pair.second ) ;
334 const_cast<SaslServerBasicImp*
>(
this)->m_trustee = trustee ;
343std::string GAuth::SaslServerBasicImp::mechanism()
const
348std::string GAuth::SaslServerBasicImp::id()
const
350 return m_authenticated ? m_id : m_trustee ;
353bool GAuth::SaslServerBasicImp::authenticated()
const
355 return m_authenticated ;
361 const std::string & config ,
const std::string & challenge_domain ) :
362 m_imp(
std::make_unique<SaslServerBasicImp>(secrets,with_apop,config,challenge_domain))
366GAuth::SaslServerBasic::~SaslServerBasic()
369G::StringArray GAuth::SaslServerBasic::mechanisms(
bool secure )
const
371 return m_imp->mechanisms( secure ) ;
374std::string GAuth::SaslServerBasic::mechanism()
const
376 return m_imp->mechanism() ;
379std::string GAuth::SaslServerBasic::preferredMechanism(
bool secure )
const
381 return m_imp->preferredMechanism( secure ) ;
384bool GAuth::SaslServerBasic::trusted(
const G::StringArray & address_wildcards ,
385 const std::string & address_display )
const
387 return m_imp->trusted( address_wildcards , address_display ) ;
390void GAuth::SaslServerBasic::reset()
395bool GAuth::SaslServerBasic::init(
bool secure ,
const std::string & mechanism )
397 return m_imp->init( secure , mechanism ) ;
400bool GAuth::SaslServerBasic::mustChallenge()
const
402 const bool plain =
G::Str::imatch( m_imp->mechanism() ,
"PLAIN" ) ;
403 const bool login = !plain &&
G::Str::imatch( m_imp->mechanism() ,
"LOGIN" ) ;
404 return !plain && !login ;
407std::string GAuth::SaslServerBasic::initialChallenge()
const
409 return m_imp->initialChallenge() ;
412std::string GAuth::SaslServerBasic::apply(
const std::string & response ,
bool & done )
414 return m_imp->apply( response , done ) ;
417bool GAuth::SaslServerBasic::authenticated()
const
419 return m_imp->authenticated() ;
422std::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 std::string lower(std::string_view)
Returns a copy of 's' in which all seven-bit upper-case characters have been replaced by lower-case c...
static void splitIntoTokens(const std::string &in, StringArray &out, std::string_view ws, char esc='\0')
Splits the string into 'ws'-delimited tokens.
static bool imatch(char, char) noexcept
Returns true if the two characters are the same, ignoring seven-bit case.
static bool headMatch(std::string_view in, std::string_view head) noexcept
Returns true if the string has the given start (or head is empty).
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(std::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_view tailView(std::string_view in, std::size_t pos, std::string_view default_={}) noexcept
Like tail() but returning a view into the input string.
static std::string head(std::string_view in, std::size_t pos, std::string_view default_={})
Returns the first part of the string up to just before the given position.
A class template like a simplified c++17 std::optional.
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'),...
bool headMatch(const StringArray &list, std::string_view head)
Returns true if any string in the array has the given start (or 'head' is empty).
std::string headMatchResidue(const StringArray &list, std::string_view head)
Returns the unmatched part of the first string in the array that has the given start.
std::vector< std::string > StringArray
A std::vector of std::strings.
Filters a list of strings with allow and deny lists.