/* * This file is part of Cockpit. * * Copyright (C) 2015 Red Hat, Inc. * * Cockpit 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. * * Cockpit 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 a copy of the GNU Lesser General Public License * along with Cockpit; If not, see . */ #define _GNU_SOURCE #include "pam-ssh-add.h" #include "retest/retest.h" #include #include #include #include #include #include #include #include #include static int unexpected_message; /* Enviroment variables we set */ static const char *env_names[] = { "XDG_RUNTIME_DIR", "HOME", "PATH", "LC_ALL", "SSH_AUTH_SOCK", NULL }; /* Holds environment values to set in pam context */ static char *env_saved[N_ELEMENTS (env_names)] = { NULL, }; typedef struct { const char *ssh_add; const char *ssh_add_arg; const char *ssh_agent; const char *ssh_agent_arg; const char *password; struct passwd *pw; } Fixture; struct _ExpectedMessage { const char *line; TAILQ_ENTRY (_ExpectedMessage) messages; }; TAILQ_HEAD (ExpectedList, _ExpectedMessage) el_head; typedef struct _ExpectedMessage ExpectedMessage; static void free_expected (ExpectedMessage *em) { free (em); em = NULL; } static void expect_message (const char *msg) { ExpectedMessage *em = NULL; em = (ExpectedMessage *) malloc(sizeof(ExpectedMessage)); if (em == NULL) assert_not_reached ("expected message allocation failed"); em->line = msg; TAILQ_INSERT_TAIL (&el_head, em, messages); } static void test_logger (int level, const char *msg) { assert (msg != NULL); if (el_head.tqh_first != NULL) { ExpectedMessage *em = el_head.tqh_first; assert_str_contains (msg, em->line); TAILQ_REMOVE (&el_head, el_head.tqh_first, messages); free_expected (em); } else { warnx ("%s", msg); unexpected_message = 1; } } static void save_environment (void) { int i; for (i = 0; env_names[i] != NULL; i++) env_saved[i] = getenv (env_names[i]); } static void restore_environment (void) { int i; for (i = 0; env_names[i] != NULL; i++) { if (env_saved[i]) setenv (env_names[i], env_saved[i], 1); else unsetenv (env_names[i]); } } static void setup (void *arg) { Fixture *fix = arg; unexpected_message = 0; if (!fix->ssh_add) fix->ssh_add = SRCDIR "/src/pam-ssh-add/mock-ssh-add"; if (!fix->ssh_agent) fix->ssh_agent = SRCDIR "/src/pam-ssh-add/mock-ssh-agent"; pam_ssh_add_program = fix->ssh_add; pam_ssh_add_arg = fix->ssh_add_arg; pam_ssh_agent_program = fix->ssh_agent; pam_ssh_agent_arg = fix->ssh_agent_arg; fix->pw = getpwuid (getuid ()); } static void teardown (void *arg) { int missed = 0; // restore original environment restore_environment (); while (el_head.tqh_first != NULL) { ExpectedMessage *em = el_head.tqh_first; warnx ("message didn't get logged: %s", em->line); TAILQ_REMOVE (&el_head, el_head.tqh_first, messages); free_expected (em); missed = 1; } if (missed) assert_not_reached ("expected messages didn't get logged"); if (unexpected_message) assert_not_reached ("got unexpected messages"); } static Fixture default_fixture = { }; static Fixture environment_fixture = { .ssh_agent = SRCDIR "/src/pam-ssh-add/mock-environment", .ssh_agent_arg = NULL }; static void run_test_agent_environment (void *data, const char *xdg_runtime, const char *xdg_runtime_expect) { Fixture *fix = data; int ret; char *xdg_expect = NULL; char *home_expect = NULL; if (xdg_runtime_expect) { if (asprintf (&xdg_expect, "XDG_RUNTIME_DIR=%s", xdg_runtime_expect) < 0) warnx ("Couldn't allocate XDG_RUNTIME_DIR expect variable"); } else { xdg_expect = strdup ("NO XDG_RUNTIME_DIR"); } if (asprintf (&home_expect, "HOME=%s", fix->pw->pw_dir) < 0) warnx ("Couldn't allocate HOME expect variable"); expect_message (xdg_expect); expect_message (home_expect); expect_message ("PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"); expect_message ("LC_ALL=C"); expect_message ("NO OTHER"); expect_message ("NO SSH_AUTH_SOCK"); expect_message ("Failed to start ssh-agent"); ret = pam_ssh_add_start_agent (fix->pw, xdg_runtime, NULL, NULL); assert_num_eq (0, ret); free (xdg_expect); free (home_expect); } static void test_environment (void *data) { run_test_agent_environment (data, NULL, getenv ("XDG_RUNTIME_DIR")); } static void test_environment_env_overides (void *data) { setenv ("PATH", "bad", 1); setenv ("LC_ALL", "bad", 1); setenv ("HOME", "bad", 1); setenv ("XDG_RUNTIME_DIR", "", 1); setenv ("SSH_AUTH_SOCK", "bad", 1); setenv ("OTHER", "bad", 1); run_test_agent_environment (data, NULL, ""); } static void test_environment_overides (void *data) { setenv ("XDG_RUNTIME_DIR", "bad", 1); run_test_agent_environment (data, "xdgover", "xdgover"); } static void test_failed_agent (void *data) { Fixture *fix = data; char *sock = NULL; char *pid = NULL; int ret; expect_message ("Bad things"); expect_message ("Failed to start ssh-agent"); ret = pam_ssh_add_start_agent (fix->pw, NULL, &sock, &pid); assert_num_eq (0, ret); assert_ptr_eq (sock, NULL); assert_ptr_eq (pid, NULL); free (sock); free (pid); } static Fixture bad_agent_fixture = { .ssh_agent_arg = "bad-vars", }; static void test_bad_agent_vars (void *data) { Fixture *fix = data; char *sock = NULL; char *pid = NULL; int ret; expect_message ("Expected agent environment variables not found"); ret = pam_ssh_add_start_agent (fix->pw, NULL, &sock, &pid); assert_num_eq (0, ret); assert_ptr_eq (sock, NULL); assert_ptr_eq (pid, NULL); free (sock); free (pid); } static Fixture good_agent_fixture = { .ssh_agent_arg = "good-vars", }; static void test_good_agent_vars (void *data) { Fixture *fix = data; char *sock = NULL; char *pid = NULL; int ret; ret = pam_ssh_add_start_agent (fix->pw, NULL, &sock, &pid); assert_num_eq (1, ret); assert_str_cmp (sock, ==, "SSH_AUTH_SOCKET=socket"); assert_str_cmp (pid, ==, "SSH_AGENT_PID=100"); free (sock); free (pid); } static Fixture keys_password_fixture = { .ssh_add_arg = NULL, .password = "foobar", }; static Fixture keys_no_password_fixture = { .ssh_add_arg = NULL, .password = NULL, }; static Fixture keys_bad_password_fixture = { .ssh_add_arg = NULL, .password = "bad", }; static void test_keys (void *data) { int ret; int expect = 1; Fixture *fix = data; const char *key_add_result; if (fix->password == NULL) { key_add_result = "Correct password 0, bad password 0, password_blanks 3"; } else if (strcmp (fix->password, "foobar") == 0) { expect = 0; key_add_result = "Correct password 3, bad password 0, password_blanks 0"; } else { key_add_result = "Correct password 0, bad password 3, password_blanks 3"; } expect_message (key_add_result); if (expect) expect_message ("Failed adding some keys"); ret = pam_ssh_add_load (fix->pw, "mock-socket", fix->password); assert_num_eq (1, ret); } static Fixture keys_environment_fixture = { .ssh_add = SRCDIR "/src/pam-ssh-add/mock-environment", .ssh_add_arg = NULL }; static void test_key_environment (void *data) { Fixture *fix = data; int ret; char *home_expect = NULL; expect_message ("ssh-add requires an agent socket"); ret = pam_ssh_add_load (fix->pw, NULL, NULL); assert_num_eq (0, ret); if (asprintf (&home_expect, "HOME=%s", fix->pw->pw_dir) < 0) warnx ("Couldn't allocate HOME expect variable"); expect_message ("NO XDG_RUNTIME_DIR"); expect_message (home_expect); expect_message ("PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"); expect_message ("LC_ALL=C"); expect_message ("NO OTHER"); expect_message ("SSH_AUTH_SOCK=mock-socket"); expect_message ("Failed adding some keys"); ret = pam_ssh_add_load (fix->pw, "mock-socket", NULL); assert_num_eq (1, ret); free (home_expect); } int main (int argc, char *argv[]) { signal (SIGPIPE, SIG_IGN); TAILQ_INIT(&el_head); save_environment (); re_fixture (setup, teardown); pam_ssh_add_log_handler = &test_logger; pam_ssh_add_verbose_mode = 0; re_testx (test_key_environment, &keys_environment_fixture, "/pam-ssh-add/add-key-environment"); re_testx (test_keys, &keys_no_password_fixture, "/pam-ssh-add/add-key-no-password"); re_testx (test_keys, &keys_bad_password_fixture, "/pam-ssh-add/add-key-bad-password"); re_testx (test_keys, &keys_password_fixture, "/pam-ssh-add/add-key-password"); re_testx (test_environment, &environment_fixture, "/pam-ssh-add/environment"); re_testx (test_environment_env_overides, &environment_fixture, "/pam-ssh-add/environment-env-overides"); re_testx (test_environment_overides, &environment_fixture, "/pam-ssh-add/environment-overides"); re_testx (test_good_agent_vars, &good_agent_fixture, "/pam-ssh-add/good-agent-vars"); re_testx (test_bad_agent_vars, &bad_agent_fixture, "/pam-ssh-add/bad-agent-vars"); re_testx (test_failed_agent, &default_fixture, "/pam-ssh-add/test-failed-agent"); return re_test_run (argc, argv); }