35#if GCONFIG_HAVE_PAM_IN_INCLUDE
38#if GCONFIG_HAVE_PAM_IN_PAM
39#include <pam/pam_appl.h>
41#include <security/pam_appl.h>
45#define G_PAM_CONST const
56 using Handle = pam_handle_t * ;
57 using Conversation =
struct pam_conv ;
58 PamImp(
Pam & pam ,
const std::string & app ,
const std::string & user ,
bool silent ) ;
61 bool authenticate(
bool ) ;
62 void check(
const std::string & ,
int )
const ;
63 static bool success(
int ) ;
64 void setCredentials(
int ) ;
65 void checkAccount(
bool ) ;
68 std::string name()
const ;
87 using ItemArray = Pam::ItemArray ;
88 static constexpr int MAGIC = 3456 ;
91 static int converseCallback(
int n , G_PAM_CONST
struct pam_message ** in ,
92 struct pam_response ** out ,
void * vp ) ;
93 static void delayCallback(
int ,
unsigned ,
void * ) ;
94 static std::string decodeStyle(
int pam_style ) ;
95 static void release(
struct pam_response * , std::size_t ) ;
96 static char * strdup_(
const char * ) ;
101G::PamImp::PamImp(
G::Pam & pam ,
const std::string & application ,
const std::string & user ,
bool silent ) :
109 G_DEBUG(
"G::PamImp::ctor: [" << application <<
"] [" << user <<
"]" ) ;
111 m_conv.conv = converseCallback ;
112 m_conv.appdata_ptr = this ;
113 m_rc = ::pam_start( application.c_str() , user.c_str() , &m_conv , &m_hpam ) ;
114 if( m_rc != PAM_SUCCESS )
116 throw Error(
"pam_start" , m_rc ) ;
120 #ifdef PAM_FAIL_DELAY
121 m_rc = ::pam_set_item( m_hpam , PAM_FAIL_DELAY ,
reinterpret_cast<const void*
>(delayCallback) ) ;
122 if( m_rc != PAM_SUCCESS )
124 ::pam_end( m_hpam , m_rc ) ;
125 throw Error(
"pam_set_item" , m_rc , ::pam_strerror(hpam(),m_rc) ) ;
134 G_DEBUG(
"G::PamImp::dtor" ) ;
135 ::pam_end( m_hpam , m_rc ) ;
143G::PamImp::Handle G::PamImp::hpam()
const
148bool G::PamImp::silent()
const
153std::string G::PamImp::decodeStyle(
int pam_style )
155 std::string defolt = std::string(
"#" ) +
Str::fromInt( pam_style ) ;
156 if( pam_style == PAM_PROMPT_ECHO_OFF )
return "password" ;
157 if( pam_style == PAM_PROMPT_ECHO_ON )
return "prompt" ;
158 if( pam_style == PAM_ERROR_MSG )
return "error" ;
159 if( pam_style == PAM_TEXT_INFO )
return "info" ;
163bool G::PamImp::authenticate(
bool require_token )
166 if( silent() ) flags |=
static_cast<int>(PAM_SILENT) ;
167 if( require_token ) flags |=
static_cast<int>(PAM_DISALLOW_NULL_AUTHTOK) ;
168 m_rc = ::pam_authenticate( hpam() , flags ) ;
169 #ifdef PAM_INCOMPLETE
170 if( m_rc == PAM_INCOMPLETE )
174 check(
"pam_authenticate" , m_rc ) ;
178std::string G::PamImp::name()
const
180 G_PAM_CONST
void * vp = nullptr ;
181 m_rc = ::pam_get_item( hpam() , PAM_USER , &vp ) ;
182 check(
"pam_get_item" , m_rc ) ;
183 const char * cp =
static_cast<const char*
>(vp) ;
184 return std::string( cp ? cp :
"" ) ;
187void G::PamImp::setCredentials(
int flag )
191 if( silent() ) flags |=
static_cast<int>(PAM_SILENT) ;
192 m_rc = ::pam_setcred( hpam() , flags ) ;
193 check(
"pam_setcred" , m_rc ) ;
196void G::PamImp::checkAccount(
bool require_token )
199 if( silent() ) flags |=
static_cast<int>(PAM_SILENT) ;
200 if( require_token ) flags |=
static_cast<int>(PAM_DISALLOW_NULL_AUTHTOK) ;
201 m_rc = ::pam_acct_mgmt( hpam() , flags ) ;
202 check(
"pam_acct_mgmt" , m_rc ) ;
205void G::PamImp::release(
struct pam_response * rsp , std::size_t n )
209 for( std::size_t i = 0U ; i < n ; i++ )
211 if( rsp[i].resp !=
nullptr )
212 std::free( rsp[i].resp ) ;
218int G::PamImp::converseCallback(
int n_in , G_PAM_CONST
struct pam_message ** in ,
219 struct pam_response ** out ,
void * vp )
221 G_ASSERT( out !=
nullptr ) ;
224 G_ERROR(
"G::Pam::converseCallback: invalid count" ) ;
225 return PAM_CONV_ERR ;
227 std::size_t n =
static_cast<std::size_t
>(n_in) ;
235 G_WARNING_ONCE(
"PamImp::converseCallback: received a complex pam converse() structure: "
236 "proceed with caution" ) ;
240 struct pam_response * rsp = nullptr ;
243 G_DEBUG(
"G::Pam::converseCallback: called back from pam with " << n <<
" item(s)" ) ;
244 PamImp * This =
static_cast<PamImp*
>(vp) ;
245 G_ASSERT( This->m_magic == MAGIC ) ;
251 ItemArray array( n ) ;
252 for( std::size_t i = 0U ; i < n ; i++ )
254 std::string & s1 =
const_cast<std::string&
>(array[i].in_type) ;
255 s1 = decodeStyle( in[i]->msg_style ) ;
257 std::string & s2 =
const_cast<std::string&
>(array[i].in) ;
258 s2 = std::string(in[i]->msg ? in[i]->msg :
"") ;
260 array[i].out_defined = false ;
265 This->m_pam.converse( array ) ;
266 G_ASSERT( array.size() == n ) ;
271 rsp =
static_cast<struct pam_response*
>( std::malloc(n*
sizeof(
struct pam_response)) ) ;
273 throw std::bad_alloc() ;
274 for( std::size_t j = 0U ; j < n ; j++ )
275 rsp[j].resp =
nullptr ;
279 for( std::size_t i = 0U ; i < n ; i++ )
281 rsp[i].resp_retcode = 0 ;
282 if( array[i].out_defined )
284 char * response = strdup_( array[i].out.c_str() ) ;
285 if( response ==
nullptr )
286 throw std::bad_alloc() ;
287 rsp[i].resp = response ;
292 G_DEBUG(
"G::Pam::converseCallback: returning to pam from callback" ) ;
297 G_ERROR(
"G::Pam::converseCallback: exception" ) ;
299 return PAM_CONV_ERR ;
303void G::PamImp::delayCallback(
int status ,
unsigned delay_usec ,
void * pam_vp )
307 G_DEBUG(
"G::Pam::delayCallback: status=" << status <<
", delay=" << delay_usec ) ;
308 if( status != PAM_SUCCESS )
310 PamImp * This =
static_cast<PamImp*
>(pam_vp) ;
311 if( This !=
nullptr )
313 G_ASSERT( This->m_magic == MAGIC ) ;
314 This->m_pam.delay( delay_usec ) ;
320 G_ERROR(
"G::Pam::delayCallback: exception" ) ;
324void G::PamImp::openSession()
327 if( silent() ) flags |=
static_cast<int>(PAM_SILENT) ;
328 m_rc = ::pam_open_session( hpam() , flags ) ;
329 check(
"pam_open_session" , m_rc ) ;
332void G::PamImp::closeSession()
335 if( silent() ) flags |=
static_cast<int>(PAM_SILENT) ;
336 m_rc = ::pam_close_session( hpam() , flags ) ;
337 check(
"pam_close_session" , m_rc ) ;
340bool G::PamImp::success(
int rc )
342 return rc == PAM_SUCCESS ;
345void G::PamImp::check(
const std::string & op ,
int rc )
const
348 throw Error( op , rc , ::pam_strerror(hpam(),rc) ) ;
351char * G::PamImp::strdup_(
const char * p )
354 char * copy =
static_cast<char*
>( std::malloc(std::strlen(p)+1U) ) ;
355 if( copy !=
nullptr )
356 std::strcpy( copy , p ) ;
362G::Pam::Pam(
const std::string & application ,
const std::string & user ,
bool silent ) :
363 m_imp(std::make_unique<
PamImp>(*this,application,user,silent))
372 G_DEBUG(
"G::Pam::authenticate" ) ;
373 return m_imp->authenticate( require_token ) ;
378 G_DEBUG(
"G::Pam::checkAccount" ) ;
379 return m_imp->checkAccount( require_token ) ;
384 G_DEBUG(
"G::Pam::establishCredentials" ) ;
385 m_imp->setCredentials( PAM_ESTABLISH_CRED ) ;
390 G_DEBUG(
"G::Pam::openSession" ) ;
391 m_imp->openSession() ;
396 G_DEBUG(
"G::Pam::closeSession" ) ;
397 m_imp->closeSession() ;
402 m_imp->setCredentials( PAM_DELETE_CRED ) ;
407 m_imp->setCredentials( PAM_REINITIALIZE_CRED ) ;
412 m_imp->setCredentials( PAM_REFRESH_CRED ) ;
421 using Timeval =
struct timeval ;
423 timeout.tv_sec = usec / 1000000U ;
424 timeout.tv_usec = usec % 1000000U ;
425 ::select( 0 ,
nullptr ,
nullptr ,
nullptr , &timeout ) ;
431 return m_imp->name() ;
A pimple-pattern implementation class for G::Pam.
An exception class for G::Pam.
A thin interface to the system PAM library, with two pure virtual methods that derived classes should...
void deleteCredentials()
Deletes credentials.
void checkAccount(bool require_token)
Does "account management", checking that the authenticated user is currently allowed to use the syste...
bool authenticate(bool require_token)
Authenticates the user.
virtual void delay(unsigned int usec)=0
Called when the pam library wants the application to introduce a delay to prevent brute-force attacks...
Pam(const std::string &app, const std::string &user, bool silent)
Constructor.
void refreshCredentials()
Refreshes credentials.
void openSession()
Starts a session.
void reinitialiseCredentials()
Reinitialises credentials.
virtual ~Pam()
Destructor.
std::string name() const
Returns the authenticated user name.
void closeSession()
Closes a session.
void establishCredentials()
Embues the authenticated user with their credentials, such as "tickets" in the form of environment va...
static std::string fromInt(int i)
Converts int 'i' to a string.