The branch, master has been updated via 88b9921 cmake: Silence warning with gcc version >= 8 via 1330009 python: Export pam_setcred flags, to be used in python testcase objects via 6ada64a libpamtest: Add a new keyword parameter to reuse the PAM handle via dd5608b python: Store the pam handle in the python test object via b0ff06b python: Store the pam env in the python test object from 0e28e3e tests: Correctly implement free_vlist()
https://git.samba.org/?p=pam_wrapper.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 88b9921bd106ad3d06062e42fd76898525a9d542 Author: Samuel Cabrero <scabr...@suse.de> Date: Mon Jun 21 12:46:20 2021 +0200 cmake: Silence warning with gcc version >= 8 src/python/pypamtest.c:1149:17: warning[-Wcast-function-type]: cast between incompatible function types from ‘PyObject * (*)(PyObject *, PyObject *, PyObject *)’ {aka ‘struct _object * (*)(struct _object *, struct _object *, struct _object *)’} to ‘PyObject * (*)(PyObject *, PyObject *)’ {aka ‘struct _object * (*)(struct _object *, struct _object *)’} Signed-off-by: Samuel Cabrero <scabr...@suse.de> Reviewed-by: Andreas Schneider <a...@samba.org> commit 1330009d5277e7e4673112be0ff9de9cf0e1a618 Author: Samuel Cabrero <scabr...@samba.org> Date: Fri Jun 18 15:38:31 2021 +0200 python: Export pam_setcred flags, to be used in python testcase objects Signed-off-by: Samuel Cabrero <scabr...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit 6ada64a7ab52c07417bc012a200ac6e9fa5d5c2f Author: Samuel Cabrero <scabr...@samba.org> Date: Fri Jun 18 10:36:17 2021 +0200 libpamtest: Add a new keyword parameter to reuse the PAM handle Add a new keyword parameter to pass and reuse a PAM handle obtained by a previous run having a test object of type PAMTEST_KEEPHANDLE. Signed-off-by: Samuel Cabrero <scabr...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit dd5608b36a5f6d7121a8ba6590de1eacd0cc48b3 Author: Samuel Cabrero <scabr...@samba.org> Date: Fri Jun 18 09:21:12 2021 +0200 python: Store the pam handle in the python test object There was no way to get the PAM handler from a PAMTEST_KEEPHANDLE test object. The PAM handle is stored in the pam_handle member of the test object. Signed-off-by: Samuel Cabrero <scabr...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit b0ff06ba02a3c08fd4a60aacf716bc97efec9588 Author: Samuel Cabrero <scabr...@samba.org> Date: Fri Jun 18 09:19:25 2021 +0200 python: Store the pam env in the python test object There was no way to retrieve the PAM environment from a PAMTEST_GETENVLIST test object. The PAM environment is stored as a dictionary in the pam_env member of the test object. Signed-off-by: Samuel Cabrero <scabr...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> ----------------------------------------------------------------------- Summary of changes: include/libpamtest.h | 22 ++++-- src/libpamtest.c | 19 +++-- src/python/CMakeLists.txt | 4 + src/python/pypamtest.c | 192 +++++++++++++++++++++++++++++++++++++++++++--- tests/test_pam_wrapper.c | 30 ++++---- 5 files changed, 229 insertions(+), 38 deletions(-) Changeset truncated at 500 lines: diff --git a/include/libpamtest.h b/include/libpamtest.h index 3fa69c7..4ebe83f 100644 --- a/include/libpamtest.h +++ b/include/libpamtest.h @@ -156,6 +156,8 @@ struct pamtest_conv_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; @@ -175,10 +177,11 @@ 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[]); + struct pam_testcase test_cases[], + pam_handle_t *pam_handle); #else #define run_pamtest_conv(service, user, conv_fn, conv_data, test_cases) \ - _pamtest_conv(service, user, conv_fn, conv_data, test_cases, sizeof(test_cases)/sizeof(test_cases[0]) + _pamtest_conv(service, user, conv_fn, conv_data, test_cases, sizeof(test_cases)/sizeof(test_cases[0], pam_handle) #endif #ifdef DOXYGEN @@ -196,6 +199,8 @@ enum pamtest_err run_pamtest_conv(const char *service, * @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; @@ -214,10 +219,11 @@ enum pamtest_err run_pamtest_conv(const char *service, enum pamtest_err run_pamtest(const char *service, const char *user, struct pamtest_conv_data *conv_data, - struct pam_testcase test_cases[]); + struct pam_testcase test_cases[], + pam_handle_t *pam_handle); #else -#define run_pamtest(service, user, conv_data, test_cases) \ - _pamtest(service, user, conv_data, test_cases, sizeof(test_cases)/sizeof(test_cases[0])) +#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 @@ -262,13 +268,15 @@ enum pamtest_err _pamtest_conv(const char *service, pam_conv_fn conv_fn, void *conv_userdata, struct pam_testcase test_cases[], - size_t num_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); + 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); diff --git a/src/libpamtest.c b/src/libpamtest.c index 4474736..6033d5a 100644 --- a/src/libpamtest.c +++ b/src/libpamtest.c @@ -66,7 +66,8 @@ enum pamtest_err _pamtest_conv(const char *service, pam_conv_fn conv_fn, void *conv_userdata, struct pam_testcase test_cases[], - size_t num_test_cases) + size_t num_test_cases, + pam_handle_t *pam_handle) { int rv; pam_handle_t *ph; @@ -82,9 +83,13 @@ enum pamtest_err _pamtest_conv(const char *service, return PAMTEST_ERR_INTERNAL; } - rv = pam_start(service, user, &conv, &ph); - if (rv != PAM_SUCCESS) { - return PAMTEST_ERR_START; + if (pam_handle == NULL) { + rv = pam_start(service, user, &conv, &ph); + if (rv != PAM_SUCCESS) { + return PAMTEST_ERR_START; + } + } else { + ph = pam_handle; } for (tcindex = 0; tcindex < num_test_cases; tcindex++) { @@ -322,7 +327,8 @@ 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) + size_t num_test_cases, + pam_handle_t *pam_handle) { struct pamtest_conv_ctx cctx = { .data = conv_data, @@ -332,5 +338,6 @@ enum pamtest_err _pamtest(const char *service, pamtest_simple_conv, &cctx, test_cases, - num_test_cases); + num_test_cases, + pam_handle); } diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 3394a84..e8730d9 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -2,3 +2,7 @@ project(pypamtest C) add_subdirectory(python2) add_subdirectory(python3) + +set_source_files_properties(pypamtest.c + DIRECTORY python2 python3 + PROPERTIES COMPILE_OPTIONS "-Wno-cast-function-type") diff --git a/src/python/pypamtest.c b/src/python/pypamtest.c index 8de05e9..008a85f 100644 --- a/src/python/pypamtest.c +++ b/src/python/pypamtest.c @@ -58,8 +58,14 @@ typedef struct { enum pamtest_ops pam_operation; int expected_rv; int flags; + + PyObject *pam_handle; + PyObject *pam_env; } TestCaseObject; +#define PyTestCase_AsTestCaseObject(py_obj) \ + (TestCaseObject *)(py_obj) + /********************************************************** *** module-specific exceptions **********************************************************/ @@ -461,6 +467,22 @@ static PyMemberDef pypamtest_test_case_members[] = { discard_const_p(char, "Additional flags for the PAM operation"), }, + { + discard_const_p(char, "pam_handle"), + T_OBJECT_EX, + offsetof(TestCaseObject, pam_handle), + READONLY, + discard_const_p(char, "Pam handle"), + }, + + { + discard_const_p(char, "pam_env"), + T_OBJECT_EX, + offsetof(TestCaseObject, pam_env), + READONLY, + discard_const_p(char, "Pam env"), + }, + { NULL, 0, 0, 0, NULL } /* Sentinel */ }; @@ -773,6 +795,8 @@ static int py_testcase_to_cstruct(PyObject *py_test, struct pam_testcase *test) int rc; long value; + memset(test, 0, sizeof(struct pam_testcase)); + rc = py_testcase_get(py_test, "pam_operation", &value); if (rc != 0) { return rc; @@ -903,6 +927,84 @@ static int py_tc_list_to_cstruct_list(PyObject *py_test_list, return 0; } +static int cstruct_to_py_testcase(PyObject *pytest, struct pam_testcase *ctest) +{ + TestCaseObject *t = PyTestCase_AsTestCaseObject(pytest); + size_t i; + int rc; + + switch (t->pam_operation) { + case PAMTEST_GETENVLIST: + if (ctest->case_out.envlist == NULL) { + break; + } + + t->pam_env = PyDict_New(); + if (t->pam_env == NULL) { + return ENOMEM; + } + for (i = 0; ctest->case_out.envlist[i] != NULL; i++) { + char *key = NULL; + char *val = NULL; + key = strdup(ctest->case_out.envlist[i]); + if (key == NULL) { + return ENOMEM; + } + val = strrchr(key, '='); + if (val == NULL) { + PyErr_Format(PyExc_IOError, + "Failed to parse PAM environment " + "variable"); + free(key); + return EINVAL; + } + *val = '\0'; + rc = PyDict_SetItem(t->pam_env, + PyUnicode_FromString(key), + PyUnicode_FromString(val + 1)); + free(key); + if (rc == -1) { + return rc; + } + } + break; + case PAMTEST_KEEPHANDLE: + t->pam_handle = PyCapsule_New(ctest->case_out.ph, NULL, NULL); + if (t->pam_handle == NULL) { + return ENOMEM; + } + break; + default: + break; + } + + return 0; +} + +static int cstruct_list_to_py_tc_list(PyObject *py_test_list, + Py_ssize_t num_tests, + struct pam_testcase *test_list) +{ + Py_ssize_t i; + PyObject *py_test = NULL; + int rc; + + for (i = 0; i < num_tests; i++) { + py_test = PySequence_GetItem(py_test_list, i); + if (py_test == NULL) { + return EIO; + } + + rc = cstruct_to_py_testcase(py_test, &test_list[i]); + Py_DECREF(py_test); + if (rc != 0) { + return EIO; + } + } + + return 0; +} + PyDoc_STRVAR(RunPamTest__doc__, "Run PAM tests\n\n" "This function runs PAM test cases and reports result\n" @@ -917,7 +1019,9 @@ PyDoc_STRVAR(RunPamTest__doc__, "conversation for PAM_PROMPT_ECHO_ON input.\n" ); -static PyObject *pypamtest_run_pamtest(PyObject *module, PyObject *args) +static PyObject *pypamtest_run_pamtest(PyObject *module, + PyObject *args, + PyObject *kwargs) { int ok; int rc; @@ -926,21 +1030,33 @@ static PyObject *pypamtest_run_pamtest(PyObject *module, PyObject *args) PyObject *py_test_list; PyObject *py_echo_off = NULL; PyObject *py_echo_on = NULL; + PyObject *py_pam_handle = NULL; Py_ssize_t num_tests; struct pam_testcase *test_list; enum pamtest_err perr; struct pamtest_conv_data conv_data; + pam_handle_t *pam_handle = NULL; TestResultObject *result = NULL; + const char * const kwnames[] = { "username", + "service", + "tests", + "echo_off", + "echo_on", + "handle", + NULL }; (void) module; /* unused */ - ok = PyArg_ParseTuple(args, - discard_const_p(char, "ssO|OO"), - &username, - &service, - &py_test_list, - &py_echo_off, - &py_echo_on); + ok = PyArg_ParseTupleAndKeywords(args, + kwargs, + discard_const_p(char, "ssO|OOO"), + discard_const_p(char *, kwnames), + &username, + &service, + &py_test_list, + &py_echo_off, + &py_echo_on, + &py_pam_handle); if (!ok) { return NULL; } @@ -976,7 +1092,23 @@ static PyObject *pypamtest_run_pamtest(PyObject *module, PyObject *args) return NULL; } - perr = _pamtest(service, username, &conv_data, test_list, num_tests); + if (py_pam_handle != NULL) { + pam_handle = (pam_handle_t *)PyCapsule_GetPointer(py_pam_handle, + NULL); + if (pam_handle == NULL) { + PyMem_Free(test_list); + PyErr_Format(PyExc_IOError, + "Failed to get the pam handle pointer"); + return NULL; + } + } + + perr = _pamtest(service, + username, + &conv_data, + test_list, + num_tests, + pam_handle); if (perr != PAMTEST_ERR_OK) { free_conv_data(&conv_data); set_pypamtest_exception(PyExc_PamTestError, @@ -986,6 +1118,18 @@ static PyObject *pypamtest_run_pamtest(PyObject *module, PyObject *args) PyMem_Free(test_list); return NULL; } + + rc = cstruct_list_to_py_tc_list(py_test_list, num_tests, test_list); + if (rc != 0) { + if (rc == ENOMEM) { + PyErr_NoMemory(); + return NULL; + } else { + PyErr_Format(PyExc_IOError, + "Cannot convert C structure to python"); + return NULL; + } + } PyMem_Free(test_list); result = construct_test_conv_result(conv_data.out_info, @@ -1003,7 +1147,7 @@ static PyMethodDef pypamtest_module_methods[] = { { discard_const_p(char, "run_pamtest"), (PyCFunction) pypamtest_run_pamtest, - METH_VARARGS, + METH_VARARGS | METH_KEYWORDS, RunPamTest__doc__, }, @@ -1115,6 +1259,34 @@ PyMODINIT_FUNC initpypamtest(void) RETURN_ON_ERROR; } + ret = PyModule_AddIntConstant(m, + "PAMTEST_FLAG_DELETE_CRED", + PAM_DELETE_CRED); + if (ret == -1) { + RETURN_ON_ERROR; + } + + ret = PyModule_AddIntConstant(m, + "PAMTEST_FLAG_ESTABLISH_CRED", + PAM_ESTABLISH_CRED); + if (ret == -1) { + RETURN_ON_ERROR; + } + + ret = PyModule_AddIntConstant(m, + "PAMTEST_FLAG_REINITIALIZE_CRED", + PAM_REINITIALIZE_CRED); + if (ret == -1) { + RETURN_ON_ERROR; + } + + ret = PyModule_AddIntConstant(m, + "PAMTEST_FLAG_REFRESH_CRED", + PAM_REFRESH_CRED); + if (ret == -1) { + RETURN_ON_ERROR; + } + pypam_object.type_obj = &pypamtest_test_case; if (PyType_Ready(pypam_object.type_obj) < 0) { RETURN_ON_ERROR; diff --git a/tests/test_pam_wrapper.c b/tests/test_pam_wrapper.c index 7e8b9ee..1e0c292 100644 --- a/tests/test_pam_wrapper.c +++ b/tests/test_pam_wrapper.c @@ -267,7 +267,7 @@ static void test_pam_authenticate(void **state) ZERO_STRUCT(conv_data); conv_data.in_echo_off = trinity_authtoks; - perr = run_pamtest("matrix", "trinity", &conv_data, tests); + perr = run_pamtest("matrix", "trinity", &conv_data, tests, NULL); assert_int_equal(perr, PAMTEST_ERR_OK); } @@ -287,7 +287,7 @@ static void test_pam_authenticate_null_password(void **state) ZERO_STRUCT(conv_data); conv_data.in_echo_off = empty_authtoks; - perr = run_pamtest("matrix", "trinity", &conv_data, tests); + perr = run_pamtest("matrix", "trinity", &conv_data, tests, NULL); assert_int_equal(perr, PAMTEST_ERR_OK); } @@ -308,7 +308,7 @@ static void test_pam_authenticate_err(void **state) ZERO_STRUCT(conv_data); conv_data.in_echo_off = trinity_authtoks; - perr = run_pamtest("matrix", "trinity", &conv_data, tests); + perr = run_pamtest("matrix", "trinity", &conv_data, tests, NULL); assert_int_equal(perr, PAMTEST_ERR_OK); } @@ -321,7 +321,7 @@ static void test_pam_acct(void **state) (void) state; /* unused */ - perr = run_pamtest("matrix", "trinity", NULL, tests); + perr = run_pamtest("matrix", "trinity", NULL, tests, NULL); assert_int_equal(perr, PAMTEST_ERR_OK); } @@ -334,7 +334,7 @@ static void test_pam_acct_err(void **state) (void) state; /* unused */ - perr = run_pamtest("matrix", "neo", NULL, tests); + perr = run_pamtest("matrix", "neo", NULL, tests, NULL); assert_int_equal(perr, PAMTEST_ERR_OK); } @@ -458,7 +458,7 @@ static void test_pam_session(void **state) (void) state; /* unused */ - perr = run_pamtest("matrix", "trinity", NULL, tests); + perr = run_pamtest("matrix", "trinity", NULL, tests, NULL); assert_int_equal(perr, PAMTEST_ERR_OK); v = string_in_list(tests[1].case_out.envlist, "HOMEDIR"); @@ -500,7 +500,7 @@ static void test_pam_chauthtok(void **state) ZERO_STRUCT(conv_data); conv_data.in_echo_off = trinity_new_authtoks; - perr = run_pamtest("matrix", "trinity", &conv_data, tests); + perr = run_pamtest("matrix", "trinity", &conv_data, tests, NULL); assert_int_equal(perr, PAMTEST_ERR_OK); } @@ -523,7 +523,7 @@ static void test_pam_chauthtok_prelim_failed(void **state) ZERO_STRUCT(conv_data); conv_data.in_echo_off = trinity_new_authtoks; - perr = run_pamtest("matrix", "trinity", &conv_data, tests); + perr = run_pamtest("matrix", "trinity", &conv_data, tests, NULL); assert_int_equal(perr, PAMTEST_ERR_OK); } @@ -546,7 +546,7 @@ static void test_pam_chauthtok_diff_passwords(void **state) ZERO_STRUCT(conv_data); conv_data.in_echo_off = trinity_new_authtoks; - perr = run_pamtest("matrix", "trinity", &conv_data, tests); + perr = run_pamtest("matrix", "trinity", &conv_data, tests, NULL); assert_int_equal(perr, PAMTEST_ERR_OK); } @@ -562,7 +562,7 @@ static void test_pam_setcred(void **state) (void) state; /* unused */ - perr = run_pamtest("matrix", "trinity", NULL, tests); + perr = run_pamtest("matrix", "trinity", NULL, tests, NULL); assert_int_equal(perr, PAMTEST_ERR_OK); /* environment is clean before setcred */ -- pam wrapper repository