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