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 ;
72 PamImp(
const PamImp & ) = delete ;
73 PamImp( PamImp && ) = delete ;
74 PamImp & operator=(
const PamImp & ) = delete ;
75 PamImp & operator=( PamImp && ) = delete ;
80 mutable int m_rc {PAM_SUCCESS} ;
81 Handle m_hpam {
nullptr} ;
86 using Error = Pam::Error ;
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 ) :
106 G_DEBUG(
"G::PamImp::ctor: [" << application <<
"] [" << user <<
"]" ) ;
108 m_conv.conv = converseCallback ;
109 m_conv.appdata_ptr = this ;
110 m_rc = ::pam_start( application.c_str() , user.c_str() , &m_conv , &m_hpam ) ;
111 if( m_rc != PAM_SUCCESS )
113 throw Error(
"pam_start" , m_rc ) ;
117 #ifdef PAM_FAIL_DELAY
118 m_rc = ::pam_set_item( m_hpam , PAM_FAIL_DELAY ,
reinterpret_cast<const void*
>(delayCallback) ) ;
119 if( m_rc != PAM_SUCCESS )
121 ::pam_end( m_hpam , m_rc ) ;
122 throw Error(
"pam_set_item" , m_rc , ::pam_strerror(hpam(),m_rc) ) ;
131 G_DEBUG(
"G::PamImp::dtor" ) ;
132 ::pam_end( m_hpam , m_rc ) ;
140G::PamImp::Handle G::PamImp::hpam()
const
145bool G::PamImp::silent()
const
150std::string G::PamImp::decodeStyle(
int pam_style )
152 std::string defolt = std::string(
"#" ) + Str::fromInt( pam_style ) ;
153 if( pam_style == PAM_PROMPT_ECHO_OFF )
return "password" ;
154 if( pam_style == PAM_PROMPT_ECHO_ON )
return "prompt" ;
155 if( pam_style == PAM_ERROR_MSG )
return "error" ;
156 if( pam_style == PAM_TEXT_INFO )
return "info" ;
160bool G::PamImp::authenticate(
bool require_token )
163 if( silent() ) flags |=
static_cast<int>(PAM_SILENT) ;
164 if( require_token ) flags |=
static_cast<int>(PAM_DISALLOW_NULL_AUTHTOK) ;
165 m_rc = ::pam_authenticate( hpam() , flags ) ;
166 #ifdef PAM_INCOMPLETE
167 if( m_rc == PAM_INCOMPLETE )
171 check(
"pam_authenticate" , m_rc ) ;
175std::string G::PamImp::name()
const
177 G_PAM_CONST
void * vp = nullptr ;
178 m_rc = ::pam_get_item( hpam() , PAM_USER , &vp ) ;
179 check(
"pam_get_item" , m_rc ) ;
180 const char * cp =
static_cast<const char*
>(vp) ;
181 return { cp ? cp :
"" } ;
184void G::PamImp::setCredentials(
int flag )
188 if( silent() ) flags |=
static_cast<int>(PAM_SILENT) ;
189 m_rc = ::pam_setcred( hpam() , flags ) ;
190 check(
"pam_setcred" , m_rc ) ;
193void G::PamImp::checkAccount(
bool require_token )
196 if( silent() ) flags |=
static_cast<int>(PAM_SILENT) ;
197 if( require_token ) flags |=
static_cast<int>(PAM_DISALLOW_NULL_AUTHTOK) ;
198 m_rc = ::pam_acct_mgmt( hpam() , flags ) ;
199 check(
"pam_acct_mgmt" , m_rc ) ;
202void G::PamImp::release(
struct pam_response * rsp , std::size_t n )
206 for( std::size_t i = 0U ; i < n ; i++ )
208 if( rsp[i].resp !=
nullptr )
209 std::free( rsp[i].resp ) ;
215int G::PamImp::converseCallback(
int n_in , G_PAM_CONST
struct pam_message ** in ,
216 struct pam_response ** out ,
void * vp )
218 G_ASSERT( out !=
nullptr ) ;
221 G_ERROR(
"G::Pam::converseCallback: invalid count" ) ;
222 return PAM_CONV_ERR ;
224 std::size_t n =
static_cast<std::size_t
>(n_in) ;
232 G_WARNING_ONCE(
"PamImp::converseCallback: received a complex pam converse() structure: "
233 "proceed with caution" ) ;
237 struct pam_response * rsp = nullptr ;
240 G_DEBUG(
"G::Pam::converseCallback: called back from pam with " << n <<
" item(s)" ) ;
241 PamImp * This =
static_cast<PamImp*
>(vp) ;
242 G_ASSERT( This->m_magic == MAGIC ) ;
248 ItemArray array( n ) ;
249 for( std::size_t i = 0U ; i < n ; i++ )
251 array[i].in_type = decodeStyle( in[i]->msg_style ) ;
252 array[i].in = std::string( in[i]->msg ? in[i]->msg :
"" ) ;
253 array[i].out_defined = false ;
258 This->m_pam.converse( array ) ;
259 G_ASSERT( array.size() == n ) ;
264 rsp =
static_cast<struct pam_response*
>( std::malloc(n*
sizeof(
struct pam_response)) ) ;
266 throw std::bad_alloc() ;
267 for( std::size_t j = 0U ; j < n ; j++ )
268 rsp[j].resp =
nullptr ;
272 for( std::size_t i = 0U ; i < n ; i++ )
274 rsp[i].resp_retcode = 0 ;
275 if( array[i].out_defined )
277 char * response = strdup_( array[i].out.c_str() ) ;
278 if( response ==
nullptr )
279 throw std::bad_alloc() ;
280 rsp[i].resp = response ;
285 G_DEBUG(
"G::Pam::converseCallback: returning to pam from callback" ) ;
290 G_ERROR(
"G::Pam::converseCallback: exception" ) ;
292 return PAM_CONV_ERR ;
296void G::PamImp::delayCallback(
int status ,
unsigned delay_usec ,
void * pam_vp )
300 G_DEBUG(
"G::Pam::delayCallback: status=" << status <<
", delay=" << delay_usec ) ;
301 if( status != PAM_SUCCESS )
303 PamImp * This =
static_cast<PamImp*
>(pam_vp) ;
304 if( This !=
nullptr )
306 G_ASSERT( This->m_magic == MAGIC ) ;
307 This->m_pam.delay( delay_usec ) ;
313 G_ERROR(
"G::Pam::delayCallback: exception" ) ;
317void G::PamImp::openSession()
320 if( silent() ) flags |=
static_cast<int>(PAM_SILENT) ;
321 m_rc = ::pam_open_session( hpam() , flags ) ;
322 check(
"pam_open_session" , m_rc ) ;
325void G::PamImp::closeSession()
328 if( silent() ) flags |=
static_cast<int>(PAM_SILENT) ;
329 m_rc = ::pam_close_session( hpam() , flags ) ;
330 check(
"pam_close_session" , m_rc ) ;
333bool G::PamImp::success(
int rc )
335 return rc == PAM_SUCCESS ;
338void G::PamImp::check(
const std::string & op ,
int rc )
const
341 throw Error( op , rc , ::pam_strerror(hpam(),rc) ) ;
344char * G::PamImp::strdup_(
const char * p )
347 char * copy =
static_cast<char*
>( std::malloc(std::strlen(p)+1U) ) ;
348 if( copy !=
nullptr )
349 std::strcpy( copy , p ) ;
355G::Pam::Pam(
const std::string & application ,
const std::string & user ,
bool silent ) :
356 m_imp(
std::make_unique<PamImp>(*this,application,user,silent))
365 G_DEBUG(
"G::Pam::authenticate" ) ;
366 return m_imp->authenticate( require_token ) ;
372 G_DEBUG(
"G::Pam::checkAccount" ) ;
373 m_imp->checkAccount( require_token ) ;
380 G_DEBUG(
"G::Pam::establishCredentials" ) ;
381 m_imp->setCredentials( PAM_ESTABLISH_CRED ) ;
388 G_DEBUG(
"G::Pam::openSession" ) ;
389 m_imp->openSession() ;
396 G_DEBUG(
"G::Pam::closeSession" ) ;
397 m_imp->closeSession() ;
404 m_imp->setCredentials( PAM_DELETE_CRED ) ;
411 m_imp->setCredentials( PAM_REINITIALIZE_CRED ) ;
418 m_imp->setCredentials( PAM_REFRESH_CRED ) ;
429 using Timeval =
struct timeval ;
431 timeout.tv_sec = usec / 1000000U ;
432 timeout.tv_usec = usec % 1000000U ;
433 ::select( 0 ,
nullptr ,
nullptr ,
nullptr , &timeout ) ;
441 return m_imp->name() ;
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...