E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gpam_linux.cpp
1 //
2 // Copyright (C) 2001-2018 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 // gpam_linux.cpp
19 //
20 // See: http://www.linux-pam.org/Linux-PAM-html/
21 //
22
23 #include "gdef.h"
24 #include "gpam.h"
25 #include "glog.h"
26 #include "gstr.h"
27 #include "gexception.h"
28 #include "gassert.h"
29 #include <cstdlib>
30 #include <cstring>
31 #include <sstream>
32 #include <stdexcept>
33 #include <new>
34 #include <memory.h>
35 #include <sys/types.h>
36 #include <sys/time.h>
37
38 #if GCONFIG_HAVE_PAM_IN_INCLUDE
39 #include <pam_appl.h>
40 #else
41 #if GCONFIG_HAVE_PAM_IN_PAM
42 #include <pam/pam_appl.h>
43 #else
44 #include <security/pam_appl.h>
45 #endif
46 #endif
47
48 namespace
49 {
50 char * strdup_( const char * p )
51 {
52 p = p ? p : "" ;
53 char * copy = static_cast<char*>( std::malloc(std::strlen(p)+1U) ) ;
54 if( copy != nullptr )
55 std::strcpy( copy , p ) ;
56 return copy ;
57 }
58 }
59
60 // ==
61
62 /// \class G::PamImp
63 /// A pimple-pattern implementation class for G::Pam.
64 ///
65 class G::PamImp
66 {
67 public:
68 typedef pam_handle_t * Handle ;
69
70 private:
71 typedef struct pam_conv Conversation ;
72 typedef G::PamError Error ;
73 typedef G::Pam::ItemArray ItemArray ;
74 enum { MAGIC = 3456 } ;
75
76 public:
77 Pam & m_pam ;
78 int m_magic ;
79 mutable int m_rc ; // required for pam_end()
80 Handle m_hpam ;
81 Conversation m_conv ;
82 bool m_silent ;
83
84 public:
85 PamImp( G::Pam & pam , const std::string & app , const std::string & user , bool silent ) ;
86 ~PamImp() ;
87 Handle hpam() const ;
88 bool silent() const ;
89 bool authenticate( bool ) ;
90 void check( const std::string & , int ) const ;
91 static bool success( int ) ;
92 void setCredentials( int ) ;
93 void checkAccount( bool ) ;
94 void openSession() ;
95 void closeSession() ;
96 std::string name() const ;
97
98 private:
99 static int converseCallback( int n , const struct pam_message ** in , struct pam_response ** out , void * vp ) ;
100 static void delayCallback( int , unsigned , void * ) ;
101 static std::string decodeStyle( int pam_style ) ;
102 static void release( struct pam_response * , size_t ) ;
103 } ;
104
105 // ==
106
107 G::PamImp::PamImp( G::Pam & pam , const std::string & application , const std::string & user , bool silent ) :
108 m_pam(pam) ,
109 m_magic(MAGIC) ,
110 m_rc(PAM_SUCCESS) ,
111 m_silent(silent)
112 {
113 G_DEBUG( "G::PamImp::ctor: [" << application << "] [" << user << "]" ) ;
114
115 m_conv.conv = converseCallback ;
116 m_conv.appdata_ptr = this ;
117 m_rc = ::pam_start( application.c_str() , user.c_str() , &m_conv , &m_hpam ) ;
118 if( m_rc != PAM_SUCCESS )
119 {
120 throw Error( "pam_start" , m_rc ) ;
121 }
122
123 // (linux-specific)
124 #ifdef PAM_FAIL_DELAY
125 m_rc = ::pam_set_item( m_hpam , PAM_FAIL_DELAY , reinterpret_cast<const void*>(delayCallback) ) ;
126 if( m_rc != PAM_SUCCESS )
127 {
128 ::pam_end( m_hpam , m_rc ) ;
129 throw Error( "pam_set_item" , m_rc , ::pam_strerror(hpam(),m_rc) ) ;
130 }
131 #endif
132 }
133
134 G::PamImp::~PamImp()
135 {
136 try
137 {
138 G_DEBUG( "G::PamImp::dtor" ) ;
139 ::pam_end( m_hpam , m_rc ) ;
140 m_magic = 0 ;
141 }
142 catch(...)
143 {
144 }
145 }
146
147 G::PamImp::Handle G::PamImp::hpam() const
148 {
149 return m_hpam ;
150 }
151
152 bool G::PamImp::silent() const
153 {
154 return m_silent ;
155 }
156
157 std::string G::PamImp::decodeStyle( int pam_style )
158 {
159 std::string defolt = std::string( "#" ) + G::Str::fromInt( pam_style ) ;
160 if( pam_style == PAM_PROMPT_ECHO_OFF ) return "password" ;
161 if( pam_style == PAM_PROMPT_ECHO_ON ) return "prompt" ;
162 if( pam_style == PAM_ERROR_MSG ) return "error" ;
163 if( pam_style == PAM_TEXT_INFO ) return "info" ;
164 return defolt ;
165 }
166
167 bool G::PamImp::authenticate( bool require_token )
168 {
169 int flags = 0 ;
170 if( silent() ) flags |= static_cast<int>(PAM_SILENT) ;
171 if( require_token ) flags |= static_cast<int>(PAM_DISALLOW_NULL_AUTHTOK) ;
172 m_rc = ::pam_authenticate( hpam() , flags ) ;
173 #ifdef PAM_INCOMPLETE
174 if( m_rc == PAM_INCOMPLETE )
175 return false ;
176 #endif
177
178 check( "pam_authenticate" , m_rc ) ;
179 return true ;
180 }
181
182 std::string G::PamImp::name() const
183 {
184 const void * vp = nullptr ;
185 m_rc = ::pam_get_item( hpam() , PAM_USER , &vp ) ;
186 check( "pam_get_item" , m_rc ) ;
187 const char * cp = reinterpret_cast<const char*>(vp) ;
188 return std::string( cp ? cp : "" ) ;
189 }
190
191 void G::PamImp::setCredentials( int flag )
192 {
193 int flags = 0 ;
194 flags |= flag ;
195 if( silent() ) flags |= static_cast<int>(PAM_SILENT) ;
196 m_rc = ::pam_setcred( hpam() , flags ) ;
197 check( "pam_setcred" , m_rc ) ;
198 }
199
200 void G::PamImp::checkAccount( bool require_token )
201 {
202 int flags = 0 ;
203 if( silent() ) flags |= static_cast<int>(PAM_SILENT) ;
204 if( require_token ) flags |= static_cast<int>(PAM_DISALLOW_NULL_AUTHTOK) ;
205 m_rc = ::pam_acct_mgmt( hpam() , flags ) ;
206 check( "pam_acct_mgmt" , m_rc ) ;
207 }
208
209 void G::PamImp::release( struct pam_response * rsp , size_t n )
210 {
211 if( rsp != nullptr )
212 {
213 for( size_t i = 0U ; i < n ; i++ )
214 {
215 if( rsp[i].resp != nullptr )
216 std::free( rsp[i].resp ) ;
217 }
218 }
219 std::free( rsp ) ;
220 }
221
222 int G::PamImp::converseCallback( int n_in , const struct pam_message ** in , struct pam_response ** out , void * vp )
223 {
224 G_ASSERT( n_in > 0 ) ;
225 G_ASSERT( out != nullptr ) ;
226 size_t n = n_in < 0 ? size_t(0U) : static_cast<size_t>(n_in) ;
227
228 // pam_conv(3) on linux points out that the pam interface is under-speficied, and on some
229 // systems, possibly including solaris, the "in" pointer is interpreted differently - this
230 // is only a problem for n greater than one, so warn about it at run-time
231 //
232 if( n > 1U )
233 {
234 G_WARNING_ONCE( "PamImp::converseCallback: received a complex pam converse() structure: proceed with caution" ) ;
235 }
236
237 *out = nullptr ;
238 struct pam_response * rsp = nullptr ;
239 try
240 {
241 G_DEBUG( "G::Pam::converseCallback: called back from pam with " << n << " item(s)" ) ;
242 PamImp * This = reinterpret_cast<PamImp*>(vp) ;
243 G_ASSERT( This->m_magic == MAGIC ) ;
244
245 // convert the c items into a c++ container -- treat
246 // "in" as a pointer to a contiguous array of pointers
247 // (see linux man pam_conv)
248 //
249 ItemArray array( n ) ;
250 for( size_t i = 0U ; i < n ; i++ )
251 {
252 std::string & s1 = const_cast<std::string&>(array[i].in_type) ;
253 s1 = decodeStyle( in[i]->msg_style ) ;
254
255 std::string & s2 = const_cast<std::string&>(array[i].in) ;
256 s2 = std::string(in[i]->msg ? in[i]->msg : "") ;
257
258 array[i].out_defined = false ;
259 }
260
261 // do the conversation
262 //
263 This->m_pam.converse( array ) ;
264 G_ASSERT( array.size() == n ) ;
265
266 // allocate the response - treat "out" as a pointer to a pointer
267 // to a contiguous array of structures (see linux man pam_conv)
268 //
269 rsp = reinterpret_cast<struct pam_response*>( std::malloc(n*sizeof(struct pam_response)) ) ;
270 if( rsp == nullptr )
271 throw std::bad_alloc() ;
272 for( size_t j = 0U ; j < n ; j++ )
273 rsp[j].resp = nullptr ;
274
275 // fill in the response from the c++ container
276 //
277 for( size_t i = 0U ; i < n ; i++ )
278 {
279 rsp[i].resp_retcode = 0 ;
280 if( array[i].out_defined )
281 {
282 char * response = strdup_( array[i].out.c_str() ) ;
283 if( response == nullptr )
284 throw std::bad_alloc() ;
285 rsp[i].resp = response ;
286 }
287 }
288
289 *out = rsp ;
290 G_DEBUG( "G::Pam::converseCallback: returning to pam from callback" ) ;
291 return PAM_SUCCESS ;
292 }
293 catch(...) // c callback
294 {
295 G_ERROR( "G::Pam::converseCallback: exception" ) ;
296 release( rsp , n ) ;
297 return PAM_CONV_ERR ;
298 }
299 }
300
301 void G::PamImp::delayCallback( int status , unsigned delay_usec , void * pam_vp )
302 {
303 try
304 {
305 G_DEBUG( "G::Pam::delayCallback: status=" << status << ", delay=" << delay_usec ) ;
306 if( status != PAM_SUCCESS )
307 {
308 PamImp * This = reinterpret_cast<PamImp*>(pam_vp) ;
309 if( This != nullptr )
310 {
311 G_ASSERT( This->m_magic == MAGIC ) ;
312 This->m_pam.delay( delay_usec ) ;
313 }
314 }
315 }
316 catch(...) // c callback
317 {
318 G_ERROR( "G::Pam::delayCallback: exception" ) ;
319 }
320 }
321
322 void G::PamImp::openSession()
323 {
324 int flags = 0 ;
325 if( silent() ) flags |= static_cast<int>(PAM_SILENT) ;
326 m_rc = ::pam_open_session( hpam() , flags ) ;
327 check( "pam_open_session" , m_rc ) ;
328 }
329
330 void G::PamImp::closeSession()
331 {
332 int flags = 0 ;
333 if( silent() ) flags |= static_cast<int>(PAM_SILENT) ;
334 m_rc = ::pam_close_session( hpam() , flags ) ;
335 check( "pam_close_session" , m_rc ) ;
336 }
337
338 bool G::PamImp::success( int rc )
339 {
340 return rc == PAM_SUCCESS ;
341 }
342
343 void G::PamImp::check( const std::string & op , int rc ) const
344 {
345 if( !success(rc) )
346 throw Error( op , rc , ::pam_strerror(hpam(),rc) ) ;
347 }
348
349 // ==
350
351 G::Pam::Pam( const std::string & application , const std::string & user , bool silent ) :
352 m_imp( new PamImp(*this,application,user,silent) )
353 {
354 }
355
356 G::Pam::~Pam()
357 {
358 delete m_imp ;
359 }
360
361 bool G::Pam::authenticate( bool require_token )
362 {
363 G_DEBUG( "G::Pam::authenticate" ) ;
364 return m_imp->authenticate( require_token ) ;
365 }
366
367 void G::Pam::checkAccount( bool require_token )
368 {
369 G_DEBUG( "G::Pam::checkAccount" ) ;
370 return m_imp->checkAccount( require_token ) ;
371 }
372
373 void G::Pam::establishCredentials()
374 {
375 G_DEBUG( "G::Pam::establishCredentials" ) ;
376 m_imp->setCredentials( PAM_ESTABLISH_CRED ) ;
377 }
378
379 void G::Pam::openSession()
380 {
381 G_DEBUG( "G::Pam::openSession" ) ;
382 m_imp->openSession() ;
383 }
384
385 void G::Pam::closeSession()
386 {
387 G_DEBUG( "G::Pam::closeSession" ) ;
388 m_imp->closeSession() ;
389 }
390
391 void G::Pam::deleteCredentials()
392 {
393 m_imp->setCredentials( PAM_DELETE_CRED ) ;
394 }
395
396 void G::Pam::reinitialiseCredentials()
397 {
398 m_imp->setCredentials( PAM_REINITIALIZE_CRED ) ;
399 }
400
401 void G::Pam::refreshCredentials()
402 {
403 m_imp->setCredentials( PAM_REFRESH_CRED ) ;
404 }
405
406 void G::Pam::delay( unsigned int usec )
407 {
408 // this is the default implementation, usually overridden
409 if( usec != 0U )
410 {
411 typedef struct timeval Timeval ;
412 Timeval timeout ;
413 timeout.tv_sec = usec / 1000000U ;
414 timeout.tv_usec = usec % 1000000U ;
415 ::select( 0 , nullptr , nullptr , nullptr , &timeout ) ;
416 }
417 }
418
419 std::string G::Pam::name() const
420 {
421 return m_imp->name() ;
422 }
423
glog.h
G::Pam::closeSession
void closeSession()
Closes a session.
Definition: gpam_linux.cpp:385
G::Pam::name
std::string name() const
Returns the authenticated user name.
Definition: gpam_linux.cpp:419
G::Pam::refreshCredentials
void refreshCredentials()
Refreshes credentials.
Definition: gpam_linux.cpp:401
gpam.h
G::Str::fromInt
static std::string fromInt(int i)
Converts int 'i' to a string.
Definition: gstr.cpp:332
G::Pam::checkAccount
void checkAccount(bool require_token)
Does "account management", checking that the authenticated user is currently allowed to use the syste...
Definition: gpam_linux.cpp:367
G::Pam::reinitialiseCredentials
void reinitialiseCredentials()
Reinitialises credentials.
Definition: gpam_linux.cpp:396
G::Pam::openSession
void openSession()
Starts a session.
Definition: gpam_linux.cpp:379
G::Pam::establishCredentials
void establishCredentials()
Embues the authenticated user with their credentials, such as "tickets" in the form of environment va...
Definition: gpam_linux.cpp:373
gstr.h
gassert.h
G::Pam::authenticate
bool authenticate(bool require_token)
Authenticates the user.
Definition: gpam_linux.cpp:361
G::Pam::Pam
Pam(const std::string &app, const std::string &user, bool silent)
Constructor.
Definition: gpam_linux.cpp:351
G::Pam::delay
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:406
G::PamImp
A pimple-pattern implementation class for G::Pam.
Definition: gpam_linux.cpp:65
gexception.h
G::Pam::~Pam
virtual ~Pam()
Destructor.
Definition: gpam_linux.cpp:356
G::Pam::deleteCredentials
void deleteCredentials()
Deletes credentials.
Definition: gpam_linux.cpp:391
G::PamError
An exception class used by G::Pam.
Definition: gpamerror.h:36
G::Pam
A thin interface to the system PAM library, with two pure virtual methods that derived classes should...
Definition: gpam.h:58