summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2019-08-19 11:34:03 +0100
committerDavid Howells <dhowells@redhat.com>2019-08-19 15:38:14 +0100
commit066bf56807c26cd3045a25f355b34c1d8a20a5aa (patch)
tree86959378e0e08ce4d2ccd6bb8d85a2680475cfa8
parentdc12d37cb28388f722644e5484a34eadc5976e4b (diff)
downloadkeyutils-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--Makefile5
-rw-r--r--keyctl.c37
-rw-r--r--keyctl.h29
-rw-r--r--keyctl_testing.c225
-rw-r--r--tests/features/limits/runtest.sh34
5 files changed, 310 insertions, 20 deletions
diff --git a/Makefile b/Makefile
index 5f8b163..a98bfee 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/keyctl.c b/keyctl.c
index 4c5d91c..5fb99f1 100644
--- a/keyctl.c
+++ b/keyctl.c
@@ -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