Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F593776
small change on pkg
No One
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
mmokhi
Aug 10 2016, 6:20 PM
2016-08-10 18:20:41 (UTC+0)
Size
23 KB
Referenced Files
None
Subscribers
None
small change on pkg
View Options
diff --git a/libpkg/pkg_audit.c b/libpkg/pkg_audit.c
index bb3d513..749f1c0 100644
--- a/libpkg/pkg_audit.c
+++ b/libpkg/pkg_audit.c
@@ -1,911 +1,931 @@
/*-
* Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
* Copyright (c) 2014 Matthew Seaman <matthew@FreeBSD.org>
* Copyright (c) 2014 Vsevolod Stakhov <vsevolod@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/mman.h>
+#include <sys/types.h>
#include <archive.h>
#include <err.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <stdio.h>
#include <string.h>
#include <utlist.h>
+#include <unistd.h>
#include <expat.h>
#ifdef HAVE_SYS_CAPSICUM_H
#include <sys/capsicum.h>
#endif
#include "pkg.h"
#include "private/pkg.h"
#include "private/event.h"
#define EQ 1
#define LT 2
#define LTE 3
#define GT 4
#define GTE 5
static const char* vop_names[] = {
[0] = "",
[EQ] = "=",
[LT] = "<",
[LTE] = "<=",
[GT] = ">",
[GTE] = ">="
};
struct pkg_audit_version {
char *version;
int type;
};
struct pkg_audit_versions_range {
struct pkg_audit_version v1;
struct pkg_audit_version v2;
struct pkg_audit_versions_range *next;
};
struct pkg_audit_cve {
char *cvename;
struct pkg_audit_cve *next;
};
struct pkg_audit_pkgname {
char *pkgname;
struct pkg_audit_pkgname *next;
};
struct pkg_audit_package {
struct pkg_audit_pkgname *names;
struct pkg_audit_versions_range *versions;
struct pkg_audit_package *next;
};
struct pkg_audit_entry {
const char *pkgname;
struct pkg_audit_package *packages;
struct pkg_audit_pkgname *names;
struct pkg_audit_versions_range *versions;
struct pkg_audit_cve *cve;
char *url;
char *desc;
char *id;
bool ref;
struct pkg_audit_entry *next;
};
/*
* The _sorted stuff.
*
* We are using the optimized search based on the following observations:
*
* - number of VuXML entries is more likely to be far greater than
* the number of installed ports; thus we should try to optimize
* the walk through all entries for a given port;
*
* - fnmatch() is good and fast, but if we will compare the audit entry
* name prefix without globbing characters to the prefix of port name
* of the same length and they are different, there is no point to
* check the rest;
*
* - (most important bit): if parsed VuXML entries are lexicographically
* sorted per the largest prefix with no globbing characters and we
* know how many succeeding entries have the same prefix we can
*
* a. skip the rest of the entries once the non-globbing prefix is
* lexicographically larger than the port name prefix of the
* same length: all successive prefixes will be larger as well;
*
* b. if we have non-globbing prefix that is lexicographically smaller
* than port name prefix, we can skip all succeeding entries with
* the same prefix; and as some port names tend to repeat due to
* multiple vulnerabilities, it could be a large win.
*/
struct pkg_audit_item {
struct pkg_audit_entry *e; /* Entry itself */
size_t noglob_len; /* Prefix without glob characters */
size_t next_pfx_incr; /* Index increment for the entry with
different prefix */
};
struct pkg_audit {
struct pkg_audit_entry *entries;
struct pkg_audit_item *items;
bool parsed;
bool loaded;
void *map;
size_t len;
};
/*
* Another small optimization to skip the beginning of the
* VuXML entry array, if possible.
*
* audit_entry_first_byte_idx[ch] represents the index
* of the first VuXML entry in the sorted array that has
* its non-globbing prefix that is started with the character
* 'ch'. It allows to skip entries from the beginning of the
* VuXML array that aren't relevant for the checked port name.
*/
static size_t audit_entry_first_byte_idx[256];
static void
pkg_audit_free_entry(struct pkg_audit_entry *e)
{
struct pkg_audit_package *ppkg, *ppkg_tmp;
struct pkg_audit_versions_range *vers, *vers_tmp;
struct pkg_audit_cve *cve, *cve_tmp;
struct pkg_audit_pkgname *pname, *pname_tmp;
if (!e->ref) {
LL_FOREACH_SAFE(e->packages, ppkg, ppkg_tmp) {
LL_FOREACH_SAFE(ppkg->versions, vers, vers_tmp) {
free(vers->v1.version);
free(vers->v2.version);
free(vers);
}
LL_FOREACH_SAFE(ppkg->names, pname, pname_tmp) {
free(pname->pkgname);
free(pname);
}
}
LL_FOREACH_SAFE(e->cve, cve, cve_tmp) {
free(cve->cvename);
free(cve);
}
free(e->url);
free(e->desc);
free(e->id);
}
free(e);
}
static void
pkg_audit_free_list(struct pkg_audit_entry *h)
{
struct pkg_audit_entry *e;
while (h) {
e = h;
h = h->next;
pkg_audit_free_entry(e);
}
}
struct pkg_audit_extract_cbdata {
int out;
const char *fname;
const char *dest;
};
static int
pkg_audit_sandboxed_extract(int fd, void *ud)
{
struct pkg_audit_extract_cbdata *cbdata = ud;
int ret, rc = EPKG_OK;
struct archive *a = NULL;
struct archive_entry *ae = NULL;
a = archive_read_new();
#if ARCHIVE_VERSION_NUMBER < 3000002
archive_read_support_compression_all(a);
#else
archive_read_support_filter_all(a);
#endif
archive_read_support_format_raw(a);
if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) {
pkg_emit_error("archive_read_open_filename(%s) failed: %s",
cbdata->fname, archive_error_string(a));
rc = EPKG_FATAL;
}
else {
while ((ret = archive_read_next_header(a, &ae)) == ARCHIVE_OK) {
if (archive_read_data_into_fd(a, cbdata->out) != ARCHIVE_OK) {
pkg_emit_error("archive_read_data_into_fd(%s) failed: %s",
cbdata->dest, archive_error_string(a));
break;
}
}
archive_read_close(a);
archive_read_free(a);
}
return (rc);
}
int
pkg_audit_fetch(const char *src, const char *dest)
{
int fd = -1, outfd = -1;
char tmp[MAXPATHLEN];
const char *tmpdir;
int retcode = EPKG_FATAL;
time_t t = 0;
struct stat st;
struct pkg_audit_extract_cbdata cbdata;
tmpdir = getenv("TMPDIR");
if (tmpdir == NULL)
tmpdir = "/tmp";
strlcpy(tmp, tmpdir, sizeof(tmp));
strlcat(tmp, "/vuln.xml.bz2.XXXXXXXX", sizeof(tmp));
if (stat(dest, &st) != -1)
t = st.st_mtime;
switch (pkg_fetch_file_tmp(NULL, src, tmp, t)) {
case EPKG_OK:
break;
case EPKG_UPTODATE:
pkg_emit_notice("vulnxml file up-to-date");
retcode = EPKG_OK;
goto cleanup;
default:
pkg_emit_error("cannot fetch vulnxml file");
goto cleanup;
}
/* Open input fd */
fd = open(tmp, O_RDONLY);
if (fd == -1) {
retcode = EPKG_FATAL;
goto cleanup;
}
/* Open out fd */
outfd = open(dest, O_RDWR|O_CREAT|O_TRUNC,
S_IRUSR|S_IRGRP|S_IROTH);
if (outfd == -1) {
pkg_emit_errno("pkg_audit_fetch", "open out fd");
goto cleanup;
}
cbdata.fname = tmp;
cbdata.out = outfd;
cbdata.dest = dest;
/* Call sandboxed */
retcode = pkg_emit_sandbox_call(pkg_audit_sandboxed_extract, fd, &cbdata);
cleanup:
unlink(tmp);
if (fd != -1)
close(fd);
if (outfd != -1)
close(outfd);
return (retcode);
}
/*
* Expand multiple names to a set of audit entries
*/
static void
pkg_audit_expand_entry(struct pkg_audit_entry *entry, struct pkg_audit_entry **head)
{
struct pkg_audit_entry *n;
struct pkg_audit_pkgname *ncur;
struct pkg_audit_package *pcur;
/* Set the name of the current entry */
if (entry->packages == NULL || entry->packages->names == NULL) {
pkg_audit_free_entry(entry);
return;
}
LL_FOREACH(entry->packages, pcur) {
LL_FOREACH(pcur->names, ncur) {
n = calloc(1, sizeof(struct pkg_audit_entry));
if (n == NULL)
err(1, "calloc(audit_entry)");
n->pkgname = ncur->pkgname;
/* Set new entry as reference entry */
n->ref = true;
n->cve = entry->cve;
n->desc = entry->desc;
n->versions = pcur->versions;
n->url = entry->url;
n->id = entry->id;
LL_PREPEND(*head, n);
}
}
LL_PREPEND(*head, entry);
}
enum vulnxml_parse_state {
VULNXML_PARSE_INIT = 0,
VULNXML_PARSE_VULN,
VULNXML_PARSE_TOPIC,
VULNXML_PARSE_PACKAGE,
VULNXML_PARSE_PACKAGE_NAME,
VULNXML_PARSE_RANGE,
VULNXML_PARSE_RANGE_GT,
VULNXML_PARSE_RANGE_GE,
VULNXML_PARSE_RANGE_LT,
VULNXML_PARSE_RANGE_LE,
VULNXML_PARSE_RANGE_EQ,
VULNXML_PARSE_CVE
};
struct vulnxml_userdata {
struct pkg_audit_entry *cur_entry;
struct pkg_audit *audit;
enum vulnxml_parse_state state;
int range_num;
};
static void
vulnxml_start_element(void *data, const char *element, const char **attributes)
{
struct vulnxml_userdata *ud = (struct vulnxml_userdata *)data;
struct pkg_audit_versions_range *vers;
struct pkg_audit_pkgname *name_entry;
struct pkg_audit_package *pkg_entry;
int i;
if (ud->state == VULNXML_PARSE_INIT && strcasecmp(element, "vuln") == 0) {
ud->cur_entry = calloc(1, sizeof(struct pkg_audit_entry));
if (ud->cur_entry == NULL)
err(1, "calloc(audit_entry)");
for (i = 0; attributes[i]; i += 2) {
if (strcasecmp(attributes[i], "vid") == 0) {
ud->cur_entry->id = strdup(attributes[i + 1]);
break;
}
}
ud->cur_entry->next = ud->audit->entries;
ud->state = VULNXML_PARSE_VULN;
}
else if (ud->state == VULNXML_PARSE_VULN && strcasecmp(element, "topic") == 0) {
ud->state = VULNXML_PARSE_TOPIC;
}
else if (ud->state == VULNXML_PARSE_VULN && strcasecmp(element, "package") == 0) {
pkg_entry = calloc(1, sizeof(struct pkg_audit_package));
if (pkg_entry == NULL)
err(1, "calloc(audit_package_entry)");
LL_PREPEND(ud->cur_entry->packages, pkg_entry);
ud->state = VULNXML_PARSE_PACKAGE;
}
else if (ud->state == VULNXML_PARSE_VULN && strcasecmp(element, "cvename") == 0) {
ud->state = VULNXML_PARSE_CVE;
}
else if (ud->state == VULNXML_PARSE_PACKAGE && strcasecmp(element, "name") == 0) {
ud->state = VULNXML_PARSE_PACKAGE_NAME;
name_entry = calloc(1, sizeof(struct pkg_audit_pkgname));
if (name_entry == NULL)
err(1, "calloc(audit_pkgname_entry)");
LL_PREPEND(ud->cur_entry->packages->names, name_entry);
}
else if (ud->state == VULNXML_PARSE_PACKAGE && strcasecmp(element, "range") == 0) {
ud->state = VULNXML_PARSE_RANGE;
vers = calloc(1, sizeof(struct pkg_audit_versions_range));
if (vers == NULL)
err(1, "calloc(audit_versions)");
LL_PREPEND(ud->cur_entry->packages->versions, vers);
ud->range_num = 0;
}
else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "gt") == 0) {
ud->range_num ++;
ud->state = VULNXML_PARSE_RANGE_GT;
}
else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "ge") == 0) {
ud->range_num ++;
ud->state = VULNXML_PARSE_RANGE_GE;
}
else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "lt") == 0) {
ud->range_num ++;
ud->state = VULNXML_PARSE_RANGE_LT;
}
else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "le") == 0) {
ud->range_num ++;
ud->state = VULNXML_PARSE_RANGE_LE;
}
else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "eq") == 0) {
ud->range_num ++;
ud->state = VULNXML_PARSE_RANGE_EQ;
}
}
static void
vulnxml_end_element(void *data, const char *element)
{
struct vulnxml_userdata *ud = (struct vulnxml_userdata *)data;
if (ud->state == VULNXML_PARSE_VULN && strcasecmp(element, "vuln") == 0) {
pkg_audit_expand_entry(ud->cur_entry, &ud->audit->entries);
ud->state = VULNXML_PARSE_INIT;
}
else if (ud->state == VULNXML_PARSE_TOPIC && strcasecmp(element, "topic") == 0) {
ud->state = VULNXML_PARSE_VULN;
}
else if (ud->state == VULNXML_PARSE_CVE && strcasecmp(element, "cvename") == 0) {
ud->state = VULNXML_PARSE_VULN;
}
else if (ud->state == VULNXML_PARSE_PACKAGE && strcasecmp(element, "package") == 0) {
ud->state = VULNXML_PARSE_VULN;
}
else if (ud->state == VULNXML_PARSE_PACKAGE_NAME && strcasecmp(element, "name") == 0) {
ud->state = VULNXML_PARSE_PACKAGE;
}
else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "range") == 0) {
ud->state = VULNXML_PARSE_PACKAGE;
}
else if (ud->state == VULNXML_PARSE_RANGE_GT && strcasecmp(element, "gt") == 0) {
ud->state = VULNXML_PARSE_RANGE;
}
else if (ud->state == VULNXML_PARSE_RANGE_GE && strcasecmp(element, "ge") == 0) {
ud->state = VULNXML_PARSE_RANGE;
}
else if (ud->state == VULNXML_PARSE_RANGE_LT && strcasecmp(element, "lt") == 0) {
ud->state = VULNXML_PARSE_RANGE;
}
else if (ud->state == VULNXML_PARSE_RANGE_LE && strcasecmp(element, "le") == 0) {
ud->state = VULNXML_PARSE_RANGE;
}
else if (ud->state == VULNXML_PARSE_RANGE_EQ && strcasecmp(element, "eq") == 0) {
ud->state = VULNXML_PARSE_RANGE;
}
}
static void
vulnxml_handle_data(void *data, const char *content, int length)
{
struct vulnxml_userdata *ud = (struct vulnxml_userdata *)data;
struct pkg_audit_versions_range *vers;
struct pkg_audit_cve *cve;
struct pkg_audit_entry *entry;
int range_type = -1;
switch(ud->state) {
case VULNXML_PARSE_INIT:
case VULNXML_PARSE_VULN:
case VULNXML_PARSE_PACKAGE:
case VULNXML_PARSE_RANGE:
/* On these states we do not need any data */
break;
case VULNXML_PARSE_TOPIC:
ud->cur_entry->desc = strndup(content, length);
break;
case VULNXML_PARSE_PACKAGE_NAME:
ud->cur_entry->packages->names->pkgname = strndup(content, length);
break;
case VULNXML_PARSE_RANGE_GT:
range_type = GT;
break;
case VULNXML_PARSE_RANGE_GE:
range_type = GTE;
break;
case VULNXML_PARSE_RANGE_LT:
range_type = LT;
break;
case VULNXML_PARSE_RANGE_LE:
range_type = LTE;
break;
case VULNXML_PARSE_RANGE_EQ:
range_type = EQ;
break;
case VULNXML_PARSE_CVE:
entry = ud->cur_entry;
cve = malloc(sizeof(struct pkg_audit_cve));
cve->cvename = strndup(content, length);
LL_PREPEND(entry->cve, cve);
break;
}
if (range_type > 0) {
vers = ud->cur_entry->packages->versions;
if (ud->range_num == 1) {
vers->v1.version = strndup(content, length);
vers->v1.type = range_type;
}
else if (ud->range_num == 2) {
vers->v2.version = strndup(content, length);
vers->v2.type = range_type;
}
}
}
static int
pkg_audit_parse_vulnxml(struct pkg_audit *audit)
{
XML_Parser parser;
struct vulnxml_userdata ud;
+ uid_t uid, euid;
int ret = EPKG_OK;
+ uid = getuid();
+ euid = geteuid();
+
+ if (uid <= 0 || euid != uid) {
+ /*
+ * User is running pkg audit with root or is using sudo.
+ * Changing user temporarily to user nobody for safety.
+ */
+ if (seteuid(65534) != 0) {
+ warnx("%s\n", "Could not change euid to nobody.");
+ }
+ }
parser = XML_ParserCreate(NULL);
XML_SetElementHandler(parser, vulnxml_start_element, vulnxml_end_element);
XML_SetCharacterDataHandler(parser, vulnxml_handle_data);
XML_SetUserData(parser, &ud);
ud.cur_entry = NULL;
ud.audit = audit;
ud.range_num = 0;
ud.state = VULNXML_PARSE_INIT;
if (XML_Parse(parser, audit->map, audit->len, XML_TRUE) == XML_STATUS_ERROR) {
pkg_emit_error("vulnxml parsing error: %s",
XML_ErrorString(XML_GetErrorCode(parser)));
ret = EPKG_FATAL;
}
XML_ParserFree(parser);
+ /* Changing back user to what it was */
+ if (seteuid(uid) != 0) {
+ warnx("%s\n", "Could not change euid back to root.");
+ }
+
return (ret);
}
/*
* Returns the length of the largest prefix without globbing
* characters, as per fnmatch().
*/
static size_t
pkg_audit_str_noglob_len(const char *s)
{
size_t n;
for (n = 0; s[n] && s[n] != '*' && s[n] != '?' &&
s[n] != '[' && s[n] != '{' && s[n] != '\\'; n++);
return (n);
}
/*
* Helper for quicksort that lexicographically orders prefixes.
*/
static int
pkg_audit_entry_cmp(const void *a, const void *b)
{
const struct pkg_audit_item *e1, *e2;
size_t min_len;
int result;
e1 = (const struct pkg_audit_item *)a;
e2 = (const struct pkg_audit_item *)b;
min_len = (e1->noglob_len < e2->noglob_len ?
e1->noglob_len : e2->noglob_len);
result = strncmp(e1->e->pkgname, e2->e->pkgname, min_len);
/*
* Additional check to see if some word is a prefix of an
* another one and, thus, should go before the former.
*/
if (result == 0) {
if (e1->noglob_len < e2->noglob_len)
result = -1;
else if (e1->noglob_len > e2->noglob_len)
result = 1;
}
return (result);
}
/*
* Sorts VuXML entries and calculates increments to jump to the
* next distinct prefix.
*/
static struct pkg_audit_item *
pkg_audit_preprocess(struct pkg_audit_entry *h)
{
struct pkg_audit_entry *e;
struct pkg_audit_item *ret;
size_t i, n, tofill;
n = 0;
LL_FOREACH(h, e)
n++;
ret = (struct pkg_audit_item *)calloc(n + 1, sizeof(ret[0]));
if (ret == NULL)
err(1, "calloc(audit_entry_sorted*)");
bzero((void *)ret, (n + 1) * sizeof(ret[0]));
n = 0;
LL_FOREACH(h, e) {
if (e->pkgname != NULL) {
ret[n].e = e;
ret[n].noglob_len = pkg_audit_str_noglob_len(e->pkgname);
ret[n].next_pfx_incr = 1;
n++;
}
}
qsort(ret, n, sizeof(*ret), pkg_audit_entry_cmp);
/*
* Determining jump indexes to the next different prefix.
* Only non-1 increments are calculated there.
*
* Due to the current usage that picks only increment for the
* first of the non-unique prefixes in a row, we could
* calculate only that one and skip calculations for the
* succeeding, but for the uniformity and clarity we're
* calculating 'em all.
*/
for (n = 1, tofill = 0; ret[n].e; n++) {
if (ret[n - 1].noglob_len != ret[n].noglob_len) {
struct pkg_audit_item *base;
base = ret + n - tofill;
for (i = 0; tofill > 1; i++, tofill--)
base[i].next_pfx_incr = tofill;
tofill = 1;
} else if (strcmp(ret[n - 1].e->pkgname,
ret[n].e->pkgname) == 0) {
tofill++;
} else {
tofill = 1;
}
}
/* Calculate jump indexes for the first byte of the package name */
bzero(audit_entry_first_byte_idx, sizeof(audit_entry_first_byte_idx));
for (n = 1, i = 0; n < 256; n++) {
while (ret[i].e != NULL &&
(size_t)(ret[i].e->pkgname[0]) < n)
i++;
audit_entry_first_byte_idx[n] = i;
}
return (ret);
}
static bool
pkg_audit_version_match(const char *pkgversion, struct pkg_audit_version *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 void
pkg_audit_print_versions(struct pkg_audit_entry *e, struct sbuf *sb)
{
struct pkg_audit_versions_range *vers;
sbuf_cat(sb, "Affected versions:\n");
LL_FOREACH(e->versions, vers) {
if (vers->v1.type > 0 && vers->v2.type > 0)
sbuf_printf(sb, "%s %s : %s %s\n",
vop_names[vers->v1.type], vers->v1.version,
vop_names[vers->v2.type], vers->v2.version);
else if (vers->v1.type > 0)
sbuf_printf(sb, "%s %s\n",
vop_names[vers->v1.type], vers->v1.version);
else
sbuf_printf(sb, "%s %s\n",
vop_names[vers->v2.type], vers->v2.version);
}
}
static void
pkg_audit_print_entry(struct pkg_audit_entry *e, struct sbuf *sb,
const char *pkgname, const char *pkgversion, bool quiet)
{
struct pkg_audit_cve *cve;
if (quiet) {
if (pkgversion != NULL)
sbuf_printf(sb, "%s-%s\n", pkgname, pkgversion);
else
sbuf_printf(sb, "%s\n", pkgname);
sbuf_finish(sb);
} else {
if (pkgversion != NULL)
sbuf_printf(sb, "%s-%s is vulnerable:\n", pkgname, pkgversion);
else {
sbuf_printf(sb, "%s is vulnerable:\n", pkgname);
pkg_audit_print_versions(e, sb);
}
sbuf_printf(sb, "%s\n", e->desc);
/* XXX: for vulnxml we should use more clever approach indeed */
if (e->cve) {
cve = e->cve;
while (cve) {
sbuf_printf(sb, "CVE: %s\n", cve->cvename);
cve = cve->next;
}
}
if (e->url)
sbuf_printf(sb, "WWW: %s\n\n", e->url);
else if (e->id)
sbuf_printf(sb,
"WWW: https://vuxml.FreeBSD.org/freebsd/%s.html\n\n",
e->id);
}
}
bool
pkg_audit_is_vulnerable(struct pkg_audit *audit, struct pkg *pkg,
bool quiet, struct sbuf **result)
{
struct pkg_audit_entry *e;
struct pkg_audit_versions_range *vers;
struct sbuf *sb;
struct pkg_audit_item *a;
bool res = false, res1, res2;
if (!audit->parsed)
return false;
a = audit->items;
a += audit_entry_first_byte_idx[(size_t)pkg->name[0]];
sb = sbuf_new_auto();
for (; (e = a->e) != NULL; a += a->next_pfx_incr) {
int cmp;
size_t i;
/*
* Audit entries are sorted, so if we had found one
* that is lexicographically greater than our name,
* it and the rest won't match our name.
*/
cmp = strncmp(pkg->name, e->pkgname, a->noglob_len);
if (cmp > 0)
continue;
else if (cmp < 0)
break;
for (i = 0; i < a->next_pfx_incr; i++) {
e = a[i].e;
if (fnmatch(e->pkgname, pkg->name, 0) != 0)
continue;
if (pkg->version == NULL) {
/*
* Assume that all versions should be checked
*/
res = true;
pkg_audit_print_entry(e, sb, pkg->name, NULL, quiet);
}
else {
LL_FOREACH(e->versions, vers) {
res1 = pkg_audit_version_match(pkg->version, &vers->v1);
res2 = pkg_audit_version_match(pkg->version, &vers->v2);
if (res1 && res2) {
res = true;
pkg_audit_print_entry(e, sb, pkg->name, pkg->version, quiet);
break;
}
}
}
if (res && quiet)
goto out;
}
}
out:
if (res) {
sbuf_finish(sb);
*result = sb;
}
else {
sbuf_delete(sb);
}
return (res);
}
struct pkg_audit *
pkg_audit_new(void)
{
struct pkg_audit *audit;
audit = calloc(1, sizeof(struct pkg_audit));
return (audit);
}
int
pkg_audit_load(struct pkg_audit *audit, const char *fname)
{
int fd;
void *mem;
struct stat st;
if ((fd = open(fname, O_RDONLY)) == -1)
return (EPKG_FATAL);
if (fstat(fd, &st) == -1) {
close(fd);
return (EPKG_FATAL);
}
if ((mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
close(fd);
return (EPKG_FATAL);
}
close(fd);
audit->map = mem;
audit->len = st.st_size;
audit->loaded = true;
return (EPKG_OK);
}
/* This can and should be executed after cap_enter(3) */
int
pkg_audit_process(struct pkg_audit *audit)
{
if (!audit->loaded)
return (EPKG_FATAL);
if (pkg_audit_parse_vulnxml(audit) == EPKG_FATAL)
return (EPKG_FATAL);
audit->items = pkg_audit_preprocess(audit->entries);
audit->parsed = true;
return (EPKG_OK);
}
void
pkg_audit_free (struct pkg_audit *audit)
{
if (audit != NULL) {
if (audit->parsed) {
pkg_audit_free_list(audit->entries);
free(audit->items);
}
if (audit->loaded) {
munmap(audit->map, audit->len);
}
free(audit);
}
}
File Metadata
Details
Attached
Mime Type
text/plain; charset=utf-8
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
571257
Default Alt Text
small change on pkg (23 KB)
Attached To
Mode
P107 small change on pkg
Attached
Detach File
Event Timeline
Log In to Comment