mirror of
https://github.com/ElvishArtisan/rivendell.git
synced 2025-04-12 23:59:34 +02:00
* Removed all CVS tags. * Removed 'const char *name' parameter from all QObject contructors.
329 lines
11 KiB
C++
329 lines
11 KiB
C++
// pam_rd.c : The PAM authentication module of Rivendell
|
|
//
|
|
// (C) Copyright 2006 Federico Grau <donfede@casagrau.org> <grauf@rfa.org>
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License version 2 as
|
|
// published by the Free Software Foundation.
|
|
//
|
|
// 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.
|
|
//
|
|
// created 2006-06-16 grauf@rfa.org
|
|
//
|
|
|
|
// ideas borrowed from:
|
|
// pam_permit.c from Linux-PAM
|
|
// by Andrew Morgan <morgan@parc.power.net> 1996/3/11
|
|
// pam_deny.c from Linux-PAM
|
|
// by Andrew Morgan <morgan@parc.power.net> 1996/3/11
|
|
// pam_mysql.c
|
|
// by James O'Kane <jo2y@midnightlinux.com> and others
|
|
// pam_winbind.c
|
|
// by Andrew Bartlett <abartlet@samba.org> 2002 and others
|
|
// Rivendell by Fred Gleason <fredg@paravelsystems.com>
|
|
|
|
/*
|
|
* include and definitions
|
|
*/
|
|
#define PAM_SM_AUTH
|
|
|
|
#include <security/pam_modules.h>
|
|
/* note: _pam_macros.h is for _drop_reply() macros. This is specific to
|
|
* Linux-PAM and not generally distributed by Sun. */
|
|
#include <security/_pam_macros.h>
|
|
|
|
#include <syslog.h>
|
|
#define SYSLOG_IDENT "pam_rd"
|
|
#define SYSLOG_FACILITY LOG_AUTHPRIV
|
|
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
#include <qapplication.h>
|
|
#include <qsqldatabase.h>
|
|
#include <rd.h>
|
|
#include <rdconfig.h>
|
|
#include <rdcheck_daemons.h>
|
|
#include <rdripc.h>
|
|
#include <rdstation.h>
|
|
#include <rduser.h>
|
|
|
|
// The pam module name, this should be unique amongst pam modules.
|
|
#define MODULE_NAME SYSLOG_IDENT
|
|
|
|
|
|
|
|
/*
|
|
* utility functions and related definitions
|
|
*/
|
|
|
|
#define PAM_RD_DEBUG (1<<0)
|
|
#define PAM_RD_TRY_FIRST_PASS (1<<1)
|
|
#define PAM_RD_USE_FIRST_PASS (1<<2)
|
|
#define PAM_RD_KILL_RD_DAEMONS (1<<3)
|
|
#define PAM_RD_IGNORE_PASS (1<<4)
|
|
#define PAM_RD_FAIL_DEFAULT_USER (1<<5)
|
|
|
|
/* utility function to parse module arguements and return an int with bits set
|
|
* to corresponding options */
|
|
static int parse_args(int argc, const char **argv, char **tmp_user)
|
|
{
|
|
int ctrl;
|
|
/* step through arguments */
|
|
for (ctrl = 0; argc-- > 0; ++argv) {
|
|
/* generic options */
|
|
if (!strcmp(*argv,"debug")) {
|
|
ctrl |= PAM_RD_DEBUG;
|
|
}
|
|
else if (!strcasecmp(*argv, "use_first_pass")) {
|
|
ctrl |= PAM_RD_USE_FIRST_PASS;
|
|
}
|
|
else if (!strcasecmp(*argv, "try_first_pass")) {
|
|
ctrl |= PAM_RD_TRY_FIRST_PASS;
|
|
}
|
|
/* pam_rd specific options */
|
|
else if (!strcasecmp(*argv, "kill_rd_daemons")) {
|
|
/* kill any previously running rivendell daemons */
|
|
ctrl |= PAM_RD_KILL_RD_DAEMONS;
|
|
}
|
|
else if (!strcasecmp(*argv, "ignore_pass")) {
|
|
/* log the user into rivendell, ignoring any password check. the
|
|
* idea is to "trust" the network logon credentials and ignore the
|
|
* rivendell credentials */
|
|
ctrl |= PAM_RD_IGNORE_PASS;
|
|
}
|
|
else if (!strncasecmp(*argv, "fail_default_user=", 18)) {
|
|
/* the Rivendell user account is set to the the user specified by
|
|
* this option if there is a failure authenticating */
|
|
*tmp_user = strdup(18+*argv);
|
|
if (*tmp_user != NULL) {
|
|
syslog(LOG_NOTICE, "DEBUG ... read fail_default_user|%s|", *tmp_user);
|
|
ctrl |= PAM_RD_FAIL_DEFAULT_USER;
|
|
} else {
|
|
syslog(LOG_ERR,
|
|
"parse_args(): invalid fail_default_user specified - ignored");
|
|
}
|
|
} else {
|
|
syslog(LOG_ERR, "parse_args(): ignoring unknown option; %s", *argv);
|
|
}
|
|
}
|
|
return ctrl;
|
|
}
|
|
|
|
|
|
/* utility function to be used for PAM conversation callback */
|
|
int converse(pam_handle_t *pamh,
|
|
int nargs,
|
|
struct pam_message **message,
|
|
struct pam_response **response)
|
|
{
|
|
int retval;
|
|
struct pam_conv *conv;
|
|
|
|
retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
|
|
if (retval == PAM_SUCCESS) {
|
|
retval = conv->conv(nargs,
|
|
(const struct pam_message **) message,
|
|
response, conv->appdata_ptr);
|
|
if ((retval != PAM_SUCCESS) && (retval != PAM_CONV_AGAIN))
|
|
syslog(LOG_ERR, "conversation failure [%s]",
|
|
pam_strerror(pamh, retval));
|
|
} else {
|
|
syslog(LOG_ERR, "couldn't obtain coversation function [%s]",
|
|
pam_strerror(pamh, retval));
|
|
}
|
|
|
|
return retval; /* propagate error status */
|
|
}
|
|
|
|
|
|
/* utility function to setup PAM conversation callback */
|
|
int ask_passwd(pam_handle_t *pamh, int pwtype)
|
|
{
|
|
struct pam_message msg[1], *mesg[1];
|
|
struct pam_response *resp = NULL;
|
|
int i = 0;
|
|
int retval;
|
|
|
|
msg[i].msg = "Password: ";
|
|
msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
|
|
mesg[i] = &msg[i];
|
|
|
|
retval = converse(pamh, ++i, mesg, &resp);
|
|
|
|
if (retval != PAM_SUCCESS) {
|
|
if (resp != NULL)
|
|
_pam_drop_reply(resp,i);
|
|
return ((retval == PAM_CONV_AGAIN)
|
|
? PAM_INCOMPLETE : PAM_AUTHINFO_UNAVAIL);
|
|
}
|
|
|
|
/* we have a password so set AUTHTOK
|
|
*/
|
|
retval=pam_set_item(pamh, pwtype, resp->resp);
|
|
_pam_drop_reply(resp,i);
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* PAM authentication management functions */
|
|
|
|
/* Performs the task of authenticating the user. */
|
|
PAM_EXTERN int pam_sm_authenticate (pam_handle_t *pamh,
|
|
int flags,
|
|
int argc,
|
|
const char **argv)
|
|
{
|
|
int retval;
|
|
int ctrl = 0;
|
|
const char *username=NULL;
|
|
char *password=NULL;
|
|
char *tmp_buf=NULL; // Temporary buffer to get default_username option.
|
|
const char *fail_default_username=RD_USER_LOGIN_NAME; // Initialize default value.
|
|
RDConfig *login_config;
|
|
RDStation *login_station;
|
|
RDUser * login_user;
|
|
QSqlDatabase *login_db;
|
|
|
|
openlog(SYSLOG_IDENT, LOG_CONS|LOG_PID, SYSLOG_FACILITY);
|
|
|
|
/* parse arguments */
|
|
ctrl = parse_args(argc, argv, &tmp_buf);
|
|
if (ctrl & PAM_RD_DEBUG) syslog(LOG_DEBUG, "pam_sm_authenticate called.");
|
|
if (ctrl & PAM_RD_DEBUG) {
|
|
syslog(LOG_DEBUG, "argument flags ctrl:%d (0x%x)", ctrl, ctrl);
|
|
}
|
|
if (ctrl & PAM_RD_FAIL_DEFAULT_USER) {
|
|
fail_default_username = tmp_buf;
|
|
}
|
|
|
|
/* get username */
|
|
retval = pam_get_user(pamh, &username, NULL);
|
|
if ((retval != PAM_SUCCESS)
|
|
|| (username == NULL)
|
|
|| (*username == '\0')) {
|
|
syslog(LOG_ERR, "get username returned error: %s",
|
|
pam_strerror(pamh, retval));
|
|
return retval;
|
|
}
|
|
if (ctrl & PAM_RD_DEBUG) syslog(LOG_DEBUG, "got user:%s", username);
|
|
|
|
if (ctrl & ~PAM_RD_IGNORE_PASS) {
|
|
/* get password */
|
|
if ((ctrl & PAM_RD_TRY_FIRST_PASS) || (ctrl & PAM_RD_USE_FIRST_PASS)) {
|
|
retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) &password);
|
|
}
|
|
|
|
/* if no password yet, and not configured to only use the first
|
|
* password, then ask */
|
|
if ((password == NULL) && (ctrl & ~PAM_RD_USE_FIRST_PASS)) {
|
|
retval = ask_passwd(pamh, PAM_AUTHTOK);
|
|
}
|
|
retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&password);
|
|
|
|
if (password == NULL) {
|
|
syslog(LOG_ERR, "Could not retrieve user's password");
|
|
return PAM_AUTHINFO_UNAVAIL;
|
|
}
|
|
if (ctrl & PAM_RD_DEBUG) syslog(LOG_DEBUG, "got password");
|
|
}
|
|
|
|
/* Load configs */
|
|
login_config=new RDConfig();
|
|
login_config->load();
|
|
|
|
/* kill daemons if requested */
|
|
if (ctrl & PAM_RD_KILL_RD_DAEMONS) {
|
|
if (ctrl & PAM_RD_DEBUG) syslog(LOG_DEBUG, "killing RD daemons");
|
|
RDKillDaemons();
|
|
}
|
|
|
|
/* authenticate the user */
|
|
login_station=new RDStation(login_config->stationName());
|
|
|
|
// Initialize QT application, without the gui. Much like the database
|
|
// connection, while not used here, it is used by underlying functions.
|
|
QApplication a(argc, (char **) argv, false); // FIXME: in QT4 there is a QCoreApplication object that could be used here better.
|
|
|
|
// Open Database.
|
|
// NOTE: while the database connection is not used in this module/file
|
|
// directly, it is required by the underlying functions, ex:
|
|
// login_user->checkPassword()
|
|
login_db=QSqlDatabase::addDatabase(login_config->mysqlDriver());
|
|
if(!login_db) {
|
|
syslog(LOG_ERR, QString().sprintf("Unable to load QSql driver: %s",
|
|
login_config->mysqlDriver().ascii()));
|
|
return PAM_AUTHINFO_UNAVAIL;
|
|
}
|
|
login_db->setDatabaseName(login_config->mysqlDbname());
|
|
login_db->setUserName(login_config->mysqlUsername());
|
|
login_db->setPassword(login_config->mysqlPassword());
|
|
login_db->setHostName(login_config->mysqlHostname());
|
|
if(!login_db->open()) {
|
|
syslog(LOG_ERR, "Unable to connet to mySQL server");
|
|
login_db->removeDatabase(login_config->mysqlDbname());
|
|
return PAM_AUTHINFO_UNAVAIL;
|
|
}
|
|
if (ctrl & PAM_RD_DEBUG) syslog(LOG_DEBUG, "connected to database");
|
|
|
|
login_user = new RDUser(username);
|
|
if (ctrl & PAM_RD_IGNORE_PASS) {
|
|
// Ignore password, simply check if corresponding user exists.
|
|
if (login_user->exists()) {
|
|
login_station->setDefaultName(username);
|
|
syslog(LOG_NOTICE,
|
|
"ignoring password. RD Default User set to: %s", username);
|
|
} else {
|
|
login_station->setDefaultName(fail_default_username);
|
|
syslog(LOG_NOTICE,
|
|
"ignoring password. RDUser not found, defaulting to: %s",
|
|
fail_default_username);
|
|
}
|
|
} else {
|
|
// Check password.
|
|
if ( login_user->checkPassword(QString(password), false) ) {
|
|
login_station->setDefaultName(username);
|
|
syslog(LOG_NOTICE,
|
|
"authenticated. RD Default User set to: %s", username);
|
|
} else {
|
|
login_station->setDefaultName(fail_default_username);
|
|
syslog(LOG_NOTICE,
|
|
"authentication failed. RDUser defaulting to: %s",
|
|
fail_default_username);
|
|
}
|
|
}
|
|
|
|
/* cleanup */
|
|
delete login_user;
|
|
login_db->removeDatabase(login_config->mysqlDbname());
|
|
delete login_station;
|
|
delete login_config;
|
|
|
|
syslog(LOG_NOTICE, "DEBUG ... end pam_rd");
|
|
return PAM_SUCCESS;
|
|
}
|
|
|
|
/* Performs setting of credentials (after the user is authenticated). In the
|
|
* case of Rivendell, the "credential" is registering with the database. */
|
|
PAM_EXTERN
|
|
int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
|
|
,const char **argv)
|
|
{
|
|
return PAM_SUCCESS;
|
|
}
|
|
|