582 lines
15 KiB
C
582 lines
15 KiB
C
/* pam_stress module */
|
|
|
|
/* $Id: pam_stress.c,v 1.12 1997/02/15 19:06:30 morgan Exp morgan $
|
|
*
|
|
* created by Andrew Morgan <morgan@parc.power.net> 1996/3/12
|
|
*
|
|
* $Log: pam_stress.c,v $
|
|
* Revision 1.12 1997/02/15 19:06:30 morgan
|
|
* fixed email
|
|
*
|
|
* Revision 1.11 1997/02/15 17:33:24 morgan
|
|
* removed fixed syslog buffer
|
|
*
|
|
* Revision 1.10 1996/12/01 03:11:35 morgan
|
|
* using _pam_macros.h now
|
|
*
|
|
* Revision 1.9 1996/11/10 20:18:10 morgan
|
|
* changes for .53 compilation
|
|
*
|
|
* Revision 1.8 1996/09/05 06:31:59 morgan
|
|
* changed return value of wipe_up from int to void
|
|
*
|
|
* Revision 1.7 1996/06/02 08:12:28 morgan
|
|
* updated for new static protocol, added STRESS to various user prompts
|
|
* and added rootok flag for pam_sm_chauthtok to look out for
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <syslog.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
/*
|
|
* here, we make definitions for the externally accessible functions
|
|
* in this file (these definitions are required for static modules
|
|
* but strongly encouraged generally) they are used to instruct the
|
|
* modules include file to define their prototypes.
|
|
*/
|
|
|
|
#define PAM_SM_AUTH
|
|
#define PAM_SM_ACCOUNT
|
|
#define PAM_SM_SESSION
|
|
#define PAM_SM_PASSWORD
|
|
|
|
#include <security/pam_modules.h>
|
|
#include <security/_pam_macros.h>
|
|
|
|
static char *_strdup(const char *x)
|
|
{
|
|
char *new;
|
|
new = malloc(strlen(x)+1);
|
|
strcpy(new,x);
|
|
return new;
|
|
}
|
|
|
|
/* log errors */
|
|
|
|
static void _pam_log(int err, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
openlog("PAM-stress", LOG_CONS|LOG_PID, LOG_AUTH);
|
|
vsyslog(err, format, args);
|
|
va_end(args);
|
|
closelog();
|
|
}
|
|
|
|
/* ---------- */
|
|
|
|
/* an internal function to turn all possible test arguments into bits
|
|
of a ctrl number */
|
|
|
|
/* generic options */
|
|
|
|
#define PAM_ST_DEBUG 01
|
|
#define PAM_ST_NO_WARN 02
|
|
#define PAM_ST_USE_PASS1 04
|
|
#define PAM_ST_TRY_PASS1 010
|
|
#define PAM_ST_ROOTOK 020
|
|
|
|
/* simulation options */
|
|
|
|
#define PAM_ST_EXPIRED 040
|
|
#define PAM_ST_FAIL_1 0100
|
|
#define PAM_ST_FAIL_2 0200
|
|
#define PAM_ST_PRELIM 0400
|
|
#define PAM_ST_REQUIRE_PWD 01000
|
|
|
|
/* some syslogging */
|
|
|
|
static void _pam_report(int ctrl, const char *name, int flags,
|
|
int argc, const char **argv)
|
|
{
|
|
if (ctrl & PAM_ST_DEBUG) {
|
|
_pam_log(LOG_DEBUG, "CALLED: %s", name);
|
|
_pam_log(LOG_DEBUG, "FLAGS : 0%o%s", flags,
|
|
(flags & PAM_SILENT) ? " (silent)":"");
|
|
_pam_log(LOG_DEBUG, "CTRL = 0%o",ctrl);
|
|
_pam_log(LOG_DEBUG, "ARGV :");
|
|
while (argc--) {
|
|
_pam_log(LOG_DEBUG, " \"%s\"", *argv++);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int _pam_parse(int argc, const char **argv)
|
|
{
|
|
int ctrl=0;
|
|
|
|
/* step through arguments */
|
|
for (ctrl=0; argc-- > 0; ++argv) {
|
|
|
|
/* generic options */
|
|
|
|
if (!strcmp(*argv,"debug"))
|
|
ctrl |= PAM_ST_DEBUG;
|
|
else if (!strcmp(*argv,"no_warn"))
|
|
ctrl |= PAM_ST_NO_WARN;
|
|
else if (!strcmp(*argv,"use_first_pass"))
|
|
ctrl |= PAM_ST_USE_PASS1;
|
|
else if (!strcmp(*argv,"try_first_pass"))
|
|
ctrl |= PAM_ST_TRY_PASS1;
|
|
else if (!strcmp(*argv,"rootok"))
|
|
ctrl |= PAM_ST_ROOTOK;
|
|
|
|
/* simulation options */
|
|
|
|
else if (!strcmp(*argv,"expired")) /* signal password needs
|
|
renewal */
|
|
ctrl |= PAM_ST_EXPIRED;
|
|
else if (!strcmp(*argv,"fail_1")) /* instruct fn 1 to fail */
|
|
ctrl |= PAM_ST_FAIL_1;
|
|
else if (!strcmp(*argv,"fail_2")) /* instruct fn 2 to fail */
|
|
ctrl |= PAM_ST_FAIL_2;
|
|
else if (!strcmp(*argv,"prelim")) /* instruct pam_sm_setcred
|
|
to fail on first call */
|
|
ctrl |= PAM_ST_PRELIM;
|
|
else if (!strcmp(*argv,"required")) /* module is fussy about the
|
|
user being authenticated */
|
|
ctrl |= PAM_ST_REQUIRE_PWD;
|
|
|
|
else {
|
|
_pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
|
|
}
|
|
}
|
|
|
|
return ctrl;
|
|
}
|
|
|
|
static int converse(pam_handle_t *pamh, int nargs
|
|
, struct pam_message **message
|
|
, struct pam_response **response)
|
|
{
|
|
int retval;
|
|
struct pam_conv *conv;
|
|
|
|
if ((retval = pam_get_item(pamh,PAM_CONV,(const void **)&conv))
|
|
== PAM_SUCCESS) {
|
|
retval = conv->conv(nargs, (const struct pam_message **) message
|
|
, response, conv->appdata_ptr);
|
|
if (retval != PAM_SUCCESS) {
|
|
_pam_log(LOG_ERR,"(pam_stress) converse returned %d",retval);
|
|
_pam_log(LOG_ERR,"that is: %s",pam_strerror(pamh, retval));
|
|
}
|
|
} else {
|
|
_pam_log(LOG_ERR,"(pam_stress) converse failed to get pam_conv");
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/* authentication management functions */
|
|
|
|
static int stress_get_password(pam_handle_t *pamh, int flags
|
|
, int ctrl, char **password)
|
|
{
|
|
char *pass;
|
|
|
|
if ( (ctrl & (PAM_ST_TRY_PASS1|PAM_ST_USE_PASS1))
|
|
&& (pam_get_item(pamh,PAM_AUTHTOK,(const void **)&pass)
|
|
== PAM_SUCCESS)
|
|
&& (pass != NULL) ) {
|
|
pass = _strdup(pass);
|
|
} else if ((ctrl & PAM_ST_USE_PASS1)) {
|
|
_pam_log(LOG_WARNING, "pam_stress: no forwarded password");
|
|
return PAM_PERM_DENIED;
|
|
} else { /* we will have to get one */
|
|
struct pam_message msg[1],*pmsg[1];
|
|
struct pam_response *resp;
|
|
int retval;
|
|
|
|
/* set up conversation call */
|
|
|
|
pmsg[0] = &msg[0];
|
|
msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
|
|
msg[0].msg = "STRESS Password: ";
|
|
resp = NULL;
|
|
|
|
if ((retval = converse(pamh,1,pmsg,&resp)) != PAM_SUCCESS) {
|
|
return retval;
|
|
}
|
|
|
|
if (resp) {
|
|
if ((resp[0].resp == NULL) && (ctrl & PAM_ST_DEBUG)) {
|
|
_pam_log(LOG_DEBUG,
|
|
"pam_sm_authenticate: NULL authtok given");
|
|
}
|
|
if ((flags & PAM_DISALLOW_NULL_AUTHTOK)
|
|
&& resp[0].resp == NULL) {
|
|
free(resp);
|
|
return PAM_AUTH_ERR;
|
|
}
|
|
|
|
pass = resp[0].resp; /* remember this! */
|
|
|
|
resp[0].resp = NULL;
|
|
} else if (ctrl & PAM_ST_DEBUG) {
|
|
_pam_log(LOG_DEBUG,"pam_sm_authenticate: no error reported");
|
|
_pam_log(LOG_DEBUG,"getting password, but NULL returned!?");
|
|
return PAM_CONV_ERR;
|
|
}
|
|
free(resp);
|
|
}
|
|
|
|
*password = pass; /* this *MUST* be free()'d by this module */
|
|
|
|
return PAM_SUCCESS;
|
|
}
|
|
|
|
/* function to clean up data items */
|
|
|
|
static void wipe_up(pam_handle_t *pamh, void *data, int error)
|
|
{
|
|
free(data);
|
|
}
|
|
|
|
PAM_EXTERN
|
|
int pam_sm_authenticate(pam_handle_t *pamh, int flags,
|
|
int argc, const char **argv)
|
|
{
|
|
const char *username;
|
|
int retval=PAM_SUCCESS;
|
|
char *pass;
|
|
int ctrl;
|
|
|
|
D(("called."));
|
|
|
|
ctrl = _pam_parse(argc,argv);
|
|
_pam_report(ctrl, "pam_sm_authenticate", flags, argc, argv);
|
|
|
|
/* try to get the username */
|
|
|
|
retval = pam_get_user(pamh, &username, "username: ");
|
|
if ((ctrl & PAM_ST_DEBUG) && (retval == PAM_SUCCESS)) {
|
|
_pam_log(LOG_DEBUG, "pam_sm_authenticate: username = %s", username);
|
|
} else if (retval != PAM_SUCCESS) {
|
|
_pam_log(LOG_WARNING, "pam_sm_authenticate: failed to get username");
|
|
return retval;
|
|
}
|
|
|
|
/* now get the password */
|
|
|
|
retval = stress_get_password(pamh,flags,ctrl,&pass);
|
|
if (retval != PAM_SUCCESS) {
|
|
_pam_log(LOG_WARNING, "pam_sm_authenticate: "
|
|
"failed to get a password");
|
|
return retval;
|
|
}
|
|
|
|
/* try to set password item */
|
|
|
|
retval = pam_set_item(pamh,PAM_AUTHTOK,pass);
|
|
if (retval != PAM_SUCCESS) {
|
|
_pam_log(LOG_WARNING, "pam_sm_authenticate: "
|
|
"failed to store new password");
|
|
_pam_overwrite(pass);
|
|
free(pass);
|
|
return retval;
|
|
}
|
|
|
|
/* clean up local copy of password */
|
|
|
|
_pam_overwrite(pass);
|
|
free(pass);
|
|
pass = NULL;
|
|
|
|
/* if we are debugging then we print the password */
|
|
|
|
if (ctrl & PAM_ST_DEBUG) {
|
|
(void) pam_get_item(pamh,PAM_AUTHTOK,(const void **)&pass);
|
|
_pam_log(LOG_DEBUG,
|
|
"pam_st_authenticate: password entered is: [%s]\n",pass);
|
|
pass = NULL;
|
|
}
|
|
|
|
/* if we signal a fail for this function then fail */
|
|
|
|
if ((ctrl & PAM_ST_FAIL_1) && retval == PAM_SUCCESS)
|
|
return PAM_PERM_DENIED;
|
|
|
|
return retval;
|
|
}
|
|
|
|
PAM_EXTERN
|
|
int pam_sm_setcred(pam_handle_t *pamh, int flags,
|
|
int argc, const char **argv)
|
|
{
|
|
int ctrl = _pam_parse(argc,argv);
|
|
|
|
D(("called. [post parsing]"));
|
|
|
|
_pam_report(ctrl, "pam_sm_setcred", flags, argc, argv);
|
|
|
|
if (ctrl & PAM_ST_FAIL_2)
|
|
return PAM_CRED_ERR;
|
|
|
|
return PAM_SUCCESS;
|
|
}
|
|
|
|
/* account management functions */
|
|
|
|
PAM_EXTERN
|
|
int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
|
|
int argc, const char **argv)
|
|
{
|
|
int ctrl = _pam_parse(argc,argv);
|
|
|
|
D(("called. [post parsing]"));
|
|
|
|
_pam_report(ctrl,"pam_sm_acct_mgmt", flags, argc, argv);
|
|
|
|
if (ctrl & PAM_ST_FAIL_1)
|
|
return PAM_PERM_DENIED;
|
|
else if (ctrl & PAM_ST_EXPIRED) {
|
|
void *text = malloc(sizeof("yes")+1);
|
|
strcpy(text,"yes");
|
|
pam_set_data(pamh,"stress_new_pwd",text,wipe_up);
|
|
if (ctrl & PAM_ST_DEBUG) {
|
|
_pam_log(LOG_DEBUG,"pam_sm_acct_mgmt: need a new password");
|
|
}
|
|
return PAM_NEW_AUTHTOK_REQD;
|
|
}
|
|
|
|
return PAM_SUCCESS;
|
|
}
|
|
|
|
PAM_EXTERN
|
|
int pam_sm_open_session(pam_handle_t *pamh, int flags,
|
|
int argc, const char **argv)
|
|
{
|
|
char *username,*service;
|
|
int ctrl = _pam_parse(argc,argv);
|
|
|
|
D(("called. [post parsing]"));
|
|
|
|
_pam_report(ctrl,"pam_sm_open_session", flags, argc, argv);
|
|
|
|
if ((pam_get_item(pamh, PAM_USER, (const void **) &username)
|
|
!= PAM_SUCCESS)
|
|
|| (pam_get_item(pamh, PAM_SERVICE, (const void **) &service)
|
|
!= PAM_SUCCESS)) {
|
|
_pam_log(LOG_WARNING,"pam_sm_open_session: for whom?");
|
|
return PAM_SESSION_ERR;
|
|
}
|
|
|
|
_pam_log(LOG_NOTICE,"pam_stress: opened [%s] session for user [%s]"
|
|
, service, username);
|
|
|
|
if (ctrl & PAM_ST_FAIL_1)
|
|
return PAM_SESSION_ERR;
|
|
|
|
return PAM_SUCCESS;
|
|
}
|
|
|
|
PAM_EXTERN
|
|
int pam_sm_close_session(pam_handle_t *pamh, int flags,
|
|
int argc, const char **argv)
|
|
{
|
|
const char *username,*service;
|
|
int ctrl = _pam_parse(argc,argv);
|
|
|
|
D(("called. [post parsing]"));
|
|
|
|
_pam_report(ctrl,"pam_sm_close_session", flags, argc, argv);
|
|
|
|
if ((pam_get_item(pamh, PAM_USER, (const void **)&username)
|
|
!= PAM_SUCCESS)
|
|
|| (pam_get_item(pamh, PAM_SERVICE, (const void **)&service)
|
|
!= PAM_SUCCESS)) {
|
|
_pam_log(LOG_WARNING,"pam_sm_close_session: for whom?");
|
|
return PAM_SESSION_ERR;
|
|
}
|
|
|
|
_pam_log(LOG_NOTICE,"pam_stress: closed [%s] session for user [%s]"
|
|
, service, username);
|
|
|
|
if (ctrl & PAM_ST_FAIL_2)
|
|
return PAM_SESSION_ERR;
|
|
|
|
return PAM_SUCCESS;
|
|
}
|
|
|
|
PAM_EXTERN
|
|
int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
|
|
int argc, const char **argv)
|
|
{
|
|
int retval;
|
|
int ctrl = _pam_parse(argc,argv);
|
|
|
|
D(("called. [post parsing]"));
|
|
|
|
_pam_report(ctrl,"pam_sm_chauthtok", flags, argc, argv);
|
|
|
|
/* this function should be called twice by the Linux-PAM library */
|
|
|
|
if (flags & PAM_PRELIM_CHECK) { /* first call */
|
|
if (ctrl & PAM_ST_DEBUG) {
|
|
_pam_log(LOG_DEBUG,"pam_sm_chauthtok: prelim check");
|
|
}
|
|
if (ctrl & PAM_ST_PRELIM)
|
|
return PAM_TRY_AGAIN;
|
|
|
|
return PAM_SUCCESS;
|
|
} else if (flags & PAM_UPDATE_AUTHTOK) { /* second call */
|
|
struct pam_message msg[3],*pmsg[3];
|
|
struct pam_response *resp;
|
|
const char *text;
|
|
char *txt=NULL;
|
|
int i;
|
|
|
|
if (ctrl & PAM_ST_DEBUG) {
|
|
_pam_log(LOG_DEBUG,"pam_sm_chauthtok: alter password");
|
|
}
|
|
|
|
if (ctrl & PAM_ST_FAIL_1)
|
|
return PAM_AUTHTOK_LOCK_BUSY;
|
|
|
|
if ( !(ctrl && PAM_ST_EXPIRED)
|
|
&& (flags & PAM_CHANGE_EXPIRED_AUTHTOK)
|
|
&& (pam_get_data(pamh,"stress_new_pwd",(const void **)&text)
|
|
!= PAM_SUCCESS || strcmp(text,"yes"))) {
|
|
return PAM_SUCCESS; /* the token has not expired */
|
|
}
|
|
|
|
/* the password should be changed */
|
|
|
|
if ((ctrl & PAM_ST_REQUIRE_PWD)
|
|
&& !(getuid() == 0 && (ctrl & PAM_ST_ROOTOK))
|
|
) { /* first get old one? */
|
|
char *pass;
|
|
|
|
if (ctrl & PAM_ST_DEBUG) {
|
|
_pam_log(LOG_DEBUG
|
|
,"pam_sm_chauthtok: getting old password");
|
|
}
|
|
retval = stress_get_password(pamh,flags,ctrl,&pass);
|
|
if (retval != PAM_SUCCESS) {
|
|
_pam_log(LOG_DEBUG
|
|
,"pam_sm_chauthtok: no password obtained");
|
|
return retval;
|
|
}
|
|
retval = pam_set_item(pamh, PAM_OLDAUTHTOK, pass);
|
|
if (retval != PAM_SUCCESS) {
|
|
_pam_log(LOG_DEBUG
|
|
,"pam_sm_chauthtok: could not set OLDAUTHTOK");
|
|
_pam_overwrite(pass);
|
|
free(pass);
|
|
return retval;
|
|
}
|
|
_pam_overwrite(pass);
|
|
free(pass);
|
|
}
|
|
|
|
/* set up for conversation */
|
|
|
|
if (!(flags & PAM_SILENT)) {
|
|
char *username;
|
|
|
|
if ( pam_get_item(pamh, PAM_USER, (const void **)&username)
|
|
|| username == NULL ) {
|
|
_pam_log(LOG_ERR,"no username set");
|
|
return PAM_USER_UNKNOWN;
|
|
}
|
|
pmsg[0] = &msg[0];
|
|
msg[0].msg_style = PAM_TEXT_INFO;
|
|
#define _LOCAL_STRESS_COMMENT "Changing STRESS password for "
|
|
txt = (char *) malloc(sizeof(_LOCAL_STRESS_COMMENT)
|
|
+strlen(username)+1);
|
|
strcpy(txt, _LOCAL_STRESS_COMMENT);
|
|
#undef _LOCAL_STRESS_COMMENT
|
|
strcat(txt, username);
|
|
msg[0].msg = txt;
|
|
i = 1;
|
|
} else {
|
|
i = 0;
|
|
}
|
|
|
|
pmsg[i] = &msg[i];
|
|
msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
|
|
msg[i++].msg = "Enter new STRESS password: ";
|
|
pmsg[i] = &msg[i];
|
|
msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
|
|
msg[i++].msg = "Retype new STRESS password: ";
|
|
resp = NULL;
|
|
|
|
retval = converse(pamh,i,pmsg,&resp);
|
|
if (txt) {
|
|
free(txt);
|
|
txt = NULL; /* clean up */
|
|
}
|
|
if (retval != PAM_SUCCESS) {
|
|
return retval;
|
|
}
|
|
|
|
if (resp == NULL) {
|
|
_pam_log(LOG_ERR, "pam_sm_chauthtok: no response from conv");
|
|
return PAM_CONV_ERR;
|
|
}
|
|
|
|
/* store the password */
|
|
|
|
if (resp[i-2].resp && resp[i-1].resp) {
|
|
if (strcmp(resp[i-2].resp,resp[i-1].resp)) {
|
|
/* passwords are not the same; forget and return error */
|
|
|
|
_pam_drop_reply(resp, i);
|
|
|
|
if (!(flags & PAM_SILENT) && !(ctrl & PAM_ST_NO_WARN)) {
|
|
pmsg[0] = &msg[0];
|
|
msg[0].msg_style = PAM_ERROR_MSG;
|
|
msg[0].msg = "Verification mis-typed; "
|
|
"password unchaged";
|
|
resp = NULL;
|
|
(void) converse(pamh,1,pmsg,&resp);
|
|
if (resp) {
|
|
_pam_drop_reply(resp, 1);
|
|
}
|
|
}
|
|
return PAM_AUTHTOK_ERR;
|
|
}
|
|
|
|
if (pam_get_item(pamh,PAM_AUTHTOK,(const void **)&text)
|
|
== PAM_SUCCESS) {
|
|
(void) pam_set_item(pamh,PAM_OLDAUTHTOK,text);
|
|
text = NULL;
|
|
}
|
|
(void) pam_set_item(pamh,PAM_AUTHTOK,resp[0].resp);
|
|
} else {
|
|
_pam_log(LOG_DEBUG,"pam_sm_chauthtok: problem with resp");
|
|
retval = PAM_SYSTEM_ERR;
|
|
}
|
|
|
|
_pam_drop_reply(resp, i); /* clean up the passwords */
|
|
} else {
|
|
_pam_log(LOG_ERR,"pam_sm_chauthtok: this must be a Linux-PAM error");
|
|
return PAM_SYSTEM_ERR;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
#ifdef PAM_STATIC
|
|
|
|
/* static module data */
|
|
|
|
struct pam_module _pam_stress_modstruct = {
|
|
"pam_stress",
|
|
pam_sm_authenticate,
|
|
pam_sm_setcred,
|
|
pam_sm_acct_mgmt,
|
|
pam_sm_open_session,
|
|
pam_sm_close_session,
|
|
pam_sm_chauthtok
|
|
};
|
|
|
|
#endif
|