1 /*
2  * Copyright (c) 2015 Andreas Schneider <asn@samba.org>
3  * Copyright (c) 2015 Jakub Hrozek <jakub.hrozek@posteo.se>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "config.h"
20 
21 #include <stdarg.h>
22 #include <stddef.h>
23 #include <setjmp.h>
24 #include <cmocka.h>
25 
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <limits.h>
32 #include <syslog.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 
36 #ifdef HAVE_SECURITY_PAM_APPL_H
37 #include <security/pam_appl.h>
38 #endif
39 #ifdef HAVE_SECURITY_PAM_MODULES_H
40 #include <security/pam_modules.h>
41 #endif
42 #ifdef HAVE_SECURITY_PAM_EXT_H
43 #include <security/pam_ext.h>
44 #endif
45 
46 #include "pwrap_compat.h"
47 #include "libpamtest.h"
48 
49 /* GCC have printf type attribute check. */
50 #ifdef HAVE_FUNCTION_ATTRIBUTE_FORMAT
51 #define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
52 #else
53 #define PRINTF_ATTRIBUTE(a,b)
54 #endif /* HAVE_FUNCTION_ATTRIBUTE_FORMAT */
55 
56 #ifndef ZERO_STRUCT
57 #define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
58 #endif
59 
60 struct pwrap_test_ctx {
61 	struct pam_conv conv;
62 	pam_handle_t *ph;
63 };
64 
65 struct pwrap_conv_data {
66 	const char **authtoks;
67 	size_t authtok_index;
68 };
69 
pwrap_conv(int num_msg,const struct pam_message ** msgm,struct pam_response ** response,void * appdata_ptr)70 static int pwrap_conv(int num_msg, const struct pam_message **msgm,
71 		      struct pam_response **response,
72 		      void *appdata_ptr)
73 {
74 	int i;
75 	struct pam_response *reply;
76 	const char *password;
77 	size_t pwlen;
78 	struct pwrap_conv_data *cdata = (struct pwrap_conv_data *) appdata_ptr;
79 
80 	if (cdata == NULL) {
81 		return PAM_CONV_ERR;
82 	}
83 
84 	reply = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response));
85 	if (reply == NULL) {
86 		return PAM_CONV_ERR;
87 	}
88 
89 	for (i=0; i < num_msg; i++) {
90 		switch (msgm[i]->msg_style) {
91 		case PAM_PROMPT_ECHO_OFF:
92 			password = (const char *) cdata->authtoks[cdata->authtok_index];
93 			if (password == NULL) {
94 				free(reply);
95 				return PAM_CONV_ERR;
96 			}
97 
98 			pwlen = strlen(password) + 1;
99 
100 			cdata->authtok_index++;
101 
102 			reply[i].resp = calloc(pwlen, sizeof(char));
103 			if (reply[i].resp == NULL) {
104 				free(reply);
105 				return PAM_CONV_ERR;
106 			}
107 			memcpy(reply[i].resp, password, pwlen);
108 			break;
109 		default:
110 			continue;
111 		}
112 	}
113 
114 	*response = reply;
115 	return PAM_SUCCESS;
116 }
117 
setup_passdb(void ** state)118 static int setup_passdb(void **state)
119 {
120 	int rv;
121 	const char *db;
122 	FILE *fp = NULL;
123 	char passdb_path[PATH_MAX];
124 
125 	(void) state;	/* unused */
126 
127 	db = getcwd(passdb_path, PATH_MAX);
128 	assert_non_null(db);
129 	assert_true(strlen(passdb_path) + sizeof("/passdb") < PATH_MAX);
130 	db = strncat(passdb_path, "/passdb", sizeof("/passdb"));
131 
132 	rv = setenv("PAM_MATRIX_PASSWD", passdb_path, 1);
133 	assert_int_equal(rv, 0);
134 
135 	fp = fopen(db, "w");
136 	assert_non_null(fp);
137 
138 	fprintf(fp, "trinity:secret:matrix\n");
139 	fprintf(fp, "neo:secret:pwrap_wrong_svc");
140 
141 	fflush(fp);
142 	fclose(fp);
143 
144 	return 0;
145 }
146 
teardown_passdb(void ** state)147 static int teardown_passdb(void **state)
148 {
149 	const char *db;
150 
151 	(void) state;	/* unused */
152 
153 	db = getenv("PAM_MATRIX_PASSWD");
154 	assert_non_null(db);
155 	unlink(db);
156 
157 	/* Don't pollute environment for other tests */
158 	unsetenv("PAM_MATRIX_PASSWD");
159 
160 	return 0;
161 }
162 
setup_ctx_only(void ** state)163 static int setup_ctx_only(void **state)
164 {
165 	struct pwrap_test_ctx *test_ctx;
166 
167 	setup_passdb(NULL);
168 
169 	test_ctx = malloc(sizeof(struct pwrap_test_ctx));
170 	assert_non_null(test_ctx);
171 
172 	test_ctx->conv.conv = pwrap_conv;
173 
174 	*state = test_ctx;
175 	return 0;
176 }
177 
setup_noconv(void ** state)178 static int setup_noconv(void **state)
179 {
180 	struct pwrap_test_ctx *test_ctx;
181 	int rv;
182 
183 	setup_ctx_only(state);
184 	test_ctx = *state;
185 
186 	/* We'll get an error if the test module talks to us */
187 	test_ctx->conv.appdata_ptr = NULL;
188 
189 	rv = pam_start("matrix", "trinity",
190 		       &test_ctx->conv, &test_ctx->ph);
191 	assert_int_equal(rv, PAM_SUCCESS);
192 
193 	*state = test_ctx;
194 	return 0;
195 }
196 
teardown_simple(void ** state)197 static int teardown_simple(void **state)
198 {
199 	struct pwrap_test_ctx *test_ctx;
200 	test_ctx = (struct pwrap_test_ctx *) *state;
201 
202 	free(test_ctx);
203 	return 0;
204 }
205 
test_env(void ** state)206 static void test_env(void **state)
207 {
208 	const char *v;
209 	struct stat sb;
210 	int ret;
211 
212 	(void) state; /* unused */
213 
214 	v = getenv("PAM_WRAPPER_RUNTIME_DIR");
215 	assert_non_null(v);
216 
217 	ret = stat(v, &sb);
218 	assert_int_not_equal(ret, -1);
219 	assert_true(S_ISDIR(sb.st_mode));
220 }
221 
test_pam_start(void ** state)222 static void test_pam_start(void **state)
223 {
224 	int rv;
225 	pam_handle_t *ph;
226 	struct pwrap_test_ctx *test_ctx;
227 
228 	test_ctx = (struct pwrap_test_ctx *) *state;
229 	test_ctx->conv.appdata_ptr = (void *) "testpassword";
230 
231 	rv = pam_start("matrix", "trinity", &test_ctx->conv, &ph);
232 	assert_int_equal(rv, PAM_SUCCESS);
233 
234 	rv = pam_end(ph, PAM_SUCCESS);
235 	assert_int_equal(rv, PAM_SUCCESS);
236 }
237 
teardown(void ** state)238 static int teardown(void **state)
239 {
240 	struct pwrap_test_ctx *test_ctx;
241 	int rv;
242 
243 	teardown_passdb(NULL);
244 
245 	test_ctx = (struct pwrap_test_ctx *) *state;
246 
247 	rv = pam_end(test_ctx->ph, PAM_SUCCESS);
248 	assert_int_equal(rv, PAM_SUCCESS);
249 
250 	return teardown_simple(state);
251 }
252 
test_pam_authenticate(void ** state)253 static void test_pam_authenticate(void **state)
254 {
255 	enum pamtest_err perr;
256 	struct pamtest_conv_data conv_data;
257 	const char *trinity_authtoks[] = {
258 		"secret",
259 		NULL,
260 	};
261 	struct pam_testcase tests[] = {
262 		pam_test(PAMTEST_AUTHENTICATE, PAM_SUCCESS),
263 	};
264 
265 	(void) state;	/* unused */
266 
267 	ZERO_STRUCT(conv_data);
268 	conv_data.in_echo_off = trinity_authtoks;
269 
270 	perr = run_pamtest("matrix", "trinity", &conv_data, tests);
271 	assert_int_equal(perr, PAMTEST_ERR_OK);
272 }
273 
test_pam_authenticate_null_password(void ** state)274 static void test_pam_authenticate_null_password(void **state)
275 {
276 	enum pamtest_err perr;
277 	struct pamtest_conv_data conv_data;
278 	const char *empty_authtoks[] = {
279 		NULL,
280 	};
281 	struct pam_testcase tests[] = {
282 		pam_test(PAMTEST_AUTHENTICATE, PAM_CRED_ERR),
283 	};
284 
285 	(void) state;	/* unused */
286 
287 	ZERO_STRUCT(conv_data);
288 	conv_data.in_echo_off = empty_authtoks;
289 
290 	perr = run_pamtest("matrix", "trinity", &conv_data, tests);
291 	assert_int_equal(perr, PAMTEST_ERR_OK);
292 }
293 
test_pam_authenticate_err(void ** state)294 static void test_pam_authenticate_err(void **state)
295 {
296 	enum pamtest_err perr;
297 	struct pamtest_conv_data conv_data;
298 	const char *trinity_authtoks[] = {
299 		"wrong_password",
300 		NULL,
301 	};
302 	struct pam_testcase tests[] = {
303 		pam_test(PAMTEST_AUTHENTICATE, PAM_AUTH_ERR),
304 	};
305 
306 	(void) state;	/* unused */
307 
308 	ZERO_STRUCT(conv_data);
309 	conv_data.in_echo_off = trinity_authtoks;
310 
311 	perr = run_pamtest("matrix", "trinity", &conv_data, tests);
312 	assert_int_equal(perr, PAMTEST_ERR_OK);
313 }
314 
test_pam_acct(void ** state)315 static void test_pam_acct(void **state)
316 {
317 	enum pamtest_err perr;
318 	struct pam_testcase tests[] = {
319 		pam_test(PAMTEST_ACCOUNT, PAM_SUCCESS),
320 	};
321 
322 	(void) state;	/* unused */
323 
324 	perr = run_pamtest("matrix", "trinity", NULL, tests);
325 	assert_int_equal(perr, PAMTEST_ERR_OK);
326 }
327 
test_pam_acct_err(void ** state)328 static void test_pam_acct_err(void **state)
329 {
330 	enum pamtest_err perr;
331 	struct pam_testcase tests[] = {
332 		pam_test(PAMTEST_ACCOUNT, PAM_PERM_DENIED),
333 	};
334 
335 	(void) state;	/* unused */
336 
337 	perr = run_pamtest("matrix", "neo", NULL, tests);
338 	assert_int_equal(perr, PAMTEST_ERR_OK);
339 }
340 
free_vlist(char ** vlist)341 static inline void free_vlist(char **vlist)
342 {
343 	free(vlist[0]);
344 	free(vlist[1]);
345 	free(vlist);
346 }
347 
test_pam_env_functions(void ** state)348 static void test_pam_env_functions(void **state)
349 {
350 	int rv;
351 	const char *v;
352 	char **vlist;
353 	struct pwrap_test_ctx *test_ctx;
354 
355 	test_ctx = (struct pwrap_test_ctx *) *state;
356 
357 	rv = pam_putenv(test_ctx->ph, "KEY=value");
358 	assert_int_equal(rv, PAM_SUCCESS);
359 	rv = pam_putenv(test_ctx->ph, "KEY2=value2");
360 	assert_int_equal(rv, PAM_SUCCESS);
361 
362 	v = pam_getenv(test_ctx->ph, "KEY");
363 	assert_non_null(v);
364 	assert_string_equal(v, "value");
365 
366 	v = pam_getenv(test_ctx->ph, "KEY2");
367 	assert_non_null(v);
368 	assert_string_equal(v, "value2");
369 
370 	vlist = pam_getenvlist(test_ctx->ph);
371 	assert_non_null(vlist);
372 	assert_non_null(vlist[0]);
373 	assert_string_equal(vlist[0], "KEY=value");
374 	assert_non_null(vlist[1]);
375 	assert_string_equal(vlist[1], "KEY2=value2");
376 	assert_null(vlist[2]);
377 	free_vlist(vlist);
378 
379 	rv = pam_putenv(test_ctx->ph, "KEY2=");
380 	assert_int_equal(rv, PAM_SUCCESS);
381 
382 	vlist = pam_getenvlist(test_ctx->ph);
383 	assert_non_null(vlist);
384 	assert_non_null(vlist[0]);
385 	assert_string_equal(vlist[0], "KEY=value");
386 	assert_non_null(vlist[1]);
387 	assert_string_equal(vlist[1], "KEY2=");
388 	assert_null(vlist[2]);
389 	free_vlist(vlist);
390 
391 #ifndef HAVE_OPENPAM
392 	/* OpenPAM does not support this feature */
393 	rv = pam_putenv(test_ctx->ph, "KEY2");
394 	assert_int_equal(rv, PAM_SUCCESS);
395 
396 	vlist = pam_getenvlist(test_ctx->ph);
397 	assert_non_null(vlist);
398 	assert_non_null(vlist[0]);
399 	assert_string_equal(vlist[0], "KEY=value");
400 	assert_null(vlist[1]);
401 	free_vlist(vlist);
402 #endif
403 }
404 
string_in_list(char ** list,const char * key)405 static const char *string_in_list(char **list, const char *key)
406 {
407 	if (list == NULL || key == NULL) {
408 		return NULL;
409 	}
410 
411 	if (strlen(key) > 0) {
412 		char key_eq[strlen(key) + 1 + 1]; /* trailing = and '\0' */
413 
414 		snprintf(key_eq, sizeof(key_eq), "%s=", key);
415 		for (size_t i = 0; list[i] != NULL; i++) {
416 			if (strncmp(list[i], key_eq, sizeof(key_eq)-1) == 0) {
417 				return list[i] + sizeof(key_eq)-1;
418 			}
419 		}
420 	}
421 
422 	return NULL;
423 }
424 
test_pam_session(void ** state)425 static void test_pam_session(void **state)
426 {
427 	enum pamtest_err perr;
428 	const char *v;
429 	struct pam_testcase tests[] = {
430 		pam_test(PAMTEST_OPEN_SESSION, PAM_SUCCESS),
431 		pam_test(PAMTEST_GETENVLIST, PAM_SUCCESS),
432 		pam_test(PAMTEST_CLOSE_SESSION, PAM_SUCCESS),
433 		pam_test(PAMTEST_GETENVLIST, PAM_SUCCESS),
434 	};
435 
436 	(void) state;	/* unused */
437 
438 	perr = run_pamtest("matrix", "trinity", NULL, tests);
439 	assert_int_equal(perr, PAMTEST_ERR_OK);
440 
441 	v = string_in_list(tests[1].case_out.envlist, "HOMEDIR");
442 	assert_non_null(v);
443 	assert_string_equal(v, "/home/trinity");
444 
445 	pamtest_free_env(tests[1].case_out.envlist);
446 
447 	/* environment is cleared after session close */
448 	assert_non_null(tests[3].case_out.envlist);
449 #ifdef HAVE_OPENPAM
450 	v = string_in_list(tests[3].case_out.envlist, "HOMEDIR");
451 	assert_non_null(v);
452 	assert_string_equal(v, "");
453 #else
454 	assert_null(tests[3].case_out.envlist[0]);
455 #endif
456 	pamtest_free_env(tests[3].case_out.envlist);
457 }
458 
test_pam_chauthtok(void ** state)459 static void test_pam_chauthtok(void **state)
460 {
461 	enum pamtest_err perr;
462 	struct pamtest_conv_data conv_data;
463 	const char *trinity_new_authtoks[] = {
464 		"secret",	    /* old password */
465 		"new_secret",	    /* new password */
466 		"new_secret",	    /* verify new password */
467 		"new_secret",	    /* login with the new password */
468 		NULL,
469 	};
470 	struct pam_testcase tests[] = {
471 		pam_test(PAMTEST_CHAUTHTOK, PAM_SUCCESS),
472 		pam_test(PAMTEST_AUTHENTICATE, PAM_SUCCESS),
473 	};
474 
475 	(void) state;	/* unused */
476 
477 	ZERO_STRUCT(conv_data);
478 	conv_data.in_echo_off = trinity_new_authtoks;
479 
480 	perr = run_pamtest("matrix", "trinity", &conv_data, tests);
481 	assert_int_equal(perr, PAMTEST_ERR_OK);
482 }
483 
test_pam_chauthtok_prelim_failed(void ** state)484 static void test_pam_chauthtok_prelim_failed(void **state)
485 {
486 	enum pamtest_err perr;
487 	struct pamtest_conv_data conv_data;
488 	const char *trinity_new_authtoks[] = {
489 		"wrong_secret",	    /* old password */
490 		"new_secret",	    /* new password */
491 		"new_secret",	    /* verify new password */
492 		NULL,
493 	};
494 	struct pam_testcase tests[] = {
495 		pam_test(PAMTEST_CHAUTHTOK, PAM_AUTH_ERR),
496 	};
497 
498 	(void) state;	/* unused */
499 
500 	ZERO_STRUCT(conv_data);
501 	conv_data.in_echo_off = trinity_new_authtoks;
502 
503 	perr = run_pamtest("matrix", "trinity", &conv_data, tests);
504 	assert_int_equal(perr, PAMTEST_ERR_OK);
505 }
506 
test_pam_chauthtok_diff_passwords(void ** state)507 static void test_pam_chauthtok_diff_passwords(void **state)
508 {
509 	enum pamtest_err perr;
510 	struct pamtest_conv_data conv_data;
511 	const char *trinity_new_authtoks[] = {
512 		"wrong_secret",		    /* old password */
513 		"new_secret",		    /* new password */
514 		"different_new_secret",	    /* verify new password */
515 		NULL,
516 	};
517 	struct pam_testcase tests[] = {
518 		pam_test(PAMTEST_CHAUTHTOK, PAM_AUTH_ERR),
519 	};
520 
521 	(void) state;	/* unused */
522 
523 	ZERO_STRUCT(conv_data);
524 	conv_data.in_echo_off = trinity_new_authtoks;
525 
526 	perr = run_pamtest("matrix", "trinity", &conv_data, tests);
527 	assert_int_equal(perr, PAMTEST_ERR_OK);
528 }
529 
test_pam_setcred(void ** state)530 static void test_pam_setcred(void **state)
531 {
532 	enum pamtest_err perr;
533 	const char *v;
534 	struct pam_testcase tests[] = {
535 		pam_test(PAMTEST_GETENVLIST, PAM_SUCCESS),
536 		pam_test(PAMTEST_SETCRED, PAM_SUCCESS),
537 		pam_test(PAMTEST_GETENVLIST, PAM_SUCCESS),
538 	};
539 
540 	(void) state;	/* unused */
541 
542 	perr = run_pamtest("matrix", "trinity", NULL, tests);
543 	assert_int_equal(perr, PAMTEST_ERR_OK);
544 
545 	/* environment is clean before setcred */
546 	assert_non_null(tests[0].case_out.envlist);
547 	assert_null(tests[0].case_out.envlist[0]);
548 	pamtest_free_env(tests[0].case_out.envlist);
549 
550 	/* and has an item after setcred */
551 	v = string_in_list(tests[2].case_out.envlist, "CRED");
552 	assert_non_null(v);
553 	assert_string_equal(v, "/tmp/trinity");
554 	pamtest_free_env(tests[2].case_out.envlist);
555 }
556 
test_pam_item_functions(void ** state)557 static void test_pam_item_functions(void **state)
558 {
559 	struct pwrap_test_ctx *test_ctx;
560 	const char *item;
561 	int rv;
562 
563 	test_ctx = (struct pwrap_test_ctx *) *state;
564 
565 	rv = pam_get_item(test_ctx->ph, PAM_USER, (const void **) &item);
566 	assert_int_equal(rv, PAM_SUCCESS);
567 	assert_string_equal(item, "trinity");
568 
569 	rv = pam_set_item(test_ctx->ph, PAM_USER_PROMPT, "test_login");
570 	assert_int_equal(rv, PAM_SUCCESS);
571 	assert_string_equal(item, "trinity");
572 
573 	rv = pam_get_item(test_ctx->ph, PAM_USER_PROMPT, (const void **) &item);
574 	assert_int_equal(rv, PAM_SUCCESS);
575 	assert_string_equal(item, "test_login");
576 
577 	rv = pam_set_item(test_ctx->ph, PAM_AUTHTOK, "mysecret");
578 #ifdef HAVE_OPENPAM
579 	/* OpenPAM allows PAM_AUTHTOK getset */
580 	assert_int_equal(rv, PAM_SUCCESS);
581 #else
582 	assert_int_equal(rv, PAM_BAD_ITEM);
583 #endif
584 
585 	rv = pam_get_item(test_ctx->ph, PAM_AUTHTOK, (const void **) &item);
586 #ifdef HAVE_OPENPAM
587 	/* OpenPAM allows PAM_AUTHTOK getset */
588 	assert_int_equal(rv, PAM_SUCCESS);
589 	assert_string_equal(item, "mysecret");
590 #else
591 	assert_int_equal(rv, PAM_BAD_ITEM);
592 #endif
593 
594 }
595 
add_to_reply(struct pam_response * res,const char * s1,const char * s2)596 static int add_to_reply(struct pam_response *res,
597 			const char *s1,
598 			const char *s2)
599 {
600 	size_t res_len;
601 	int rv;
602 
603 	res_len = strlen(s1) + strlen(s2) + 1;
604 
605 	res->resp = calloc(res_len, sizeof(char));
606 	if (res->resp == NULL) {
607 		return ENOMEM;
608 	}
609 
610 	rv = snprintf(res->resp, res_len, "%s%s", s1, s2);
611 	if (rv < 0) {
612 		return EIO;
613 	}
614 
615 	return 0;
616 }
617 
pwrap_echo_conv(int num_msg,const struct pam_message ** msgm,struct pam_response ** response,void * appdata_ptr)618 static int pwrap_echo_conv(int num_msg,
619 			   const struct pam_message **msgm,
620 			   struct pam_response **response,
621 			   void *appdata_ptr)
622 {
623 	int i;
624 	struct pam_response *reply;
625 	int *resp_array = appdata_ptr;
626 
627 	reply = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response));
628 	if (reply == NULL) {
629 		return PAM_CONV_ERR;
630 	}
631 
632 	for (i=0; i < num_msg; i++) {
633 		switch (msgm[i]->msg_style) {
634 		case PAM_PROMPT_ECHO_OFF:
635 			add_to_reply(&reply[i], "echo off: ", msgm[i]->msg);
636 			break;
637 		case PAM_PROMPT_ECHO_ON:
638 			add_to_reply(&reply[i], "echo on: ", msgm[i]->msg);
639 			break;
640 		case PAM_TEXT_INFO:
641 			resp_array[0] = 1;
642 			break;
643 		case PAM_ERROR_MSG:
644 			resp_array[1] = 1;
645 			break;
646 		default:
647 			break;
648 		}
649 	}
650 
651 	*response = reply;
652 	return PAM_SUCCESS;
653 }
654 
655 static int vprompt_test_fn(pam_handle_t *pamh, int style,
656 			   char **response, const char *fmt, ...) PRINTF_ATTRIBUTE(4, 5);
657 
vprompt_test_fn(pam_handle_t * pamh,int style,char ** response,const char * fmt,...)658 static int vprompt_test_fn(pam_handle_t *pamh, int style,
659 			   char **response, const char *fmt, ...)
660 {
661 	va_list args;
662 	int rv;
663 
664 	va_start(args, fmt);
665 	rv = pam_vprompt(pamh, style, response, fmt, args);
666 	va_end(args);
667 
668 	return rv;
669 }
670 
test_pam_prompt(void ** state)671 static void test_pam_prompt(void **state)
672 {
673 	struct pwrap_test_ctx *test_ctx;
674 	int rv;
675 	char *response;
676 	int resp_array[2];
677 
678 	test_ctx = (struct pwrap_test_ctx *) *state;
679 
680 	memset(resp_array, 0, sizeof(resp_array));
681 
682 	test_ctx->conv.conv = pwrap_echo_conv;
683 	test_ctx->conv.appdata_ptr = resp_array;
684 
685 	rv = pam_start("matrix", "trinity",
686 		       &test_ctx->conv, &test_ctx->ph);
687 	assert_int_equal(rv, PAM_SUCCESS);
688 
689 	rv = pam_prompt(test_ctx->ph, PAM_PROMPT_ECHO_OFF, &response, "no echo");
690 	assert_int_equal(rv, PAM_SUCCESS);
691 	assert_string_equal(response, "echo off: no echo");
692 	free(response);
693 
694 	rv = vprompt_test_fn(test_ctx->ph, PAM_PROMPT_ECHO_OFF, &response, "no echo");
695 	assert_int_equal(rv, PAM_SUCCESS);
696 	assert_string_equal(response, "echo off: no echo");
697 	free(response);
698 
699 	rv = pam_prompt(test_ctx->ph, PAM_PROMPT_ECHO_ON, &response, "echo");
700 	assert_int_equal(rv, PAM_SUCCESS);
701 	assert_string_equal(response, "echo on: echo");
702 	free(response);
703 
704 	rv = vprompt_test_fn(test_ctx->ph, PAM_PROMPT_ECHO_ON, &response, "echo");
705 	assert_int_equal(rv, PAM_SUCCESS);
706 	assert_string_equal(response, "echo on: echo");
707 	free(response);
708 
709 	assert_int_equal(resp_array[0], 0);
710 	pam_info(test_ctx->ph, "info");
711 	assert_int_equal(resp_array[0], 1);
712 
713 	assert_int_equal(resp_array[1], 0);
714 	pam_error(test_ctx->ph, "error");
715 	assert_int_equal(resp_array[1], 1);
716 }
717 
test_pam_strerror(void ** state)718 static void test_pam_strerror(void **state)
719 {
720 	struct pwrap_test_ctx *test_ctx;
721 	const char *s = NULL;
722 
723 	test_ctx = (struct pwrap_test_ctx *) *state;
724 
725 	s = pam_strerror(test_ctx->ph, PAM_AUTH_ERR);
726 	assert_non_null(s);
727 }
728 
test_pam_authenticate_db_opt(void ** state)729 static void test_pam_authenticate_db_opt(void **state)
730 {
731 	enum pamtest_err perr;
732 	struct pamtest_conv_data conv_data;
733 	char auth_info_msg[PAM_MAX_MSG_SIZE] = { '\0' };
734 	char *info_arr[] = {
735 		auth_info_msg,
736 		NULL,
737 	};
738 	const char *trinity_authtoks[] = {
739 		"secret_ro",
740 		NULL,
741 	};
742 	struct pam_testcase tests[] = {
743 		pam_test(PAMTEST_AUTHENTICATE, PAM_SUCCESS),
744 	};
745 
746 	(void) state;	/* unused */
747 
748 	ZERO_STRUCT(conv_data);
749 
750 	conv_data.in_echo_on = trinity_authtoks;
751 	conv_data.out_info = info_arr;
752 
753 	perr = run_pamtest("matrix_opt", "trinity_ro", &conv_data, tests);
754 	assert_int_equal(perr, PAMTEST_ERR_OK);
755 
756 	assert_string_equal(auth_info_msg, "Authentication succeeded");
757 }
758 
test_pam_authenticate_db_opt_err(void ** state)759 static void test_pam_authenticate_db_opt_err(void **state)
760 {
761 	enum pamtest_err perr;
762 	struct pamtest_conv_data conv_data;
763 	char auth_err_msg[PAM_MAX_MSG_SIZE] = { '\0' };
764 	char *err_arr[] = {
765 		auth_err_msg,
766 		NULL,
767 	};
768 	const char *trinity_authtoks[] = {
769 		"wrong_secret",
770 		NULL,
771 	};
772 	struct pam_testcase tests[] = {
773 		pam_test(PAMTEST_AUTHENTICATE, PAM_AUTH_ERR),
774 	};
775 
776 	(void) state;	/* unused */
777 
778 	ZERO_STRUCT(conv_data);
779 	conv_data.in_echo_on = trinity_authtoks;
780 	conv_data.out_err = err_arr;
781 
782 	perr = run_pamtest("matrix_opt", "trinity_ro", &conv_data, tests);
783 	assert_int_equal(perr, PAMTEST_ERR_OK);
784 
785 	assert_string_equal(auth_err_msg, "Authentication failed");
786 }
787 
788 
789 #ifdef HAVE_PAM_VSYSLOG
790 static void vsyslog_test_fn(const pam_handle_t *pamh,
791 			    int priority,
792 			    const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
793 
vsyslog_test_fn(const pam_handle_t * pamh,int priority,const char * fmt,...)794 static void vsyslog_test_fn(const pam_handle_t *pamh,
795 			    int priority,
796 			    const char *fmt, ...)
797 {
798 	va_list args;
799 
800 	va_start(args, fmt);
801 	pam_vsyslog(pamh, priority, fmt, args);
802 	va_end(args);
803 }
804 
test_pam_vsyslog(void ** state)805 static void test_pam_vsyslog(void **state)
806 {
807 	struct pwrap_test_ctx *test_ctx;
808 
809 	test_ctx = (struct pwrap_test_ctx *) *state;
810 
811 	pam_syslog(test_ctx->ph, LOG_INFO, "This is pam_wrapper test\n");
812 	vsyslog_test_fn(test_ctx->ph, LOG_INFO, "This is pam_wrapper test\n");
813 }
814 #endif /* HAVE_PAM_VSYSLOG */
815 
test_libpamtest_strerror(void ** state)816 static void test_libpamtest_strerror(void **state)
817 {
818 	const char *s;
819 
820 	(void) state;	/* unused */
821 
822 	s = pamtest_strerror(PAMTEST_ERR_OK);
823 	assert_non_null(s);
824 
825 	s = pamtest_strerror(PAMTEST_ERR_START);
826 	assert_non_null(s);
827 
828 	s = pamtest_strerror(PAMTEST_ERR_CASE);
829 	assert_non_null(s);
830 
831 	s = pamtest_strerror(PAMTEST_ERR_OP);
832 	assert_non_null(s);
833 
834 	s = pamtest_strerror(PAMTEST_ERR_END);
835 	assert_non_null(s);
836 
837 	s = pamtest_strerror(PAMTEST_ERR_KEEPHANDLE);
838 	assert_non_null(s);
839 
840 	s = pamtest_strerror(PAMTEST_ERR_INTERNAL);
841 	assert_non_null(s);
842 }
843 
844 #define test_setenv(env) setenv(env, "test_"env, 1)
845 
846 #define test_getenv(envlist, key) do {				    \
847 	const char *__v;					    \
848 	__v = string_in_list(envlist, key);			    \
849 	assert_non_null(__v);					    \
850 	assert_string_equal(__v, "test_"key);			    \
851 } while(0);
852 
test_get_set(void ** state)853 static void test_get_set(void **state)
854 {
855 #ifndef HAVE_OPENPAM
856 	const char *svc;
857 #endif
858 	enum pamtest_err perr;
859 	struct pam_testcase tests[] = {
860 		pam_test(PAMTEST_OPEN_SESSION, PAM_SUCCESS),
861 		pam_test(PAMTEST_GETENVLIST, PAM_SUCCESS),
862 	};
863 
864 	(void) state;	/* unused */
865 
866 #ifndef HAVE_OPENPAM
867 	test_setenv("PAM_SERVICE");
868 #endif
869 	test_setenv("PAM_USER");
870 	test_setenv("PAM_USER_PROMPT");
871 	test_setenv("PAM_TTY");
872 	test_setenv("PAM_RUSER");
873 	test_setenv("PAM_RHOST");
874 	test_setenv("PAM_AUTHTOK");
875 	test_setenv("PAM_OLDAUTHTOK");
876 #ifdef PAM_XDISPLAY
877 	test_setenv("PAM_XDISPLAY");
878 #endif
879 #ifdef PAM_AUTHTOK_TYPE
880 	test_setenv("PAM_AUTHTOK_TYPE");
881 #endif
882 
883 	perr = run_pamtest("pwrap_get_set", "trinity", NULL, tests);
884 	assert_int_equal(perr, PAMTEST_ERR_OK);
885 
886 	/* PAM_SERVICE is a special case, Linux's libpam lowercases it.
887 	 * OpenPAM only allows PAM_SERVICE to be set by pam_start()
888 	 */
889 #ifndef HAVE_OPENPAM
890 	svc = string_in_list(tests[1].case_out.envlist, "PAM_SERVICE");
891 	assert_non_null(svc);
892 	assert_string_equal(svc, "test_pam_service");
893 #endif
894 
895 	test_getenv(tests[1].case_out.envlist, "PAM_USER");
896 	test_getenv(tests[1].case_out.envlist, "PAM_USER_PROMPT");
897 	test_getenv(tests[1].case_out.envlist, "PAM_TTY");
898 	test_getenv(tests[1].case_out.envlist, "PAM_RUSER");
899 	test_getenv(tests[1].case_out.envlist, "PAM_RHOST");
900 	test_getenv(tests[1].case_out.envlist, "PAM_AUTHTOK");
901 	test_getenv(tests[1].case_out.envlist, "PAM_OLDAUTHTOK");
902 #ifdef PAM_XDISPLAY
903 	test_getenv(tests[1].case_out.envlist, "PAM_XDISPLAY");
904 #endif
905 #ifdef PAM_AUTHTOK_TYPE
906 	test_getenv(tests[1].case_out.envlist, "PAM_AUTHTOK_TYPE");
907 #endif
908 
909 	pamtest_free_env(tests[1].case_out.envlist);
910 }
911 
test_libpamtest_keepopen(void ** state)912 static void test_libpamtest_keepopen(void **state)
913 {
914 	int rv;
915 	enum pamtest_err perr;
916 	struct pamtest_conv_data conv_data;
917 	const char *trinity_authtoks[] = {
918 		"secret",
919 		NULL,
920 	};
921 	struct pam_testcase tests[] = {
922 		pam_test(PAMTEST_AUTHENTICATE, PAM_SUCCESS),
923 		pam_test(PAMTEST_KEEPHANDLE, PAM_SUCCESS),
924 	};
925 
926 	(void) state;	/* unused */
927 
928 	ZERO_STRUCT(conv_data);
929 	conv_data.in_echo_off = trinity_authtoks;
930 
931 	perr = run_pamtest("matrix", "trinity", &conv_data, tests);
932 	assert_int_equal(perr, PAMTEST_ERR_OK);
933 
934 	assert_non_null(tests[1].case_out.ph);
935 
936 	rv = pam_end(tests[1].case_out.ph, PAM_SUCCESS);
937 	assert_int_equal(rv, PAM_SUCCESS);
938 }
939 
test_libpamtest_get_failed_test(void ** state)940 static void test_libpamtest_get_failed_test(void **state)
941 {
942 	enum pamtest_err perr;
943 	struct pamtest_conv_data conv_data;
944 	const char *trinity_authtoks[] = {
945 		"secret",
946 		NULL,
947 	};
948 	struct pam_testcase tests[] = {
949 		pam_test(PAMTEST_AUTHENTICATE, PAM_AUTH_ERR),
950 	};
951 	const struct pam_testcase *failed_tc;
952 
953 	(void) state;	/* unused */
954 
955 	ZERO_STRUCT(conv_data);
956 	conv_data.in_echo_off = trinity_authtoks;
957 
958 	perr = run_pamtest("matrix", "trinity", &conv_data, tests);
959 	assert_int_not_equal(perr, PAMTEST_ERR_OK);
960 
961 	failed_tc = pamtest_failed_case(tests);
962 	assert_ptr_equal(failed_tc, &tests[0]);
963 }
964 
main(void)965 int main(void) {
966 	int rc;
967 
968 	const struct CMUnitTest init_tests[] = {
969 		cmocka_unit_test(test_env),
970 		cmocka_unit_test_setup_teardown(test_pam_start,
971 						setup_ctx_only,
972 						teardown_simple),
973 		cmocka_unit_test_setup_teardown(test_pam_authenticate,
974 						setup_passdb,
975 						teardown_passdb),
976 		cmocka_unit_test_setup_teardown(test_pam_authenticate_null_password,
977 						setup_passdb,
978 						teardown_passdb),
979 		cmocka_unit_test_setup_teardown(test_pam_authenticate_err,
980 						setup_passdb,
981 						teardown_passdb),
982 		cmocka_unit_test_setup_teardown(test_pam_acct,
983 						setup_passdb,
984 						teardown_passdb),
985 		cmocka_unit_test_setup_teardown(test_pam_acct_err,
986 						setup_passdb,
987 						teardown_passdb),
988 		cmocka_unit_test_setup_teardown(test_pam_env_functions,
989 						setup_noconv,
990 						teardown),
991 		cmocka_unit_test_setup_teardown(test_pam_session,
992 						setup_passdb,
993 						teardown_passdb),
994 		cmocka_unit_test_setup_teardown(test_pam_chauthtok,
995 						setup_passdb,
996 						teardown_passdb),
997 		cmocka_unit_test_setup_teardown(test_pam_chauthtok_prelim_failed,
998 						setup_passdb,
999 						teardown_passdb),
1000 		cmocka_unit_test_setup_teardown(test_pam_chauthtok_diff_passwords,
1001 						setup_passdb,
1002 						teardown_passdb),
1003 		cmocka_unit_test_setup_teardown(test_pam_setcred,
1004 						setup_passdb,
1005 						teardown_passdb),
1006 		cmocka_unit_test_setup_teardown(test_pam_item_functions,
1007 						setup_noconv,
1008 						teardown),
1009 		cmocka_unit_test_setup_teardown(test_pam_prompt,
1010 						setup_ctx_only,
1011 						teardown),
1012 		cmocka_unit_test_setup_teardown(test_pam_strerror,
1013 						setup_noconv,
1014 						teardown),
1015 		cmocka_unit_test_setup_teardown(test_pam_authenticate_db_opt,
1016 						setup_ctx_only,
1017 						teardown_simple),
1018 		cmocka_unit_test_setup_teardown(test_pam_authenticate_db_opt_err,
1019 						setup_ctx_only,
1020 						teardown_simple),
1021 #ifdef HAVE_PAM_VSYSLOG
1022 		cmocka_unit_test_setup_teardown(test_pam_vsyslog,
1023 						setup_noconv,
1024 						teardown),
1025 #endif
1026 		cmocka_unit_test_setup_teardown(test_libpamtest_keepopen,
1027 						setup_passdb,
1028 						teardown_passdb),
1029 		cmocka_unit_test_setup_teardown(test_libpamtest_get_failed_test,
1030 						setup_passdb,
1031 						teardown_passdb),
1032 		cmocka_unit_test(test_libpamtest_strerror),
1033 		cmocka_unit_test(test_get_set),
1034 	};
1035 
1036 	rc = cmocka_run_group_tests(init_tests, NULL, NULL);
1037 
1038 	return rc;
1039 }
1040 
1041