mirror of
				https://github.com/ElvishArtisan/rivendell.git
				synced 2025-11-04 16:14:03 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			330 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			330 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(RD_CONF_FILE);
 | 
						|
    login_config->load();
 | 
						|
    login_config->setModuleName("pam_rd");
 | 
						|
 | 
						|
    /* 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;
 | 
						|
}
 | 
						|
 |