2 * Copyright (c) 2015 Andreas Schneider <asn@samba.org>
3 * Copyright (c) 2015 Jakub Hrozek <jakub.hrozek@posteo.se>
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.
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.
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/>.
19 #ifndef __LIBPAMTEST_H_
20 #define __LIBPAMTEST_H_
23 #include <security/pam_appl.h>
26 * @defgroup pamtest The pamtest API
32 * @brief The enum which describes the operations performed by pamtest().
35 /** run pam_authenticate to authenticate the account */
37 /** run pam_setcred() to establish/delete user credentials */
39 /** run pam_acct_mgmt() to validate the PAM account */
41 /** run pam_open_session() to start a PAM session */
43 /** run pam_close_session() to end a PAM session */
44 PAMTEST_CLOSE_SESSION
,
45 /** run pam_chauthtok() to update the authentication token */
49 * If this option is set the test will call pam_getenvlist() and copy
50 * the environment into case_out.envlist.
52 PAMTEST_GETENVLIST
= 20,
54 * This will prevent calling pam_end() and will just return the
55 * PAM handle in case_out.ph.
62 * @brief The PAM testcase struction. Use the pam_test and pam_test_flags
63 * macros to fill them.
68 enum pamtest_ops pam_operation
; /* The pam operation to run */
69 int expected_rv
; /* What we expect the op to return */
70 int flags
; /* Extra flags to pass to the op */
72 int op_rv
; /* What the op really returns */
75 char **envlist
; /* output of PAMTEST_ENVLIST */
76 pam_handle_t
*ph
; /* output of PAMTEST_KEEPHANDLE */
77 } case_out
; /* depends on pam_operation, mostly unused */
80 /** Initializes a pam_tescase structure. */
81 #define pam_test(op, expected) { op, expected, 0, 0, { .envlist = NULL } }
82 /** Initializes a CMUnitTest structure with additional PAM flags. */
83 #define pam_test_flags(op, expected, flags) { op, expected, flags, 0, { .envlist = NULL } }
86 * @brief The return code of the pamtest function
89 /** Testcases returns correspond with input */
91 /** pam_start() failed */
93 /** A testcase failed. Use pamtest_failed_case */
95 /** Could not run a test case */
99 /** Handled internally */
100 PAMTEST_ERR_KEEPHANDLE
,
101 /** Internal error - bad input or similar */
102 PAMTEST_ERR_INTERNAL
,
106 * @brief PAM conversation function, defined in pam_conv(3)
108 * This is just a typedef to use in our declarations. See man pam_conv(3)
111 typedef int (*pam_conv_fn
)(int num_msg
,
112 const struct pam_message
**msg
,
113 struct pam_response
**resp
,
117 * @brief This structure should be used when using run_pamtest,
118 * which uses an internal conversation function.
120 struct pamtest_conv_data
{
121 /** When the conversation function receives PAM_PROMPT_ECHO_OFF,
122 * it reads the auth token from the in_echo_off array and keeps
123 * an index internally.
125 const char **in_echo_off
;
126 /** When the conversation function receives PAM_PROMPT_ECHO_ON,
127 * it reads the input from the in_echo_off array and keeps
128 * an index internally.
130 const char **in_echo_on
;
132 /** Captures messages through PAM_TEXT_INFO. The test caller is
133 * responsible for allocating enough space in the array.
136 /** Captures messages through PAM_ERROR_MSG. The test caller is
137 * responsible for allocating enough space in the array.
144 * @brief Run libpamtest test cases
146 * This is using the default libpamtest conversation function.
148 * @param[in] service The PAM service to use in the conversation
150 * @param[in] user The user to run conversation as
152 * @param[in] conv_fn Test-specific conversation function
154 * @param[in] conv_userdata Test-specific conversation data
156 * @param[in] test_cases List of libpamtest test cases. Must end with
157 * PAMTEST_CASE_SENTINEL
162 * const struct pam_testcase tests[] = {
163 * pam_test(PAM_AUTHENTICATE, PAM_SUCCESS),
166 * rc = run_pamtest(tests, NULL, NULL);
172 * @return PAMTEST_ERR_OK on success, else the error code matching the failure.
174 enum pamtest_err
run_pamtest_conv(const char *service
,
178 struct pam_testcase test_cases
[]);
180 #define run_pamtest_conv(service, user, conv_fn, conv_data, test_cases) \
181 _pamtest_conv(service, user, conv_fn, conv_data, test_cases, sizeof(test_cases)/sizeof(test_cases[0])
186 * @brief Run libpamtest test cases
188 * This is using the default libpamtest conversation function.
190 * @param[in] service The PAM service to use in the conversation
192 * @param[in] user The user to run conversation as
194 * @param[in] conv_data Test-specific conversation data
196 * @param[in] test_cases List of libpamtest test cases. Must end with
197 * PAMTEST_CASE_SENTINEL
202 * const struct pam_testcase tests[] = {
203 * pam_test(PAM_AUTHENTICATE, PAM_SUCCESS),
206 * rc = run_pamtest(tests, NULL, NULL);
212 * @return PAMTEST_ERR_OK on success, else the error code matching the failure.
214 enum pamtest_err
run_pamtest(const char *service
,
216 struct pamtest_conv_data
*conv_data
,
217 struct pam_testcase test_cases
[]);
219 #define run_pamtest(service, user, conv_data, test_cases) \
220 _pamtest(service, user, conv_data, test_cases, sizeof(test_cases)/sizeof(test_cases[0]))
225 * @brief Helper you can call if run_pamtest() fails.
227 * If PAMTEST_ERR_CASE is returned by run_pamtest() you should call this
228 * function get a pointer to the failed test case.
230 * @param[in] test_cases The array of tests.
232 * @return a pointer to the array of test_cases[] that corresponds to the
233 * first test case where the expected error code doesn't match the real error
236 const struct pam_testcase
*pamtest_failed_case(struct pam_testcase
*test_cases
);
238 #define pamtest_failed_case(test_cases) \
239 _pamtest_failed_case(test_cases, sizeof(test_cases) / sizeof(test_cases[0]))
243 * @brief return a string representation of libpamtest error code.
245 * @param[in] perr libpamtest error code
247 * @return String representation of the perr argument. Never returns NULL.
249 const char *pamtest_strerror(enum pamtest_err perr
);
252 * @brief This frees the string array returned by the PAMTEST_GETENVLIST test.
254 * @param[in] envlist The array to free.
256 void pamtest_free_env(char **envlist
);
259 /* Internal function protypes */
260 enum pamtest_err
_pamtest_conv(const char *service
,
264 struct pam_testcase test_cases
[],
265 size_t num_test_cases
);
267 enum pamtest_err
_pamtest(const char *service
,
269 struct pamtest_conv_data
*conv_data
,
270 struct pam_testcase test_cases
[],
271 size_t num_test_cases
);
273 const struct pam_testcase
*_pamtest_failed_case(struct pam_testcase test_cases
[],
274 size_t num_test_cases
);
278 #endif /* __LIBPAMTEST_H_ */