diff options
Diffstat (limited to 'modules/pam_stress/pam_stress.c')
-rw-r--r-- | modules/pam_stress/pam_stress.c | 534 |
1 files changed, 534 insertions, 0 deletions
diff --git a/modules/pam_stress/pam_stress.c b/modules/pam_stress/pam_stress.c new file mode 100644 index 0000000..6c7a625 --- /dev/null +++ b/modules/pam_stress/pam_stress.c @@ -0,0 +1,534 @@ +/* + * pam_stress module + * + * created by Andrew Morgan <morgan@linux.kernel.org> 1996/3/12 + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> + +#include <syslog.h> + +#include <stdarg.h> +#include <string.h> +#include <unistd.h> + +#include <security/pam_modules.h> +#include <security/_pam_macros.h> +#include <security/pam_ext.h> + +/* ---------- */ + +/* 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 (const pam_handle_t *pamh, int ctrl, const char *name, + int flags, int argc, const char **argv) +{ + if (ctrl & PAM_ST_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, "CALLED: %s", name); + pam_syslog(pamh, LOG_DEBUG, "FLAGS : 0%o%s", + flags, (flags & PAM_SILENT) ? " (silent)":""); + pam_syslog(pamh, LOG_DEBUG, "CTRL = 0%o", ctrl); + pam_syslog(pamh, LOG_DEBUG, "ARGV :"); + while (argc--) { + pam_syslog(pamh, LOG_DEBUG, " \"%s\"", *argv++); + } + } +} + +static int +_pam_parse (const pam_handle_t *pamh, 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_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + } + + return ctrl; +} + +static int converse(pam_handle_t *pamh, int nargs + , const struct pam_message **message + , struct pam_response **response) +{ + int retval; + const void *void_conv; + const struct pam_conv *conv; + + retval = pam_get_item(pamh,PAM_CONV,&void_conv); + conv = void_conv; + if (retval == PAM_SUCCESS && conv) { + retval = conv->conv(nargs, message, response, conv->appdata_ptr); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "converse returned %d: %s", + retval, pam_strerror(pamh, retval)); + } + } else { + pam_syslog(pamh, LOG_ERR, "converse failed to get pam_conv"); + if (retval == PAM_SUCCESS) + retval = PAM_BAD_ITEM; /* conv was null */ + } + + return retval; +} + +/* authentication management functions */ + +static int stress_get_password(pam_handle_t *pamh, int flags + , int ctrl, char **password) +{ + const void *pam_pass; + char *pass; + + if ( (ctrl & (PAM_ST_TRY_PASS1|PAM_ST_USE_PASS1)) + && (pam_get_item(pamh,PAM_AUTHTOK,&pam_pass) + == PAM_SUCCESS) + && (pam_pass != NULL) ) { + if ((pass = strdup(pam_pass)) == NULL) + return PAM_BUF_ERR; + } else if ((ctrl & PAM_ST_USE_PASS1)) { + pam_syslog(pamh, LOG_WARNING, "no forwarded password"); + return PAM_PERM_DENIED; + } else { /* we will have to get one */ + struct pam_message msg[1]; + const struct pam_message *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_syslog(pamh, 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_syslog(pamh, LOG_DEBUG, + "pam_sm_authenticate: no error reported"); + pam_syslog(pamh, 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 UNUSED, void *data, int error UNUSED) +{ + free(data); +} + +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(pamh, argc, argv); + _pam_report(pamh, ctrl, "pam_sm_authenticate", flags, argc, argv); + + /* try to get the username */ + + retval = pam_get_user(pamh, &username, "username: "); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, + "pam_sm_authenticate: cannot determine user name: %s", + pam_strerror(pamh, retval)); + return retval; + } + else if (ctrl & PAM_ST_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, + "pam_sm_authenticate: username = %s", username); + } + + /* now get the password */ + + retval = stress_get_password(pamh,flags,ctrl,&pass); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, 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); + _pam_overwrite(pass); /* clean up local copy of password */ + free(pass); + pass = NULL; + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_WARNING, + "pam_sm_authenticate: failed to store new password"); + return retval; + } + + /* if we are debugging then we print the password */ + + if (ctrl & PAM_ST_DEBUG) { + const void *pam_pass; + (void) pam_get_item(pamh,PAM_AUTHTOK,&pam_pass); + pam_syslog(pamh, LOG_DEBUG, + "pam_st_authenticate: password entered is: [%s]", + (const char *)pam_pass); + } + + /* 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; +} + +int pam_sm_setcred(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + int ctrl = _pam_parse(pamh, argc, argv); + + D(("called. [post parsing]")); + + _pam_report(pamh, ctrl, "pam_sm_setcred", flags, argc, argv); + + if (ctrl & PAM_ST_FAIL_2) + return PAM_CRED_ERR; + + return PAM_SUCCESS; +} + +/* account management functions */ + +int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + int ctrl = _pam_parse(pamh, argc, argv); + + D(("called. [post parsing]")); + + _pam_report(pamh, ctrl,"pam_sm_acct_mgmt", flags, argc, argv); + + if (ctrl & PAM_ST_FAIL_1) + return PAM_PERM_DENIED; + else if (ctrl & PAM_ST_EXPIRED) { + int retval; + void *text = strdup("yes"); + if (!text) + return PAM_BUF_ERR; + retval = pam_set_data(pamh,"stress_new_pwd",text,wipe_up); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_DEBUG, + "pam_sm_acct_mgmt: failed setting stress_new_pwd"); + free(text); + return retval; + } + + if (ctrl & PAM_ST_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, + "pam_sm_acct_mgmt: need a new password"); + } + return PAM_NEW_AUTHTOK_REQD; + } + + return PAM_SUCCESS; +} + +int pam_sm_open_session(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + const void *username, *service; + int ctrl = _pam_parse(pamh, argc, argv); + + D(("called. [post parsing]")); + + _pam_report(pamh, ctrl,"pam_sm_open_session", flags, argc, argv); + + if ((pam_get_item(pamh, PAM_USER, &username) + != PAM_SUCCESS || !username) + || (pam_get_item(pamh, PAM_SERVICE, &service) + != PAM_SUCCESS || !service)) { + pam_syslog(pamh, LOG_WARNING, "pam_sm_open_session: for whom?"); + return PAM_SESSION_ERR; + } + + pam_syslog(pamh, LOG_NOTICE, "opened [%s] session for user [%s]", + (const char *)service, (const char *)username); + + if (ctrl & PAM_ST_FAIL_1) + return PAM_SESSION_ERR; + + return PAM_SUCCESS; +} + +int pam_sm_close_session(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + const void *username, *service; + int ctrl = _pam_parse(pamh, argc, argv); + + D(("called. [post parsing]")); + + _pam_report(pamh, ctrl,"pam_sm_close_session", flags, argc, argv); + + if ((pam_get_item(pamh, PAM_USER, &username) + != PAM_SUCCESS || !username) + || (pam_get_item(pamh, PAM_SERVICE, &service) + != PAM_SUCCESS || !service)) { + pam_syslog(pamh, LOG_WARNING, "pam_sm_close_session: for whom?"); + return PAM_SESSION_ERR; + } + + pam_syslog(pamh, LOG_NOTICE, "closed [%s] session for user [%s]", + (const char *)service, (const char *)username); + + if (ctrl & PAM_ST_FAIL_2) + return PAM_SESSION_ERR; + + return PAM_SUCCESS; +} + +int pam_sm_chauthtok(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + int retval; + int ctrl = _pam_parse(pamh, argc, argv); + + D(("called. [post parsing]")); + + _pam_report(pamh, 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_syslog(pamh, 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]; + const struct pam_message *pmsg[3]; + struct pam_response *resp; + const void *text; + char *txt=NULL; + int i; + + if (ctrl & PAM_ST_DEBUG) { + pam_syslog(pamh, 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", &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_syslog(pamh, LOG_DEBUG, + "pam_sm_chauthtok: getting old password"); + } + retval = stress_get_password(pamh,flags,ctrl,&pass); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_DEBUG, + "pam_sm_chauthtok: no password obtained"); + return retval; + } + retval = pam_set_item(pamh, PAM_OLDAUTHTOK, pass); + _pam_overwrite(pass); + free(pass); + pass = NULL; + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_DEBUG, + "pam_sm_chauthtok: could not set OLDAUTHTOK"); + return retval; + } + } + + /* set up for conversation */ + + if (!(flags & PAM_SILENT)) { + const void *username; + + if ( pam_get_item(pamh, PAM_USER, &username) + || username == NULL ) { + pam_syslog(pamh, LOG_ERR, "no username set"); + return PAM_USER_UNKNOWN; + } + pmsg[0] = &msg[0]; + msg[0].msg_style = PAM_TEXT_INFO; + if (asprintf(&txt, "Changing STRESS password for %s.", + (const char *)username) < 0) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + return PAM_BUF_ERR; + } + + 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_syslog(pamh, 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 unchanged"; + 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,&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_syslog(pamh, LOG_DEBUG, + "pam_sm_chauthtok: problem with resp"); + retval = PAM_SYSTEM_ERR; + } + + _pam_drop_reply(resp, i); /* clean up the passwords */ + } else { + pam_syslog(pamh, LOG_ERR, + "pam_sm_chauthtok: this must be a Linux-PAM error"); + return PAM_SYSTEM_ERR; + } + + return retval; +} |