diff --git a/Makefile.am b/Makefile.am index ba8f768..e2348e1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = lib bin modules include +SUBDIRS = lib bin modules include t if WITH_DOC SUBDIRS += doc diff --git a/configure.ac b/configure.ac index 0402183..fbf5f2c 100644 --- a/configure.ac +++ b/configure.ac @@ -110,19 +110,20 @@ AC_ARG_ENABLE([werror], [CFLAGS="${CFLAGS} -Werror"]) AC_CONFIG_FILES([ + Makefile bin/Makefile bin/openpam_dump_policy/Makefile bin/pamtest/Makefile bin/su/Makefile + doc/Makefile + doc/man/Makefile include/Makefile include/security/Makefile lib/Makefile modules/Makefile - modules/pam_unix/Makefile modules/pam_deny/Makefile modules/pam_permit/Makefile - doc/Makefile - doc/man/Makefile - Makefile + modules/pam_unix/Makefile + t/Makefile ]) AC_OUTPUT diff --git a/t/Makefile.am b/t/Makefile.am new file mode 100644 index 0000000..75eff1d --- /dev/null +++ b/t/Makefile.am @@ -0,0 +1,14 @@ +# $Id$ + +INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/lib + +# tests +TESTS = t_openpam_readword +check_PROGRAMS = $(TESTS) + +# libt - common support code +check_LIBRARIES = libt.a +libt_a_SOURCES = t_main.c + +# link with libpam and libt +LDADD = $(top_builddir)/lib/libpam.la $(srcdir)/libt.a diff --git a/t/t.h b/t/t.h new file mode 100644 index 0000000..3523fa0 --- /dev/null +++ b/t/t.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2012 Dag-Erling Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#ifndef T_H_INCLUDED +#define T_H_INCLUDED + +struct t_test { + int (*func)(void); + const char *desc; +}; + +#define T_FUNC(n, d) \ + static int t_ ## n ## _func(void); \ + static const struct t_test t_ ## n = \ + { t_ ## n ## _func, d }; \ + static int t_ ## n ## _func(void) + +#define T(n) \ + &t_ ## n + +const struct t_test **t_prepare(int, char **); +void t_cleanup(void); + +void t_verbose(const char *, ...); + +#endif diff --git a/t/t_main.c b/t/t_main.c new file mode 100644 index 0000000..c4f6b6e --- /dev/null +++ b/t/t_main.c @@ -0,0 +1,104 @@ +/*- + * Copyright (c) 2012 Dag-Erling Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#include +#include +#include +#include +#include + +#include "t.h" + +static int verbose; + +void +t_verbose(const char *fmt, ...) +{ + va_list ap; + + if (verbose) { + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + } +} + +static void +usage(void) +{ + + fprintf(stderr, "usage: %s\n", getprogname()); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + const struct t_test **t_plan; + const char *desc; + int n, pass, fail; + int opt; + + while ((opt = getopt(argc, argv, "v")) != -1) + switch (opt) { + case 'v': + verbose = 1; + break; + default: + usage(); + } + + argc -= optind; + argv += optind; + + /* prepare the test plan */ + if ((t_plan = t_prepare(argc, argv)) == NULL) + errx(1, "no plan\n"); + + /* count the tests */ + for (n = 0; t_plan[n] != NULL; ++n) + /* nothing */; + printf("1..%d\n", n); + + /* run the tests */ + for (n = pass = fail = 0; t_plan[n] != NULL; ++n) { + desc = t_plan[n]->desc ? t_plan[n]->desc : "no description"; + if ((*t_plan[n]->func)()) { + printf("ok %d - %s\n", n + 1, desc); + ++pass; + } else { + printf("not ok %d - %s\n", n + 1, desc); + ++fail; + } + } + + /* clean up and exit */ + t_cleanup(); + exit(fail > 0 ? 1 : 0); +} diff --git a/t/t_openpam_readword.c b/t/t_openpam_readword.c new file mode 100644 index 0000000..d8a5ae1 --- /dev/null +++ b/t/t_openpam_readword.c @@ -0,0 +1,338 @@ +/*- + * Copyright (c) 2012 Dag-Erling Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "t.h" + +static char *filename; +static FILE *f; + +/* + * Open the temp file and immediately unlink it so it doesn't leak in case + * of premature exit. + */ +static void +orw_open(void) +{ + int fd; + + if ((fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0) + err(1, "%s(): %s", __func__, filename); + if ((f = fdopen(fd, "r+")) == NULL) + err(1, "%s(): %s", __func__, filename); + if (unlink(filename) < 0) + err(1, "%s(): %s", __func__, filename); +} + +/* + * Write text to the temp file. + */ +static void +orw_output(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(f, fmt, ap); + va_end(ap); + if (ferror(f)) + err(1, "%s", filename); +} + +/* + * Rewind the temp file. + */ +static void +orw_rewind(void) +{ + + errno = 0; + rewind(f); + if (errno != 0) + err(1, "%s(): %s", __func__, filename); +} + +/* + * Read a word from the temp file and verify that the result matches our + * expectations: whether a word was read at all, how many lines were read + * (in case of quoted or escaped newlines), whether we reached the end of + * the file and whether we reached the end of the line. + */ +static int +orw_expect(const char *expected, int lines, int eof, int eol) +{ + int ch, lineno = 0; + char *got; + size_t len; + + got = openpam_readword(f, &lineno, &len); + if (ferror(f)) + err(1, "%s(): %s", __func__, filename); + if (expected != NULL && got == NULL) { + t_verbose("expected <<%s>>, got nothing\n", expected); + return (0); + } + if (expected == NULL && got != NULL) { + t_verbose("expected nothing, got <<%s>>\n", got); + return (0); + } + if (expected != NULL && got != NULL && strcmp(expected, got) != 0) { + t_verbose("expected <<%s>>, got <<%s>>\n", expected, got); + return (0); + } + if (lineno != lines) { + t_verbose("expected to advance %d lines, advanced %d lines\n"); + return (0); + } + if (eof && !feof(f)) { + t_verbose("expected EOF, but didn't get it\n"); + return (0); + } + if (!eof && feof(f)) { + t_verbose("didn't expect EOF, but got it anyway\n"); + return (0); + } + ch = fgetc(f); + if (ferror(f)) + err(1, "%s(): %s", __func__, filename); + if (eol && ch != '\n') { + t_verbose("expected EOL, but didn't get it\n"); + return (0); + } + if (!eol && ch == '\n') { + t_verbose("didn't expect EOL, but got it anyway\n"); + return (0); + } + if (ch != EOF) + ungetc(ch, f); + return (1); +} + +/* + * Close the temp file. + */ +void +orw_close(void) +{ + + if (fclose(f) != 0) + err(1, "%s(): %s", __func__, filename); +} + + +/*************************************************************************** + * Lines without words + */ + +T_FUNC(empty_input, "empty input") +{ + int ret; + + orw_open(); + ret = orw_expect(NULL, 0 /*lines*/, 1 /*eof*/, 0 /*eol*/); + orw_close(); + return (ret); +} + +T_FUNC(empty_line, "empty line") +{ + int ret; + + orw_open(); + orw_output("\n"); + orw_rewind(); + ret = orw_expect(NULL, 0 /*lines*/, 0 /*eof*/, 1 /*eol*/); + orw_close(); + return (ret); +} + +T_FUNC(whitespace_only, "whitespace only") +{ + int ret; + + orw_open(); + orw_output(" \t\r\n"); + orw_rewind(); + ret = orw_expect(NULL, 0 /*lines*/, 0 /*eof*/, 1 /*eol*/); + orw_close(); + return (ret); +} + +T_FUNC(comment_only, "comment only") +{ + int ret; + + orw_open(); + orw_output("# comment\n"); + orw_rewind(); + ret = orw_expect(NULL, 0 /*lines*/, 0 /*eof*/, 1 /*eol*/); + orw_close(); + return (ret); +} + +T_FUNC(whitespace_before_comment, "whitespace before comment") +{ + int ret; + + orw_open(); + orw_output("\t# comment\n"); + orw_rewind(); + ret = orw_expect(NULL, 0 /*lines*/, 0 /*eof*/, 1 /*eol*/); + orw_close(); + return (ret); +} + + +/*************************************************************************** + * Simple cases - no quotes or escapes + */ + +T_FUNC(single_word, "single word") +{ + const char *word = "hello"; + int ret; + + orw_open(); + orw_output("%s\n", word); + orw_rewind(); + ret = orw_expect(word, 0 /*lines*/, 0 /*eof*/, 1 /*eol*/); + orw_close(); + return (ret); +} + +T_FUNC(whitespace_before_word, "whitespace before word") +{ + const char *word = "hello"; + int ret; + + orw_open(); + orw_output(" %s\n", word); + orw_rewind(); + ret = orw_expect(word, 0 /*lines*/, 0 /*eof*/, 1 /*eol*/); + orw_close(); + return (ret); +} + +T_FUNC(whitespace_after_word, "whitespace after word") +{ + const char *word = "hello"; + int ret; + + orw_open(); + orw_output("%s \n", word); + orw_rewind(); + ret = orw_expect(word, 0 /*lines*/, 0 /*eof*/, 0 /*eol*/); + orw_close(); + return (ret); +} + +T_FUNC(comment_after_word, "comment after word") +{ + const char *word = "hello"; + int ret; + + orw_open(); + orw_output("%s # comment\n", word); + orw_rewind(); + ret = orw_expect(word, 0 /*lines*/, 0 /*eof*/, 0 /*eol*/) && + orw_expect(NULL, 0 /*lines*/, 0 /*eof*/, 1 /*eol*/); + orw_close(); + return (ret); +} + +T_FUNC(word_with_hash, "word with hash") +{ + const char *word = "hello#world"; + int ret; + + orw_open(); + orw_output("%s\n", word); + orw_rewind(); + ret = orw_expect(word, 0 /*lines*/, 0 /*eof*/, 1 /*eol*/); + orw_close(); + return (ret); +} + +T_FUNC(two_words, "two words") +{ + const char *word[] = { "hello", "world" }; + int ret; + + orw_open(); + orw_output("%s %s\n", word[0], word[1]); + orw_rewind(); + ret = orw_expect(word[0], 0 /*lines*/, 0 /*eof*/, 0 /*eol*/) && + orw_expect(word[1], 0 /*lines*/, 0 /*eof*/, 1 /*eol*/); + orw_close(); + return (ret); +} + + + +const struct t_test *t_plan[] = { + T(empty_input), + T(empty_line), + T(whitespace_only), + T(comment_only), + T(whitespace_before_comment), + T(single_word), + T(whitespace_before_word), + T(whitespace_after_word), + T(comment_after_word), + T(word_with_hash), + T(two_words), + NULL +}; + +const struct t_test ** +t_prepare(int argc, char *argv[]) +{ + + (void)argc; + (void)argv; + asprintf(&filename, "%s.%d.tmp", getprogname(), getpid()); + if (filename == NULL) + err(1, "asprintf()"); + return (t_plan); +} + +void +t_cleanup(void) +{ +}