diff --git a/pam/meson.build b/pam/meson.build index 51657ff..8413b58 100644 --- a/pam/meson.build +++ b/pam/meson.build @@ -17,3 +17,29 @@ pam_gnome_keyring = shared_library('pam_gnome_keyring', ], name_prefix: '', ) + +# pam tests +pam_wrapper = dependency('pam_wrapper', required: true) +libpamtest = dependency('libpamtest', required: true) + +subdir('servicedir') + +test_bin = executable('pam_test', + sources: [ + 'test-pam.c', + ], + dependencies: [ + libpamtest, + glib_deps, + ], +) + +test('pam-test', + test_bin, + env: { + 'LD_PRELOAD': 'libpam_wrapper.so', + 'PAM_WRAPPER': '1', + 'PAM_WRAPPER_DEBUGLEVEL': '5', + 'PAM_WRAPPER_SERVICE_DIR': meson.current_build_dir() + '/servicedir', + }, +) diff --git a/pam/servicedir/meson.build b/pam/servicedir/meson.build new file mode 100644 index 0000000..04adb0f --- /dev/null +++ b/pam/servicedir/meson.build @@ -0,0 +1,11 @@ +custom_target('pam-test-service', + command: 'true', + output: 'null', + depend_files: configure_file( + input: 'pam-test-service.in', + output: 'pam-test-service', + configuration: configuration_data({ + 'KEYRING_PAM': pam_gnome_keyring.full_path(), + }), + ), +) diff --git a/pam/servicedir/pam-test-service.in b/pam/servicedir/pam-test-service.in new file mode 100644 index 0000000..3d67797 --- /dev/null +++ b/pam/servicedir/pam-test-service.in @@ -0,0 +1,3 @@ +auth required @KEYRING_PAM@ +password required @KEYRING_PAM@ +session required @KEYRING_PAM@ diff --git a/pam/test-pam.c b/pam/test-pam.c new file mode 100644 index 0000000..19fabe4 --- /dev/null +++ b/pam/test-pam.c @@ -0,0 +1,173 @@ +/* + * libsecret + * + * Copyright (C) 2023 GNOME Foundation Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received copies of the GNU General Public License and + * the GNU Lesser General Public License along with this program. If + * not, see http://www.gnu.org/licenses/. + * + * Author: Dhanuka Warusadura + */ + +#include +#include +#include +#include +#include + +#include + +#define SERVICE "pam-test-service" +#define BUFFER_SIZE 100 + +typedef struct { + gchar *control_path; + GSocketAddress *address; + GSocketService *service; + GThread *pam_test; + gboolean success; +} Test; + +static gchar dir_path[] = "/tmp/pam_test_XXXXXX"; + +static gboolean +is_bytes_exchanged (GSocketService *service, + GSocketConnection *connection, + GObject *source_object, + gpointer data) +{ + Test *test = data; + GInputStream *input; + GError *error = NULL; + char buffer[BUFFER_SIZE + 1]; + + g_printf ("Incoming signal detected\n"); + + if (g_socket_service_is_active (service)) + test->success = TRUE; + else + return FALSE; + + input = g_io_stream_get_input_stream (G_IO_STREAM(connection)); + + if (g_input_stream_read (input, + buffer, + BUFFER_SIZE, + NULL, + &error) > 0) + return TRUE; + else + return FALSE; +} + +static void +teardown (Test *test) +{ + g_assert_true (g_socket_service_is_active (test->service)); + g_thread_join (test->pam_test); + g_socket_service_stop (test->service); + g_assert_false (g_socket_service_is_active (test->service)); + + g_object_unref (test->address); + g_unlink (test->control_path); + g_free (test->control_path); + g_free (test); + g_rmdir (dir_path); + + g_printf ("Teardown completed\n"); +} + +static void * +pam_auth_tests (gpointer data) +{ + Test *test = data; + enum pamtest_err ret; + + g_printf ("Executing PAM auth tests\n"); + + const char *auth_tokens[] = { + "password", + NULL + }; + + struct pamtest_conv_data conv_data = { + .in_echo_off = auth_tokens + }; + + struct pam_testcase tests[] = { + pam_test (PAMTEST_AUTHENTICATE, PAM_SUCCESS) + }; + + g_assert_true (g_socket_service_is_active (test->service)); + ret = run_pamtest (SERVICE, g_get_user_name (), &conv_data, tests, NULL); + g_assert_cmpint (ret, ==, PAMTEST_ERR_OK); + + return NULL; +} + +static void +mock_service (void) +{ + GError *error = NULL; + Test *test; + + g_printf ("Starting mock service\n"); + + test = g_malloc (sizeof (Test)); + test->success = FALSE; + + g_mkdtemp (dir_path); + g_setenv ("GNOME_KEYRING_CONTROL", dir_path, TRUE); + test->control_path = g_strconcat (dir_path, "/control", NULL); + + test->address = g_unix_socket_address_new (test->control_path); + test->service = g_socket_service_new (); + g_socket_service_stop (test->service); + g_assert_false (g_socket_service_is_active (test->service)); + + g_signal_connect (test->service, + "incoming", + G_CALLBACK (is_bytes_exchanged), + test); + + g_socket_listener_add_address (G_SOCKET_LISTENER (test->service), + test->address, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + NULL, + NULL, + &error); + g_assert_no_error (error); + + g_socket_service_start (test->service); + g_assert_true (g_socket_service_is_active (test->service)); + + test->pam_test = g_thread_new ("pam_test", pam_auth_tests, test); + + do + g_main_context_iteration (NULL, TRUE); + while (!test->success); + + teardown (test); +} + +int +main(int argc, + char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + g_test_add_func ("/pam/test_pam_authtok", mock_service); + + return g_test_run(); +}