1 /* pam_stress module */
3 /* $Id: pam_stress.c,v 1.12 1997/02/15 19:06:30 morgan Exp morgan $
5 * created by Andrew Morgan <morgan@parc.power.net> 1996/3/12
7 * $Log: pam_stress.c,v $
8 * Revision 1.12 1997/02/15 19:06:30 morgan
11 * Revision 1.11 1997/02/15 17:33:24 morgan
12 * removed fixed syslog buffer
14 * Revision 1.10 1996/12/01 03:11:35 morgan
15 * using _pam_macros.h now
17 * Revision 1.9 1996/11/10 20:18:10 morgan
18 * changes for .53 compilation
20 * Revision 1.8 1996/09/05 06:31:59 morgan
21 * changed return value of wipe_up from int to void
23 * Revision 1.7 1996/06/02 08:12:28 morgan
24 * updated for new static protocol, added STRESS to various user prompts
25 * and added rootok flag for pam_sm_chauthtok to look out for
37 * here, we make definitions for the externally accessible functions
38 * in this file (these definitions are required for static modules
39 * but strongly encouraged generally) they are used to instruct the
40 * modules include file to define their prototypes.
44 #define PAM_SM_ACCOUNT
45 #define PAM_SM_SESSION
46 #define PAM_SM_PASSWORD
48 #include <security/pam_modules.h>
49 #include <security/_pam_macros.h>
51 static char *_strdup(const char *x)
54 new = malloc(strlen(x)+1);
61 static void _pam_log(int err, const char *format, ...)
65 va_start(args, format);
66 openlog("PAM-stress", LOG_CONS|LOG_PID, LOG_AUTH);
67 vsyslog(err, format, args);
74 /* an internal function to turn all possible test arguments into bits
79 #define PAM_ST_DEBUG 01
80 #define PAM_ST_NO_WARN 02
81 #define PAM_ST_USE_PASS1 04
82 #define PAM_ST_TRY_PASS1 010
83 #define PAM_ST_ROOTOK 020
85 /* simulation options */
87 #define PAM_ST_EXPIRED 040
88 #define PAM_ST_FAIL_1 0100
89 #define PAM_ST_FAIL_2 0200
90 #define PAM_ST_PRELIM 0400
91 #define PAM_ST_REQUIRE_PWD 01000
95 static void _pam_report(int ctrl, const char *name, int flags,
96 int argc, const char **argv)
98 if (ctrl & PAM_ST_DEBUG) {
99 _pam_log(LOG_DEBUG, "CALLED: %s", name);
100 _pam_log(LOG_DEBUG, "FLAGS : 0%o%s", flags,
101 (flags & PAM_SILENT) ? " (silent)":"");
102 _pam_log(LOG_DEBUG, "CTRL = 0%o",ctrl);
103 _pam_log(LOG_DEBUG, "ARGV :");
105 _pam_log(LOG_DEBUG, " \"%s\"", *argv++);
110 static int _pam_parse(int argc, const char **argv)
114 /* step through arguments */
115 for (ctrl=0; argc-- > 0; ++argv) {
117 /* generic options */
119 if (!strcmp(*argv,"debug"))
120 ctrl |= PAM_ST_DEBUG;
121 else if (!strcmp(*argv,"no_warn"))
122 ctrl |= PAM_ST_NO_WARN;
123 else if (!strcmp(*argv,"use_first_pass"))
124 ctrl |= PAM_ST_USE_PASS1;
125 else if (!strcmp(*argv,"try_first_pass"))
126 ctrl |= PAM_ST_TRY_PASS1;
127 else if (!strcmp(*argv,"rootok"))
128 ctrl |= PAM_ST_ROOTOK;
130 /* simulation options */
132 else if (!strcmp(*argv,"expired")) /* signal password needs
134 ctrl |= PAM_ST_EXPIRED;
135 else if (!strcmp(*argv,"fail_1")) /* instruct fn 1 to fail */
136 ctrl |= PAM_ST_FAIL_1;
137 else if (!strcmp(*argv,"fail_2")) /* instruct fn 2 to fail */
138 ctrl |= PAM_ST_FAIL_2;
139 else if (!strcmp(*argv,"prelim")) /* instruct pam_sm_setcred
140 to fail on first call */
141 ctrl |= PAM_ST_PRELIM;
142 else if (!strcmp(*argv,"required")) /* module is fussy about the
143 user being authenticated */
144 ctrl |= PAM_ST_REQUIRE_PWD;
147 _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
154 static int converse(pam_handle_t *pamh, int nargs
155 , struct pam_message **message
156 , struct pam_response **response)
159 struct pam_conv *conv;
161 if ((retval = pam_get_item(pamh,PAM_CONV,(const void **)&conv))
163 retval = conv->conv(nargs, (const struct pam_message **) message
164 , response, conv->appdata_ptr);
165 if (retval != PAM_SUCCESS) {
166 _pam_log(LOG_ERR,"(pam_stress) converse returned %d",retval);
167 _pam_log(LOG_ERR,"that is: %s",pam_strerror(pamh, retval));
170 _pam_log(LOG_ERR,"(pam_stress) converse failed to get pam_conv");
176 /* authentication management functions */
178 static int stress_get_password(pam_handle_t *pamh, int flags
179 , int ctrl, char **password)
183 if ( (ctrl & (PAM_ST_TRY_PASS1|PAM_ST_USE_PASS1))
184 && (pam_get_item(pamh,PAM_AUTHTOK,(const void **)&pass)
186 && (pass != NULL) ) {
187 pass = _strdup(pass);
188 } else if ((ctrl & PAM_ST_USE_PASS1)) {
189 _pam_log(LOG_WARNING, "pam_stress: no forwarded password");
190 return PAM_PERM_DENIED;
191 } else { /* we will have to get one */
192 struct pam_message msg[1],*pmsg[1];
193 struct pam_response *resp;
196 /* set up conversation call */
199 msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
200 msg[0].msg = "STRESS Password: ";
203 if ((retval = converse(pamh,1,pmsg,&resp)) != PAM_SUCCESS) {
208 if ((resp[0].resp == NULL) && (ctrl & PAM_ST_DEBUG)) {
210 "pam_sm_authenticate: NULL authtok given");
212 if ((flags & PAM_DISALLOW_NULL_AUTHTOK)
213 && resp[0].resp == NULL) {
218 pass = resp[0].resp; /* remember this! */
221 } else if (ctrl & PAM_ST_DEBUG) {
222 _pam_log(LOG_DEBUG,"pam_sm_authenticate: no error reported");
223 _pam_log(LOG_DEBUG,"getting password, but NULL returned!?");
229 *password = pass; /* this *MUST* be free()'d by this module */
234 /* function to clean up data items */
236 static void wipe_up(pam_handle_t *pamh, void *data, int error)
242 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
243 int argc, const char **argv)
245 const char *username;
246 int retval=PAM_SUCCESS;
252 ctrl = _pam_parse(argc,argv);
253 _pam_report(ctrl, "pam_sm_authenticate", flags, argc, argv);
255 /* try to get the username */
257 retval = pam_get_user(pamh, &username, "username: ");
258 if ((ctrl & PAM_ST_DEBUG) && (retval == PAM_SUCCESS)) {
259 _pam_log(LOG_DEBUG, "pam_sm_authenticate: username = %s", username);
260 } else if (retval != PAM_SUCCESS) {
261 _pam_log(LOG_WARNING, "pam_sm_authenticate: failed to get username");
265 /* now get the password */
267 retval = stress_get_password(pamh,flags,ctrl,&pass);
268 if (retval != PAM_SUCCESS) {
269 _pam_log(LOG_WARNING, "pam_sm_authenticate: "
270 "failed to get a password");
274 /* try to set password item */
276 retval = pam_set_item(pamh,PAM_AUTHTOK,pass);
277 if (retval != PAM_SUCCESS) {
278 _pam_log(LOG_WARNING, "pam_sm_authenticate: "
279 "failed to store new password");
280 _pam_overwrite(pass);
285 /* clean up local copy of password */
287 _pam_overwrite(pass);
291 /* if we are debugging then we print the password */
293 if (ctrl & PAM_ST_DEBUG) {
294 (void) pam_get_item(pamh,PAM_AUTHTOK,(const void **)&pass);
296 "pam_st_authenticate: password entered is: [%s]\n",pass);
300 /* if we signal a fail for this function then fail */
302 if ((ctrl & PAM_ST_FAIL_1) && retval == PAM_SUCCESS)
303 return PAM_PERM_DENIED;
309 int pam_sm_setcred(pam_handle_t *pamh, int flags,
310 int argc, const char **argv)
312 int ctrl = _pam_parse(argc,argv);
314 D(("called. [post parsing]"));
316 _pam_report(ctrl, "pam_sm_setcred", flags, argc, argv);
318 if (ctrl & PAM_ST_FAIL_2)
324 /* account management functions */
327 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
328 int argc, const char **argv)
330 int ctrl = _pam_parse(argc,argv);
332 D(("called. [post parsing]"));
334 _pam_report(ctrl,"pam_sm_acct_mgmt", flags, argc, argv);
336 if (ctrl & PAM_ST_FAIL_1)
337 return PAM_PERM_DENIED;
338 else if (ctrl & PAM_ST_EXPIRED) {
339 void *text = malloc(sizeof("yes")+1);
341 pam_set_data(pamh,"stress_new_pwd",text,wipe_up);
342 if (ctrl & PAM_ST_DEBUG) {
343 _pam_log(LOG_DEBUG,"pam_sm_acct_mgmt: need a new password");
345 return PAM_NEW_AUTHTOK_REQD;
352 int pam_sm_open_session(pam_handle_t *pamh, int flags,
353 int argc, const char **argv)
355 char *username,*service;
356 int ctrl = _pam_parse(argc,argv);
358 D(("called. [post parsing]"));
360 _pam_report(ctrl,"pam_sm_open_session", flags, argc, argv);
362 if ((pam_get_item(pamh, PAM_USER, (const void **) &username)
364 || (pam_get_item(pamh, PAM_SERVICE, (const void **) &service)
366 _pam_log(LOG_WARNING,"pam_sm_open_session: for whom?");
367 return PAM_SESSION_ERR;
370 _pam_log(LOG_NOTICE,"pam_stress: opened [%s] session for user [%s]"
371 , service, username);
373 if (ctrl & PAM_ST_FAIL_1)
374 return PAM_SESSION_ERR;
380 int pam_sm_close_session(pam_handle_t *pamh, int flags,
381 int argc, const char **argv)
383 const char *username,*service;
384 int ctrl = _pam_parse(argc,argv);
386 D(("called. [post parsing]"));
388 _pam_report(ctrl,"pam_sm_close_session", flags, argc, argv);
390 if ((pam_get_item(pamh, PAM_USER, (const void **)&username)
392 || (pam_get_item(pamh, PAM_SERVICE, (const void **)&service)
394 _pam_log(LOG_WARNING,"pam_sm_close_session: for whom?");
395 return PAM_SESSION_ERR;
398 _pam_log(LOG_NOTICE,"pam_stress: closed [%s] session for user [%s]"
399 , service, username);
401 if (ctrl & PAM_ST_FAIL_2)
402 return PAM_SESSION_ERR;
408 int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
409 int argc, const char **argv)
412 int ctrl = _pam_parse(argc,argv);
414 D(("called. [post parsing]"));
416 _pam_report(ctrl,"pam_sm_chauthtok", flags, argc, argv);
418 /* this function should be called twice by the Linux-PAM library */
420 if (flags & PAM_PRELIM_CHECK) { /* first call */
421 if (ctrl & PAM_ST_DEBUG) {
422 _pam_log(LOG_DEBUG,"pam_sm_chauthtok: prelim check");
424 if (ctrl & PAM_ST_PRELIM)
425 return PAM_TRY_AGAIN;
428 } else if (flags & PAM_UPDATE_AUTHTOK) { /* second call */
429 struct pam_message msg[3],*pmsg[3];
430 struct pam_response *resp;
435 if (ctrl & PAM_ST_DEBUG) {
436 _pam_log(LOG_DEBUG,"pam_sm_chauthtok: alter password");
439 if (ctrl & PAM_ST_FAIL_1)
440 return PAM_AUTHTOK_LOCK_BUSY;
442 if ( !(ctrl && PAM_ST_EXPIRED)
443 && (flags & PAM_CHANGE_EXPIRED_AUTHTOK)
444 && (pam_get_data(pamh,"stress_new_pwd",(const void **)&text)
445 != PAM_SUCCESS || strcmp(text,"yes"))) {
446 return PAM_SUCCESS; /* the token has not expired */
449 /* the password should be changed */
451 if ((ctrl & PAM_ST_REQUIRE_PWD)
452 && !(getuid() == 0 && (ctrl & PAM_ST_ROOTOK))
453 ) { /* first get old one? */
456 if (ctrl & PAM_ST_DEBUG) {
458 ,"pam_sm_chauthtok: getting old password");
460 retval = stress_get_password(pamh,flags,ctrl,&pass);
461 if (retval != PAM_SUCCESS) {
463 ,"pam_sm_chauthtok: no password obtained");
466 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, pass);
467 if (retval != PAM_SUCCESS) {
469 ,"pam_sm_chauthtok: could not set OLDAUTHTOK");
470 _pam_overwrite(pass);
474 _pam_overwrite(pass);
478 /* set up for conversation */
480 if (!(flags & PAM_SILENT)) {
483 if ( pam_get_item(pamh, PAM_USER, (const void **)&username)
484 || username == NULL ) {
485 _pam_log(LOG_ERR,"no username set");
486 return PAM_USER_UNKNOWN;
489 msg[0].msg_style = PAM_TEXT_INFO;
490 #define _LOCAL_STRESS_COMMENT "Changing STRESS password for "
491 txt = (char *) malloc(sizeof(_LOCAL_STRESS_COMMENT)
492 +strlen(username)+1);
493 strcpy(txt, _LOCAL_STRESS_COMMENT);
494 #undef _LOCAL_STRESS_COMMENT
495 strcat(txt, username);
503 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
504 msg[i++].msg = "Enter new STRESS password: ";
506 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
507 msg[i++].msg = "Retype new STRESS password: ";
510 retval = converse(pamh,i,pmsg,&resp);
513 txt = NULL; /* clean up */
515 if (retval != PAM_SUCCESS) {
520 _pam_log(LOG_ERR, "pam_sm_chauthtok: no response from conv");
524 /* store the password */
526 if (resp[i-2].resp && resp[i-1].resp) {
527 if (strcmp(resp[i-2].resp,resp[i-1].resp)) {
528 /* passwords are not the same; forget and return error */
530 _pam_drop_reply(resp, i);
532 if (!(flags & PAM_SILENT) && !(ctrl & PAM_ST_NO_WARN)) {
534 msg[0].msg_style = PAM_ERROR_MSG;
535 msg[0].msg = "Verification mis-typed; "
538 (void) converse(pamh,1,pmsg,&resp);
540 _pam_drop_reply(resp, 1);
543 return PAM_AUTHTOK_ERR;
546 if (pam_get_item(pamh,PAM_AUTHTOK,(const void **)&text)
548 (void) pam_set_item(pamh,PAM_OLDAUTHTOK,text);
551 (void) pam_set_item(pamh,PAM_AUTHTOK,resp[0].resp);
553 _pam_log(LOG_DEBUG,"pam_sm_chauthtok: problem with resp");
554 retval = PAM_SYSTEM_ERR;
557 _pam_drop_reply(resp, i); /* clean up the passwords */
559 _pam_log(LOG_ERR,"pam_sm_chauthtok: this must be a Linux-PAM error");
560 return PAM_SYSTEM_ERR;
569 /* static module data */
571 struct pam_module _pam_stress_modstruct = {
577 pam_sm_close_session,