Schlimm is an attempt to create a fork of the SLiM desktop manager.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

460 lines
13 KiB

/*
* This file is part of schlimm
* Copyright (C) 2019-2021 Moritz Strohm <ncc1988@posteo.de>
* and others (see the AUTHORS file).
*
* SLiM - Simple Login Manager
* Copyright (C) 2007 Martin Parm
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "PAM.h"
namespace PAM
{
// Auth_Exception class:
Auth_Exception::Auth_Exception(
pam_handle_t* _pam_handle,
const std::string& _func_name,
int _errnum
):
Schlimm::Exception(pam_strerror(_pam_handle, _errnum), _func_name, _errnum)
{
}
//Cred_Exception class:
Cred_Exception::Cred_Exception(
pam_handle_t* _pam_handle,
const std::string& _func_name,
int _errnum
):
Schlimm::Exception(pam_strerror(_pam_handle, _errnum), _func_name, _errnum)
{
}
//Functions:
int conv(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr)
{
*resp = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response));
if (appdata_ptr == nullptr) {
return PAM_CONV_ERR;
}
Schlimm::Panel* panel = *static_cast<Schlimm::Panel**>(appdata_ptr);
int result = PAM_SUCCESS;
int i;
for (i = 0; i < num_msg; i++) {
(*resp)[i].resp = 0;
(*resp)[i].resp_retcode = 0;
switch (msg[i]->msg_style) {
case PAM_PROMPT_ECHO_ON:
/* We assume PAM is asking for the username */
panel->eventHandler(Schlimm::Panel::Get_Name);
switch (panel->getAction()) {
case Schlimm::Panel::Suspend:
case Schlimm::Panel::Halt:
case Schlimm::Panel::Reboot:
(*resp)[i].resp=strdup("root");
break;
case Schlimm::Panel::Console:
case Schlimm::Panel::Exit:
case Schlimm::Panel::Login:
(*resp)[i].resp=strdup(panel->getName().c_str());
break;
default:
break;
}
break;
case PAM_PROMPT_ECHO_OFF:
/* We assume PAM is asking for the password */
switch (panel->getAction()) {
case Schlimm::Panel::Console:
case Schlimm::Panel::Exit:
/* We should leave now! */
result = PAM_CONV_ERR;
break;
default:
panel->eventHandler(Schlimm::Panel::Get_Passwd);
(*resp)[i].resp=strdup(panel->getPasswd().c_str());
break;
}
break;
case PAM_ERROR_MSG:
case PAM_TEXT_INFO:
/* We simply write these to the log
TODO: Maybe we should simply ignore them */
Schlimm::Log::log(msg[i]->msg);
break;
}
if (result!=PAM_SUCCESS) break;
}
if (result != PAM_SUCCESS) {
for (i = 0; i < num_msg; i++) {
if ((*resp)[i].resp == 0) {
continue;
}
free((*resp)[i].resp);
(*resp)[i].resp = 0;
}
free(*resp);
*resp = 0;
}
return result;
}
//PAMAuth class:
int PAMAuth::_end(void)
{
int result = pam_end(pam_handle, last_result);
pam_handle = 0;
return result;
}
PAMAuth::PAMAuth():
pam_handle(0),
last_result(PAM_SUCCESS)
{
pam_conversation.conv=PAM::conv;
}
PAMAuth::PAMAuth(conversation* conv, void* data):
pam_handle(0),
last_result(PAM_SUCCESS)
{
pam_conversation.conv=conv;
pam_conversation.appdata_ptr=data;
}
PAMAuth::~PAMAuth(void)
{
if (pam_handle) {
_end();
}
}
void PAMAuth::setup()
{
//TO BE IMPLEMENTED
}
void PAMAuth::setPanel(Schlimm::Panel* panel)
{
this->panel = panel;
pam_conversation.appdata_ptr = static_cast<void*>(panel);
}
void PAMAuth::start(const std::string& service)
{
switch ((last_result=pam_start(service.c_str(), NULL, &pam_conversation, &pam_handle))) {
default:
throw Schlimm::Exception(pam_strerror(pam_handle, last_result), "pam_start()", last_result);
case PAM_SUCCESS:
break;
}
return;
}
void PAMAuth::end(void)
{
switch ((last_result=_end())) {
default:
throw Schlimm::Exception(
pam_strerror(pam_handle, last_result),
"pam_end()",
last_result
);
case PAM_SUCCESS:
break;
}
return;
}
void PAMAuth::set_item(const PAMAuth::ItemType item, const void* value)
{
switch ((last_result=pam_set_item(pam_handle, item, value))) {
default:
_end();
throw Schlimm::Exception(
pam_strerror(pam_handle, last_result),
"pam_set_item()",
last_result
);
case PAM_SUCCESS:
break;
}
return;
}
const void* PAMAuth::get_item(const PAMAuth::ItemType item)
{
const void* data;
switch ((last_result=pam_get_item(pam_handle, item, &data))) {
default:
case PAM_SYSTEM_ERR:
#ifdef __LIBPAM_VERSION
case PAM_BAD_ITEM:
#endif
_end();
throw Schlimm::Exception(
pam_strerror(pam_handle, last_result),
"pam_get_item()",
last_result
);
case PAM_PERM_DENIED: /* The value of item was NULL */
case PAM_SUCCESS:
break;
}
return data;
}
#ifdef __LIBPAM_VERSION
void PAMAuth::fail_delay(const unsigned int micro_sec)
{
switch ((last_result=pam_fail_delay(pam_handle, micro_sec))) {
default:
_end();
throw Schlimm::Exception(
pam_strerror(pam_handle, last_result),
"fail_delay()",
last_result
);
case PAM_SUCCESS:
break;
}
return;
}
#endif
void PAMAuth::authenticate(void)
{
switch ((last_result=pam_authenticate(pam_handle, 0))) {
default:
case PAM_ABORT:
case PAM_AUTHINFO_UNAVAIL:
_end();
throw Schlimm::Exception(
pam_strerror(pam_handle, last_result),
"pam_authenticate()",
last_result
);
case PAM_USER_UNKNOWN:
case PAM_MAXTRIES:
case PAM_CRED_INSUFFICIENT:
case PAM_AUTH_ERR:
throw Auth_Exception(pam_handle, "pam_authentication()", last_result);
case PAM_SUCCESS:
break;
}
switch ((last_result=pam_acct_mgmt(pam_handle, PAM_SILENT))) {
/* The documentation and implementation of Linux PAM differs:
PAM_NEW_AUTHTOKEN_REQD is described in the documentation but
don't exists in the actual implementation. This issue needs
to be fixes at some point. */
default:
/* case PAM_NEW_AUTHTOKEN_REQD: */
case PAM_ACCT_EXPIRED:
case PAM_USER_UNKNOWN:
_end();
throw Schlimm::Exception(
pam_strerror(pam_handle, last_result),
"pam_acct_mgmt()",
last_result
);
case PAM_AUTH_ERR:
case PAM_PERM_DENIED:
throw Auth_Exception(pam_handle, "pam_acct_mgmt()", last_result);
case PAM_SUCCESS:
break;
}
return;
}
void PAMAuth::openSession(void)
{
switch ((last_result=pam_setcred(pam_handle, PAM_ESTABLISH_CRED))) {
default:
case PAM_CRED_ERR:
case PAM_CRED_UNAVAIL:
_end();
throw Schlimm::Exception(
pam_strerror(pam_handle, last_result),
"pam_setcred()",
last_result
);
case PAM_CRED_EXPIRED:
case PAM_USER_UNKNOWN:
throw Cred_Exception(pam_handle, "pam_setcred()", last_result);
case PAM_SUCCESS:
break;
}
switch ((last_result=pam_open_session(pam_handle, 0))) {
/* The documentation and implementation of Linux PAM differs:
PAM_SESSION_ERROR is described in the documentation but
don't exists in the actual implementation. This issue needs
to be fixes at some point. */
default:
/* case PAM_SESSION_ERROR: */
pam_setcred(pam_handle, PAM_DELETE_CRED);
_end();
throw Schlimm::Exception(
pam_strerror(pam_handle, last_result),
"pam_open_session()",
last_result
);
case PAM_SUCCESS:
break;
}
return;
}
void PAMAuth::closeSession(void)
{
switch ((last_result=pam_close_session(pam_handle, 0))) {
/* The documentation and implementation of Linux PAM differs:
PAM_SESSION_ERROR is described in the documentation but
don't exists in the actual implementation. This issue needs
to be fixes at some point. */
default:
/* case PAM_SESSION_ERROR: */
pam_setcred(pam_handle, PAM_DELETE_CRED);
_end();
throw Schlimm::Exception(
pam_strerror(pam_handle, last_result),
"pam_close_session",
last_result
);
case PAM_SUCCESS:
break;
}
switch ((last_result=pam_setcred(pam_handle, PAM_DELETE_CRED))) {
default:
case PAM_CRED_ERR:
case PAM_CRED_UNAVAIL:
case PAM_CRED_EXPIRED:
case PAM_USER_UNKNOWN:
_end();
throw Schlimm::Exception(
pam_strerror(pam_handle, last_result),
"pam_setcred()",
last_result
);
case PAM_SUCCESS:
break;
}
return;
}
void PAMAuth::setenv(const std::string& key, const std::string& value)
{
std::string name_value = key+"="+value;
switch ((last_result = pam_putenv(pam_handle, name_value.c_str()))) {
default:
case PAM_PERM_DENIED:
case PAM_ABORT:
case PAM_BUF_ERR:
#ifdef __LIBPAM_VERSION
case PAM_BAD_ITEM:
#endif
_end();
throw Schlimm::Exception(
pam_strerror(pam_handle, last_result),
"pam_putenv()",
last_result
);
case PAM_SUCCESS:
break;
}
return;
}
void PAMAuth::delenv(const std::string& key)
{
switch ((last_result = pam_putenv(pam_handle, key.c_str()))) {
default:
case PAM_PERM_DENIED:
case PAM_ABORT:
case PAM_BUF_ERR:
#ifdef __LIBPAM_VERSION
case PAM_BAD_ITEM:
#endif
_end();
throw Schlimm::Exception(
pam_strerror(pam_handle, last_result),
"pam_putenv()",
last_result
);
case PAM_SUCCESS:
break;
}
return;
}
const char* PAMAuth::getenv(const std::string& key)
{
return pam_getenv(pam_handle, key.c_str());
}
char** PAMAuth::getenvlist(void)
{
return pam_getenvlist(pam_handle);
}
}