- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gpam_linux.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 // 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 extern "C"
38 {
39 // in '/usr/include/security' or '/usr/include/pam'
40 #include <pam_appl.h>
41 }
42
43 namespace
44 {
45 char * strdup_( const char * p )
46 {
47 p = p ? p : "" ;
48 char * copy = static_cast<char*>( std::malloc(std::strlen(p)+1U) ) ;
49 if( copy != NULL )
50 std::strcpy( copy , p ) ;
51 return copy ;
52 }
53 }
54
55 // ==
56
60 class G::PamImp
61 {
62 public:
63 typedef pam_handle_t * Handle ;
64
65 private:
66 typedef struct pam_conv Conversation ;
67 typedef G::Pam::Error Error ;
68 typedef G::Pam::ItemArray ItemArray ;
69 enum { MAGIC = 3456 } ;
70
71 public:
72 Pam & m_pam ;
73 int m_magic ;
74 mutable int m_rc ; // required for pam_end()
75 Handle m_hpam ;
76 Conversation m_conv ;
77 bool m_silent ;
78
79 public:
80 PamImp( G::Pam & pam , const std::string & app , const std::string & user , bool silent ) ;
81 ~PamImp() ;
82 Handle hpam() const ;
83 bool silent() const ;
84 bool authenticate( bool ) ;
85 void check( const std::string & , int ) const ;
86 static bool success( int ) ;
87 void setCredentials( int ) ;
88 void checkAccount( bool ) ;
89 void openSession() ;
90 void closeSession() ;
91 std::string name() const ;
92
93 private:
94 static int converse( int n , const struct pam_message ** in , struct pam_response ** out , void * vp ) ;
95 static void delay( int , unsigned , void * ) ;
96 static std::string decodeStyle( int pam_style ) ;
97 static void release( struct pam_response * , size_t ) ;
98 } ;
99
100 // ==
101
102 G::PamImp::PamImp( G::Pam & pam , const std::string & application , const std::string & user , bool silent ) :
103 m_pam(pam) ,
104 m_magic(MAGIC) ,
105 m_rc(PAM_SUCCESS) ,
106 m_silent(silent)
107 {
108 G_DEBUG( "G::PamImp::ctor: [" << application << "] [" << user << "]" ) ;
109
110 m_conv.conv = converse ;
111 m_conv.appdata_ptr = this ;
112 m_rc = ::pam_start( application.c_str() , user.c_str() , &m_conv , &m_hpam ) ;
113 if( m_rc != PAM_SUCCESS )
114 {
115 throw Error( "pam_start" , m_rc ) ;
116 }
117
118 // (linux-specific)
119 #ifdef PAM_FAIL_DELAY
120 m_rc = ::pam_set_item( m_hpam , PAM_FAIL_DELAY , reinterpret_cast<const void*>(delay) ) ;
121 if( m_rc != PAM_SUCCESS )
122 {
123 ::pam_end( m_hpam , m_rc ) ;
124 throw Error( "pam_set_item" , m_rc , ::pam_strerror(hpam(),m_rc) ) ;
125 }
126 #endif
127 }
128
129 G::PamImp::~PamImp()
130 {
131 try
132 {
133 G_DEBUG( "G::PamImp::dtor" ) ;
134 ::pam_end( m_hpam , m_rc ) ;
135 m_magic = 0 ;
136 }
137 catch(...)
138 {
139 }
140 }
141
142 G::PamImp::Handle G::PamImp::hpam() const
143 {
144 return m_hpam ;
145 }
146
147 bool G::PamImp::silent() const
148 {
149 return m_silent ;
150 }
151
152 std::string G::PamImp::decodeStyle( int pam_style )
153 {
154 std::string defolt = std::string( "#" ) + G::Str::fromInt( pam_style ) ;
155 if( pam_style == PAM_PROMPT_ECHO_OFF ) return "password" ;
156 if( pam_style == PAM_PROMPT_ECHO_ON ) return "prompt" ;
157 if( pam_style == PAM_ERROR_MSG ) return "error" ;
158 if( pam_style == PAM_TEXT_INFO ) return "info" ;
159 return defolt ;
160 }
161
162 bool G::PamImp::authenticate( bool require_token )
163 {
164 int flags = 0 ;
165 if( silent() ) flags |= PAM_SILENT ;
166 if( require_token ) flags |= PAM_DISALLOW_NULL_AUTHTOK ;
167 m_rc = ::pam_authenticate( hpam() , flags ) ;
168 #ifdef PAM_INCOMPLETE
169 if( m_rc == PAM_INCOMPLETE )
170 return false ;
171 #endif
172
173 check( "pam_authenticate" , m_rc ) ;
174 return true ;
175 }
176
177 std::string G::PamImp::name() const
178 {
179 const void * vp = NULL ;
180 m_rc = ::pam_get_item( hpam() , PAM_USER , &vp ) ;
181 check( "pam_get_item" , m_rc ) ;
182 const char * cp = reinterpret_cast<const char*>(vp) ;
183 return std::string( cp ? cp : "" ) ;
184 }
185
186 void G::PamImp::setCredentials( int flag )
187 {
188 int flags = 0 ;
189 flags |= flag ;
190 if( silent() ) flags |= PAM_SILENT ;
191 m_rc = ::pam_setcred( hpam() , flags ) ;
192 check( "pam_setcred" , m_rc ) ;
193 }
194
195 void G::PamImp::checkAccount( bool require_token )
196 {
197 int flags = 0 ;
198 if( silent() ) flags |= PAM_SILENT ;
199 if( require_token ) flags |= PAM_DISALLOW_NULL_AUTHTOK ;
200 m_rc = ::pam_acct_mgmt( hpam() , flags ) ;
201 check( "pam_acct_mgmt" , m_rc ) ;
202 }
203
204 void G::PamImp::release( struct pam_response * rsp , size_t n )
205 {
206 if( rsp != NULL )
207 {
208 for( size_t i = 0U ; i < n ; i++ )
209 {
210 if( rsp[i].resp != NULL )
211 std::free( rsp[i].resp ) ;
212 }
213 }
214 std::free( rsp ) ;
215 }
216
217 int G::PamImp::converse( int n_in , const struct pam_message ** in , struct pam_response ** out , void * vp )
218 {
219 G_ASSERT( n_in > 0 ) ;
220 G_ASSERT( out != NULL ) ;
221 size_t n = n_in < 0 ? size_t(0U) : static_cast<size_t>(n_in) ;
222
223 // pam_conv(3) on linux points out that the pam interface is under-speficied, and on some
224 // systems, possibly including solaris, the "in" pointer is interpreted differently - this
225 // is only a problem for n greater than one, so warn about it at run-time
226 //
227 if( n > 1U )
228 {
229 static bool warned = false ;
230 if( !warned )
231 {
232 G_WARNING( "PamImp::converse: received a complex pam converse() structure: proceed with caution" ) ;
233 warned = true ;
234 }
235 }
236
237 *out = NULL ;
238 struct pam_response * rsp = NULL ;
239 try
240 {
241 G_DEBUG( "G::Pam::converse: " << 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 == NULL )
271 throw std::bad_alloc() ;
272 for( size_t j = 0U ; j < n ; j++ )
273 rsp[j].resp = NULL ;
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 == NULL )
284 throw std::bad_alloc() ;
285 rsp[i].resp = response ;
286 }
287 }
288
289 *out = rsp ;
290 G_DEBUG( "G::Pam::converse: complete" ) ;
291 return PAM_SUCCESS ;
292 }
293 catch(...) // c callback
294 {
295 G_ERROR( "G::Pam::converse: exception" ) ;
296 release( rsp , n ) ;
297 return PAM_CONV_ERR ;
298 }
299 }
300
301 void G::PamImp::delay( int status , unsigned delay_usec , void * pam_vp )
302 {
303 try
304 {
305 G_DEBUG( "G::Pam::delay: status=" << status << ", delay=" << delay_usec ) ;
306 if( status != PAM_SUCCESS )
307 {
308 PamImp * This = reinterpret_cast<PamImp*>(pam_vp) ;
309 if( This != NULL )
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::delay: exception" ) ;
319 }
320 }
321
322 void G::PamImp::openSession()
323 {
324 int flags = 0 ;
325 if( silent() ) flags |= 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 |= 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 if( usec != 0U )
409 {
410 typedef struct timeval Timeval ; // std:: ??
411 Timeval timeout ;
412 timeout.tv_sec = 0 ;
413 timeout.tv_usec = usec ;
414 ::select( 0 , NULL , NULL , NULL , &timeout ) ;
415 }
416 }
417
418 std::string G::Pam::name() const
419 {
420 return m_imp->name() ;
421 }
422
G::PamImp::m_silent
bool m_silent
Definition: gpam_linux.cpp:77
G::PamImp::PamImp
PamImp(G::Pam &pam, const std::string &app, const std::string &user, bool silent)
Definition: gpam_linux.cpp:102
glog.h
G::Pam::closeSession
void closeSession()
Closes a session.
Definition: gpam_linux.cpp:385
G::PamImp::checkAccount
void checkAccount(bool)
Definition: gpam_linux.cpp:195
G::Pam::name
std::string name() const
Returns the authenticated user name.
Definition: gpam_linux.cpp:418
G::PamImp::check
void check(const std::string &, int) const
Definition: gpam_linux.cpp:343
G::PamImp::success
static bool success(int)
Definition: gpam_linux.cpp:338
G::Pam::refreshCredentials
void refreshCredentials()
Refreshes credentials.
Definition: gpam_linux.cpp:401
gpam.h
G::PamImp::hpam
Handle hpam() const
Definition: gpam_linux.cpp:142
G::Str::fromInt
static std::string fromInt(int i)
Converts int 'i' to a string.
Definition: gstr.cpp:236
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::PamImp::openSession
void openSession()
Definition: gpam_linux.cpp:322
G::Pam::reinitialiseCredentials
void reinitialiseCredentials()
Reinitialises credentials.
Definition: gpam_linux.cpp:396
G::Pam::ItemArray
std::vector< Item > ItemArray
Definition: gpam.h:69
G::Pam::openSession
void openSession()
Starts a session.
Definition: gpam_linux.cpp:379
G::Pam::Error
An exception class used by G::Pam.
Definition: gpam.h:72
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
G::PamImp::authenticate
bool authenticate(bool)
Definition: gpam_linux.cpp:162
G::PamImp::m_pam
Pam & m_pam
Definition: gpam_linux.cpp:72
G_ASSERT
#define G_ASSERT(test)
Definition: gassert.h:30
gstr.h
gassert.h
G::Pam::authenticate
bool authenticate(bool require_token)
Authenticates the user.
Definition: gpam_linux.cpp:361
G_ERROR
#define G_ERROR(expr)
Definition: glog.h:108
G::PamImp::~PamImp
~PamImp()
Definition: gpam_linux.cpp:129
G::Pam::Pam
Pam(const std::string &app, const std::string &user, bool silent)
Constructor.
Definition: gpam_linux.cpp:351
G::PamImp::m_rc
int m_rc
Definition: gpam_linux.cpp:74
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_DEBUG
#define G_DEBUG(expr)
Definition: glog.h:95
G::PamImp
A pimple-pattern implementation class for Pam.
Definition: gpam_linux.cpp:60
gexception.h
G::PamImp::m_conv
Conversation m_conv
Definition: gpam_linux.cpp:76
G::Pam::~Pam
virtual ~Pam()
Destructor.
Definition: gpam_linux.cpp:356
G::PamImp::silent
bool silent() const
Definition: gpam_linux.cpp:147
G::PamImp::Handle
pam_handle_t * Handle
Definition: gpam_linux.cpp:63
G::Pam::deleteCredentials
void deleteCredentials()
Deletes credentials.
Definition: gpam_linux.cpp:391
G::PamImp::m_magic
int m_magic
Definition: gpam_linux.cpp:73
G::PamImp::closeSession
void closeSession()
Definition: gpam_linux.cpp:330
G::PamImp::name
std::string name() const
Definition: gpam_linux.cpp:177
G::PamImp::m_hpam
Handle m_hpam
Definition: gpam_linux.cpp:75
G::PamImp::setCredentials
void setCredentials(int)
Definition: gpam_linux.cpp:186
G_WARNING
#define G_WARNING(expr)
Definition: glog.h:107
G::Pam
A thin abstract interface to the system PAM library.
Definition: gpam.h:58