Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

440 lines (386 sloc) 9.388 kb
/*
* Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.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
* 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(S) ``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(S) 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.
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/stat.h>
#define _WITH_GETLINE
#include <archive.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sysexits.h>
#include <pkg.h>
#include "pkgcli.h"
#define EQ 1
#define LT 2
#define LTE 3
#define GT 4
#define GTE 5
struct version_entry {
char *version;
int type;
};
struct audit_entry {
char *pkgname;
struct version_entry v1;
struct version_entry v2;
char *url;
char *desc;
SLIST_ENTRY(audit_entry) next;
};
SLIST_HEAD(audit_head, audit_entry);
void
usage_audit(void)
{
fprintf(stderr, "usage: pkg audit [-F] <pattern>\n\n");
fprintf(stderr, "For more information see 'pkg help add'.\n");
}
static int
fetch_and_extract(const char *src, const char *dest)
{
struct archive *a = NULL;
struct archive_entry *ae = NULL;
int fd = -1;
char tmp[MAXPATHLEN];
const char *tmpdir;
int retcode = EPKG_FATAL;
int ret;
time_t t = 0;
struct stat st;
tmpdir = getenv("TMPDIR");
if (tmpdir == NULL)
tmpdir = "/tmp";
strlcpy(tmp, tmpdir, sizeof(tmp));
strlcat(tmp, "/auditfile.tbz", sizeof(tmp));
if (stat(dest, &st) != -1) {
t = st.st_mtime;
}
switch (pkg_fetch_file(src, tmp, t)) {
case EPKG_OK:
break;
case EPKG_UPTODATE:
printf("Audit file up-to-date.\n");
retcode = EPKG_OK;
goto cleanup;
default:
warnx("Cannot fetch audit file!");
goto cleanup;
}
a = archive_read_new();
archive_read_support_compression_all(a);
archive_read_support_format_tar(a);
if (archive_read_open_filename(a, tmp, 4096) != ARCHIVE_OK) {
warnx("archive_read_open_filename(%s): %s",
tmp, archive_error_string(a));
goto cleanup;
}
while ((ret = archive_read_next_header(a, &ae)) == ARCHIVE_OK) {
fd = open(dest, O_RDWR|O_CREAT|O_TRUNC,
S_IRUSR|S_IRGRP|S_IROTH);
if (fd < 0) {
warn("open(%s)", dest);
goto cleanup;
}
if (archive_read_data_into_fd(a, fd) != ARCHIVE_OK) {
warnx("archive_read_data_into_fd(%s): %s",
dest, archive_error_string(a));
goto cleanup;
}
}
retcode = EPKG_OK;
cleanup:
unlink(tmp);
if (a != NULL)
archive_read_finish(a);
if (fd > 0)
close(fd);
return (retcode);
}
/* Fuuuu */
static void
parse_pattern(struct audit_entry *e, char *pattern, size_t len)
{
size_t i;
char *start = pattern;
char *end;
char **dest = &e->pkgname;
char **next_dest = NULL;
struct version_entry *v = &e->v1;
int skipnext;
int type;
for (i = 0; i < len; i++) {
type = 0;
skipnext = 0;
if (pattern[i] == '=') {
type = EQ;
}
if (pattern[i] == '<') {
if (pattern[i+1] == '=') {
skipnext = 1;
type = LTE;
} else {
type = LT;
}
}
if (pattern[i] == '>') {
if (pattern[i+1] == '=') {
skipnext = 1;
type = GTE;
} else {
type = GT;
}
}
if (type != 0) {
v->type = type;
next_dest = &v->version;
v = &e->v2;
}
if (next_dest != NULL || i == len - 1) {
end = pattern + i;
*dest = strndup(start, end - start);
i += skipnext;
start = pattern + i + 1;
dest = next_dest;
next_dest = NULL;
}
}
}
static int
parse_db(const char *path, struct audit_head *h)
{
struct audit_entry *e;
FILE *fp;
size_t linecap = 0;
ssize_t linelen;
char *line = NULL;
char *column;
uint8_t column_id;
if ((fp = fopen(path, "r")) == NULL)
return (EPKG_FATAL);
while ((linelen = getline(&line, &linecap, fp)) > 0) {
column_id = 0;
if (line[0] == '#')
continue;
if ((e = calloc(1, sizeof(struct audit_entry))) == NULL)
err(1, "calloc(audit_entry)");
while ((column = strsep(&line, "|")) != NULL)
{
switch (column_id) {
case 0:
parse_pattern(e, column, linelen);
break;
case 1:
e->url = strdup(column);
break;
case 2:
e->desc = strdup(column);
break;
default:
warn("extra column in audit file");
}
column_id++;
}
SLIST_INSERT_HEAD(h, e, next);
}
return EPKG_OK;
}
static bool
match_version(const char *pkgversion, struct version_entry *v)
{
bool res = false;
/*
* Return true so it is easier for the caller to handle case where there is
* only one version to match: the missing one will always match.
*/
if (v->version == NULL)
return true;
switch (pkg_version_cmp(pkgversion, v->version)) {
case -1:
if (v->type == LT || v->type == LTE)
res = true;
break;
case 0:
if (v->type == EQ || v->type == LTE || v->type == GTE)
res = true;
break;
case 1:
if (v->type == GT || v->type == GTE)
res = true;
break;
}
return res;
}
static bool
is_vulnerable(struct audit_head *h, struct pkg *pkg)
{
struct audit_entry *e;
const char *pkgname;
const char *pkgversion;
bool res = false, res1, res2;
pkg_get(pkg,
PKG_NAME, &pkgname,
PKG_VERSION, &pkgversion
);
SLIST_FOREACH(e, h, next) {
if (fnmatch(e->pkgname, pkgname, 0) != 0)
continue;
res1 = match_version(pkgversion, &e->v1);
res2 = match_version(pkgversion, &e->v2);
if (res1 && res2) {
res = true;
printf("%s-%s is vulnerable:\n", pkgname, pkgversion);
printf("%s\n", e->desc);
printf("WWW: %s\n\n", e->url);
}
}
return res;
}
static void
free_audit_list(struct audit_head *h)
{
struct audit_entry *e;
while (!SLIST_EMPTY(h)) {
e = SLIST_FIRST(h);
SLIST_REMOVE_HEAD(h, next);
free(e->v1.version);
free(e->v2.version);
free(e->url);
free(e->desc);
}
}
int
exec_audit(int argc, char **argv)
{
struct audit_head h = SLIST_HEAD_INITIALIZER();
struct pkgdb *db = NULL;
struct pkgdb_it *it = NULL;
struct pkg *pkg = NULL;
const char *db_dir;
char *name;
char *version;
char audit_file[MAXPATHLEN + 1];
unsigned int vuln = 0;
bool fetch = false;
int ch;
int ret = EX_OK;
const char *portaudit_site = NULL;
if (pkg_config_string(PKG_CONFIG_DBDIR, &db_dir) != EPKG_OK) {
warnx("PKG_DBIR is missing");
return (EX_CONFIG);
}
snprintf(audit_file, sizeof(audit_file), "%s/auditfile", db_dir);
while ((ch = getopt(argc, argv, "qF")) != -1) {
switch (ch) {
case 'q':
quiet = true;
break;
case 'F':
fetch = true;
break;
default:
usage_audit();
return(EX_USAGE);
}
}
argc -= optind;
argv += optind;
if (fetch == true) {
if (pkg_config_string(PKG_CONFIG_PORTAUDIT_SITE, &portaudit_site) != EPKG_OK) {
return (EPKG_FATAL);
}
if (fetch_and_extract(portaudit_site, audit_file) != EPKG_OK) {
return (EX_IOERR);
}
}
if (argc > 2) {
usage_audit();
return (EX_USAGE);
}
if (argc == 1) {
name = argv[0];
version = strrchr(name, '-');
if (version == NULL)
err(EX_USAGE, "bad package name format: %s", name);
version[0] = '\0';
version++;
pkg_new(&pkg, PKG_FILE);
pkg_set(pkg,
PKG_NAME, name,
PKG_VERSION, version);
if (parse_db(audit_file, &h) != EPKG_OK) {
if (errno == ENOENT)
warnx("unable to open audit file, try running 'pkg audit -F' first");
else
warn("unable to open audit file %s", audit_file);
ret = EX_DATAERR;
goto cleanup;
}
is_vulnerable(&h, pkg);
goto cleanup;
}
if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
/*
* if the database doesn't exist a normal user can't create it
* it just means there is no package
*/
if (geteuid() == 0)
return (EX_IOERR);
return (EX_OK);
}
if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL)
{
warnx("cannot query local database");
ret = EX_IOERR;
goto cleanup;
}
if (parse_db(audit_file, &h) != EPKG_OK) {
if (errno == ENOENT)
warnx("unable to open audit file, try running 'pkg audit -F' first");
else
warn("unable to open audit file %s", audit_file);
ret = EX_DATAERR;
goto cleanup;
}
while ((ret = pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC)) == EPKG_OK) {
if (is_vulnerable(&h, pkg)) {
vuln++;
}
}
if (ret == EPKG_END && vuln == 0)
ret = EX_OK;
printf("%u problem(s) in your installed packages found.\n", vuln);
cleanup:
pkgdb_it_free(it);
pkgdb_close(db);
pkg_free(pkg);
free_audit_list(&h);
return (ret);
}
Jump to Line
Something went wrong with that request. Please try again.