diff options
author | David Howells <dhowells@redhat.com> | 2019-08-19 11:34:03 +0100 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2019-08-19 15:38:14 +0100 |
commit | 066bf56807c26cd3045a25f355b34c1d8a20a5aa (patch) | |
tree | 86959378e0e08ce4d2ccd6bb8d85a2680475cfa8 | |
parent | dc12d37cb28388f722644e5484a34eadc5976e4b (diff) | |
download | keyutils-066bf56807c26cd3045a25f355b34c1d8a20a5aa.tar.gz |
test: Test all possible type, description and payload lengths to add_key
Test all possible type, description and payload lengths to add_key() to
make sure that the kernel doesn't crash when handling them.
The bulk of this test is implemented in C in the keyctl command so that it
completes in a reasonable amount of time (testing over a million different
sizes of payload from shell script is just too slow).
Signed-off-by: David Howells <dhowells@redhat.com>
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | keyctl.c | 37 | ||||
-rw-r--r-- | keyctl.h | 29 | ||||
-rw-r--r-- | keyctl_testing.c | 225 | ||||
-rw-r--r-- | tests/features/limits/runtest.sh | 34 |
5 files changed, 310 insertions, 20 deletions
@@ -149,8 +149,9 @@ endif %.o: %.c keyutils.h Makefile $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $< -keyctl: keyctl.o $(LIB_DEPENDENCY) - $(CC) -L. $(CFLAGS) $(LDFLAGS) $(RPATH) -o $@ $< -lkeyutils +keyctl: keyctl.o keyctl_testing.o $(LIB_DEPENDENCY) + $(CC) -L. $(CFLAGS) $(LDFLAGS) $(RPATH) -o $@ keyctl.o keyctl_testing.o -lkeyutils +keyctl.o keyctl_testing.o: keyctl.h request-key: request-key.o $(LIB_DEPENDENCY) $(CC) -L. $(CFLAGS) $(LDFLAGS) $(RPATH) -o $@ $< -lkeyutils @@ -23,14 +23,7 @@ #include <asm/unistd.h> #include "keyutils.h" #include <limits.h> - -struct command { - void (*action)(int argc, char *argv[]) __attribute__((noreturn)); - const char *name; - const char *format; -}; - -#define nr __attribute__((noreturn)) +#include "keyctl.h" static nr void act_keyctl___version(int argc, char *argv[]); static nr void act_keyctl_show(int argc, char *argv[]); @@ -81,7 +74,7 @@ static nr void act_keyctl_pkey_verify(int argc, char *argv[]); static nr void act_keyctl_move(int argc, char *argv[]); static nr void act_keyctl_supports(int argc, char *argv[]); -const struct command commands[] = { +static const struct command commands[] = { { act_keyctl___version, "--version", "" }, { act_keyctl_add, "add", "<type> <desc> <data> <keyring>" }, { act_keyctl_chgrp, "chgrp", "<key> <gid>" }, @@ -134,12 +127,13 @@ const struct command commands[] = { { act_keyctl_timeout, "timeout", "<key> <timeout>" }, { act_keyctl_unlink, "unlink", "<key> [<keyring>]" }, { act_keyctl_update, "update", "<key> <data>" }, + { act_keyctl_test, "--test", "..." }, { NULL, NULL, NULL } }; static int dump_key_tree(key_serial_t keyring, const char *name, int hex_key_IDs); static void format(void) __attribute__((noreturn)); -static void error(const char *msg) __attribute__((noreturn)); +void error(const char *msg) __attribute__((noreturn)); static key_serial_t get_key_id(char *arg); static void *read_file(const char *name, size_t *_size); @@ -152,19 +146,27 @@ static int verbose; /* * handle an error */ -static inline void error(const char *msg) +void error(const char *msg) { perror(msg); exit(1); - -} /* end error() */ +} /*****************************************************************************/ /* - * execute the appropriate subcommand + * */ int main(int argc, char *argv[]) { + do_command(argc, argv, commands, ""); +} + +/* + * Execute the appropriate subcommand. + */ +void do_command(int argc, char **argv, + const struct command *commands, const char *group) +{ const struct command *cmd, *best; int n; @@ -194,7 +196,7 @@ int main(int argc, char *argv[]) /* partial match */ if (best) { - fprintf(stderr, "Ambiguous command\n"); + fprintf(stderr, "Ambiguous %scommand\n", group); exit(2); } @@ -202,13 +204,12 @@ int main(int argc, char *argv[]) } if (!best) { - fprintf(stderr, "Unknown command\n"); + fprintf(stderr, "Unknown %scommand\n", group); exit(2); } best->action(argc, argv); - -} /* end main() */ +} /*****************************************************************************/ /* diff --git a/keyctl.h b/keyctl.h new file mode 100644 index 0000000..e061334 --- /dev/null +++ b/keyctl.h @@ -0,0 +1,29 @@ +/* keyctl program definitions + * + * Copyright (C) 2019 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +struct command { + void (*action)(int argc, char *argv[]) __attribute__((noreturn)); + const char *name; + const char *format; +}; + +#define nr __attribute__((noreturn)) + +/* + * keyctl.c + */ +extern nr void do_command(int, char **, const struct command *, const char *); +extern nr void error(const char *); + +/* + * keyctl_testing.c + */ +extern nr void act_keyctl_test(int, char *[]); diff --git a/keyctl_testing.c b/keyctl_testing.c new file mode 100644 index 0000000..3161467 --- /dev/null +++ b/keyctl_testing.c @@ -0,0 +1,225 @@ +/* Tests built into the keyctl program + * + * Copyright (C) 2019 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define _GNU_SOURCE +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <sys/stat.h> +#include <asm/unistd.h> +#include "keyutils.h" +#include <limits.h> +#include "keyctl.h" + +static nr void act_keyctl_test_limits(int, char *[]); +static nr void act_keyctl_test_limits2(int, char *[]); + +static const struct command test_commands[] = { + { act_keyctl_test_limits, "limits", "" }, + { act_keyctl_test_limits2, "limits2", "" }, + { NULL, NULL, NULL } +}; + +static void test_format(void) __attribute__((noreturn)); +static void test_format(void) +{ + const struct command *cmd; + + fprintf(stderr, "Format:\n"); + + for (cmd = test_commands; cmd->name; cmd++) + fprintf(stderr, " keyctl --test %s %s\n", cmd->name, cmd->format); + + fprintf(stderr, "\n"); + fprintf(stderr, "Key/keyring ID:\n"); + fprintf(stderr, " <nnn> numeric keyring ID\n"); + fprintf(stderr, " @t thread keyring\n"); + fprintf(stderr, " @p process keyring\n"); + fprintf(stderr, " @s session keyring\n"); + fprintf(stderr, " @u user keyring\n"); + fprintf(stderr, " @us user default session keyring\n"); + fprintf(stderr, " @g group keyring\n"); + fprintf(stderr, " @a assumed request_key authorisation key\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "<type> can be \"user\" for a user-defined keyring\n"); + fprintf(stderr, "If you do this, prefix the description with \"<subtype>:\"\n"); + + exit(2); +} + +/* + * Provide some testing functions for "keyctl --test" + */ +void act_keyctl_test(int argc, char *argv[]) +{ + if (argc < 2) + test_format(); + + do_command(argc, argv, test_commands, "test "); +} + +/* + * Test the limits of the type and description fields in add_user(). + */ +static void act_keyctl_test_limits(int argc, char *argv[]) +{ + key_serial_t key; + char buf[8192]; + int i, nr_fail = 0; + + if (argc != 1) + test_format(); + + setvbuf(stdout, NULL, _IONBF, 0); + + for (i = 0; i < sizeof(buf); i++) { + if (i % 32 == 0) { + if (i != 0) + putchar('\n'); + printf("TEST SIZE %d", i); + } + + buf[i] = 0; + + putchar('.'); + if (add_key(buf, "wibble", "a", 1, KEY_SPEC_THREAD_KEYRING) == -1) { + if (i == 0 || i >= 32) { + if (errno != EINVAL) { + putchar('\n'); + fprintf(stderr, "%d type failed: %m\n", i); + nr_fail++; + } + } else { + if (errno != ENODEV) { + putchar('\n'); + fprintf(stderr, "%d type failed: %m\n", i); + nr_fail++; + } + } + } else { + putchar('\n'); + fprintf(stderr, "%d type unexpectedly succeeded\n", i); + nr_fail++; + } + + putchar('_'); + key = add_key("user", buf, "a", 1, KEY_SPEC_THREAD_KEYRING); + if (key == -1) { + if (i == 0 || i >= 4096) { + if (errno != EINVAL) { + putchar('\n'); + fprintf(stderr, "%d desc failed: %m\n", i); + nr_fail++; + } + } else { + putchar('\n'); + fprintf(stderr, "%d desc wrong error: %m\n", i); + nr_fail++; + } + } else { + if (i == 0 || i >= 4096) { + putchar('\n'); + fprintf(stderr, "%d desc unexpectedly succeeded\n", i); + nr_fail++; + } + + if (keyctl_unlink(key, KEY_SPEC_THREAD_KEYRING) == -1) { + putchar('\n'); + fprintf(stderr, "Unlink failed: %m\n"); + nr_fail++; + } + } + + buf[i] = 'a'; + if (nr_fail > 20) { + fprintf(stderr, "Aborting with too many failures\n"); + exit(1); + } + } + + putchar('\n'); + exit(nr_fail ? 1 : 0); +} + +/* + * Test the limits of the payload field in add_user(). The user-type will only + * accept sizes in the range 1-32767 bytes, though add_key() will accept up to + * just shy of 1MiB. + */ +static void act_keyctl_test_limits2(int argc, char *argv[]) +{ + key_serial_t key; + char buf[1030 * 1024]; + int i, nr_fail = 0; + + if (argc != 1) + test_format(); + + setvbuf(stdout, NULL, _IONBF, 0); + memset(buf, 'a', sizeof(buf)); + + for (i = 0; i < sizeof(buf); i++) { + if (i % 2048 == 0) { + if (i != 0) + putchar('\n'); + printf("TEST SIZE %7d ", i); + } + + if (i % (2048 / 32) == 0) + putchar('.'); + + key = add_key("user", "a", buf, i, KEY_SPEC_THREAD_KEYRING); + if (key == -1) { + if (i == 0 || i > 32767) { + if (errno != EINVAL) { + putchar('\n'); + fprintf(stderr, "%d desc failed: %m\n", i); + nr_fail++; + } + } else if (errno == EDQUOT) { + /* This might happen due to us creating keys + * really fast. + */ + } else { + putchar('\n'); + fprintf(stderr, "%d desc wrong error: %m\n", i); + nr_fail++; + } + } else { + if (i == 0 || i > 32767) { + putchar('\n'); + fprintf(stderr, "%d desc unexpectedly succeeded\n", i); + nr_fail++; + } + + if (keyctl_unlink(key, KEY_SPEC_THREAD_KEYRING) == -1) { + putchar('\n'); + fprintf(stderr, "Unlink failed: %m\n"); + nr_fail++; + } + } + + if (nr_fail > 20) { + fprintf(stderr, "Aborting with too many failures\n"); + exit(1); + } + } + + putchar('\n'); + exit(nr_fail ? 1 : 0); +} diff --git a/tests/features/limits/runtest.sh b/tests/features/limits/runtest.sh new file mode 100644 index 0000000..3af2f5a --- /dev/null +++ b/tests/features/limits/runtest.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +. ../../prepare.inc.sh +. ../../toolbox.inc.sh + + +# ---- do the actual testing ---- + +result=PASS +echo "++++ BEGINNING TEST" >$OUTPUTFILE + +# This doesn't work on MIPS earler than 3.19 because of a kernel bug +kver=`uname -r` +kmch=`uname -m` +if kernel_at_or_later_than 3.19 || + [ "$kmch" != "mips" -a "$kmch" != "mips64" ] +then + marker "TEST TYPE AND DESC LIMITS" + if ! keyctl --test limits + then + failed + fi +fi + +marker "TEST PAYLOAD LIMIT" +if ! keyctl --test limits2 +then + failed +fi + +echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE + +# --- then report the results in the database --- +toolbox_report_result $TEST $result |