aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Hrozek <jakub.hrozek@posteo.se>2015-10-09 15:39:11 +0200
committerAndreas Schneider <asn@samba.org>2015-12-10 13:31:19 +0100
commit983b813269c3d9cf49e3aebebfaed79cbdd19323 (patch)
treed7487487f817994c95575a5a63e60a24012f0275
parent4dbb1a9cc98879295035cb6ea71750c7084d21b7 (diff)
downloadpam_wrapper-983b813269c3d9cf49e3aebebfaed79cbdd19323.tar.gz
pam_wrapper-983b813269c3d9cf49e3aebebfaed79cbdd19323.tar.xz
pam_wrapper-983b813269c3d9cf49e3aebebfaed79cbdd19323.zip
pamtest: Add libpamtest
-rw-r--r--CMakeLists.txt11
-rw-r--r--include/CMakeLists.txt14
-rw-r--r--include/libpamtest.h93
-rw-r--r--libpamtest.pc.cmake4
-rw-r--r--pam_wrapper.pc.cmake4
-rw-r--r--src/CMakeLists.txt24
-rw-r--r--src/libpamtest.c216
7 files changed, 364 insertions, 2 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b2635cd..4623409 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -49,6 +49,7 @@ configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
# check subdirectories
add_subdirectory(src)
+add_subdirectory(include)
if (UNIT_TESTING)
find_package(CMocka REQUIRED)
@@ -69,6 +70,16 @@ install(
pkgconfig
)
+configure_file(libpamtest.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/libpamtest.pc @ONLY)
+install(
+ FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/libpamtest.pc
+ DESTINATION
+ ${LIB_INSTALL_DIR}/pkgconfig
+ COMPONENT
+ pkgconfig
+)
+
# cmake config files
configure_file(pam_wrapper-config-version.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/pam_wrapper-config-version.cmake @ONLY)
configure_file(pam_wrapper-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/pam_wrapper-config.cmake @ONLY)
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
new file mode 100644
index 0000000..5eaaba6
--- /dev/null
+++ b/include/CMakeLists.txt
@@ -0,0 +1,14 @@
+project(pam_wrapper-headers C)
+
+set(libpamtest_HDRS
+ libpamtest.h
+)
+
+install(
+ FILES
+ ${libpamtest_HDRS}
+ DESTINATION
+ ${INCLUDE_INSTALL_DIR}
+ COMPONENT
+ headers
+)
diff --git a/include/libpamtest.h b/include/libpamtest.h
new file mode 100644
index 0000000..b6de813
--- /dev/null
+++ b/include/libpamtest.h
@@ -0,0 +1,93 @@
+/*
+ * 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 <stdint.h>
+#include <security/pam_appl.h>
+
+/* operations */
+enum pamtest_ops {
+ /* These operations correspond to libpam ops */
+ PAMTEST_AUTHENTICATE,
+ PAMTEST_SETCRED,
+ PAMTEST_ACCOUNT,
+ PAMTEST_OPEN_SESSION,
+ PAMTEST_CLOSE_SESSION,
+ PAMTEST_CHAUTHTOK,
+
+ /* These operation affect test output */
+ PAMTEST_GETENVLIST, /* Call pam_getenvlist. */
+ PAMTEST_KEEPHANDLE, /* Don't call pam_end() but return handle */
+
+ /* The two below can't be set by API user, but are useful if pam_start()
+ * or pam_end() fails and the API user wants to find out what happened
+ * with pamtest_failed_case()
+ */
+ PAMTEST_START,
+ PAMTEST_END,
+
+ /* Boundary.. */
+ PAMTEST_SENTINEL,
+};
+
+struct pamtest_case {
+ 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 */
+};
+
+enum pamtest_err {
+ PAMTEST_ERR_OK, /* Testcases returns correspond with input */
+ PAMTEST_ERR_START, /* pam_start() failed */
+ PAMTEST_ERR_CASE, /* A testcase failed. Use pamtest_failed_case */
+ PAMTEST_ERR_OP, /* Could not run a test case */
+ PAMTEST_ERR_END, /* pam_end failed */
+ PAMTEST_ERR_KEEPHANDLE, /* Handled internally */
+ PAMTEST_ERR_INTERNAL, /* Internal error - bad input or similar */
+};
+
+typedef int (*pam_conv_fn)(int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr);
+
+enum pamtest_err pamtest_ex(const char *service,
+ const char *user,
+ pam_conv_fn conv_fn,
+ void *conv_userdata,
+ struct pamtest_case *test_cases);
+
+void pamtest_free_env(char **envlist);
+
+const struct pamtest_case *pamtest_failed_case(struct pamtest_case *test_cases);
+
+enum pamtest_err pamtest(const char *service,
+ const char *user,
+ void *conv_userdata,
+ struct pamtest_case *test_cases);
+
+#endif /* __LIBPAMTEST_H_ */
diff --git a/libpamtest.pc.cmake b/libpamtest.pc.cmake
new file mode 100644
index 0000000..20758c7
--- /dev/null
+++ b/libpamtest.pc.cmake
@@ -0,0 +1,4 @@
+Name: libpamtest
+Description: A helper library for PAM testing
+Version: @APPLICATION_VERSION@
+Libs: @LIB_INSTALL_DIR@/libpamtest.so
diff --git a/pam_wrapper.pc.cmake b/pam_wrapper.pc.cmake
index 6377dfe..3c1c848 100644
--- a/pam_wrapper.pc.cmake
+++ b/pam_wrapper.pc.cmake
@@ -1,4 +1,4 @@
Name: @APPLICATION_NAME@
-Description: The uid_wrapper library
+Description: The pam_wrapper library
Version: @APPLICATION_VERSION@
-Libs: @LIB_INSTALL_DIR@/@UID_WRAPPER_LIB@
+Libs: @LIB_INSTALL_DIR@/@PAM_WRAPPER_LIB@
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e99a033..b09a78f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -21,6 +21,30 @@ install(
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
)
+set(pamtest_SOURCES
+ libpamtest.c
+)
+
+set(pamtest_HEADERS
+ ${CMAKE_SOURCE_DIR}/include/libpamtest.h
+)
+include_directories(${CMAKE_SOURCE_DIR}/include)
+
+add_library(pamtest SHARED
+ ${pamtest_SOURCES}
+ ${pamtest_HEADERS}
+)
+target_link_libraries(pamtest
+ ${PAM_LIBRARIES})
+
+install(
+ TARGETS
+ pamtest
+ RUNTIME DESTINATION ${BIN_INSTALL_DIR}
+ LIBRARY DESTINATION ${LIB_INSTALL_DIR}
+ ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
+)
+
add_library(pam_matrix MODULE modules/pam_matrix.c)
set_property(TARGET pam_matrix PROPERTY PREFIX "")
diff --git a/src/libpamtest.c b/src/libpamtest.c
new file mode 100644
index 0000000..1990a18
--- /dev/null
+++ b/src/libpamtest.c
@@ -0,0 +1,216 @@
+/*
+ * 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/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "libpamtest.h"
+
+static enum pamtest_err run_test_case(pam_handle_t *ph,
+ struct pamtest_case *tc)
+{
+ switch (tc->pam_operation) {
+ case PAMTEST_AUTHENTICATE:
+ tc->op_rv = pam_authenticate(ph, tc->flags);
+ return PAMTEST_ERR_OK;
+ case PAMTEST_SETCRED:
+ tc->op_rv = pam_setcred(ph, tc->flags);
+ return PAMTEST_ERR_OK;
+ case PAMTEST_ACCOUNT:
+ tc->op_rv = pam_acct_mgmt(ph, tc->flags);
+ return PAMTEST_ERR_OK;
+ case PAMTEST_OPEN_SESSION:
+ tc->op_rv = pam_open_session(ph, tc->flags);
+ return PAMTEST_ERR_OK;
+ case PAMTEST_CLOSE_SESSION:
+ tc->op_rv = pam_close_session(ph, tc->flags);
+ return PAMTEST_ERR_OK;
+ case PAMTEST_CHAUTHTOK:
+ tc->op_rv = pam_chauthtok(ph, tc->flags);
+ return PAMTEST_ERR_OK;
+ case PAMTEST_GETENVLIST:
+ tc->case_out.envlist = pam_getenvlist(ph);
+ return PAMTEST_ERR_OK;
+ case PAMTEST_KEEPHANDLE:
+ tc->case_out.ph = ph;
+ return PAMTEST_ERR_KEEPHANDLE;
+ default:
+ return PAMTEST_ERR_OP;
+ }
+
+ return PAMTEST_ERR_OP;
+}
+
+enum pamtest_err pamtest_ex(const char *service,
+ const char *user,
+ pam_conv_fn conv_fn,
+ void *conv_userdata,
+ struct pamtest_case *test_cases)
+{
+ int rv;
+ pam_handle_t *ph;
+ struct pam_conv conv;
+ size_t tcindex;
+ struct pamtest_case *tc;
+ bool call_pam_end = true;
+
+ conv.conv = conv_fn;
+ conv.appdata_ptr = conv_userdata;
+
+ if (test_cases == NULL) {
+ return PAMTEST_ERR_INTERNAL;
+ }
+
+ rv = pam_start(service, user, &conv, &ph);
+ if (rv != PAM_SUCCESS) {
+ return PAMTEST_ERR_START;
+ }
+
+ for (tcindex = 0;
+ test_cases[tcindex].pam_operation != PAMTEST_SENTINEL;
+ tcindex++) {
+ tc = &test_cases[tcindex];
+
+ rv = run_test_case(ph, tc);
+ if (rv == PAMTEST_ERR_KEEPHANDLE) {
+ call_pam_end = false;
+ continue;
+ } else if (rv != PAMTEST_ERR_OK) {
+ return PAMTEST_ERR_INTERNAL;
+ }
+
+ if (tc->op_rv != tc->expected_rv) {
+ break;
+ }
+ }
+
+ if (call_pam_end == true) {
+ rv = pam_end(ph, tc->op_rv);
+ if (rv != PAM_SUCCESS) {
+ return PAMTEST_ERR_END;
+ }
+ }
+
+ if (test_cases[tcindex].pam_operation != PAMTEST_SENTINEL) {
+ return PAMTEST_ERR_CASE;
+ }
+
+ return PAMTEST_ERR_OK;
+}
+
+void pamtest_free_env(char **envlist)
+{
+ if (envlist == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; envlist[i] != NULL; i++) {
+ free(envlist[i]);
+ }
+ free(envlist);
+}
+
+const struct pamtest_case *pamtest_failed_case(struct pamtest_case *test_cases)
+{
+ size_t tcindex;
+
+ for (tcindex = 0;
+ test_cases[tcindex].pam_operation != PAMTEST_SENTINEL;
+ tcindex++) {
+ const struct pamtest_case *tc = &test_cases[tcindex];
+
+ if (tc->expected_rv != tc->op_rv) {
+ return tc;
+ }
+ }
+
+ /* Nothing failed */
+ return NULL;
+}
+
+struct pamtest_conv_data {
+ const char **conv_input;
+ size_t conv_index;
+};
+
+static int pamtest_simple_conv(int num_msg,
+ const struct pam_message **msgm,
+ struct pam_response **response,
+ void *appdata_ptr)
+{
+ int i;
+ struct pam_response *reply;
+ const char *password;
+ size_t pwlen;
+ struct pamtest_conv_data *cdata = \
+ (struct pamtest_conv_data *) appdata_ptr;
+
+ if (cdata == NULL) {
+ return PAM_CONV_ERR;
+ }
+
+ reply = (struct pam_response *) calloc(num_msg,
+ sizeof(struct pam_response));
+ if (reply == NULL) {
+ return PAM_CONV_ERR;
+ }
+
+ for (i=0; i < num_msg; i++) {
+ switch (msgm[i]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+ password = (const char *) \
+ cdata->conv_input[cdata->conv_index];
+ if (password == NULL) {
+ return PAM_CONV_ERR;
+ }
+
+ pwlen = strlen(password) + 1;
+
+ cdata->conv_index++;
+
+ reply[i].resp = calloc(pwlen, sizeof(char));
+ if (reply[i].resp == NULL) {
+ free(reply);
+ return PAM_CONV_ERR;
+ }
+ memcpy(reply[i].resp, password, pwlen);
+ break;
+ default:
+ continue;
+ }
+ }
+
+ *response = reply;
+ return PAM_SUCCESS;
+}
+
+enum pamtest_err pamtest(const char *service,
+ const char *user,
+ void *conv_userdata,
+ struct pamtest_case *test_cases)
+{
+ struct pamtest_conv_data cdata;
+
+ cdata.conv_input = conv_userdata;
+ cdata.conv_index = 0;
+
+ return pamtest_ex(service, user,
+ pamtest_simple_conv, &cdata,
+ test_cases);
+}