diff options
Diffstat (limited to 'third_party/pam_wrapper/libpamtest.h')
-rw-r--r-- | third_party/pam_wrapper/libpamtest.h | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/third_party/pam_wrapper/libpamtest.h b/third_party/pam_wrapper/libpamtest.h new file mode 100644 index 0000000..4b4a50e --- /dev/null +++ b/third_party/pam_wrapper/libpamtest.h @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2015 Andreas Schneider <asn@samba.org> + * Copyright (c) 2015 Jakub Hrozek <jakub.hrozek@posteo.se> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIBPAMTEST_H_ +#define __LIBPAMTEST_H_ + +#include <stddef.h> +#include <stdint.h> +#include <security/pam_appl.h> + +/** + * @defgroup pamtest The pamtest API + * + * @{ + */ + +/** + * @brief The enum which describes the operations performed by pamtest(). + */ +enum pamtest_ops { + /** run pam_authenticate to authenticate the account */ + PAMTEST_AUTHENTICATE, + /** run pam_setcred() to establish/delete user credentials */ + PAMTEST_SETCRED, + /** run pam_acct_mgmt() to validate the PAM account */ + PAMTEST_ACCOUNT, + /** run pam_open_session() to start a PAM session */ + PAMTEST_OPEN_SESSION, + /** run pam_close_session() to end a PAM session */ + PAMTEST_CLOSE_SESSION, + /** run pam_chauthtok() to update the authentication token */ + PAMTEST_CHAUTHTOK, + + /** + * If this option is set the test will call pam_getenvlist() and copy + * the environment into case_out.envlist. + */ + PAMTEST_GETENVLIST = 20, + /** + * This will prevent calling pam_end() and will just return the + * PAM handle in case_out.ph. + */ + PAMTEST_KEEPHANDLE, +}; + + +/** + * @brief The PAM testcase struction. Use the pam_test and pam_test_flags + * macros to fill them. + * + * @see run_pamtest() + */ +struct pam_testcase { + enum pamtest_ops pam_operation; /* The pam operation to run */ + int expected_rv; /* What we expect the op to return */ + int flags; /* Extra flags to pass to the op */ + + int op_rv; /* What the op really returns */ + + union { + char **envlist; /* output of PAMTEST_ENVLIST */ + pam_handle_t *ph; /* output of PAMTEST_KEEPHANDLE */ + } case_out; /* depends on pam_operation, mostly unused */ +}; + +/** Initializes a pam_tescase structure. */ +#define pam_test(op, expected) { op, expected, 0, 0, { .envlist = NULL } } +/** Initializes a CMUnitTest structure with additional PAM flags. */ +#define pam_test_flags(op, expected, flags) { op, expected, flags, 0, { .envlist = NULL } } + +/** + * @brief The return code of the pamtest function + */ +enum pamtest_err { + /** Testcases returns correspond with input */ + PAMTEST_ERR_OK, + /** pam_start() failed */ + PAMTEST_ERR_START, + /** A testcase failed. Use pamtest_failed_case */ + PAMTEST_ERR_CASE, + /** Could not run a test case */ + PAMTEST_ERR_OP, + /** pam_end failed */ + PAMTEST_ERR_END, + /** Handled internally */ + PAMTEST_ERR_KEEPHANDLE, + /** Internal error - bad input or similar */ + PAMTEST_ERR_INTERNAL, +}; + +/** + * @brief PAM conversation function, defined in pam_conv(3) + * + * This is just a typedef to use in our declarations. See man pam_conv(3) + * for more details. + */ +typedef int (*pam_conv_fn)(int num_msg, + const struct pam_message **msg, + struct pam_response **resp, + void *appdata_ptr); + +/** + * @brief This structure should be used when using run_pamtest, + * which uses an internal conversation function. + */ +struct pamtest_conv_data { + /** When the conversation function receives PAM_PROMPT_ECHO_OFF, + * it reads the auth token from the in_echo_off array and keeps + * an index internally. + */ + const char **in_echo_off; + /** When the conversation function receives PAM_PROMPT_ECHO_ON, + * it reads the input from the in_echo_off array and keeps + * an index internally. + */ + const char **in_echo_on; + /** Captures messages through PAM_ERROR_MSG. The test caller is + * responsible for allocating enough space in the array. + */ + char **out_err; + /** Captures messages through PAM_TEXT_INFO. The test caller is + * responsible for allocating enough space in the array. + */ + char **out_info; +}; + +#ifdef DOXYGEN +/** + * @brief Run libpamtest test cases + * + * This is using the default libpamtest conversation function. + * + * @param[in] service The PAM service to use in the conversation + * + * @param[in] user The user to run conversation as + * + * @param[in] conv_fn Test-specific conversation function + * + * @param[in] conv_userdata Test-specific conversation data + * + * @param[in] test_cases List of libpamtest test cases. Must end with + * PAMTEST_CASE_SENTINEL + * + * @param[in] pam_handle The PAM handle to use to run the tests + * + * @code + * int main(void) { + * int rc; + * const struct pam_testcase tests[] = { + * pam_test(PAM_AUTHENTICATE, PAM_SUCCESS), + * }; + * + * rc = run_pamtest(tests, NULL, NULL); + * + * return rc; + * } + * @endcode + * + * @return PAMTEST_ERR_OK on success, else the error code matching the failure. + */ +enum pamtest_err run_pamtest_conv(const char *service, + const char *user, + pam_conv_fn conv_fn, + void *conv_userdata, + struct pam_testcase test_cases[], + pam_handle_t *pam_handle); +#else +#define run_pamtest_conv(service, user, conv_fn, conv_data, test_cases, pam_handle) \ + _pamtest_conv(service, user, conv_fn, conv_data, test_cases, sizeof(test_cases)/sizeof(test_cases[0], pam_handle) +#endif + +#ifdef DOXYGEN +/** + * @brief Run libpamtest test cases + * + * This is using the default libpamtest conversation function. + * + * @param[in] service The PAM service to use in the conversation + * + * @param[in] user The user to run conversation as + * + * @param[in] conv_data Test-specific conversation data + * + * @param[in] test_cases List of libpamtest test cases. Must end with + * PAMTEST_CASE_SENTINEL + * + * @param[in] pam_handle The PAM handle to use to run the tests + * + * @code + * int main(void) { + * int rc; + * const struct pam_testcase tests[] = { + * pam_test(PAM_AUTHENTICATE, PAM_SUCCESS), + * }; + * + * rc = run_pamtest(tests, NULL, NULL); + * + * return rc; + * } + * @endcode + * + * @return PAMTEST_ERR_OK on success, else the error code matching the failure. + */ +enum pamtest_err run_pamtest(const char *service, + const char *user, + struct pamtest_conv_data *conv_data, + struct pam_testcase test_cases[], + pam_handle_t *pam_handle); +#else +#define run_pamtest(service, user, conv_data, test_cases, pam_handle) \ + _pamtest(service, user, conv_data, test_cases, sizeof(test_cases)/sizeof(test_cases[0]), pam_handle) +#endif + +#ifdef DOXYGEN +/** + * @brief Helper you can call if run_pamtest() fails. + * + * If PAMTEST_ERR_CASE is returned by run_pamtest() you should call this + * function get a pointer to the failed test case. + * + * @param[in] test_cases The array of tests. + * + * @return a pointer to the array of test_cases[] that corresponds to the + * first test case where the expected error code doesn't match the real error + * code. + */ +const struct pam_testcase *pamtest_failed_case(struct pam_testcase *test_cases); +#else +#define pamtest_failed_case(test_cases) \ + _pamtest_failed_case(test_cases, sizeof(test_cases) / sizeof(test_cases[0])) +#endif + +/** + * @brief return a string representation of libpamtest error code. + * + * @param[in] perr libpamtest error code + * + * @return String representation of the perr argument. Never returns NULL. + */ +const char *pamtest_strerror(enum pamtest_err perr); + +/** + * @brief This frees the string array returned by the PAMTEST_GETENVLIST test. + * + * @param[in] envlist The array to free. + */ +void pamtest_free_env(char **envlist); + + +/* Internal function protypes */ +enum pamtest_err _pamtest_conv(const char *service, + const char *user, + pam_conv_fn conv_fn, + void *conv_userdata, + struct pam_testcase test_cases[], + size_t num_test_cases, + pam_handle_t *pam_handle); + +enum pamtest_err _pamtest(const char *service, + const char *user, + struct pamtest_conv_data *conv_data, + struct pam_testcase test_cases[], + size_t num_test_cases, + pam_handle_t *pam_handle); + +const struct pam_testcase *_pamtest_failed_case(struct pam_testcase test_cases[], + size_t num_test_cases); + +/** @} */ + +#endif /* __LIBPAMTEST_H_ */ |