From 2cf300e266449394a47e0b6c9df330a173cdbb81 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Mon, 20 Jan 2020 18:35:40 +0100 Subject: [PATCH 1/6] python: Fix typos Signed-off-by: Bastien Nocera --- src/python/pypamtest.c | 4 ++-- tests/pypamtest_test.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/python/pypamtest.c b/src/python/pypamtest.c index 905c652..4147364 100644 --- a/src/python/pypamtest.c +++ b/src/python/pypamtest.c @@ -303,7 +303,7 @@ set_pypamtest_exception(PyObject *exc, if (test_repr[0] != '\0' && failed != NULL) { PyErr_Format(exc, - "Error [%d]: Test case %s retured [%d]", + "Error [%d]: Test case %s returned [%d]", perr, test_repr, failed->op_rv); } else { obj = Py_BuildValue(discard_const_p(char, "(i,s)"), @@ -906,7 +906,7 @@ static int py_tc_list_to_cstruct_list(PyObject *py_test_list, PyDoc_STRVAR(RunPamTest__doc__, "Run PAM tests\n\n" "This function runs PAM test cases and reports result\n" -"Paramaters:\n" +"Parameters:\n" "service: The PAM service to use in the conversation (string)\n" "username: The user to run PAM conversation as\n" "test_list: Sequence of pypamtest.TestCase objects\n" diff --git a/tests/pypamtest_test.py b/tests/pypamtest_test.py index 32ef65d..ed12a71 100755 --- a/tests/pypamtest_test.py +++ b/tests/pypamtest_test.py @@ -127,7 +127,7 @@ class PyPamTestRunTest(unittest.TestCase): self.assertRaisesRegexp(pypamtest.PamTestError, "Error \[2\]: Test case { pam_operation \[0\] " "expected_rv \[0\] flags \[0\] } " - "retured \[\d\]", + "returned \[\d\]", pypamtest.run_pamtest, "neo", "matrix_py", [tc], [ neo_password ]) -- 2.24.1 From a9ac1c5e1a15644c45647aab84eca42c0c36955a Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 22 Jan 2020 11:49:31 +0100 Subject: [PATCH 2/6] python: Add failure test We only had successful pam tests, add a failing one, and check that it fails. Signed-off-by: Bastien Nocera --- tests/pypamtest_test.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/pypamtest_test.py b/tests/pypamtest_test.py index ed12a71..030f01a 100755 --- a/tests/pypamtest_test.py +++ b/tests/pypamtest_test.py @@ -115,6 +115,11 @@ class PyPamTestRunTest(unittest.TestCase): self.assertSequenceEqual(res.info, (u'Authentication succeeded',)) self.assertSequenceEqual(res.errors, ()) + def test_run_failed_auth(self): + neo_password = "not-the-secret" + tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=7) # PAM_AUTH_ERR + res = pypamtest.run_pamtest("neo", "matrix_py", [tc], [ neo_password ]) + def test_repr(self): tc = pypamtest.TestCase(pypamtest.PAMTEST_CHAUTHTOK, 1, 2) r = repr(tc) -- 2.24.1 From 131b9633106af1a7f3ce5c2fefd5b6f5ace3c703 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 22 Jan 2020 11:50:37 +0100 Subject: [PATCH 3/6] python: Fix crash when the PAM module outputs too much data This code expected each input (whether echo on or echo off input), to generate at most one info or error output, which is obviously not correct. A PAM module with external inputs can throw dozens of messages and warnings even if the only expected input is a password. Allocate those placeholder arrays to be as big as possible to accomodate chatty PAM modules. Closes: https://bugzilla.samba.org/show_bug.cgi?id=14245 Signed-off-by: Bastien Nocera --- src/python/pypamtest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/pypamtest.c b/src/python/pypamtest.c index 4147364..8de05e9 100644 --- a/src/python/pypamtest.c +++ b/src/python/pypamtest.c @@ -852,8 +852,8 @@ static int fill_conv_data(PyObject *py_echo_off, return ENOMEM; } - conv_data->out_info = new_conv_list(conv_count); - conv_data->out_err = new_conv_list(conv_count); + conv_data->out_info = new_conv_list(PAM_CONV_MSG_MAX); + conv_data->out_err = new_conv_list(PAM_CONV_MSG_MAX); if (conv_data->out_info == NULL || conv_data->out_err == NULL) { free_conv_data(conv_data); return ENOMEM; -- 2.24.1 From a20806b6aef73aa187cfde2e9b676c0ce5ab263e Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 22 Jan 2020 12:17:03 +0100 Subject: [PATCH 4/6] modules: Add pam_chatty module Add a simple PAM module that will output "num_lines" lines of PAM info and/or error output. Signed-off-by: Bastien Nocera --- src/modules/CMakeLists.txt | 2 +- src/modules/pam_chatty.c | 176 +++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 src/modules/pam_chatty.c diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt index 8e13a0b..e956f4c 100644 --- a/src/modules/CMakeLists.txt +++ b/src/modules/CMakeLists.txt @@ -1,6 +1,6 @@ project(pam_wrapper-modules C) -set(PAM_MODULES pam_matrix pam_get_items pam_set_items) +set(PAM_MODULES pam_matrix pam_get_items pam_set_items pam_chatty) set(PAM_LIBRARIES pam) if (HAVE_PAM_MISC) diff --git a/src/modules/pam_chatty.c b/src/modules/pam_chatty.c new file mode 100644 index 0000000..5ffed5c --- /dev/null +++ b/src/modules/pam_chatty.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2015 Andreas Schneider + * Copyright (c) 2015 Jakub Hrozek + * Copyright (c) 2020 Bastien Nocera + * + * 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef discard_const +#define discard_const(ptr) ((void *)((uintptr_t)(ptr))) +#endif + +#ifndef discard_const_p +#define discard_const_p(type, ptr) ((type *)discard_const(ptr)) +#endif + +#ifdef HAVE_SECURITY_PAM_APPL_H +#include +#endif +#ifdef HAVE_SECURITY_PAM_MODULES_H +#include +#endif +#ifdef HAVE_SECURITY_PAM_EXT_H +#include +#endif + +#include "pwrap_compat.h" + +#define VERBOSE_KEY "verbose" +#define ERROR_KEY "error" +#define INFO_KEY "info" +#define NUM_LINES_KEY "num_lines=" + +#define DEFAULT_NUM_LINES 3 + +/* We only return up to 16 messages from the PAM conversation. + * Value from src/python/pypamtest.c */ +#define PAM_CONV_MSG_MAX 16 + +#define PAM_CHATTY_FLG_VERBOSE (1 << 0) +#define PAM_CHATTY_FLG_ERROR (1 << 1) +#define PAM_CHATTY_FLG_INFO (1 << 1) + +#ifndef discard_const +#define discard_const(ptr) ((void *)((uintptr_t)(ptr))) +#endif + +#ifndef discard_const_p +#define discard_const_p(type, ptr) ((type *)discard_const(ptr)) +#endif + +static int pam_chatty_conv(pam_handle_t *pamh, + const int msg_style, + const char *msg) +{ + int ret; + const struct pam_conv *conv; + const struct pam_message *mesg[1]; + struct pam_response *r; + struct pam_message *pam_msg; + + ret = pam_get_item(pamh, PAM_CONV, (const void **) &conv); + if (ret != PAM_SUCCESS) { + return ret; + } + + pam_msg = malloc(sizeof(struct pam_message)); + if (pam_msg == NULL) { + return PAM_BUF_ERR; + } + + pam_msg->msg_style = msg_style; + pam_msg->msg = discard_const_p(char, msg); + + mesg[0] = (const struct pam_message *) pam_msg; + ret = conv->conv(1, mesg, &r, conv->appdata_ptr); + free(pam_msg); + + return ret; +} + +/* Evaluate command line arguments and store info about them in the + * pam_matrix context + */ +static unsigned int parse_args(int argc, + const char *argv[], + unsigned int *num_lines) +{ + unsigned int flags = 0; + + *num_lines = DEFAULT_NUM_LINES; + + for (; argc-- > 0; ++argv) { + if (strncmp(*argv, NUM_LINES_KEY, strlen(NUM_LINES_KEY)) == 0) { + if (*(*argv+strlen(NUM_LINES_KEY)) != '\0') { + *num_lines = atoi(*argv+strlen(NUM_LINES_KEY)); + if (*num_lines <= DEFAULT_NUM_LINES) + *num_lines = DEFAULT_NUM_LINES; + if (*num_lines > PAM_CONV_MSG_MAX) + *num_lines = PAM_CONV_MSG_MAX; + } + } else if (strncmp(*argv, VERBOSE_KEY, + strlen(VERBOSE_KEY)) == 0) { + flags |= PAM_CHATTY_FLG_VERBOSE; + } else if (strncmp(*argv, ERROR_KEY, + strlen(ERROR_KEY)) == 0) { + flags |= PAM_CHATTY_FLG_ERROR; + } else if (strncmp(*argv, INFO_KEY, + strlen(INFO_KEY)) == 0) { + flags |= PAM_CHATTY_FLG_INFO; + } + } + + return flags; +} + +PAM_EXTERN int +pam_sm_authenticate(pam_handle_t *pamh, int flags, + int argc, const char *argv[]) +{ + unsigned int optflags, num_lines; + + optflags = parse_args (argc, argv, &num_lines); + if (!(optflags & PAM_CHATTY_FLG_VERBOSE)) + return PAM_SUCCESS; + + if (optflags & PAM_CHATTY_FLG_INFO) { + unsigned int i; + + for (i = 0; i < num_lines; i++) { + pam_chatty_conv(pamh, + PAM_TEXT_INFO, + "Authentication succeeded"); + } + } + + if (optflags & PAM_CHATTY_FLG_ERROR) { + unsigned int i; + + for (i = 0; i < num_lines; i++) { + pam_chatty_conv(pamh, + PAM_ERROR_MSG, + "Authentication generated an error"); + } + } + + return PAM_SUCCESS; +} -- 2.24.1 From 62fbc9cf9a2e1b622001ca32d0121d87bf5e7007 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 22 Jan 2020 12:21:05 +0100 Subject: [PATCH 5/6] tests: Add service file for chatty module So we can test it. Signed-off-by: Bastien Nocera --- tests/CMakeLists.txt | 3 +++ tests/services/chatty.in | 1 + 2 files changed, 4 insertions(+) create mode 100644 tests/services/chatty.in diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 997c15e..eb0477c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -22,6 +22,9 @@ configure_file(services/matrix_py.in ${CMAKE_CURRENT_BINARY_DIR}/services/matrix configure_file(services/pwrap_get_set.in ${CMAKE_CURRENT_BINARY_DIR}/services/pwrap_get_set @ONLY) +set(PAM_CHATTY_PATH "${CMAKE_BINARY_DIR}/src/modules/pam_chatty.so") +configure_file(services/chatty.in ${CMAKE_CURRENT_BINARY_DIR}/services/chatty @ONLY) + if (OSX) set(TEST_ENVIRONMENT DYLD_FORCE_FLAT_NAMESPACE=1;DYLD_INSERT_LIBRARIES=${PAM_WRAPPER_LOCATION};PAM_WRAPPER=1;PAM_WRAPPER_SERVICE_DIR=${CMAKE_CURRENT_BINARY_DIR}/services}) add_definitions(-DOSX) diff --git a/tests/services/chatty.in b/tests/services/chatty.in new file mode 100644 index 0000000..0099b50 --- /dev/null +++ b/tests/services/chatty.in @@ -0,0 +1 @@ +auth required @PAM_CHATTY_PATH@ verbose num_lines=16 info error -- 2.24.1 From 3e4dce22628f8caf05764b73e234554c07f269a8 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 22 Jan 2020 12:22:30 +0100 Subject: [PATCH 6/6] tests: Add test for verbose PAM modules Signed-off-by: Bastien Nocera --- tests/pypamtest_test.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/pypamtest_test.py b/tests/pypamtest_test.py index 030f01a..c4534bb 100755 --- a/tests/pypamtest_test.py +++ b/tests/pypamtest_test.py @@ -120,6 +120,11 @@ class PyPamTestRunTest(unittest.TestCase): tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=7) # PAM_AUTH_ERR res = pypamtest.run_pamtest("neo", "matrix_py", [tc], [ neo_password ]) + def test_run_chatty_auth(self): + neo_password = "secret" + tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE) + res = pypamtest.run_pamtest("neo", "chatty", [tc], [ neo_password ]) + def test_repr(self): tc = pypamtest.TestCase(pypamtest.PAMTEST_CHAUTHTOK, 1, 2) r = repr(tc) -- 2.24.1