From d04062511a4862f5a0083ec5353ac7dd46decdd2 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Tue, 13 Oct 2015 15:34:47 +0200 Subject: WIP: pam_wrapper tests --- Makefile.am | 38 ++++- configure.ac | 3 + src/external/cwrap.m4 | 18 +++ src/tests/cwrap/Makefile.am | 28 +++- src/tests/cwrap/cwrap_test_setup.sh | 6 + src/tests/cwrap/mock_pam_responder.c | 204 +++++++++++++++++++++++++++ src/tests/cwrap/pam_services/test_pam_sss.in | 4 + src/tests/cwrap/test_wrapper_pam_sss.c | 96 +++++++++++++ 8 files changed, 394 insertions(+), 3 deletions(-) create mode 100644 src/tests/cwrap/mock_pam_responder.c create mode 100644 src/tests/cwrap/pam_services/test_pam_sss.in create mode 100644 src/tests/cwrap/test_wrapper_pam_sss.c diff --git a/Makefile.am b/Makefile.am index a9ff93b66..2b26f1c14 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1772,7 +1772,7 @@ responder_socket_access_tests_LDADD = \ $(SSSD_LIBS) \ $(SSSD_INTERNAL_LTLIBS) \ libsss_test_common.la -endif +endif # HAVE_CHECK stress_tests_SOURCES = \ src/tests/stress-tests.c @@ -2729,6 +2729,40 @@ pam_sss_la_LDFLAGS = \ -avoid-version \ -Wl,--version-script,$(srcdir)/src/sss_client/sss_pam.exports +if HAVE_PAM_WRAPPER +if HAVE_LIBPAMTEST +check_LTLIBRARIES += \ + pam_test_sss.la \ + $(NULL) + +cwrap_test_modules = pam_test_sss.la +endif # HAVE_LIBPAMTEST +endif # HAVE_PAM_WRAPPER + +pam_test_sss_la_SOURCES = \ + $(pam_sss_la_SOURCES) \ + src/tests/cwrap/mock_pam_responder.c \ + src/responder/common/responder_packet.c \ + src/responder/pam/pamsrv_extract.c \ + src/responder/pam/pamsrv_reply.c \ + src/responder/pam/pamsrv_p11.c \ + $(NULL) +pam_test_sss_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(NULL) +pam_test_sss_la_LIBADD = \ + $(pam_sss_la_LIBADD) \ + libsss_debug.la \ + libsss_util.la \ + $(NULL) +pam_test_sss_la_LDFLAGS = \ + $(pam_sss_la_LDFLAGS) \ + -rpath /nowhere \ + -Wl,-wrap,sss_pam_make_request \ + -Wl,-wrap,sss_packet_get_body \ + -Wl,-wrap,sss_parse_name_for_domains \ + $(NULL) + if BUILD_SUDO libsss_sudo_la_SOURCES = \ @@ -3643,7 +3677,7 @@ endif CLEANFILES = *.X */*.X */*/*.X -tests: all $(check_PROGRAMS) +tests: all $(check_PROGRAMS) $(cwrap_test_modules) (cd src/tests/cwrap && $(MAKE) $(AM_MAKEFLAGS) $@) || exit 1; diff --git a/configure.ac b/configure.ac index c6debe20a..214f64dba 100644 --- a/configure.ac +++ b/configure.ac @@ -391,6 +391,8 @@ AM_CONDITIONAL([HAVE_CHECK], [test x$have_check != x]) AM_CHECK_CMOCKA AM_CHECK_UID_WRAPPER AM_CHECK_NSS_WRAPPER +AM_CHECK_PAM_WRAPPER +AM_CHECK_LIBPAMTEST SSS_ENABLE_INTGCHECK_REQS @@ -407,6 +409,7 @@ AC_CONFIG_FILES([Makefile contrib/sssd.spec src/examples/rwtab src/doxy.config src/sysv/sssd src/sysv/gentoo/sssd src/sysv/SUSE/sssd po/Makefile.in src/man/Makefile src/tests/cwrap/Makefile src/tests/intg/Makefile + src/tests/cwrap/pam_services/test_pam_sss src/providers/ipa/ipa_hbac.pc src/providers/ipa/ipa_hbac.doxy src/lib/idmap/sss_idmap.pc src/lib/idmap/sss_idmap.doxy src/sss_client/sudo/sss_sudo.doxy diff --git a/src/external/cwrap.m4 b/src/external/cwrap.m4 index b8489cc76..f06de876d 100644 --- a/src/external/cwrap.m4 +++ b/src/external/cwrap.m4 @@ -28,3 +28,21 @@ AC_DEFUN([AM_CHECK_NSS_WRAPPER], [ AM_CHECK_WRAPPER(nss_wrapper, HAVE_NSS_WRAPPER) ]) + +AC_DEFUN([AM_CHECK_PAM_WRAPPER], +[ + AM_CHECK_WRAPPER(pam_wrapper, HAVE_PAM_WRAPPER) +]) + +dnl A macro to check presence of cmocka on the system +AC_DEFUN([AM_CHECK_LIBPAMTEST], +[ + have_libpamtest="no" + PKG_CHECK_EXISTS(libpamtest, + dnl PKG_CHECK_EXISTS ACTION-IF-FOUND + [PKG_CHECK_MODULES([PAMTEST], [libpamtest], [have_libpamtest="yes"])], + dnl PKG_CHECK_EXISTS ACTION-IF-NOT-FOUND + [AC_MSG_WARN([No libpamtest library found, some tests will not be built])] + ) + AM_CONDITIONAL([HAVE_LIBPAMTEST], [test x$have_libpamtest = xyes]) +]) diff --git a/src/tests/cwrap/Makefile.am b/src/tests/cwrap/Makefile.am index 34b5d8bea..9eeb79e7c 100644 --- a/src/tests/cwrap/Makefile.am +++ b/src/tests/cwrap/Makefile.am @@ -53,9 +53,19 @@ check_PROGRAMS += \ $(NULL) endif # HAVE_UID_WRAPPER endif # HAVE_NSS_WRAPPER + +if HAVE_PAM_WRAPPER +if HAVE_LIBPAMTEST +check_PROGRAMS += \ + pam_sss_wrapper-tests \ + $(NULL) +endif # HAVE_LIBPAMTEST +endif # HAVE_PAM_WRAPPER + endif # HAVE_CMOCKA -TESTS = $(check_PROGRAMS) +TESTS = $(check_PROGRAMS) \ + $(NULL) become_user_tests_SOURCES = \ test_become_user.c \ @@ -167,4 +177,20 @@ responder_common_tests_LDADD = \ $(abs_top_builddir)/libsss_test_common.la \ $(NULL) +pam_sss_wrapper_tests_SOURCES = \ + test_wrapper_pam_sss.c \ + $(NULL) +pam_sss_wrapper_tests_CFLAGS = \ + $(AM_CFLAGS) \ + $(NULL) +pam_sss_wrapper_tests_LDADD = \ + $(CMOCKA_LIBS) \ + $(TALLOC_LIBS) \ + $(POPT_LIBS) \ + $(PAM_LIBS) \ + $(PAMTEST_LIBS) \ + $(abs_top_builddir)/libsss_debug.la \ + $(abs_top_builddir)/libsss_test_common.la \ + $(NULL) + tests: $(check_PROGRAMS) diff --git a/src/tests/cwrap/cwrap_test_setup.sh b/src/tests/cwrap/cwrap_test_setup.sh index 0d35cb7e5..0cec62a71 100755 --- a/src/tests/cwrap/cwrap_test_setup.sh +++ b/src/tests/cwrap/cwrap_test_setup.sh @@ -5,6 +5,7 @@ pkg-config --exists uid_wrapper || exit 1 nss_wrapper=$(pkg-config --libs nss_wrapper) uid_wrapper=$(pkg-config --libs uid_wrapper) +pam_wrapper=$(pkg-config --libs pam_wrapper) if [ -z $nss_wrapper -o -z $uid_wrapper ]; then echo "Cannot locate cwrap libraries" exit 2 @@ -15,3 +16,8 @@ export NSS_WRAPPER_PASSWD=$CWRAP_TEST_SRCDIR/passwd export NSS_WRAPPER_GROUP=$CWRAP_TEST_SRCDIR/group export UID_WRAPPER=1 export UID_WRAPPER_ROOT=1 + +if [ ! -z $pam_wrapper ]; then + export PAM_WRAPPER=1 + export PAM_WRAPPER_CONFDIR=$(pwd)/pam_services +fi diff --git a/src/tests/cwrap/mock_pam_responder.c b/src/tests/cwrap/mock_pam_responder.c new file mode 100644 index 000000000..8c057ac7d --- /dev/null +++ b/src/tests/cwrap/mock_pam_responder.c @@ -0,0 +1,204 @@ +/* + Copyright (C) 2015 Red Hat + + SSSD tests: PAM tests + + 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 +#include +#include +#include "sss_cli.h" + +#include "responder/pam/pamsrv.h" +#include "responder/common/responder_packet.h" +#include "providers/data_provider.h" + +/* FIXME - move the definition to a private header */ +struct sss_packet { + size_t memsize; + + /* Structure of the buffer: + * Bytes Content + * --------------------------------- + * 0-15 packet header + * 0-3 packet length (uint32_t) + * 4-7 command type (uint32_t) + * 8-11 status (uint32_t) + * 12-15 reserved + * 16+ packet body */ + uint8_t *buffer; + + /* io pointer */ + size_t iop; +}; + +/* Make linker happy */ +int __wrap_sss_parse_name_for_domains(TALLOC_CTX *memctx, + struct sss_domain_info *domains, + const char *default_domain, + const char *orig, + char **domain, char **name) +{ + char *atsign; + + atsign = strrchr(orig, '@'); + if (atsign == NULL) { + *domain = NULL; + *name = talloc_strdup(memctx, orig); + if (*name == NULL) { + return ENOMEM; + } + return EOK; + } + + *name = talloc_strndup(memctx, orig, atsign - orig); + *domain = talloc_strdup(memctx, atsign+1); + if (*name == NULL || *domain == NULL) { + return ENOMEM; + } + + return EOK; +} + +void __wrap_sss_packet_get_body(struct sss_packet *packet, uint8_t **body, size_t *blen) +{ + *body = packet->buffer; + *blen = packet->memsize; +} + +static struct cli_ctx * +mock_pam_cctx(TALLOC_CTX *mem_ctx, + enum sss_cli_command cmd, + int cli_protocol_version, + struct sss_cli_req_data *rd) +{ + struct cli_ctx *cctx = NULL; + int ret; + + cctx = talloc_zero(mem_ctx, struct cli_ctx); + if (!cctx) goto fail; + + cctx->creq = talloc_zero(cctx, struct cli_request); + if (cctx->creq == NULL) goto fail; + + cctx->cli_protocol_version = talloc_zero(cctx, + struct cli_protocol_version); + if (cctx->cli_protocol_version == NULL) goto fail; + + cctx->cli_protocol_version->version = cli_protocol_version; + + cctx->creq = talloc_zero(cctx, struct cli_request); + if (cctx->creq == NULL) goto fail; + + ret = sss_packet_new(cctx->creq, 0, cmd, &cctx->creq->in); + if (ret != EOK) goto fail; + + cctx->rctx = talloc_zero(cctx, struct resp_ctx); + if (cctx->rctx == NULL) goto fail; + + cctx->creq->in->buffer = discard_const(rd->data); + cctx->creq->in->memsize = rd->len; + + return cctx; + +fail: + talloc_free(cctx); + return NULL; +} + +static struct pam_data * +mock_pam_data(TALLOC_CTX *mem_ctx, enum sss_cli_command cmd) +{ + struct pam_data *pd = NULL; + + pd = talloc_zero(mem_ctx, struct pam_data); + if (pd == NULL) goto fail; + + pd->cmd = cmd; + pd->authtok = sss_authtok_new(pd); + pd->newauthtok = sss_authtok_new(pd); + if (pd->authtok == NULL || pd->newauthtok == NULL) goto fail; + + return pd; + +fail: + talloc_free(pd); + return NULL; +} + +/* Receives a packed response and returns a mock reply */ +int __wrap_sss_pam_make_request(enum sss_cli_command cmd, + struct sss_cli_req_data *rd, + uint8_t **repbuf, size_t *replen, + int *errnop) +{ + errno_t ret; + TALLOC_CTX *test_ctx; + struct cli_ctx *cctx; + struct pam_data *pd; + + test_ctx = talloc_new(NULL); + if (test_ctx == NULL) { + return ENOMEM; + } + + /* The PAM responder functions expect both cctx and pd to be talloc + * contexts + */ + cctx = mock_pam_cctx(test_ctx, cmd, 3, rd); + pd = mock_pam_data(test_ctx, cmd); + if (cctx == NULL || pd == NULL) { + ret = ENOMEM; + goto done; + } + + ret = pam_forwarder_parse_data(cctx, pd); + if (ret != EOK) { + goto done; + } + + if (cmd == SSS_PAM_AUTHENTICATE) { + if (strcmp(pd->user, "testuser") == 0) { + const char *password; + size_t pwlen; + + ret = sss_authtok_get_password(pd->authtok, &password, &pwlen); + if (ret != EOK) { + ret = EINVAL; + goto done; + } + + if (strncmp(password, "secret", pwlen) == 0) { + pd->pam_status = PAM_SUCCESS; + } else { + pd->pam_status = PAM_AUTH_ERR; + } + } + } + + ret = pamsrv_reply_packet(cctx->creq, pd, cmd, &cctx->creq->out); + if (ret != EOK) { + goto done; + } + + *repbuf = malloc(cctx->creq->out->memsize); + memcpy(*repbuf, cctx->creq->out->buffer, cctx->creq->out->memsize); + *replen = cctx->creq->out->memsize; + + ret = EOK; +done: + return ret; +} diff --git a/src/tests/cwrap/pam_services/test_pam_sss.in b/src/tests/cwrap/pam_services/test_pam_sss.in new file mode 100644 index 000000000..8cff06112 --- /dev/null +++ b/src/tests/cwrap/pam_services/test_pam_sss.in @@ -0,0 +1,4 @@ +auth required @abs_top_builddir@/.libs/pam_test_sss.so +account required @abs_top_builddir@/.libs/pam_test_sss.so +password required @abs_top_builddir@/.libs/pam_test_sss.so +session required @abs_top_builddir@/.libs/pam_test_sss.so diff --git a/src/tests/cwrap/test_wrapper_pam_sss.c b/src/tests/cwrap/test_wrapper_pam_sss.c new file mode 100644 index 000000000..c0d1fa541 --- /dev/null +++ b/src/tests/cwrap/test_wrapper_pam_sss.c @@ -0,0 +1,96 @@ +/* + Copyright (C) 2015 Red Hat + + SSSD tests: PAM tests + + 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 +#include + +#include "util/util.h" +#include "tests/cmocka/common_mock.h" + +static void test_pam_authenticate(void **state) +{ + enum pamtest_err perr; + const char *testuser_authtoks[] = { + "secret", + NULL, + }; + struct pamtest_case tests[] = { + { PAMTEST_AUTHENTICATE, PAM_SUCCESS, 0, 0 }, + { PAMTEST_SENTINEL, 0, 0, 0 }, + }; + + (void) state; /* unused */ + + perr = pamtest("test_pam_sss", "testuser", testuser_authtoks, tests); + assert_int_equal(perr, PAMTEST_ERR_OK); +} + +static void test_pam_authenticate_err(void **state) +{ + enum pamtest_err perr; + const char *testuser_authtoks[] = { + "wrong_secret", + NULL, + }; + struct pamtest_case tests[] = { + { PAMTEST_AUTHENTICATE, PAM_AUTH_ERR, 0, 0 }, + { PAMTEST_SENTINEL, 0, 0, 0 }, + }; + + (void) state; /* unused */ + + perr = pamtest("test_pam_sss", "testuser", testuser_authtoks, tests); + assert_int_equal(perr, PAMTEST_ERR_OK); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_pam_authenticate), + cmocka_unit_test(test_pam_authenticate_err), + }; + + /* Set debug level to invalid value so we can deside if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + tests_set_cwd(); + + return cmocka_run_group_tests(tests, NULL, NULL); +} -- cgit