From b53e002de0d70645a99037f89056f51d4bbb575d Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Thu, 19 Mar 2020 18:33:16 +0100 Subject: [PATCH 1/2] added dependency to linux-pam --- .travis.yml | 1 + CMakeLists.txt | 7 ++++++- README.md | 6 ++++++ cmake/FindPam.cmake | 22 ++++++++++++++++++++++ test/test_pam.cc | 13 +++++++++++++ 5 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 cmake/FindPam.cmake create mode 100644 test/test_pam.cc diff --git a/.travis.yml b/.travis.yml index cb75952..fce4617 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,7 @@ addons: - python3-wheel - ninja-build - libconfig-dev + - libpam0g-dev - valgrind env: diff --git a/CMakeLists.txt b/CMakeLists.txt index 54e7b26..bfe643f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ project(webfused VERSION 0.1.0 DESCRIPTION "Webfuse daemon") option(WITHOUT_TESTS "disable unit tests" OFF) +set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake") + set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) @@ -14,6 +16,7 @@ pkg_check_modules(UUID REQUIRED uuid) pkg_check_modules(LIBCONFIG REQUIRED libconfig) pkg_check_modules(OPENSSL REQUIRED openssl) pkg_check_modules(WEBFUSE REQUIRED libwebfuse-adapter) +find_package(Pam REQUIRED) add_definitions(-D_FILE_OFFSET_BITS=64) @@ -33,7 +36,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(C_WARNINGS -Wall -Wextra) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(coverage) add_library(userdb STATIC @@ -76,6 +78,7 @@ target_link_libraries(webfused PUBLIC ${OPENSSL_LIBRARIES} ${WEBFUSE_LIBRARIES} ${UUID_LIBRARIES} + ${PAM_LIBRARIES} ) target_compile_options(webfused PUBLIC ${OPENSSL_CFLAGS_OTHER}) @@ -130,6 +133,7 @@ add_executable(alltests test/test_stderr_logger.cc test/test_syslog_logger.cc test/test_daemon.cc + test/test_pam.cc ) target_include_directories(alltests PRIVATE @@ -165,6 +169,7 @@ target_link_libraries(alltests PRIVATE ${LIBCONFIG_LIBRARIES} ${WEBFUSE_LIBRARIES} ${UUID_LIBRARIES} + ${PAM_LIBRARIES} ${OPENSSL_LIBRARIES} ${GMOCK_LIBRARIES} ${GTEST_LIBRARIES} diff --git a/README.md b/README.md index 92b59de..de31921 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ switch to *user* and *group* provided in config file. - [jansson](https://github.com/akheron/jansson) - [openssl](https://www.openssl.org/) - [libconfig](https://hyperrealm.github.io/libconfig/) +- [linux-pam](http://www.linux-pam.org/) - [Google Test](https://github.com/google/googletest) *(Test only)* ### Installing dependencies @@ -240,6 +241,11 @@ To install libfuse, meson is needed. Please refer to [meson quick guide](https:/ sudo apt update sudo apt install libconfig-dev +#### linux-pam + + sudo apt update + sudo apt install libpam0g-dev + #### GoogleTest Installation of GoogleTest is optional webfuse library, but required to compile tests. diff --git a/cmake/FindPam.cmake b/cmake/FindPam.cmake new file mode 100644 index 0000000..181be36 --- /dev/null +++ b/cmake/FindPam.cmake @@ -0,0 +1,22 @@ +find_package(PkgConfig) +pkg_check_modules(PC_PAM QUIET pam) +set(PAM_DEFINITIONS ${PC_PAM_CFLAGS_OTHER}) + +find_path(PAM_INCLUDE_DIR security/pam_appl.h + HINTS ${PC_PAM_INCLUDEDIR} ${PC_PAM_INCLUDE_DIRS} +) + +find_library(PAM_LIBRARY NAMES pam + HINTS ${PC_PAM_LIBDIR} ${PC_PAM_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Pam DEFAULT_MSG + PAM_LIBRARY PAM_INCLUDE_DIR +) + +mark_as_advanced(PAM_INCLUDE_DIR PAM_LIBRARY) + +set(PAM_LIBRARIES ${PAM_LIBRARY}) +set(PAM_INCLUDE_DIRS ${PAM_INCLUDE_DIR}) diff --git a/test/test_pam.cc b/test/test_pam.cc new file mode 100644 index 0000000..0f16bac --- /dev/null +++ b/test/test_pam.cc @@ -0,0 +1,13 @@ +#include +#include + +TEST(pam, start) +{ + pam_handle_t * handle = nullptr; + struct pam_conv conv = { nullptr, nullptr }; + int rc = pam_start("test", nullptr, &conv, &handle); + if (PAM_SUCCESS == rc) + { + pam_end(handle, 0); + } +} \ No newline at end of file From ca348795f32446f4e04222a2193881a6ac9bc0f2 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Thu, 19 Mar 2020 21:53:49 +0100 Subject: [PATCH 2/2] added pam authenticator --- CMakeLists.txt | 10 +- README.md | 17 +- etc/webfused.conf | 7 + src/webfused/auth/factory.c | 5 + src/webfused/auth/file_authenticator.c | 2 +- src/webfused/auth/pam_authenticator.c | 174 ++++++++++++++++ src/webfused/auth/pam_authenticator.h | 25 +++ test/mock_pam.cc | 99 +++++++++ test/mock_pam.hpp | 45 ++++ test/test_pam.cc | 13 -- test/test_pam_authenticator.cc | 277 +++++++++++++++++++++++++ test/test_stderr_logger.cc | 8 +- test/test_syslog_logger.cc | 8 +- 13 files changed, 669 insertions(+), 21 deletions(-) create mode 100644 src/webfused/auth/pam_authenticator.c create mode 100644 src/webfused/auth/pam_authenticator.h create mode 100644 test/mock_pam.cc create mode 100644 test/mock_pam.hpp delete mode 100644 test/test_pam.cc create mode 100644 test/test_pam_authenticator.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index bfe643f..34cda4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ add_library(webfused-static STATIC src/webfused/auth/authenticator.c src/webfused/auth/factory.c src/webfused/auth/file_authenticator.c + src/webfused/auth/pam_authenticator.c src/webfused/log/log.c src/webfused/log/logger.c src/webfused/log/manager.c @@ -122,18 +123,19 @@ add_executable(alltests test/mock_logger.cc test/mock_credentials.cc test/mock_settings.cc + test/mock_pam.cc test/test_config_factory.cc test/test_config.cc test/test_settings.cc test/test_auth_factory.cc test/test_file_authenticator.cc + test/test_pam_authenticator.cc test/test_mountpoint_factory.cc test/test_log.cc test/test_log_manager.cc test/test_stderr_logger.cc test/test_syslog_logger.cc test/test_daemon.cc - test/test_pam.cc ) target_include_directories(alltests PRIVATE @@ -164,6 +166,12 @@ target_link_libraries(alltests PRIVATE -Wl,--wrap=wfd_config_set_logger -Wl,--wrap=wfd_config_set_user + -Wl,--wrap=pam_start + -Wl,--wrap=pam_end + -Wl,--wrap=pam_strerror + -Wl,--wrap=pam_authenticate + -Wl,--wrap=pam_acct_mgmt + webfused-static userdb ${LIBCONFIG_LIBRARIES} diff --git a/README.md b/README.md index de31921..046990a 100644 --- a/README.md +++ b/README.md @@ -39,10 +39,10 @@ server: authentication: ( { - provider = "file" + provider = "pam" settings: { - file = "/etc/webfused/passwd" + service_name = "webfused" } } ) @@ -104,9 +104,10 @@ Otherwise, plain websockes without TLS are used. | provider | string | *-required-* | Name of the authentication provider (see below) | | settings | object | *-empty-* | Provider specific settings (see below) -Currently, only one provider is specified: +Currently, the following providers are supported: - *file*: file based authentication +- *pam*: authentication based on Linux PAM ### File Authenticaton Provider @@ -114,7 +115,15 @@ Allows authentication against a file containing username and password. | Setting | Type | Default value | Description | | -------- | ------ | ------------- | ------------------------------- | -| file | string | *-empty-* | Path to the authentication file | +| file | string | *-required-* | Path to the authentication file | + +### PAM Authenticaton Provider + +Allows authentication using Linux PAM. + +| Setting | Type | Default value | Description | +| ------------ | ------ | ------------- | ---------------------- | +| service_name | string | webfused | PAM service identifier | ### Filesystems diff --git a/etc/webfused.conf b/etc/webfused.conf index a8df9b5..a6ffb11 100644 --- a/etc/webfused.conf +++ b/etc/webfused.conf @@ -25,6 +25,13 @@ authentication: file = "/etc/webfused/passwd" } } + # { + # provider = "pam" + # settings: + # { + # service_name = "webfused" + # } + # } ) filesystems: diff --git a/src/webfused/auth/factory.c b/src/webfused/auth/factory.c index 8bd9421..f44a4a8 100644 --- a/src/webfused/auth/factory.c +++ b/src/webfused/auth/factory.c @@ -1,5 +1,6 @@ #include "webfused/auth/factory.h" #include "webfused/auth/file_authenticator.h" +#include "webfused/auth/pam_authenticator.h" #include "webfused/config/settings.h" #include "webfused/log/log.h" @@ -16,6 +17,10 @@ wfd_authenticator_create( { result = wfd_file_authenticator_create(settings, authenticator); } + else if (0 == strcmp("pam", provider)) + { + result = wfd_pam_authenticator_create(settings, authenticator); + } else { WFD_ERROR("failed to create authenticator: unknown type \"%s\"", provider); diff --git a/src/webfused/auth/file_authenticator.c b/src/webfused/auth/file_authenticator.c index 61dae0a..371bbeb 100644 --- a/src/webfused/auth/file_authenticator.c +++ b/src/webfused/auth/file_authenticator.c @@ -61,7 +61,7 @@ wfd_file_authenticator_get_type( return "username"; } -static struct wfd_authenticator_vtable +static struct wfd_authenticator_vtable const wfd_file_authenticator_vtable = { .dispose = &wfd_file_authenticator_dispose, diff --git a/src/webfused/auth/pam_authenticator.c b/src/webfused/auth/pam_authenticator.c new file mode 100644 index 0000000..4b1c592 --- /dev/null +++ b/src/webfused/auth/pam_authenticator.c @@ -0,0 +1,174 @@ +#include "webfused/auth/pam_authenticator.h" +#include "webfused/auth/authenticator.h" +#include "webfused/config/settings.h" +#include "webfused/log/log.h" + +#include "webfuse/adapter/credentials.h" + +#include +#include +#include + +struct wfd_pam_authenticator +{ + char * service_name; +}; + +struct wfd_pam_credentials +{ + char const * username; + char const * password; +}; + +static int +wfd_pam_conversation( + int count, + struct pam_message const ** messages, + struct pam_response * * ret_responses, + void * user_data) +{ + int result = PAM_SUCCESS; + struct pam_response * responses = malloc(count * sizeof(struct pam_response)); + struct wfd_pam_credentials * creds = user_data; + + for(int i = 0; (PAM_SUCCESS == result) && (i < count); i++) + { + struct pam_response * response = &responses[i]; + struct pam_message const * message = messages[i]; + + response->resp_retcode = 0; + response->resp = NULL; + + switch (message->msg_style) + { + case PAM_PROMPT_ECHO_ON: + response->resp = strdup(creds->username); + break; + case PAM_PROMPT_ECHO_OFF: + response->resp = strdup(creds->password); + break; + default: + free(responses); + result = PAM_CONV_ERR; + } + } + + if (PAM_SUCCESS == result) + { + *ret_responses = responses; + } + + return result; +} + +static void +wfd_pam_authenticator_dispose( + void * data) +{ + struct wfd_pam_authenticator * authenticator = data; + + free(authenticator->service_name); + free(authenticator); +} + +static bool +wfd_pam_authenticator_authenticate( + struct wf_credentials * credentials, + void * user_data) +{ + bool result = false; + struct wfd_pam_authenticator * authenticator = user_data; + + char const * username = wf_credentials_get(credentials, "username"); + char const * password = wf_credentials_get(credentials, "password"); + if ((NULL != username) && (NULL != password)) + { + struct wfd_pam_credentials creds = + { + .username = username, + .password = password + }; + + struct pam_conv const conversation = + { + .conv = &wfd_pam_conversation, + .appdata_ptr = &creds + }; + + pam_handle_t * handle = NULL; + bool cleanup_handle = false; + result = true; + { + int rc = pam_start(authenticator->service_name, NULL, + &conversation, &handle); + result = (PAM_SUCCESS == rc); + cleanup_handle = result; + if (!result) + { + WFD_ERROR("failed to start pam conversation: %s", pam_strerror(handle, rc)); + } + } + + if (result) + { + int rc = pam_authenticate(handle, PAM_DISALLOW_NULL_AUTHTOK); + result = (PAM_SUCCESS == rc); + if (!result) + { + WFD_INFO("failed to authenticate user \'%s\' (pam_authenticate): %s", + username, pam_strerror(handle, rc)); + } + } + + if (result) + { + int rc = pam_acct_mgmt(handle, PAM_DISALLOW_NULL_AUTHTOK); + result = (PAM_SUCCESS == rc); + if (!result) + { + WFD_INFO("failed to authenticate user \'%s\' (pam_acct_mgmt): %s", + username, pam_strerror(handle, rc)); + } + } + + if (cleanup_handle) + { + pam_end(handle, 0); + } + } + + WFD_INFO("authenticate user \'%s\': %s", + (NULL != username) ? username : "", + result ? "success" : "failure"); + + return result; +} + +static char const * +wfd_pam_authenticator_get_type( + void * data) +{ + (void) data; + return "username"; +} + +static struct wfd_authenticator_vtable const +wfd_pam_authenticator_vtable = +{ + .dispose = &wfd_pam_authenticator_dispose, + .authenticate = &wfd_pam_authenticator_authenticate, + .get_type = &wfd_pam_authenticator_get_type +}; + +bool +wfd_pam_authenticator_create( + struct wfd_settings * settings, + struct wfd_authenticator * authenticator) +{ + struct wfd_pam_authenticator * data = malloc(sizeof(struct wfd_pam_authenticator)); + data->service_name = strdup(wfd_settings_get_string_or_default(settings, "service_name", "webfused")); + + authenticator->vtable = &wfd_pam_authenticator_vtable; + authenticator->data = data; + return true; +} diff --git a/src/webfused/auth/pam_authenticator.h b/src/webfused/auth/pam_authenticator.h new file mode 100644 index 0000000..c97c1fe --- /dev/null +++ b/src/webfused/auth/pam_authenticator.h @@ -0,0 +1,25 @@ +#ifndef WFD_AUTH_PAM_AUTHENTICATOR_H +#define WFD_AUTH_PAM_AUTHENTICATOR_H + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfd_authenticator; +struct wfd_settings; + +extern bool +wfd_pam_authenticator_create( + struct wfd_settings * settings, + struct wfd_authenticator * authenticator); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/mock_pam.cc b/test/mock_pam.cc new file mode 100644 index 0000000..b637d9b --- /dev/null +++ b/test/mock_pam.cc @@ -0,0 +1,99 @@ +#include "mock_pam.hpp" + +extern "C" +{ + +static webfused_test::IPam * wfd_MockPam = nullptr; + +extern int __real_pam_start( + char const * service_name, + char const * user, + struct pam_conv const * conversation, + pam_handle_t * * handle); +extern int __real_pam_end(pam_handle_t * handle, int status); +extern int __real_pam_authenticate(pam_handle_t * handle, int flags); +extern int __real_pam_acct_mgmt(pam_handle_t * handle, int flags); +extern char const * __real_pam_strerror(pam_handle_t * handle, int errnum); + +int __wrap_pam_start( + char const * service_name, + char const * user, + struct pam_conv const * conversation, + pam_handle_t * * handle) +{ + if (nullptr == wfd_MockPam) + { + return __real_pam_start(service_name, user, conversation, handle); + } + else + { + return wfd_MockPam->start(service_name, user, conversation, handle); + } + +} + +int __wrap_pam_end(pam_handle_t * handle, int status) +{ + if (nullptr == wfd_MockPam) + { + return __real_pam_end(handle, status); + } + else + { + return wfd_MockPam->end(handle, status); + } +} + +int __wrap_pam_authenticate(pam_handle_t * handle, int flags) +{ + if (nullptr == wfd_MockPam) + { + return __real_pam_authenticate(handle, flags); + } + else + { + return wfd_MockPam->authenticate(handle, flags); + } +} + +int __wrap_pam_acct_mgmt(pam_handle_t * handle, int flags) +{ + if (nullptr == wfd_MockPam) + { + return __real_pam_acct_mgmt(handle, flags); + } + else + { + return wfd_MockPam->acct_mgmt(handle, flags); + } +} + +char const * __wrap_pam_strerror(pam_handle_t * handle, int errnum) +{ + if (nullptr == wfd_MockPam) + { + return __real_pam_strerror(handle, errnum); + } + else + { + return wfd_MockPam->strerror(handle, errnum); + } +} + + +} + +namespace webfused_test +{ + +MockPam::MockPam() +{ + wfd_MockPam = this; +} + +MockPam::~MockPam() +{ + wfd_MockPam = nullptr; +} + +} diff --git a/test/mock_pam.hpp b/test/mock_pam.hpp new file mode 100644 index 0000000..a191dc8 --- /dev/null +++ b/test/mock_pam.hpp @@ -0,0 +1,45 @@ +#ifndef WFD_MOCK_PAM_HPP +#define WFD_MOCK_PAM_HPP + +#include +#include + +namespace webfused_test +{ + +class IPam +{ +public: + virtual ~IPam() = default; + virtual int start( + char const * service_name, + char const * user, + struct pam_conv const * conversation, + pam_handle_t * * handle) = 0; + virtual int end(pam_handle_t * handle, int status) = 0; + virtual int authenticate(pam_handle_t * handle, int flags) = 0; + virtual int acct_mgmt(pam_handle_t * handle, int flags) = 0; + virtual char const * strerror(pam_handle_t * handle, int errnum) = 0; +}; + +class MockPam: public IPam +{ +public: + MockPam(); + ~MockPam() override; + + MOCK_METHOD4(start, int ( + char const * service_name, + char const * user, + struct pam_conv const * conversation, + pam_handle_t * * handle)); + + MOCK_METHOD2(end, int(pam_handle_t * handle, int status)); + MOCK_METHOD2(authenticate, int(pam_handle_t * handle, int flags)); + MOCK_METHOD2(acct_mgmt, int (pam_handle_t * handle, int flags)); + MOCK_METHOD2(strerror, char const * (pam_handle_t * handle, int errnum)); +}; + +} + +#endif diff --git a/test/test_pam.cc b/test/test_pam.cc deleted file mode 100644 index 0f16bac..0000000 --- a/test/test_pam.cc +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include - -TEST(pam, start) -{ - pam_handle_t * handle = nullptr; - struct pam_conv conv = { nullptr, nullptr }; - int rc = pam_start("test", nullptr, &conv, &handle); - if (PAM_SUCCESS == rc) - { - pam_end(handle, 0); - } -} \ No newline at end of file diff --git a/test/test_pam_authenticator.cc b/test/test_pam_authenticator.cc new file mode 100644 index 0000000..6f9c4a4 --- /dev/null +++ b/test/test_pam_authenticator.cc @@ -0,0 +1,277 @@ +#include "webfused/auth/pam_authenticator.h" +#include "webfused/auth/authenticator.h" +#include "webfused/config/settings.h" +#include "webfused/auth/factory.h" + +#include "mock_credentials.hpp" +#include "mock_settings.hpp" +#include "mock_pam.hpp" + +#include + + +using ::webfused_test::MockSettings; +using ::webfused_test::MockCredentials; +using ::webfused_test::MockPam; +using ::testing::_; +using ::testing::Return; +using ::testing::StrEq; +using ::testing::Invoke; +using ::testing::StrictMock; + +TEST(pam_authenticator, create) +{ + wfd_authenticator authenticator; + bool success = wfd_pam_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + wfd_authenticator_dispose(authenticator); +} + +TEST(pam_authenticator, create_via_factory) +{ + wfd_authenticator authenticator; + bool success = wfd_authenticator_create("pam", nullptr, &authenticator); + ASSERT_TRUE(success); + + wfd_authenticator_dispose(authenticator); +} + +TEST(pam_authenticator, get_type) +{ + wfd_authenticator authenticator; + bool success = wfd_pam_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + ASSERT_STREQ("username", wfd_authenticator_get_type(authenticator)); + + wfd_authenticator_dispose(authenticator); +} + +TEST(pam_authenticator, authenticate) +{ + MockPam pam; + EXPECT_CALL(pam, start(StrEq("webfused"), nullptr, _, _)).Times(1).WillOnce(Return(PAM_SUCCESS)); + EXPECT_CALL(pam, authenticate(_, PAM_DISALLOW_NULL_AUTHTOK)).Times(1).WillOnce(Return(PAM_SUCCESS)); + EXPECT_CALL(pam, acct_mgmt(_, PAM_DISALLOW_NULL_AUTHTOK)).Times(1).WillOnce(Return(PAM_SUCCESS)); + EXPECT_CALL(pam, end(_, _)).Times(1).WillOnce(Return(PAM_SUCCESS)); + + wfd_authenticator authenticator; + bool success = wfd_pam_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + MockCredentials creds; + EXPECT_CALL(creds, get(StrEq("username"))).Times(1).WillOnce(Return("bob")); + EXPECT_CALL(creds, get(StrEq("password"))).Times(1).WillOnce(Return("secret")); + + bool is_authenticated = wfd_authenticator_authenticate(authenticator, nullptr); + ASSERT_TRUE(is_authenticated); + + wfd_authenticator_dispose(authenticator); +} + +TEST(pam_authenticator, authenticate_with_custom_service_name) +{ + MockPam pam; + EXPECT_CALL(pam, start(StrEq("brummni"), nullptr, _, _)).Times(1).WillOnce(Return(PAM_SUCCESS)); + EXPECT_CALL(pam, authenticate(_, PAM_DISALLOW_NULL_AUTHTOK)).Times(1).WillOnce(Return(PAM_SUCCESS)); + EXPECT_CALL(pam, acct_mgmt(_, PAM_DISALLOW_NULL_AUTHTOK)).Times(1).WillOnce(Return(PAM_SUCCESS)); + EXPECT_CALL(pam, end(_, _)).Times(1).WillOnce(Return(PAM_SUCCESS)); + + MockSettings settings; + EXPECT_CALL(settings, getStringOrDefault(StrEq("service_name"), StrEq("webfused"))) + .Times(1).WillOnce(Return("brummni")); + + wfd_authenticator authenticator; + bool success = wfd_pam_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + MockCredentials creds; + EXPECT_CALL(creds, get(StrEq("username"))).Times(1).WillOnce(Return("bob")); + EXPECT_CALL(creds, get(StrEq("password"))).Times(1).WillOnce(Return("secret")); + + bool is_authenticated = wfd_authenticator_authenticate(authenticator, nullptr); + ASSERT_TRUE(is_authenticated); + + wfd_authenticator_dispose(authenticator); +} + +namespace +{ + +int valid_conversation( + char const * service_name, + char const * user, + struct pam_conv const * conv, + pam_handle_t * * handle) +{ + (void) service_name; + (void) user; + (void) handle; + + pam_message request_username = {PAM_PROMPT_ECHO_ON, "username"}; + pam_message request_password = {PAM_PROMPT_ECHO_OFF, "password"}; + pam_message const * messages[2] = + { + &request_username, + &request_password + }; + pam_response * responses; + int rc = conv->conv(2, messages, &responses, conv->appdata_ptr); + if (PAM_SUCCESS == rc) + { + free(responses); + } + + return rc; +} + +} + +TEST(pam_authenticator, conversation_with_valid_messages) +{ + MockPam pam; + EXPECT_CALL(pam, start(StrEq("webfused"), nullptr, _, _)) + .Times(1).WillOnce(Invoke(&valid_conversation)); + EXPECT_CALL(pam, authenticate(_, PAM_DISALLOW_NULL_AUTHTOK)).Times(1).WillOnce(Return(PAM_SUCCESS)); + EXPECT_CALL(pam, acct_mgmt(_, PAM_DISALLOW_NULL_AUTHTOK)).Times(1).WillOnce(Return(PAM_SUCCESS)); + EXPECT_CALL(pam, end(_, _)).Times(1).WillOnce(Return(PAM_SUCCESS)); + + wfd_authenticator authenticator; + bool success = wfd_pam_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + MockCredentials creds; + EXPECT_CALL(creds, get(StrEq("username"))).Times(1).WillOnce(Return("bob")); + EXPECT_CALL(creds, get(StrEq("password"))).Times(1).WillOnce(Return("secret")); + + bool is_authenticated = wfd_authenticator_authenticate(authenticator, nullptr); + ASSERT_TRUE(is_authenticated); + + wfd_authenticator_dispose(authenticator); +} + +namespace +{ + +int invalid_conversation( + char const * service_name, + char const * user, + struct pam_conv const * conv, + pam_handle_t * * handle) +{ + (void) service_name; + (void) user; + (void) handle; + + pam_message invalid_request = {-1, "invalid"}; + pam_message const * messages[2] = + { + &invalid_request + }; + pam_response * responses; + int rc = conv->conv(1, messages, &responses, conv->appdata_ptr); + + return rc; +} + +} + +TEST(pam_authenticator, conversation_with_invalid_messages) +{ + MockPam pam; + EXPECT_CALL(pam, start(StrEq("webfused"), nullptr, _, _)) + .Times(1).WillOnce(Invoke(&invalid_conversation)); + + wfd_authenticator authenticator; + bool success = wfd_pam_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + MockCredentials creds; + EXPECT_CALL(creds, get(StrEq("username"))).Times(1).WillOnce(Return("bob")); + EXPECT_CALL(creds, get(StrEq("password"))).Times(1).WillOnce(Return("secret")); + + bool is_authenticated = wfd_authenticator_authenticate(authenticator, nullptr); + ASSERT_FALSE(is_authenticated); + + wfd_authenticator_dispose(authenticator); +} + +TEST(pam_authenticator, authenticate_fail_authenticate) +{ + MockPam pam; + EXPECT_CALL(pam, start(StrEq("webfused"), nullptr, _, _)).Times(1).WillOnce(Return(PAM_SUCCESS)); + EXPECT_CALL(pam, authenticate(_, PAM_DISALLOW_NULL_AUTHTOK)).Times(1).WillOnce(Return(-1)); + EXPECT_CALL(pam, end(_, _)).Times(1).WillOnce(Return(PAM_SUCCESS)); + + wfd_authenticator authenticator; + bool success = wfd_pam_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + MockCredentials creds; + EXPECT_CALL(creds, get(StrEq("username"))).Times(1).WillOnce(Return("bob")); + EXPECT_CALL(creds, get(StrEq("password"))).Times(1).WillOnce(Return("secret")); + + bool is_authenticated = wfd_authenticator_authenticate(authenticator, nullptr); + ASSERT_FALSE(is_authenticated); + + wfd_authenticator_dispose(authenticator); +} + +TEST(pam_authenticator, authenticate_fail_acct_mgmt) +{ + MockPam pam; + EXPECT_CALL(pam, start(StrEq("webfused"), nullptr, _, _)).Times(1).WillOnce(Return(PAM_SUCCESS)); + EXPECT_CALL(pam, authenticate(_, PAM_DISALLOW_NULL_AUTHTOK)).Times(1).WillOnce(Return(PAM_SUCCESS)); + EXPECT_CALL(pam, acct_mgmt(_, PAM_DISALLOW_NULL_AUTHTOK)).Times(1).WillOnce(Return(-1)); + EXPECT_CALL(pam, end(_, _)).Times(1).WillOnce(Return(PAM_SUCCESS)); + + wfd_authenticator authenticator; + bool success = wfd_pam_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + MockCredentials creds; + EXPECT_CALL(creds, get(StrEq("username"))).Times(1).WillOnce(Return("bob")); + EXPECT_CALL(creds, get(StrEq("password"))).Times(1).WillOnce(Return("secret")); + + bool is_authenticated = wfd_authenticator_authenticate(authenticator, nullptr); + ASSERT_FALSE(is_authenticated); + + wfd_authenticator_dispose(authenticator); +} + +TEST(pam_authenticator, authenticate_fail_missing_username) +{ + StrictMock pam; + + wfd_authenticator authenticator; + bool success = wfd_pam_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + MockCredentials creds; + EXPECT_CALL(creds, get(StrEq("username"))).Times(1).WillOnce(Return(nullptr)); + EXPECT_CALL(creds, get(StrEq("password"))).Times(1).WillOnce(Return("secret")); + + bool is_authenticated = wfd_authenticator_authenticate(authenticator, nullptr); + ASSERT_FALSE(is_authenticated); + + wfd_authenticator_dispose(authenticator); +} + +TEST(pam_authenticator, authenticate_fail_missing_password) +{ + StrictMock pam; + + wfd_authenticator authenticator; + bool success = wfd_pam_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + MockCredentials creds; + EXPECT_CALL(creds, get(StrEq("username"))).Times(1).WillOnce(Return("bob")); + EXPECT_CALL(creds, get(StrEq("password"))).Times(1).WillOnce(Return(nullptr)); + + bool is_authenticated = wfd_authenticator_authenticate(authenticator, nullptr); + ASSERT_FALSE(is_authenticated); + + wfd_authenticator_dispose(authenticator); +} diff --git a/test/test_stderr_logger.cc b/test/test_stderr_logger.cc index 923bf59..311618a 100644 --- a/test/test_stderr_logger.cc +++ b/test/test_stderr_logger.cc @@ -21,7 +21,13 @@ TEST(stderr_logger, init_via_manager) TEST(stderr_logger, log) { ASSERT_TRUE(wfd_stderr_logger_init(WFD_LOGLEVEL_ALL, NULL)); - WFD_INFO("some information"); + + WFD_FATAL("webfused test"); + WFD_ERROR("webfused test"); + WFD_WARN ("webfused test"); + WFD_INFO ("webfused test"); + WFD_DEBUG("webfused test"); + wfd_log(-1, "webfused test"); wfd_logger_close(); } \ No newline at end of file diff --git a/test/test_syslog_logger.cc b/test/test_syslog_logger.cc index 1d04740..9976560 100644 --- a/test/test_syslog_logger.cc +++ b/test/test_syslog_logger.cc @@ -39,7 +39,13 @@ TEST(syslog_logger, init_via_manager) TEST(syslog_logger, log) { ASSERT_TRUE(wfd_syslog_logger_init(WFD_LOGLEVEL_ALL, NULL)); - WFD_INFO("some information"); + + WFD_FATAL("webfused test"); + WFD_ERROR("webfused test"); + WFD_WARN ("webfused test"); + WFD_INFO ("webfused test"); + WFD_DEBUG("webfused test"); + wfd_log(-1, "webfused test"); wfd_logger_close(); } \ No newline at end of file