gsaslclient_native.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2013 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gsaslclient_native.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gnet.h"
23 #include "gauth.h"
24 #include "gsaslclient.h"
25 #include "gmd5.h"
26 #include "gstr.h"
27 #include "gdebug.h"
28 #include <algorithm> // set_intersection
29 #include <set>
30 
31 namespace
32 {
33  const char * login_challenge_1 = "Username:" ;
34  const char * login_challenge_2 = "Password:" ;
35 }
36 
41 {
42 public:
44  explicit SaslClientImp( const SaslClient::Secrets & ) ;
45  static std::string clientResponse( const std::string & secret ,
46  const std::string & challenge , bool cram , bool & error ) ;
47  static std::string digest( const std::string & secret , const std::string & challenge ) ;
48  static std::string cramDigest( const std::string & secret , const std::string & challenge ) ;
49 } ;
50 
51 // ===
52 
54  m_secrets(secrets)
55 {
56 }
57 
58 std::string GAuth::SaslClientImp::clientResponse( const std::string & secret ,
59  const std::string & challenge , bool cram , bool & error )
60 {
61  try
62  {
63  G_DEBUG( "GAuth::SaslClientImp::clientResponse: challenge=\"" << challenge << "\"" ) ;
64  return cram ? cramDigest(secret,challenge) : digest(secret,challenge) ;
65  }
66  catch( std::exception & e )
67  {
68  std::string what = e.what() ;
69  G_DEBUG( "GAuth::SaslClient: " << what ) ;
70  error = true ;
71  }
72  return std::string() ;
73 }
74 
75 std::string GAuth::SaslClientImp::cramDigest( const std::string & secret , const std::string & challenge )
76 {
77  return G::Md5::printable(G::Md5::hmac(secret,challenge,G::Md5::Masked())) ;
78 }
79 
80 std::string GAuth::SaslClientImp::digest( const std::string & secret , const std::string & challenge )
81 {
82  return G::Md5::printable(G::Md5::digest(challenge,secret)) ;
83 }
84 
85 // ===
86 
87 GAuth::SaslClient::SaslClient( const SaslClient::Secrets & secrets , const std::string & server_name ) :
88  m_imp(new SaslClientImp(secrets) )
89 {
90  G_IGNORE_PARAMETER(std::string,server_name) ;
91  G_DEBUG( "GAuth::SaslClient::ctor: server-name=\"" << server_name << "\", active=" << active() ) ;
92 }
93 
95 {
96  delete m_imp ;
97 }
98 
100 {
101  return m_imp->m_secrets.valid() ;
102 }
103 
104 std::string GAuth::SaslClient::initial_response( const std::string & mechanism , bool & done ,
105  bool & error , bool & sensitive ) const
106 {
107  done = false ;
108  error = false ;
109  sensitive = false ;
110 
111  std::string auth("AUTH") ;
112  std::string sep(" ") ;
113 
114  std::string rsp ;
115  if( mechanism == "XOAUTH2" )
116  {
117  std::string secret = m_imp->m_secrets.secret(mechanism) ;
118  rsp = auth + sep + mechanism + sep + secret ;
119  error = secret.empty() ;
120  done = true ;
121  sensitive = true ;
122  }
123  else
124  {
125  rsp = auth + sep + mechanism ;
126  }
127 
128  return rsp ;
129 }
130 
131 std::string GAuth::SaslClient::response( const std::string & mechanism , const std::string & challenge ,
132  bool & done , bool & error , bool & sensitive ) const
133 {
134  done = false ;
135  error = false ;
136  sensitive = false ;
137 
138  std::string rsp ;
139  if( mechanism == "CRAM-MD5" || mechanism == "APOP" )
140  {
141  const bool cram = mechanism == "CRAM-MD5" ;
142  std::string id = m_imp->m_secrets.id(mechanism) ;
143  std::string secret = m_imp->m_secrets.secret(mechanism) ;
144  error = id.empty() || secret.empty() ;
145  if( !error )
146  rsp = id + " " + SaslClientImp::clientResponse( secret , challenge , cram , error ) ;
147 
148  done = true ;
149  }
150  else if( mechanism == "PLAIN" )
151  {
152  std::string sep( 1U , '\0' ) ;
153  std::string id = m_imp->m_secrets.id(mechanism) ;
154  std::string secret = m_imp->m_secrets.secret(mechanism) ;
155  rsp = sep + id + sep + secret ;
156  error = id.empty() || secret.empty() ;
157  done = true ;
158  sensitive = true ;
159  }
160  else if( mechanism == "LOGIN" )
161  {
162  if( challenge == login_challenge_1 )
163  {
164  rsp = m_imp->m_secrets.id(mechanism) ;
165  error = rsp.empty() ;
166  done = false ;
167  }
168  else if( challenge == login_challenge_2 )
169  {
170  rsp = m_imp->m_secrets.secret(mechanism) ;
171  error = rsp.empty() ;
172  done = true ;
173  sensitive = true ;
174  }
175  else
176  {
177  error = true ;
178  }
179  }
180  else
181  {
182  error = true ;
183  }
184 
185  if( error )
186  {
187  G_WARNING( "GAuth::SaslClient: invalid challenge" ) ;
188  done = true ;
189  }
190 
191  return rsp ;
192 }
193 
194 std::string GAuth::SaslClient::preferred( const G::Strings & mechanism_list ) const
195 {
196  G_DEBUG( "GAuth::SaslClient::preferred: server's mechanisms: [" << G::Str::join(mechanism_list,",") << "]" ) ;
197 
198  // short-circuit if no secrets
199  //
200  if( !active() )
201  return std::string() ;
202 
203  const std::string login( "LOGIN" ) ;
204  const std::string plain( "PLAIN" ) ;
205  const std::string xoauth2( "XOAUTH2" ) ;
206  const std::string cram( "CRAM-MD5" ) ;
207 
208  // create a them set
209  std::set<std::string> them ;
210  for( G::Strings::const_iterator p = mechanism_list.begin() ; p != mechanism_list.end() ; ++p )
211  them.insert( G::Str::upper(*p) ) ;
212 
213  // create an us set
214  std::set<std::string> us ;
215  if( !m_imp->m_secrets.id(login).empty() ) us.insert(login) ;
216  if( !m_imp->m_secrets.id(plain).empty() ) us.insert(plain) ;
217  if( !m_imp->m_secrets.id(xoauth2).empty() ) us.insert(xoauth2) ;
218  if( !m_imp->m_secrets.id(cram).empty() ) us.insert(cram) ;
219 
220  // get the intersection
221  std::set<std::string> both ;
222  std::set_intersection( them.begin() , them.end() , us.begin() , us.end() , std::inserter(both,both.end()) ) ;
223 
224  // preferred order: cram, xoauth2, plain, login
225  std::string m ;
226  if( m.empty() && both.find(cram) != both.end() ) m = cram ;
227  if( m.empty() && both.find(xoauth2) != both.end() ) m = xoauth2 ;
228  if( m.empty() && both.find(plain) != both.end() ) m = plain ;
229  if( m.empty() && both.find(login) != both.end() ) m = login ;
230  G_DEBUG( "GAuth::SaslClient::preferred: we prefer \"" << m << "\"" ) ;
231 
232  return m ;
233 }
234 
235 // ==
236 
238 {
239 }
240 
An overload discriminator for G::Md5::hmac()
Definition: gmd5.h:43
std::string preferred(const G::Strings &mechanisms) const
Returns the name of the preferred mechanism taken from the given set.
static std::string digest(const std::string &secret, const std::string &challenge)
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.
std::list< std::string > Strings
A std::list of std::strings.
Definition: gstrings.h:39
SaslClientImp(const SaslClient::Secrets &)
SaslClient(const Secrets &secrets, const std::string &server_name)
Constructor. The secrets reference is kept.
bool active() const
Returns true if the constructor's secrets object is valid.
static std::string hmac(const std::string &key, const std::string &input)
Computes a Hashed Message Authentication Code using MD5 as the hash function.
A private pimple-pattern implementation class used by GAuth::SaslClient.
An interface used by GAuth::SaslClient to obtain authentication secrets.
Definition: gsaslclient.h:52
static std::string cramDigest(const std::string &secret, const std::string &challenge)
static std::string upper(const std::string &s)
Returns a copy of 's' in which all lowercase characters have been replaced by uppercase characters...
Definition: gstr.cpp:426
#define G_DEBUG(expr)
Definition: glog.h:95
static std::string digest(const std::string &input)
Creates an MD5 digest.
const SaslClient::Secrets & m_secrets
static std::string clientResponse(const std::string &secret, const std::string &challenge, bool cram, bool &error)
~SaslClient()
Destructor.
std::string initial_response(const std::string &mechanism, bool &done, bool &error, bool &sensitive) const
Returns an initial_response for authentication.
static std::string join(const Strings &strings, const std::string &sep)
Concatenates a set of strings.
Definition: gstr.cpp:799
static std::string printable(const std::string &input)
Converts a binary string into a printable form, using a lowercase hexadecimal encoding.
#define G_WARNING(expr)
Definition: glog.h:107