1 /*
2 * pam_stress module
3 *
4 * created by Andrew Morgan <morgan@linux.kernel.org> 1996/3/12
5 */
6
7 #include "config.h"
8
9 #include <stdlib.h>
10 #include <stdio.h>
11
12 #include <syslog.h>
13
14 #include <stdarg.h>
15 #include <string.h>
16 #include <unistd.h>
17
18 #include <security/pam_modules.h>
19 #include <security/_pam_macros.h>
20 #include <security/pam_ext.h>
21 #include "pam_inline.h"
22
23 /* ---------- */
24
25 /* an internal function to turn all possible test arguments into bits
26 of a ctrl number */
27
28 /* generic options */
29
30 #define PAM_ST_DEBUG 01
31 #define PAM_ST_NO_WARN 02
32 #define PAM_ST_USE_PASS1 04
33 #define PAM_ST_TRY_PASS1 010
34 #define PAM_ST_ROOTOK 020
35
36 /* simulation options */
37
38 #define PAM_ST_EXPIRED 040
39 #define PAM_ST_FAIL_1 0100
40 #define PAM_ST_FAIL_2 0200
41 #define PAM_ST_PRELIM 0400
42 #define PAM_ST_REQUIRE_PWD 01000
43
44 /* some syslogging */
45
46 static void
47 _pam_report (const pam_handle_t *pamh, int ctrl, const char *name,
48 int flags, int argc, const char **argv)
49 {
50 if (ctrl & PAM_ST_DEBUG) {
51 pam_syslog(pamh, LOG_DEBUG, "CALLED: %s", name);
52 pam_syslog(pamh, LOG_DEBUG, "FLAGS : 0%o%s",
53 flags, (flags & PAM_SILENT) ? " (silent)":"");
54 pam_syslog(pamh, LOG_DEBUG, "CTRL = 0%o", ctrl);
55 pam_syslog(pamh, LOG_DEBUG, "ARGV :");
56 while (argc--) {
57 pam_syslog(pamh, LOG_DEBUG, " \"%s\"", *argv++);
58 }
59 }
60 }
61
62 static int
63 _pam_parse (const pam_handle_t *pamh, int argc, const char **argv)
64 {
65 int ctrl=0;
66
67 /* step through arguments */
68 for (ctrl=0; argc-- > 0; ++argv) {
69
70 /* generic options */
71
72 if (!strcmp(*argv,"debug"))
73 ctrl |= PAM_ST_DEBUG;
74 else if (!strcmp(*argv,"no_warn"))
75 ctrl |= PAM_ST_NO_WARN;
76 else if (!strcmp(*argv,"use_first_pass"))
77 ctrl |= PAM_ST_USE_PASS1;
78 else if (!strcmp(*argv,"try_first_pass"))
79 ctrl |= PAM_ST_TRY_PASS1;
80 else if (!strcmp(*argv,"rootok"))
81 ctrl |= PAM_ST_ROOTOK;
82
83 /* simulation options */
84
85 else if (!strcmp(*argv,"expired")) /* signal password needs
86 renewal */
87 ctrl |= PAM_ST_EXPIRED;
88 else if (!strcmp(*argv,"fail_1")) /* instruct fn 1 to fail */
89 ctrl |= PAM_ST_FAIL_1;
90 else if (!strcmp(*argv,"fail_2")) /* instruct fn 2 to fail */
91 ctrl |= PAM_ST_FAIL_2;
92 else if (!strcmp(*argv,"prelim")) /* instruct pam_sm_setcred
93 to fail on first call */
94 ctrl |= PAM_ST_PRELIM;
95 else if (!strcmp(*argv,"required")) /* module is fussy about the
96 user being authenticated */
97 ctrl |= PAM_ST_REQUIRE_PWD;
98
99 else {
100 pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
101 }
102 }
103
104 return ctrl;
105 }
106
107 static int converse(pam_handle_t *pamh, int nargs
108 , const struct pam_message **message
109 , struct pam_response **response)
110 {
111 int retval;
112 const void *void_conv;
113 const struct pam_conv *conv;
114
115 retval = pam_get_item(pamh,PAM_CONV,&void_conv);
116 conv = void_conv;
117 if (retval == PAM_SUCCESS && conv) {
118 retval = conv->conv(nargs, message, response, conv->appdata_ptr);
119 if (retval != PAM_SUCCESS) {
120 pam_syslog(pamh, LOG_ERR, "converse returned %d: %s",
121 retval, pam_strerror(pamh, retval));
122 }
123 } else {
124 pam_syslog(pamh, LOG_ERR, "converse failed to get pam_conv");
125 if (retval == PAM_SUCCESS)
126 retval = PAM_BAD_ITEM; /* conv was null */
127 }
128
129 return retval;
130 }
131
132 /* authentication management functions */
133
134 static int stress_get_password(pam_handle_t *pamh, int flags
135 , int ctrl, char **password)
136 {
137 const void *pam_pass;
138 char *pass;
139
140 if ( (ctrl & (PAM_ST_TRY_PASS1|PAM_ST_USE_PASS1))
141 && (pam_get_item(pamh,PAM_AUTHTOK,&pam_pass)
142 == PAM_SUCCESS)
143 && (pam_pass != NULL) ) {
144 if ((pass = strdup(pam_pass)) == NULL)
145 return PAM_BUF_ERR;
146 } else if ((ctrl & PAM_ST_USE_PASS1)) {
147 pam_syslog(pamh, LOG_WARNING, "no forwarded password");
148 return PAM_PERM_DENIED;
149 } else { /* we will have to get one */
150 struct pam_message msg[1];
151 const struct pam_message *pmsg[1];
152 struct pam_response *resp;
153 int retval;
154
155 /* set up conversation call */
156
157 pmsg[0] = &msg[0];
158 msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
159 msg[0].msg = "STRESS Password: ";
160 resp = NULL;
161
162 if ((retval = converse(pamh,1,pmsg,&resp)) != PAM_SUCCESS) {
163 return retval;
164 }
165
166 if (resp) {
167 if ((resp[0].resp == NULL) && (ctrl & PAM_ST_DEBUG)) {
168 pam_syslog(pamh, LOG_DEBUG,
169 "pam_sm_authenticate: NULL authtok given");
170 }
171 if ((flags & PAM_DISALLOW_NULL_AUTHTOK)
172 && resp[0].resp == NULL) {
173 free(resp);
174 return PAM_AUTH_ERR;
175 }
176
177 pass = resp[0].resp; /* remember this! */
178
179 resp[0].resp = NULL;
180 } else {
181 if (ctrl & PAM_ST_DEBUG) {
182 pam_syslog(pamh, LOG_DEBUG,
183 "pam_sm_authenticate: no error reported");
184 pam_syslog(pamh, LOG_DEBUG,
185 "getting password, but NULL returned!?");
186 }
187 return PAM_CONV_ERR;
188 }
189 free(resp);
190 }
191
192 *password = pass; /* this *MUST* be free()'d by this module */
193
194 return PAM_SUCCESS;
195 }
196
197 /* function to clean up data items */
198
199 static void
200 wipe_up (pam_handle_t *pamh UNUSED, void *data, int error UNUSED)
201 {
202 free(data);
203 }
204
205 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
206 int argc, const char **argv)
207 {
208 const char *username;
209 int retval=PAM_SUCCESS;
210 char *pass;
211 int ctrl;
212
213 D(("called."));
214
215 ctrl = _pam_parse(pamh, argc, argv);
216 _pam_report(pamh, ctrl, "pam_sm_authenticate", flags, argc, argv);
217
218 /* try to get the username */
219
220 retval = pam_get_user(pamh, &username, "username: ");
221 if (retval != PAM_SUCCESS) {
222 pam_syslog(pamh, LOG_NOTICE,
223 "pam_sm_authenticate: cannot determine user name: %s",
224 pam_strerror(pamh, retval));
225 return retval;
226 }
227 else if (ctrl & PAM_ST_DEBUG) {
228 pam_syslog(pamh, LOG_DEBUG,
229 "pam_sm_authenticate: username = %s", username);
230 }
231
232 /* now get the password */
233
234 retval = stress_get_password(pamh,flags,ctrl,&pass);
235 if (retval != PAM_SUCCESS) {
236 pam_syslog(pamh, LOG_WARNING,
237 "pam_sm_authenticate: failed to get a password");
238 return retval;
239 }
240
241 /* try to set password item */
242
243 retval = pam_set_item(pamh,PAM_AUTHTOK,pass);
244 pam_overwrite_string(pass); /* clean up local copy of password */
245 free(pass);
246 pass = NULL;
247 if (retval != PAM_SUCCESS) {
248 pam_syslog(pamh, LOG_WARNING,
249 "pam_sm_authenticate: failed to store new password");
250 return retval;
251 }
252
253 /* if we are debugging then we print the password */
254
255 if (ctrl & PAM_ST_DEBUG) {
256 const void *pam_pass;
257 (void) pam_get_item(pamh,PAM_AUTHTOK,&pam_pass);
258 pam_syslog(pamh, LOG_DEBUG,
259 "pam_st_authenticate: password entered is: [%s]",
260 (const char *)pam_pass);
261 }
262
263 /* if we signal a fail for this function then fail */
264
265 if ((ctrl & PAM_ST_FAIL_1) && retval == PAM_SUCCESS)
266 return PAM_PERM_DENIED;
267
268 return retval;
269 }
270
271 int pam_sm_setcred(pam_handle_t *pamh, int flags,
272 int argc, const char **argv)
273 {
274 int ctrl = _pam_parse(pamh, argc, argv);
275
276 D(("called. [post parsing]"));
277
278 _pam_report(pamh, ctrl, "pam_sm_setcred", flags, argc, argv);
279
280 if (ctrl & PAM_ST_FAIL_2)
281 return PAM_CRED_ERR;
282
283 return PAM_SUCCESS;
284 }
285
286 /* account management functions */
287
288 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
289 int argc, const char **argv)
290 {
291 int ctrl = _pam_parse(pamh, argc, argv);
292
293 D(("called. [post parsing]"));
294
295 _pam_report(pamh, ctrl,"pam_sm_acct_mgmt", flags, argc, argv);
296
297 if (ctrl & PAM_ST_FAIL_1)
298 return PAM_PERM_DENIED;
299 else if (ctrl & PAM_ST_EXPIRED) {
300 int retval;
301 void *text = strdup("yes");
302 if (!text)
303 return PAM_BUF_ERR;
304 retval = pam_set_data(pamh,"stress_new_pwd",text,wipe_up);
305 if (retval != PAM_SUCCESS) {
306 pam_syslog(pamh, LOG_DEBUG,
307 "pam_sm_acct_mgmt: failed setting stress_new_pwd");
308 free(text);
309 return retval;
310 }
311
312 if (ctrl & PAM_ST_DEBUG) {
313 pam_syslog(pamh, LOG_DEBUG,
314 "pam_sm_acct_mgmt: need a new password");
315 }
316 return PAM_NEW_AUTHTOK_REQD;
317 }
318
319 return PAM_SUCCESS;
320 }
321
322 int pam_sm_open_session(pam_handle_t *pamh, int flags,
323 int argc, const char **argv)
324 {
325 const void *username, *service;
326 int ctrl = _pam_parse(pamh, argc, argv);
327
328 D(("called. [post parsing]"));
329
330 _pam_report(pamh, ctrl,"pam_sm_open_session", flags, argc, argv);
331
332 if ((pam_get_item(pamh, PAM_USER, &username)
333 != PAM_SUCCESS || !username)
334 || (pam_get_item(pamh, PAM_SERVICE, &service)
335 != PAM_SUCCESS || !service)) {
336 pam_syslog(pamh, LOG_WARNING, "pam_sm_open_session: for whom?");
337 return PAM_SESSION_ERR;
338 }
339
340 pam_syslog(pamh, LOG_NOTICE, "opened [%s] session for user [%s]",
341 (const char *)service, (const char *)username);
342
343 if (ctrl & PAM_ST_FAIL_1)
344 return PAM_SESSION_ERR;
345
346 return PAM_SUCCESS;
347 }
348
349 int pam_sm_close_session(pam_handle_t *pamh, int flags,
350 int argc, const char **argv)
351 {
352 const void *username, *service;
353 int ctrl = _pam_parse(pamh, argc, argv);
354
355 D(("called. [post parsing]"));
356
357 _pam_report(pamh, ctrl,"pam_sm_close_session", flags, argc, argv);
358
359 if ((pam_get_item(pamh, PAM_USER, &username)
360 != PAM_SUCCESS || !username)
361 || (pam_get_item(pamh, PAM_SERVICE, &service)
362 != PAM_SUCCESS || !service)) {
363 pam_syslog(pamh, LOG_WARNING, "pam_sm_close_session: for whom?");
364 return PAM_SESSION_ERR;
365 }
366
367 pam_syslog(pamh, LOG_NOTICE, "closed [%s] session for user [%s]",
368 (const char *)service, (const char *)username);
369
370 if (ctrl & PAM_ST_FAIL_2)
371 return PAM_SESSION_ERR;
372
373 return PAM_SUCCESS;
374 }
375
376 int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
377 int argc, const char **argv)
378 {
379 int retval;
380 int ctrl = _pam_parse(pamh, argc, argv);
381
382 D(("called. [post parsing]"));
383
384 _pam_report(pamh, ctrl,"pam_sm_chauthtok", flags, argc, argv);
385
386 /* this function should be called twice by the Linux-PAM library */
387
388 if (flags & PAM_PRELIM_CHECK) { /* first call */
389 if (ctrl & PAM_ST_DEBUG) {
390 pam_syslog(pamh, LOG_DEBUG, "pam_sm_chauthtok: prelim check");
391 }
392 if (ctrl & PAM_ST_PRELIM)
393 return PAM_TRY_AGAIN;
394
395 return PAM_SUCCESS;
396 } else if (flags & PAM_UPDATE_AUTHTOK) { /* second call */
397 struct pam_message msg[3];
398 const struct pam_message *pmsg[3];
399 struct pam_response *resp;
400 const void *text;
401 char *txt=NULL;
402 int i;
403
404 if (ctrl & PAM_ST_DEBUG) {
405 pam_syslog(pamh, LOG_DEBUG, "pam_sm_chauthtok: alter password");
406 }
407
408 if (ctrl & PAM_ST_FAIL_1)
409 return PAM_AUTHTOK_LOCK_BUSY;
410
411 if ( !(ctrl & PAM_ST_EXPIRED)
412 && (flags & PAM_CHANGE_EXPIRED_AUTHTOK)
413 && (pam_get_data(pamh,"stress_new_pwd", &text)
414 != PAM_SUCCESS || strcmp(text,"yes"))) {
415 return PAM_SUCCESS; /* the token has not expired */
416 }
417
418 /* the password should be changed */
419
420 if ((ctrl & PAM_ST_REQUIRE_PWD)
421 && !(getuid() == 0 && (ctrl & PAM_ST_ROOTOK))
422 ) { /* first get old one? */
423 char *pass;
424
425 if (ctrl & PAM_ST_DEBUG) {
426 pam_syslog(pamh, LOG_DEBUG,
427 "pam_sm_chauthtok: getting old password");
428 }
429 retval = stress_get_password(pamh,flags,ctrl,&pass);
430 if (retval != PAM_SUCCESS) {
431 pam_syslog(pamh, LOG_DEBUG,
432 "pam_sm_chauthtok: no password obtained");
433 return retval;
434 }
435 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, pass);
436 pam_overwrite_string(pass);
437 free(pass);
438 pass = NULL;
439 if (retval != PAM_SUCCESS) {
440 pam_syslog(pamh, LOG_DEBUG,
441 "pam_sm_chauthtok: could not set OLDAUTHTOK");
442 return retval;
443 }
444 }
445
446 /* set up for conversation */
447
448 if (!(flags & PAM_SILENT)) {
449 const void *username;
450
451 if ( pam_get_item(pamh, PAM_USER, &username)
452 || username == NULL ) {
453 pam_syslog(pamh, LOG_ERR, "no username set");
454 return PAM_USER_UNKNOWN;
455 }
456 pmsg[0] = &msg[0];
457 msg[0].msg_style = PAM_TEXT_INFO;
458 if (asprintf(&txt, "Changing STRESS password for %s.",
459 (const char *)username) < 0) {
460 pam_syslog(pamh, LOG_CRIT, "out of memory");
461 return PAM_BUF_ERR;
462 }
463
464 msg[0].msg = txt;
465 i = 1;
466 } else {
467 i = 0;
468 }
469
470 pmsg[i] = &msg[i];
471 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
472 msg[i++].msg = "Enter new STRESS password: ";
473 pmsg[i] = &msg[i];
474 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
475 msg[i++].msg = "Retype new STRESS password: ";
476 resp = NULL;
477
478 retval = converse(pamh,i,pmsg,&resp);
479 if (txt) {
480 free(txt);
481 txt = NULL; /* clean up */
482 }
483 if (retval != PAM_SUCCESS) {
484 return retval;
485 }
486
487 if (resp == NULL) {
488 pam_syslog(pamh, LOG_ERR,
489 "pam_sm_chauthtok: no response from conv");
490 return PAM_CONV_ERR;
491 }
492
493 /* store the password */
494
495 if (resp[i-2].resp && resp[i-1].resp) {
496 if (strcmp(resp[i-2].resp,resp[i-1].resp)) {
497 /* passwords are not the same; forget and return error */
498
499 pam_drop_response(resp, i);
500
501 if (!(flags & PAM_SILENT) && !(ctrl & PAM_ST_NO_WARN)) {
502 pmsg[0] = &msg[0];
503 msg[0].msg_style = PAM_ERROR_MSG;
504 msg[0].msg = "Verification mis-typed; "
505 "password unchanged";
506 resp = NULL;
507 (void) converse(pamh,1,pmsg,&resp);
508 if (resp) {
509 pam_drop_response(resp, 1);
510 }
511 }
512 return PAM_AUTHTOK_ERR;
513 }
514
515 if (pam_get_item(pamh,PAM_AUTHTOK,&text)
516 == PAM_SUCCESS) {
517 (void) pam_set_item(pamh,PAM_OLDAUTHTOK,text);
518 text = NULL;
519 }
520 (void) pam_set_item(pamh,PAM_AUTHTOK,resp[0].resp);
521 } else {
522 pam_syslog(pamh, LOG_DEBUG,
523 "pam_sm_chauthtok: problem with resp");
524 retval = PAM_SYSTEM_ERR;
525 }
526
527 pam_drop_response(resp, i); /* clean up the passwords */
528 } else {
529 pam_syslog(pamh, LOG_ERR,
530 "pam_sm_chauthtok: this must be a Linux-PAM error");
531 return PAM_SYSTEM_ERR;
532 }
533
534 return retval;
535 }