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_
24 #include <security/pam_appl.h>
27 * @defgroup pamtest The pamtest API
33 * @brief The enum which describes the operations performed by pamtest().
36 /** run pam_authenticate to authenticate the account */
38 /** run pam_setcred() to establish/delete user credentials */
40 /** run pam_acct_mgmt() to validate the PAM account */
42 /** run pam_open_session() to start a PAM session */
44 /** run pam_close_session() to end a PAM session */
45 PAMTEST_CLOSE_SESSION
,
46 /** run pam_chauthtok() to update the authentication token */
50 * If this option is set the test will call pam_getenvlist() and copy
51 * the environment into case_out.envlist.
53 PAMTEST_GETENVLIST
= 20,
55 * This will prevent calling pam_end() and will just return the
56 * PAM handle in case_out.ph.
63 * @brief The PAM testcase struction. Use the pam_test and pam_test_flags
64 * macros to fill them.
69 enum pamtest_ops pam_operation
; /* The pam operation to run */
70 int expected_rv
; /* What we expect the op to return */
71 int flags
; /* Extra flags to pass to the op */
73 int op_rv
; /* What the op really returns */
76 char **envlist
; /* output of PAMTEST_ENVLIST */
77 pam_handle_t
*ph
; /* output of PAMTEST_KEEPHANDLE */
78 } case_out
; /* depends on pam_operation, mostly unused */
81 /** Initializes a pam_tescase structure. */
82 #define pam_test(op, expected) { op, expected, 0, 0, { .envlist = NULL } }
83 /** Initializes a CMUnitTest structure with additional PAM flags. */
84 #define pam_test_flags(op, expected, flags) { op, expected, flags, 0, { .envlist = NULL } }
87 * @brief The return code of the pamtest function
90 /** Testcases returns correspond with input */
92 /** pam_start() failed */
94 /** A testcase failed. Use pamtest_failed_case */
96 /** Could not run a test case */
100 /** Handled internally */
101 PAMTEST_ERR_KEEPHANDLE
,
102 /** Internal error - bad input or similar */
103 PAMTEST_ERR_INTERNAL
,
107 * @brief PAM conversation function, defined in pam_conv(3)
109 * This is just a typedef to use in our declarations. See man pam_conv(3)
112 typedef int (*pam_conv_fn
)(int num_msg
,
113 const struct pam_message
**msg
,
114 struct pam_response
**resp
,
118 * @brief This structure should be used when using run_pamtest,
119 * which uses an internal conversation function.
121 struct pamtest_conv_data
{
122 /** When the conversation function receives PAM_PROMPT_ECHO_OFF,
123 * it reads the auth token from the in_echo_off array and keeps
124 * an index internally.
126 const char **in_echo_off
;
127 /** When the conversation function receives PAM_PROMPT_ECHO_ON,
128 * it reads the input from the in_echo_off array and keeps
129 * an index internally.
131 const char **in_echo_on
;
132 /** Captures messages through PAM_ERROR_MSG. The test caller is
133 * responsible for allocating enough space in the array.
136 /** Captures messages through PAM_TEXT_INFO. 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
159 * @param[in] pam_handle The PAM handle to use to run the tests
164 * const struct pam_testcase tests[] = {
165 * pam_test(PAM_AUTHENTICATE, PAM_SUCCESS),
168 * rc = run_pamtest(tests, NULL, NULL);
174 * @return PAMTEST_ERR_OK on success, else the error code matching the failure.
176 enum pamtest_err
run_pamtest_conv(const char *service
,
180 struct pam_testcase test_cases
[],
181 pam_handle_t
*pam_handle
);
183 #define run_pamtest_conv(service, user, conv_fn, conv_data, test_cases, pam_handle) \
184 _pamtest_conv(service, user, conv_fn, conv_data, test_cases, sizeof(test_cases)/sizeof(test_cases[0], pam_handle)
189 * @brief Run libpamtest test cases
191 * This is using the default libpamtest conversation function.
193 * @param[in] service The PAM service to use in the conversation
195 * @param[in] user The user to run conversation as
197 * @param[in] conv_data Test-specific conversation data
199 * @param[in] test_cases List of libpamtest test cases. Must end with
200 * PAMTEST_CASE_SENTINEL
202 * @param[in] pam_handle The PAM handle to use to run the tests
207 * const struct pam_testcase tests[] = {
208 * pam_test(PAM_AUTHENTICATE, PAM_SUCCESS),
211 * rc = run_pamtest(tests, NULL, NULL);
217 * @return PAMTEST_ERR_OK on success, else the error code matching the failure.
219 enum pamtest_err
run_pamtest(const char *service
,
221 struct pamtest_conv_data
*conv_data
,
222 struct pam_testcase test_cases
[],
223 pam_handle_t
*pam_handle
);
225 #define run_pamtest(service, user, conv_data, test_cases, pam_handle) \
226 _pamtest(service, user, conv_data, test_cases, sizeof(test_cases)/sizeof(test_cases[0]), pam_handle)
231 * @brief Helper you can call if run_pamtest() fails.
233 * If PAMTEST_ERR_CASE is returned by run_pamtest() you should call this
234 * function get a pointer to the failed test case.
236 * @param[in] test_cases The array of tests.
238 * @return a pointer to the array of test_cases[] that corresponds to the
239 * first test case where the expected error code doesn't match the real error
242 const struct pam_testcase
*pamtest_failed_case(struct pam_testcase
*test_cases
);
244 #define pamtest_failed_case(test_cases) \
245 _pamtest_failed_case(test_cases, sizeof(test_cases) / sizeof(test_cases[0]))
249 * @brief return a string representation of libpamtest error code.
251 * @param[in] perr libpamtest error code
253 * @return String representation of the perr argument. Never returns NULL.
255 const char *pamtest_strerror(enum pamtest_err perr
);
258 * @brief This frees the string array returned by the PAMTEST_GETENVLIST test.
260 * @param[in] envlist The array to free.
262 void pamtest_free_env(char **envlist
);
265 /* Internal function protypes */
266 enum pamtest_err
_pamtest_conv(const char *service
,
270 struct pam_testcase test_cases
[],
271 size_t num_test_cases
,
272 pam_handle_t
*pam_handle
);
274 enum pamtest_err
_pamtest(const char *service
,
276 struct pamtest_conv_data
*conv_data
,
277 struct pam_testcase test_cases
[],
278 size_t num_test_cases
,
279 pam_handle_t
*pam_handle
);
281 const struct pam_testcase
*_pamtest_failed_case(struct pam_testcase test_cases
[],
282 size_t num_test_cases
);
286 #endif /* __LIBPAMTEST_H_ */