From f8e5c55a036f0e2e2a958e30456270f3f9eba933 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 14:52:13 +0200 Subject: Adding upstream version 1.9.5p2. Signed-off-by: Daniel Baumann --- lib/iolog/regress/host_port/host_port_test.c | 145 +++++++++++ lib/iolog/regress/iolog_json/check_iolog_json.c | 265 ++++++++++++++++++++ lib/iolog/regress/iolog_json/test1.in | 34 +++ lib/iolog/regress/iolog_json/test2.in | 28 +++ lib/iolog/regress/iolog_json/test2.out.ok | 34 +++ lib/iolog/regress/iolog_json/test3.in | 22 ++ .../regress/iolog_mkpath/check_iolog_mkpath.c | 91 +++++++ lib/iolog/regress/iolog_path/check_iolog_path.c | 273 +++++++++++++++++++++ lib/iolog/regress/iolog_path/data | 96 ++++++++ lib/iolog/regress/iolog_util/check_iolog_util.c | 148 +++++++++++ 10 files changed, 1136 insertions(+) create mode 100644 lib/iolog/regress/host_port/host_port_test.c create mode 100644 lib/iolog/regress/iolog_json/check_iolog_json.c create mode 100644 lib/iolog/regress/iolog_json/test1.in create mode 100644 lib/iolog/regress/iolog_json/test2.in create mode 100644 lib/iolog/regress/iolog_json/test2.out.ok create mode 100644 lib/iolog/regress/iolog_json/test3.in create mode 100644 lib/iolog/regress/iolog_mkpath/check_iolog_mkpath.c create mode 100644 lib/iolog/regress/iolog_path/check_iolog_path.c create mode 100644 lib/iolog/regress/iolog_path/data create mode 100644 lib/iolog/regress/iolog_util/check_iolog_util.c (limited to 'lib/iolog/regress') diff --git a/lib/iolog/regress/host_port/host_port_test.c b/lib/iolog/regress/host_port/host_port_test.c new file mode 100644 index 0000000..109393a --- /dev/null +++ b/lib/iolog/regress/host_port/host_port_test.c @@ -0,0 +1,145 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2019-2020 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#ifdef HAVE_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif +#include +#include + +#include "sudo_compat.h" +#include "sudo_fatal.h" +#include "sudo_iolog.h" +#include "sudo_util.h" + +sudo_dso_public int main(int argc, char *argv[]); + +/* + * Test that iolog_parse_host_port() works as expected. + */ + +struct host_port_test { + const char *str; /* input string */ + const char *host; /* parsed host */ + const char *port; /* parsed port */ + bool tls; /* parsed TLS flag */ + char *defport; /* default port */ + char *defport_tls; /* default port */ + bool ret; /* return value */ +}; + +static struct host_port_test test_data[] = { + /* No TLS */ + { "xerxes", "xerxes", "12345", false, "12345", NULL, true }, + { "xerxes:12345", "xerxes", "12345", false, "67890", NULL, true }, + { "127.0.0.1", "127.0.0.1", "12345", false, "12345", NULL, true }, + { "127.0.0.1:12345", "127.0.0.1", "12345", false, "67890", NULL, true }, + { "[::1]", "::1", "12345", false, "12345", NULL, true }, + { "[::1]:12345", "::1", "12345", false, "67890", NULL, true }, + + /* With TLS */ + { "xerxes(tls)", "xerxes", "12345", true, "5678", "12345", true }, + { "xerxes:12345(tls)", "xerxes", "12345", true, "5678", "67890", true }, + { "127.0.0.1(tls)", "127.0.0.1", "12345", true, "5678", "12345", true }, + { "127.0.0.1:12345(tls)", "127.0.0.1", "12345", true, "5678", "67890", true }, + { "[::1](tls)", "::1", "12345", true, "5678", "12345", true }, + { "[::1]:12345(tls)", "::1", "12345", true, "5678", "67890", true }, + + /* Errors */ + { "xerxes:", NULL, NULL, false, "12345", NULL, false }, /* missing port */ + { "127.0.0.1:", NULL, NULL, false, "12345", NULL, false }, /* missing port */ + { "[::1:12345", NULL, NULL, false, "67890", NULL, false }, /* missing bracket */ + { "[::1]:", NULL, NULL, false, "12345", NULL, false }, /* missing port */ + { NULL } +}; + +int +main(int argc, char *argv[]) +{ + int i, errors = 0, ntests = 0; + char *host, *port, *copy = NULL; + bool ret, tls; + + initprogname(argc > 0 ? argv[0] : "host_port_test"); + + for (i = 0; test_data[i].str != NULL; i++) { + host = port = NULL; + tls = false; + free(copy); + if ((copy = strdup(test_data[i].str)) == NULL) + sudo_fatal_nodebug(NULL); + + ntests++; + ret = iolog_parse_host_port(copy, &host, &port, &tls, + test_data[i].defport, test_data[i].defport_tls); + if (ret != test_data[i].ret) { + sudo_warnx_nodebug("test #%d: %s: returned %s, expected %s", + ntests, test_data[i].str, ret ? "true" : "false", + test_data[i].ret ? "true" : "false"); + errors++; + continue; + } + if (!ret) + continue; + + if (host == NULL) { + sudo_warnx_nodebug("test #%d: %s: NULL host", + ntests, test_data[i].str); + errors++; + continue; + } + if (strcmp(host, test_data[i].host) != 0) { + sudo_warnx_nodebug("test #%d: %s: bad host, expected %s, got %s", + ntests, test_data[i].str, test_data[i].host, host); + errors++; + continue; + } + if (port == NULL) { + sudo_warnx_nodebug("test #%d: %s: NULL port", + ntests, test_data[i].str); + errors++; + continue; + } + if (strcmp(port, test_data[i].port) != 0) { + sudo_warnx_nodebug("test #%d: %s: bad port, expected %s, got %s", + ntests, test_data[i].str, test_data[i].port, port); + errors++; + continue; + } + if (tls != test_data[i].tls) { + sudo_warnx_nodebug("test #%d: %s: bad tls, expected %s, got %s", + ntests, test_data[i].str, test_data[i].tls ? "true" : "false", + tls ? "true" : "false"); + errors++; + continue; + } + } + free(copy); + if (ntests != 0) { + printf("%s: %d tests run, %d errors, %d%% success rate\n", + getprogname(), ntests, errors, (ntests - errors) * 100 / ntests); + } + exit(errors); +} diff --git a/lib/iolog/regress/iolog_json/check_iolog_json.c b/lib/iolog/regress/iolog_json/check_iolog_json.c new file mode 100644 index 0000000..a967ba6 --- /dev/null +++ b/lib/iolog/regress/iolog_json/check_iolog_json.c @@ -0,0 +1,265 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2020 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include + +#define SUDO_ERROR_WRAP 0 + +#include "sudo_compat.h" +#include "sudo_util.h" +#include "sudo_fatal.h" + +#include "iolog_json.h" + +sudo_dso_public int main(int argc, char *argv[]); + +bool +json_print_object(struct json_container *json, struct json_object *object) +{ + struct json_item *item; + struct json_value json_value; + bool ret = false; + + TAILQ_FOREACH(item, &object->items, entries) { + switch (item->type) { + case JSON_STRING: + json_value.type = JSON_STRING; + json_value.u.string = item->u.string; + if (!sudo_json_add_value(json, item->name, &json_value)) + goto oom; + break; + case JSON_NUMBER: + json_value.type = JSON_NUMBER; + json_value.u.number = item->u.number; + if (!sudo_json_add_value(json, item->name, &json_value)) + goto oom; + break; + case JSON_OBJECT: + if (!sudo_json_open_object(json, item->name)) + goto oom; + if (!json_print_object(json, &item->u.child)) + goto done; + if (!sudo_json_close_object(json)) + goto oom; + break; + case JSON_ARRAY: + if (!sudo_json_open_array(json, item->name)) + goto oom; + if (!json_print_object(json, &item->u.child)) + goto done; + if (!sudo_json_close_array(json)) + goto oom; + break; + case JSON_BOOL: + json_value.type = JSON_BOOL; + json_value.u.boolean = item->u.boolean; + if (!sudo_json_add_value(json, item->name, &json_value)) + goto oom; + break; + case JSON_NULL: + json_value.type = JSON_NULL; + if (!sudo_json_add_value(json, item->name, &json_value)) + goto oom; + break; + default: + sudo_warnx("unsupported JSON type %d", item->type); + goto done; + } + } + + ret = true; + goto done; + +oom: + sudo_warnx("%s: %s", __func__, "unable to allocate memory"); +done: + return ret; +} + +static bool +json_format(struct json_container *json, struct json_object *object) +{ + struct json_item *item; + bool ret = false; + + /* First object holds all the actual data. */ + item = TAILQ_FIRST(&object->items); + if (item->type != JSON_OBJECT) { + sudo_warnx("expected JSON_OBJECT, got %d", item->type); + goto done; + } + object = &item->u.child; + + if (!json_print_object(json, object)) + goto done; + + ret = true; + +done: + return ret; +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-c] input_file ...\n", + getprogname()); + exit(EXIT_FAILURE); +} + +static bool +compare(FILE *fp, const char *infile, struct json_container *json) +{ + const char *cp; + unsigned int lineno = 0; + size_t linesize = 0; + char *line = NULL; + ssize_t len; + + cp = sudo_json_get_buf(json); + + while ((len = getdelim(&line, &linesize, '\n', fp)) != -1) { + lineno++; + + /* skip open/close brace, not present in formatted output */ + if (lineno == 1 && strcmp(line, "{\n") == 0) + continue; + if (*cp == '\0' && strcmp(line, "}\n") == 0) + continue; + + /* Ignore newlines in output to make comparison easier. */ + if (*cp == '\n') + cp++; + if (line[len - 1] == '\n') + len--; + + if (strncmp(line, cp, len) != 0) { + fprintf(stderr, "%s: mismatch on line %u\n", infile, lineno); + fprintf(stderr, "expected: %s", line); + fprintf(stderr, "got : %.*s\n", (int)len, cp); + return false; + } + cp += len; + } + free(line); + + return true; +} + +int +main(int argc, char *argv[]) +{ + struct json_object root; + int ch, i, tests = 0, errors = 0; + bool cat = false; + + initprogname(argc > 0 ? argv[0] : "check_iolog_json"); + + while ((ch = getopt(argc, argv, "c")) != -1) { + switch (ch) { + case 'c': + cat = true; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + for (i = 0; i < argc; i++) { + struct json_container json; + const char *infile = argv[i]; + const char *outfile = argv[i]; + const char *cp; + char pathbuf[PATH_MAX]; + FILE *infp = NULL; + FILE *outfp = NULL; + + tests++; + + if (!sudo_json_init(&json, 4, false, true)) { + errors++; + continue; + } + + /* Parse input file. */ + if ((infp = fopen(infile, "r")) == NULL) { + sudo_warn("%s", argv[1]); + errors++; + goto next; + } + if (!iolog_parse_json(infp, infile, &root)) { + errors++; + goto next; + } + + /* Format as pretty-printed JSON */ + if (!json_format(&json, &root)) { + errors++; + goto next; + } + + /* Check for a .out.ok file in the same location as the .in file. */ + cp = strrchr(infile, '.'); + if (cp != NULL && strcmp(cp, ".in") == 0) { + snprintf(pathbuf, sizeof(pathbuf), "%.*s.out.ok", + (int)(cp - infile), infile); + if ((outfp = fopen(pathbuf, "r")) != NULL) + outfile = pathbuf; + } + if (outfp == NULL) + outfp = infp; + + /* Compare output to expected output. */ + rewind(outfp); + if (!compare(outfp, outfile, &json)) + errors++; + + /* Write the formatted output to stdout for -c (cat) */ + if (cat) { + fprintf(stdout, "{%s\n}\n", sudo_json_get_buf(&json)); + fflush(stdout); + } + +next: + free_json_items(&root.items); + sudo_json_free(&json); + if (infp != NULL) + fclose(infp); + if (outfp != NULL && outfp != infp) + fclose(outfp); + } + + if (tests != 0) { + printf("iolog_json: %d test%s run, %d errors, %d%% success rate\n", + tests, tests == 1 ? "" : "s", errors, + (tests - errors) * 100 / tests); + } + + exit(errors); +} diff --git a/lib/iolog/regress/iolog_json/test1.in b/lib/iolog/regress/iolog_json/test1.in new file mode 100644 index 0000000..8ad3689 --- /dev/null +++ b/lib/iolog/regress/iolog_json/test1.in @@ -0,0 +1,34 @@ +{ + "timestamp": { + "seconds": 1584993067, + "nanoseconds": 880288287 + }, + "columns": 80, + "command": "/usr/bin/make", + "lines": 24, + "runargv": [ + "make", + "test" + ], + "runenv": [ + "LANG=en_US.UTF-8", + "PATH=/bin:/sbin:/usr/games:/usr/bin:/usr/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin", + "TERM=vt100", + "MAIL=/var/mail/root", + "LOGNAME=root", + "USER=root", + "HOME=/root", + "SHELL=/bin/ksh", + "SUDO_COMMAND=/usr/bin/make test", + "SUDO_USER=millert", + "SUDO_UID=8036", + "SUDO_GID=20", + "A__z=\"*SHLVL" + ], + "runuid": 0, + "runuser": "root", + "submitcwd": "/home/test", + "submithost": "sudo.ws", + "submituser": "millert", + "ttyname": "/dev/console" +} diff --git a/lib/iolog/regress/iolog_json/test2.in b/lib/iolog/regress/iolog_json/test2.in new file mode 100644 index 0000000..df7170f --- /dev/null +++ b/lib/iolog/regress/iolog_json/test2.in @@ -0,0 +1,28 @@ +{ + "timestamp": { "seconds": 1584993067, "nanoseconds": 880288287 }, + "columns": 80, + "command": "/usr/bin/make", + "lines": 24, + "runargv": [ "make", "test" ], + "runenv": [ + "LANG=en_US.UTF-8", + "PATH=/bin:/sbin:/usr/games:/usr/bin:/usr/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin", + "TERM=vt100", + "MAIL=/var/mail/root", + "LOGNAME=root", + "USER=root", + "HOME=/root", + "SHELL=/bin/ksh", + "SUDO_COMMAND=/usr/bin/make test", + "SUDO_USER=millert", + "SUDO_UID=8036", + "SUDO_GID=20", + "A__z=\"*SHLVL" + ], + "runuid": 0, + "runuser": "root", + "submitcwd": "/home/test", + "submithost": "sudo.ws", + "submituser": "millert", + "ttyname": "/dev/console" +} diff --git a/lib/iolog/regress/iolog_json/test2.out.ok b/lib/iolog/regress/iolog_json/test2.out.ok new file mode 100644 index 0000000..8ad3689 --- /dev/null +++ b/lib/iolog/regress/iolog_json/test2.out.ok @@ -0,0 +1,34 @@ +{ + "timestamp": { + "seconds": 1584993067, + "nanoseconds": 880288287 + }, + "columns": 80, + "command": "/usr/bin/make", + "lines": 24, + "runargv": [ + "make", + "test" + ], + "runenv": [ + "LANG=en_US.UTF-8", + "PATH=/bin:/sbin:/usr/games:/usr/bin:/usr/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin", + "TERM=vt100", + "MAIL=/var/mail/root", + "LOGNAME=root", + "USER=root", + "HOME=/root", + "SHELL=/bin/ksh", + "SUDO_COMMAND=/usr/bin/make test", + "SUDO_USER=millert", + "SUDO_UID=8036", + "SUDO_GID=20", + "A__z=\"*SHLVL" + ], + "runuid": 0, + "runuser": "root", + "submitcwd": "/home/test", + "submithost": "sudo.ws", + "submituser": "millert", + "ttyname": "/dev/console" +} diff --git a/lib/iolog/regress/iolog_json/test3.in b/lib/iolog/regress/iolog_json/test3.in new file mode 100644 index 0000000..ea2df89 --- /dev/null +++ b/lib/iolog/regress/iolog_json/test3.in @@ -0,0 +1,22 @@ +{ + "true": false, + "false": true, + "number": 1234567890, + "null": null, + "string": "nonsense", + "scope": { + "a": "b", + "bah": null + }, + "array1": [ + "foo", + "bar", + [ + 123, + null, + false, + "fizz", + "buzz" + ] + ] +} diff --git a/lib/iolog/regress/iolog_mkpath/check_iolog_mkpath.c b/lib/iolog/regress/iolog_mkpath/check_iolog_mkpath.c new file mode 100644 index 0000000..bb13787 --- /dev/null +++ b/lib/iolog/regress/iolog_mkpath/check_iolog_mkpath.c @@ -0,0 +1,91 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2020 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define SUDO_ERROR_WRAP 0 + +#include "sudo_compat.h" +#include "sudo_util.h" +#include "sudo_fatal.h" +#include "sudo_iolog.h" + +sudo_dso_public int main(int argc, char *argv[]); + +static const char *test_paths[] = { + "testdir/a/b/c/user", /* create new */ + "testdir/a/b/c/user", /* open existing */ + "testdir/a/b/c/user.XXXXXX", /* mkdtemp new */ + NULL +}; + +static void +test_iolog_mkpath(const char *testdir, int *ntests, int *nerrors) +{ + const char **tp; + char *path; + + iolog_set_owner(geteuid(), getegid()); + + for (tp = test_paths; *tp != NULL; tp++) { + if (asprintf(&path, "%s/%s", testdir, *tp) == -1) + sudo_fatalx("unable to allocate memory"); + + (*ntests)++; + if (!iolog_mkpath(path)) { + sudo_warnx("unable to mkpath %s", path); + (*nerrors)++; + } + free(path); + } +} + +int +main(int argc, char *argv[]) +{ + char testdir[] = "mkpath.XXXXXX"; + char *rmargs[] = { "rm", "-rf", NULL, NULL }; + int status, tests = 0, errors = 0; + + initprogname(argc > 0 ? argv[0] : "check_iolog_mkpath"); + + if (mkdtemp(testdir) == NULL) + sudo_fatal("unable to create test dir"); + rmargs[2] = testdir; + + test_iolog_mkpath(testdir, &tests, &errors); + + if (tests != 0) { + printf("iolog_mkpath: %d test%s run, %d errors, %d%% success rate\n", + tests, tests == 1 ? "" : "s", errors, + (tests - errors) * 100 / tests); + } + + /* Clean up (avoid running via shell) */ + execvp("rm", rmargs); + wait(&status); + + exit(errors); +} diff --git a/lib/iolog/regress/iolog_path/check_iolog_path.c b/lib/iolog/regress/iolog_path/check_iolog_path.c new file mode 100644 index 0000000..8598384 --- /dev/null +++ b/lib/iolog/regress/iolog_path/check_iolog_path.c @@ -0,0 +1,273 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2011-2013 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define SUDO_ERROR_WRAP 0 + +#include "sudo_compat.h" +#include "sudo_util.h" +#include "sudo_fatal.h" +#include "sudo_iolog.h" + +static struct iolog_escape_data { + char sessid[7]; + char *user; + char *group; + char *runas_user; + char *runas_group; + char *host; + char *command; +} escape_data; + +sudo_dso_public int main(int argc, char *argv[]); + +static void +usage(void) +{ + fprintf(stderr, "usage: %s datafile\n", getprogname()); + exit(EXIT_FAILURE); +} + +static void +reset_escape_data(struct iolog_escape_data *data) +{ + free(data->user); + free(data->group); + free(data->runas_user); + free(data->runas_group); + free(data->host); + free(data->command); + memset(data, 0, sizeof(*data)); +} + +static size_t +fill_seq(char *str, size_t strsize, void *unused) +{ + int len; + + /* Path is of the form /var/log/sudo-io/00/00/01. */ + len = snprintf(str, strsize, "%c%c/%c%c/%c%c", escape_data.sessid[0], + escape_data.sessid[1], escape_data.sessid[2], escape_data.sessid[3], + escape_data.sessid[4], escape_data.sessid[5]); + if (len < 0) + return strsize; /* handle non-standard snprintf() */ + return len; +} + +static size_t +fill_user(char *str, size_t strsize, void *unused) +{ + return strlcpy(str, escape_data.user, strsize); +} + +static size_t +fill_group(char *str, size_t strsize, void *unused) +{ + return strlcpy(str, escape_data.group, strsize); +} + +static size_t +fill_runas_user(char *str, size_t strsize, void *unused) +{ + return strlcpy(str, escape_data.runas_user, strsize); +} + +static size_t +fill_runas_group(char *str, size_t strsize, void *unused) +{ + return strlcpy(str, escape_data.runas_group, strsize); +} + +static size_t +fill_hostname(char *str, size_t strsize, void *unused) +{ + return strlcpy(str, escape_data.host, strsize); +} + +static size_t +fill_command(char *str, size_t strsize, void *unused) +{ + return strlcpy(str, escape_data.command, strsize); +} + +/* Note: "seq" must be first in the list. */ +static struct iolog_path_escape path_escapes[] = { + { "seq", fill_seq }, + { "user", fill_user }, + { "group", fill_group }, + { "runas_user", fill_runas_user }, + { "runas_group", fill_runas_group }, + { "hostname", fill_hostname }, + { "command", fill_command }, + { NULL, NULL } +}; + +static int +do_check(char *dir_in, char *file_in, char *tdir_out, char *tfile_out) +{ + char dir[PATH_MAX], dir_out[PATH_MAX]; + char file[PATH_MAX], file_out[PATH_MAX]; + struct tm *timeptr; + time_t now; + int error = 0; + + /* + * Expand any strftime(3) escapes + * XXX - want to pass timeptr to expand_iolog_path + */ + time(&now); + timeptr = localtime(&now); + if (timeptr == NULL) + sudo_fatalx("localtime returned NULL"); + strftime(dir_out, sizeof(dir_out), tdir_out, timeptr); + strftime(file_out, sizeof(file_out), tfile_out, timeptr); + + if (!expand_iolog_path(dir_in, dir, sizeof(dir), &path_escapes[1], NULL)) + sudo_fatalx("unable to expand I/O log dir"); + if (!expand_iolog_path(file_in, file, sizeof(file), &path_escapes[0], dir)) + sudo_fatalx("unable to expand I/O log file"); + + if (strcmp(dir, dir_out) != 0) { + sudo_warnx("%s: expected %s, got %s", dir_in, dir_out, dir); + error = 1; + } + if (strcmp(file, file_out) != 0) { + sudo_warnx("%s: expected %s, got %s", file_in, file_out, file); + error = 1; + } + + return error; +} + +#define MAX_STATE 12 + +int +main(int argc, char *argv[]) +{ + size_t len; + FILE *fp; + char line[2048]; + char *file_in = NULL, *file_out = NULL; + char *dir_in = NULL, *dir_out = NULL; + int state = 0; + int errors = 0; + int tests = 0; + + initprogname(argc > 0 ? argv[0] : "check_iolog_path"); + + if (argc != 2) + usage(); + + fp = fopen(argv[1], "r"); + if (fp == NULL) + sudo_fatalx("unable to open %s", argv[1]); + + /* + * Input consists of 12 lines: + * sequence number + * user name + * user gid + * runas user name + * runas gid + * hostname [short form] + * command + * dir [with escapes] + * file [with escapes] + * expanded dir + * expanded file + * empty line + */ + while (fgets(line, sizeof(line), fp) != NULL) { + len = strcspn(line, "\n"); + line[len] = '\0'; + + switch (state) { + case 0: + strlcpy(escape_data.sessid, line, sizeof(escape_data.sessid)); + break; + case 1: + if ((escape_data.user = strdup(line)) == NULL) + sudo_fatal(NULL); + break; + case 2: + if ((escape_data.group = strdup(line)) == NULL) + sudo_fatal(NULL); + break; + case 3: + if ((escape_data.runas_user = strdup(line)) == NULL) + sudo_fatal(NULL); + break; + case 4: + if ((escape_data.runas_group = strdup(line)) == NULL) + sudo_fatal(NULL); + break; + case 5: + if ((escape_data.host = strdup(line)) == NULL) + sudo_fatal(NULL); + break; + case 6: + if ((escape_data.command = strdup(line)) == NULL) + sudo_fatal(NULL); + break; + case 7: + if (dir_in != NULL) + free(dir_in); + dir_in = strdup(line); + break; + case 8: + if (file_in != NULL) + free(file_in); + file_in = strdup(line); + break; + case 9: + if (dir_out != NULL) + free(dir_out); + dir_out = strdup(line); + break; + case 10: + if (file_out != NULL) + free(file_out); + file_out = strdup(line); + break; + case 11: + errors += do_check(dir_in, file_in, dir_out, file_out); + tests++; + reset_escape_data(&escape_data); + break; + default: + sudo_fatalx("internal error, invalid state %d", state); + } + state = (state + 1) % MAX_STATE; + } + + if (tests != 0) { + printf("iolog_path: %d test%s run, %d errors, %d%% success rate\n", + tests, tests == 1 ? "" : "s", errors, + (tests - errors) * 100 / tests); + } + + exit(errors); +} diff --git a/lib/iolog/regress/iolog_path/data b/lib/iolog/regress/iolog_path/data new file mode 100644 index 0000000..48dc87e --- /dev/null +++ b/lib/iolog/regress/iolog_path/data @@ -0,0 +1,96 @@ +000001 +nobody +nogroup +root +root +somehost +id +/var/log/sudo-io +%%{bogus} +/var/log/sudo-io +%%{bogus} + +000001 +nobody +nogroup +root +wheel +somehost +id +/var/log/sudo-io +%%{seq} +/var/log/sudo-io +%%{seq} + +000001 +nobody +nogroup +root +wheel +somehost +id +/var/log/sudo-io +%{seq} +/var/log/sudo-io +00/00/01 + +000001 +nobody +nogroup +root +wheel +somehost +id +/var/log/sudo-io/%{user} +%{seq} +/var/log/sudo-io/nobody +00/00/01 + +000001 +nobody +nogroup +root +wheel +somehost +su +/var/log/sudo-io/%{user}/%{runas_user} +%{command}_%Y%m%s_%H%M +/var/log/sudo-io/nobody/root +su_%Y%m%s_%H%M + +000001 +nobody +nogroup +root +wheel +somehost +su +/var/log/sudo-io/ +//%{user}/%{runas_user}/%{command}_%Y%m%s_%H%M +/var/log/sudo-io +/nobody/root/su_%Y%m%s_%H%M + +000001 +nobody +nogroup +root +wheel +somehost +su +/var/log/sudo-io/%d%m%Y +%{user}/%{runas_user}/%{command} +/var/log/sudo-io/%d%m%Y +nobody/root/su + +000001 +nobody +nogroup +root +wheel +somehost +su +//////// +%{user}/%{runas_user}/%{command} + +nobody/root/su + diff --git a/lib/iolog/regress/iolog_util/check_iolog_util.c b/lib/iolog/regress/iolog_util/check_iolog_util.c new file mode 100644 index 0000000..de94ee1 --- /dev/null +++ b/lib/iolog/regress/iolog_util/check_iolog_util.c @@ -0,0 +1,148 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2018 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include + +#define SUDO_ERROR_WRAP 0 + +#include "sudo_compat.h" +#include "sudo_util.h" +#include "sudo_fatal.h" +#include "sudo_iolog.h" + +sudo_dso_public int main(int argc, char *argv[]); + +static struct parse_delay_test { + const char *input; + const char *next_field; + struct timespec expected_delay; +} parse_delay_tests[] = { + { "10.99999999999 X", "X", { 10, 999999999 } }, /* clamp to nsec */ + { "10.999999999 X", "X", { 10, 999999999 } }, /* nsec */ + { "10.999999 X", "X", { 10, 999999000 } }, /* usec -> nsec */ + { "10.000999999 X", "X", { 10, 999999 } }, + { "10.9 X", "X", { 10, 900000000 } }, + { "10.0 X", "X", { 10, 0 } } +}; + +/* + * Test iolog_parse_delay() + */ +void +test_parse_delay(int *ntests, int *nerrors) +{ + unsigned int i; + + for (i = 0; i < nitems(parse_delay_tests); i++) { + struct timespec delay; + struct parse_delay_test *test = &parse_delay_tests[i]; + char *cp = iolog_parse_delay(test->input, &delay, "."); + if (cp == NULL) { + sudo_warnx("%s:%u failed to parse delay: %s", __func__, + i, test->input); + (*nerrors)++; + continue; + } + if (strcmp(cp, test->next_field) != 0) { + sudo_warnx("%s:%u next field (want \"%s\", got \"%s\"", __func__, + i, test->next_field, cp); + (*nerrors)++; + continue; + } + if (delay.tv_sec != test->expected_delay.tv_sec) { + sudo_warnx("%s:%u wrong seconds (want %lld, got %lld)", __func__, + i, (long long)test->expected_delay.tv_sec, + (long long)delay.tv_sec); + (*nerrors)++; + continue; + } + if (delay.tv_nsec != test->expected_delay.tv_nsec) { + sudo_warnx("%s:%u wrong nanoseconds (want %ld, got %ld)", __func__, + i, test->expected_delay.tv_nsec, delay.tv_nsec); + (*nerrors)++; + continue; + } + } + (*ntests) += i; +} + +static struct adjust_delay_test { + struct timespec in_delay; + struct timespec out_delay; + struct timespec max_delay; + double scale_factor; +} adjust_delay_tests[] = { + { { 10, 300 }, { 10, 300 }, { 0, 0 }, 1.0 }, + { { 10, 300 }, { 5, 150 }, { 0, 0 }, 2.0 }, + { { 5, 300 }, { 2, 500000150 }, { 0, 0 }, 2.0 }, + { { 0, 1000000 }, { 0, 333333 }, { 0, 0 }, 3 }, + { { 10, 1000000 }, { 3, 333666666 }, { 0, 0 }, 3 }, + { { 5, 150 }, { 10, 300 }, { 0, 0 }, 0.5 }, + { { 5, 500000000 }, { 11, 0 }, { 0, 0 }, 0.5 }, + { { 5, 150 }, { 5, 0 }, { 5, 0 }, 0.5 } +}; + +/* + * Test iolog_adjust_delay() + */ +void +test_adjust_delay(int *ntests, int *nerrors) +{ + unsigned int i; + + for (i = 0; i < nitems(adjust_delay_tests); i++) { + struct adjust_delay_test *test = &adjust_delay_tests[i]; + + iolog_adjust_delay(&test->in_delay, + sudo_timespecisset(&test->max_delay) ? &test->max_delay : NULL, + test->scale_factor); + if (!sudo_timespeccmp(&test->in_delay, &test->out_delay, ==)) { + sudo_warnx("%s:%u want {%lld, %ld}, got {%lld, %ld}", __func__, i, + (long long)test->out_delay.tv_sec, test->out_delay.tv_nsec, + (long long)test->in_delay.tv_sec, test->in_delay.tv_nsec); + (*nerrors)++; + } + } + (*ntests) += i; +} + +int +main(int argc, char *argv[]) +{ + int tests = 0, errors = 0; + + initprogname(argc > 0 ? argv[0] : "check_iolog_util"); + + test_parse_delay(&tests, &errors); + + test_adjust_delay(&tests, &errors); + + if (tests != 0) { + printf("iolog_util: %d test%s run, %d errors, %d%% success rate\n", + tests, tests == 1 ? "" : "s", errors, + (tests - errors) * 100 / tests); + } + + exit(errors); +} -- cgit v1.2.3