minix/commands/pkg_install/admin/audit.c

508 lines
12 KiB
C

/* $NetBSD: audit.c,v 1.16 2010/06/16 23:02:48 joerg Exp $ */
#if HAVE_CONFIG_H
#include "config.h"
#endif
#ifndef __minix
#include <nbcompat.h>
#endif
#if HAVE_SYS_CDEFS_H
#include <sys/cdefs.h>
#endif
#ifndef __minix
__RCSID("$NetBSD: audit.c,v 1.16 2010/06/16 23:02:48 joerg Exp $");
#endif
/*-
* Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>.
* 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.
* 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 COPYRIGHT HOLDERS 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
* COPYRIGHT HOLDERS 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.
*/
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if HAVE_ERR_H
#include <err.h>
#endif
#if HAVE_ERRNO_H
#include <errno.h>
#endif
#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if HAVE_SIGNAL_H
#include <signal.h>
#endif
#if HAVE_STDIO_H
#include <stdio.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#endif
#if HAVE_TIME_H
#include <time.h>
#endif
#if defined(NETBSD) || defined(__minix)
#include <unistd.h>
#else
#include <nbcompat/unistd.h>
#endif
#include <fetch.h>
#include "admin.h"
#include "lib.h"
static int check_signature = 0;
static const char *limit_vul_types = NULL;
static int update_pkg_vuln = 0;
static struct pkg_vulnerabilities *pv;
static const char audit_options[] = "est:";
static void
parse_options(int argc, char **argv, const char *options)
{
int ch;
optreset = 1;
/*
* optind == 0 is interpreted as partial reset request
* by GNU getopt, so compensate against this and cleanup
* at the end.
*/
optind = 1;
++argc;
--argv;
while ((ch = getopt(argc, argv, options)) != -1) {
switch (ch) {
case 'e':
check_eol = "yes";
break;
case 's':
check_signature = 1;
break;
case 't':
limit_vul_types = optarg;
break;
case 'u':
update_pkg_vuln = 1;
break;
default:
usage();
/* NOTREACHED */
}
}
--optind; /* See above comment. */
}
static int
check_exact_pkg(const char *pkg)
{
return audit_package(pv, pkg, limit_vul_types, quiet ? 0 : 1);
}
static int
check_batch_exact_pkgs(const char *fname)
{
FILE *f;
char buf[4096], *line, *eol;
int ret;
ret = 0;
if (strcmp(fname, "-") == 0)
f = stdin;
else {
f = fopen(fname, "r");
if (f == NULL)
err(EXIT_FAILURE, "Failed to open input file %s",
fname);
}
while ((line = fgets(buf, sizeof(buf), f)) != NULL) {
eol = line + strlen(line);
if (eol == line)
continue;
--eol;
if (*eol == '\n') {
if (eol == line)
continue;
*eol = '\0';
}
ret |= check_exact_pkg(line);
}
if (f != stdin)
fclose(f);
return ret;
}
static int
check_one_installed_pkg(const char *pkg, void *cookie)
{
int *ret = cookie;
*ret |= check_exact_pkg(pkg);
return 0;
}
static int
check_installed_pattern(const char *pattern)
{
int ret = 0;
match_installed_pkgs(pattern, check_one_installed_pkg, &ret);
return ret;
}
static void
check_and_read_pkg_vulnerabilities(void)
{
struct stat st;
time_t now;
if (pkg_vulnerabilities_file == NULL)
errx(EXIT_FAILURE, "PKG_VULNERABILITIES is not set");
if (verbose >= 1) {
if (stat(pkg_vulnerabilities_file, &st) == -1) {
if (errno == ENOENT)
errx(EXIT_FAILURE,
"pkg-vulnerabilities not found, run %s -d",
getprogname());
errx(EXIT_FAILURE, "pkg-vulnerabilities not readable");
}
now = time(NULL);
now -= st.st_mtime;
if (now < 0)
warnx("pkg-vulnerabilities is from the future");
else if (now > 86400 * 7)
warnx("pkg-vulnerabilities is out of date (%ld days old)",
(long)(now / 86400));
else if (verbose >= 2)
warnx("pkg-vulnerabilities is %ld day%s old",
(long)(now / 86400), now / 86400 == 1 ? "" : "s");
}
pv = read_pkg_vulnerabilities_file(pkg_vulnerabilities_file, 0, check_signature);
}
void
audit_pkgdb(int argc, char **argv)
{
int rv;
parse_options(argc, argv, audit_options);
argv += optind;
check_and_read_pkg_vulnerabilities();
rv = 0;
if (*argv == NULL)
rv |= check_installed_pattern("*");
else {
for (; *argv != NULL; ++argv)
rv |= check_installed_pattern(*argv);
}
free_pkg_vulnerabilities(pv);
if (rv == 0 && verbose >= 1)
fputs("No vulnerabilities found\n", stderr);
exit(rv ? EXIT_FAILURE : EXIT_SUCCESS);
}
void
audit_pkg(int argc, char **argv)
{
int rv;
parse_options(argc, argv, audit_options);
argv += optind;
check_and_read_pkg_vulnerabilities();
rv = 0;
for (; *argv != NULL; ++argv)
rv |= check_exact_pkg(*argv);
free_pkg_vulnerabilities(pv);
if (rv == 0 && verbose >= 1)
fputs("No vulnerabilities found\n", stderr);
exit(rv ? EXIT_FAILURE : EXIT_SUCCESS);
}
void
audit_batch(int argc, char **argv)
{
int rv;
parse_options(argc, argv, audit_options);
argv += optind;
check_and_read_pkg_vulnerabilities();
rv = 0;
for (; *argv != NULL; ++argv)
rv |= check_batch_exact_pkgs(*argv);
free_pkg_vulnerabilities(pv);
if (rv == 0 && verbose >= 1)
fputs("No vulnerabilities found\n", stderr);
exit(rv ? EXIT_FAILURE : EXIT_SUCCESS);
}
void
check_pkg_vulnerabilities(int argc, char **argv)
{
parse_options(argc, argv, "s");
if (argc != optind + 1)
usage();
pv = read_pkg_vulnerabilities_file(argv[optind], 0, check_signature);
free_pkg_vulnerabilities(pv);
}
void
fetch_pkg_vulnerabilities(int argc, char **argv)
{
struct pkg_vulnerabilities *pv_check;
char *buf;
size_t buf_len, buf_fetched;
ssize_t cur_fetched;
struct url *url;
struct url_stat st;
fetchIO *f;
int fd;
struct stat sb;
char my_flags[20];
const char *flags;
parse_options(argc, argv, "su");
if (argc != optind)
usage();
if (verbose >= 2)
fprintf(stderr, "Fetching %s\n", pkg_vulnerabilities_url);
url = fetchParseURL(pkg_vulnerabilities_url);
if (url == NULL)
errx(EXIT_FAILURE,
"Could not parse location of pkg_vulnerabilities: %s",
fetchLastErrString);
flags = fetch_flags;
if (update_pkg_vuln) {
fd = open(pkg_vulnerabilities_file, O_RDONLY);
if (fd != -1 && fstat(fd, &sb) != -1) {
url->last_modified = sb.st_mtime;
snprintf(my_flags, sizeof(my_flags), "%si",
fetch_flags);
flags = my_flags;
} else
update_pkg_vuln = 0;
if (fd != -1)
close(fd);
}
f = fetchXGet(url, &st, flags);
if (f == NULL && update_pkg_vuln &&
fetchLastErrCode == FETCH_UNCHANGED) {
if (verbose >= 1)
fprintf(stderr, "%s is not newer\n",
pkg_vulnerabilities_url);
exit(EXIT_SUCCESS);
}
if (f == NULL)
errx(EXIT_FAILURE, "Could not fetch vulnerability file: %s",
fetchLastErrString);
/*
if (st.size > SSIZE_MAX - 1)
errx(EXIT_FAILURE, "pkg-vulnerabilities is too large");
*/
buf_len = st.size;
buf = xmalloc(buf_len + 1);
buf_fetched = 0;
while (buf_fetched < buf_len) {
cur_fetched = fetchIO_read(f, buf + buf_fetched,
buf_len - buf_fetched);
if (cur_fetched == 0)
errx(EXIT_FAILURE,
"Truncated pkg-vulnerabilities received");
else if (cur_fetched == -1)
errx(EXIT_FAILURE,
"IO error while fetching pkg-vulnerabilities: %s",
fetchLastErrString);
buf_fetched += cur_fetched;
}
buf[buf_len] = '\0';
pv_check = read_pkg_vulnerabilities_memory(buf, buf_len, check_signature);
free_pkg_vulnerabilities(pv_check);
fd = open(pkg_vulnerabilities_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1)
err(EXIT_FAILURE, "Cannot create pkg-vulnerability file %s",
pkg_vulnerabilities_file);
if (write(fd, buf, buf_len) != (ssize_t)buf_len)
err(EXIT_FAILURE, "Cannot write pkg-vulnerability file");
if (close(fd) == -1)
err(EXIT_FAILURE, "Cannot close pkg-vulnerability file after write");
free(buf);
exit(EXIT_SUCCESS);
}
static int
check_pkg_history_pattern(const char *pkg, const char *pattern)
{
const char *delim, *end_base;
if ((delim = strchr(pattern, '*')) != NULL) {
if ((end_base = strrchr(pattern, '-')) == NULL)
errx(EXIT_FAILURE, "Missing - in wildcard pattern %s",
pattern);
if ((delim = strchr(pattern, '>')) != NULL ||
(delim = strchr(pattern, '<')) != NULL)
errx(EXIT_FAILURE,
"Mixed relational and wildcard patterns in %s",
pattern);
} else if ((delim = strchr(pattern, '>')) != NULL) {
end_base = delim;
if ((delim = strchr(pattern, '<')) != NULL && delim < end_base)
errx(EXIT_FAILURE, "Inverted operators in %s",
pattern);
} else if ((delim = strchr(pattern, '<')) != NULL) {
end_base = delim;
} else if ((end_base = strrchr(pattern, '-')) == NULL) {
errx(EXIT_FAILURE, "Missing - in absolute pattern %s",
pattern);
}
if (strncmp(pkg, pattern, end_base - pattern) != 0)
return 0;
if (pkg[end_base - pattern] != '\0')
return 0;
return 1;
}
static int
check_pkg_history1(const char *pkg, const char *pattern)
{
const char *open_brace, *close_brace, *inner_brace, *suffix, *iter;
size_t prefix_len, suffix_len, middle_len;
char *expanded_pkg;
open_brace = strchr(pattern, '{');
if (open_brace == NULL) {
if ((close_brace = strchr(pattern, '}')) != NULL)
errx(EXIT_FAILURE, "Unbalanced {} in pattern %s",
pattern);
return check_pkg_history_pattern(pkg, pattern);
}
close_brace = strchr(open_brace, '}');
if (strchr(pattern, '}') != close_brace)
errx(EXIT_FAILURE, "Unbalanced {} in pattern %s",
pattern);
while ((inner_brace = strchr(open_brace + 1, '{')) != NULL) {
if (inner_brace >= close_brace)
break;
open_brace = inner_brace;
}
expanded_pkg = xmalloc(strlen(pattern)); /* {} are going away... */
prefix_len = open_brace - pattern;
suffix = close_brace + 1;
suffix_len = strlen(suffix) + 1;
memcpy(expanded_pkg, pattern, prefix_len);
++open_brace;
do {
iter = strchr(open_brace, ',');
if (iter == NULL || iter > close_brace)
iter = close_brace;
middle_len = iter - open_brace;
memcpy(expanded_pkg + prefix_len, open_brace, middle_len);
memcpy(expanded_pkg + prefix_len + middle_len, suffix,
suffix_len);
if (check_pkg_history1(pkg, expanded_pkg)) {
free(expanded_pkg);
return 1;
}
open_brace = iter + 1;
} while (iter < close_brace);
free(expanded_pkg);
return 0;
}
static void
check_pkg_history(const char *pkg)
{
size_t i;
for (i = 0; i < pv->entries; ++i) {
if (!quick_pkg_match(pv->vulnerability[i], pkg))
continue;
if (strcmp("eol", pv->classification[i]) == 0)
continue;
if (check_pkg_history1(pkg, pv->vulnerability[i]) == 0)
continue;
printf("%s %s %s\n", pv->vulnerability[i],
pv->classification[i], pv->advisory[i]);
}
}
void
audit_history(int argc, char **argv)
{
parse_options(argc, argv, "st:");
argv += optind;
check_and_read_pkg_vulnerabilities();
for (; *argv != NULL; ++argv)
check_pkg_history(*argv);
free_pkg_vulnerabilities(pv);
exit(EXIT_SUCCESS);
}