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::response( const std::string & mechanism , const std::string & challenge ,
105  bool & done , bool & error , bool & sensitive ) const
106 {
107  done = false ;
108  error = false ;
109  sensitive = false ;
110 
111  std::string rsp ;
112  if( mechanism == "CRAM-MD5" || mechanism == "APOP" )
113  {
114  const bool cram = mechanism == "CRAM-MD5" ;
115  std::string id = m_imp->m_secrets.id(mechanism) ;
116  std::string secret = m_imp->m_secrets.secret(mechanism) ;
117  error = id.empty() || secret.empty() ;
118  if( !error )
119  rsp = id + " " + SaslClientImp::clientResponse( secret , challenge , cram , error ) ;
120 
121  done = true ;
122  }
123  else if( mechanism == "PLAIN" )
124  {
125  std::string sep( 1U , '\0' ) ;
126  std::string id = m_imp->m_secrets.id(mechanism) ;
127  std::string secret = m_imp->m_secrets.secret(mechanism) ;
128  rsp = sep + id + sep + secret ;
129  error = id.empty() || secret.empty() ;
130  done = true ;
131  sensitive = true ;
132  }
133  else if( mechanism == "LOGIN" )
134  {
135  if( challenge == login_challenge_1 )
136  {
137  rsp = m_imp->m_secrets.id(mechanism) ;
138  error = rsp.empty() ;
139  done = false ;
140  }
141  else if( challenge == login_challenge_2 )
142  {
143  rsp = m_imp->m_secrets.secret(mechanism) ;
144  error = rsp.empty() ;
145  done = true ;
146  sensitive = true ;
147  }
148  else
149  {
150  error = true ;
151  }
152  }
153  else
154  {
155  error = true ;
156  }
157 
158  if( error )
159  {
160  G_WARNING( "GAuth::SaslClient: invalid challenge" ) ;
161  done = true ;
162  }
163 
164  return rsp ;
165 }
166 
167 std::string GAuth::SaslClient::preferred( const G::Strings & mechanism_list ) const
168 {
169  G_DEBUG( "GAuth::SaslClient::preferred: server's mechanisms: [" << G::Str::join(mechanism_list,",") << "]" ) ;
170 
171  // short-circuit if no secrets
172  //
173  if( !active() )
174  return std::string() ;
175 
176  const std::string login( "LOGIN" ) ;
177  const std::string plain( "PLAIN" ) ;
178  const std::string cram( "CRAM-MD5" ) ;
179 
180  // create a them set
181  std::set<std::string> them ;
182  for( G::Strings::const_iterator p = mechanism_list.begin() ; p != mechanism_list.end() ; ++p )
183  them.insert( G::Str::upper(*p) ) ;
184 
185  // create an us set
186  std::set<std::string> us ;
187  if( !m_imp->m_secrets.id(login).empty() ) us.insert(login) ;
188  if( !m_imp->m_secrets.id(plain).empty() ) us.insert(plain) ;
189  if( !m_imp->m_secrets.id(cram).empty() ) us.insert(cram) ;
190 
191  // get the intersection
192  std::set<std::string> both ;
193  std::set_intersection( them.begin() , them.end() , us.begin() , us.end() , std::inserter(both,both.end()) ) ;
194 
195  // preferred order: cram, plain, login
196  std::string m ;
197  if( m.empty() && both.find(cram) != both.end() ) m = cram ;
198  if( m.empty() && both.find(plain) != both.end() ) m = plain ;
199  if( m.empty() && both.find(login) != both.end() ) m = login ;
200  G_DEBUG( "GAuth::SaslClient::preferred: we prefer \"" << m << "\"" ) ;
201 
202  return m ;
203 }
204 
205 // ==
206 
208 {
209 }
210 
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.
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