E-MailRelay
gsaslserverpam.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 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/// \file gsaslserverpam.cpp
19///
20
21#include "gdef.h"
22#include "gpam.h"
23#include "gsaslserverpam.h"
24#include "gexception.h"
25#include "gstr.h"
26#include "glog.h"
27
28namespace GAuth
29{
30 class PamImp ;
31 class SaslServerPamImp ;
32}
33
34//| \class GAuth::SaslServerPamImp
35/// A private implementation class used by GAuth::SaslServerPam.
36///
38{
39public:
40 SaslServerPamImp( bool valid , const std::string & config , bool allow_apop ) ;
41 virtual ~SaslServerPamImp() ;
42 bool active() const ;
43 bool init( const std::string & mechanism ) ;
44 std::string apply( const std::string & pwd , bool & done ) ;
45 std::string id() const ;
46 bool authenticated() const ;
47
48public:
49 SaslServerPamImp( const SaslServerPamImp & ) = delete ;
50 SaslServerPamImp( SaslServerPamImp && ) = delete ;
51 void operator=( const SaslServerPamImp & ) = delete ;
52 void operator=( SaslServerPamImp && ) = delete ;
53
54private:
55 bool m_active ;
56 bool m_allow_apop ;
57 std::unique_ptr<PamImp> m_pam ;
58} ;
59
60//| \class GAuth::PamImp
61/// A private implementation of the G::Pam interface used by
62/// GAuth::SaslServerPamImp, which is itself a private implementation
63/// class used by GAuth::SaslServerPam.
64///
65class GAuth::PamImp : public G::Pam
66{
67public:
68 using ItemArray = GAuth::PamImp::ItemArray ;
69 G_EXCEPTION_CLASS( NoPrompt , "no password prompt received from pam module" ) ;
70
71 PamImp( const std::string & app , const std::string & id ) ;
72 ~PamImp() override ;
73 void fail() ;
74 void apply( const std::string & ) ;
75 std::string id() const ;
76
77protected:
78 void converse( ItemArray & ) override ;
79 void delay( unsigned int usec ) override ;
80
81public:
82 PamImp( const PamImp & ) = delete ;
83 PamImp( PamImp && ) = delete ;
84 void operator=( const PamImp & ) = delete ;
85 void operator=( PamImp && ) = delete ;
86
87private:
88 std::string m_app ;
89 std::string m_id ;
90 std::string m_pwd ;
91} ;
92
93GAuth::PamImp::PamImp( const std::string & app , const std::string & id ) :
94 G::Pam(app,id,true) ,
95 m_app(app) ,
96 m_id(id)
97{
98 G_DEBUG( "GAuth::PamImp::ctor: [" << app << "] [" << id << "]" ) ;
99}
100
101GAuth::PamImp::~PamImp()
102= default;
103
104std::string GAuth::PamImp::id() const
105{
106 return m_id ;
107}
108
109void GAuth::PamImp::converse( ItemArray & items )
110{
111 bool done = false ;
112 for( auto & item : items )
113 {
114 if( item.in_type == "password" )
115 {
116 item.out = m_pwd ;
117 item.out_defined = true ;
118 done = true ;
119 }
120 }
121 if( !done )
122 {
123 throw NoPrompt() ;
124 }
125}
126
127void GAuth::PamImp::apply( const std::string & pwd )
128{
129 m_pwd = pwd ;
130 authenticate( true ) ; // base class -- calls converse() -- thows on error
131}
132
133void GAuth::PamImp::delay( unsigned int )
134{
135 // TODO asynchronous implementation of pam delay callback
136 // ... but that would require the SaslServer interface be made asynchronous
137 // so the result of the apply() (ie. the next challenge) gets delivered
138 // via a callback -- the complexity trade-off is not compelling
139}
140
141// ==
142
143GAuth::SaslServerPamImp::SaslServerPamImp( bool active , const std::string & /*config*/ , bool allow_apop ) :
144 m_active(active) ,
145 m_allow_apop(allow_apop)
146{
147}
148
149GAuth::SaslServerPamImp::~SaslServerPamImp()
150= default;
151
152bool GAuth::SaslServerPamImp::active() const
153{
154 return m_active ;
155}
156
157bool GAuth::SaslServerPamImp::init( const std::string & mechanism )
158{
159 return
160 G::Str::upper(mechanism) == "PLAIN" ||
161 ( m_allow_apop && G::Str::upper(mechanism) == "APOP" ) ;
162}
163
164std::string GAuth::SaslServerPamImp::id() const
165{
166 return m_pam ? m_pam->id() : std::string() ;
167}
168
169std::string GAuth::SaslServerPamImp::apply( const std::string & response , bool & done )
170{
171 // parse the PLAIN response
172 std::string sep( 1U , '\0' ) ;
173 std::string s = G::Str::tail( response , response.find(sep) , std::string() ) ;
174 std::string id = G::Str::head( s , s.find(sep) , std::string() ) ;
175 std::string pwd = G::Str::tail( s , s.find(sep) , std::string() ) ;
176
177 m_pam = std::make_unique<PamImp>( "emailrelay" , id ) ;
178
179 try
180 {
181 m_pam->apply( pwd ) ;
182 }
183 catch( G::Pam::Error & e )
184 {
185 G_WARNING( "GAuth::SaslServer::apply: " << e.what() ) ;
186 m_pam.reset() ;
187 }
188 catch( PamImp::NoPrompt & e )
189 {
190 G_WARNING( "GAuth::SaslServer::apply: pam error: " << e.what() ) ;
191 m_pam.reset() ;
192 }
193
194 done = true ; // (only single challenge-response supported)
195 return std::string() ; // challenge
196}
197
198// ==
199
200GAuth::SaslServerPam::SaslServerPam( const SaslServerSecrets & secrets , const std::string & config , bool allow_apop ) :
201 m_imp(std::make_unique<SaslServerPamImp>(secrets.valid(),config,allow_apop))
202{
203}
204
205GAuth::SaslServerPam::~SaslServerPam()
206= default ;
207
208std::string GAuth::SaslServerPam::mechanisms( char ) const
209{
210 return "PLAIN" ;
211}
212
213std::string GAuth::SaslServerPam::mechanism() const
214{
215 return "PLAIN" ;
216}
217
218bool GAuth::SaslServerPam::trusted( const GNet::Address & ) const
219{
220 return false ;
221}
222
223bool GAuth::SaslServerPam::active() const
224{
225 return m_imp->active() ;
226}
227
228bool GAuth::SaslServerPam::mustChallenge() const
229{
230 return false ;
231}
232
233bool GAuth::SaslServerPam::init( const std::string & mechanism )
234{
235 return m_imp->init( mechanism ) ;
236}
237
238std::string GAuth::SaslServerPam::initialChallenge() const
239{
240 return std::string() ;
241}
242
243std::string GAuth::SaslServerPam::apply( const std::string & response , bool & done )
244{
245 return m_imp->apply( response , done ) ;
246}
247
248bool GAuth::SaslServerPam::authenticated() const
249{
250 return !m_imp->id().empty() ;
251}
252
253std::string GAuth::SaslServerPam::id() const
254{
255 return m_imp->id() ;
256}
257
258bool GAuth::SaslServerPam::requiresEncryption() const
259{
260 return true ;
261}
262
A private implementation of the G::Pam interface used by GAuth::SaslServerPamImp, which is itself a p...
void converse(ItemArray &) override
Called to pass a message to the user, or request a password etc.
void delay(unsigned int usec) override
Called when the pam library wants the application to introduce a delay to prevent brute-force attacks...
A private implementation class used by GAuth::SaslServerPam.
SaslServerPam(const SaslServerSecrets &, const std::string &config, bool allow_apop)
Constructor.
An interface used by GAuth::SaslServer to obtain authentication secrets.
The GNet::Address class encapsulates a TCP/UDP transport address.
Definition: gaddress.h:53
An exception class for G::Pam.
Definition: gpam.h:69
A thin interface to the system PAM library, with two pure virtual methods that derived classes should...
Definition: gpam.h:59
static std::string tail(const std::string &in, std::size_t pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
Definition: gstr.cpp:1287
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...
Definition: gstr.cpp:754
static std::string head(const std::string &in, std::size_t pos, const std::string &default_=std::string())
Returns the first part of the string up to just before the given position.
Definition: gstr.cpp:1273
SASL authentication classes.
Definition: gcram.cpp:36
Low-level classes.
Definition: galign.h:28