ports/126853: ports-mgmt/portaudit: speed up audit of installed packages

Eygene Ryabinkin rea-fbsd at codelabs.ru
Tue Aug 26 12:10:02 UTC 2008


>Number:         126853
>Category:       ports
>Synopsis:       ports-mgmt/portaudit: speed up audit of installed packages
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-ports-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Aug 26 12:10:02 UTC 2008
>Closed-Date:
>Last-Modified:
>Originator:     Eygene Ryabinkin
>Release:        FreeBSD 7.0-STABLE i386
>Organization:
Code Labs
>Environment:

System: FreeBSD XXX 7.0-STABLE FreeBSD 7.0-STABLE #1: Wed Aug 20 14:32:07 MSD 2008 root at XXX:/usr/obj/usr/src/sys/XXX i386

>Description:

Currently on my machines portaudit(1) takes some seconds to perform the
search for the vulnerable packages (system has >500 installed packages).
The reason is that portaudit(1) uses awk and invokes pkg_info a number
of times.

I had mimicked the awk part of auditing of the installed packages
in the new utility, pkg_audit.  It is thought to be added to the
pkg_install bundle.

With this utility I achieved 15x speedup on my system.  The
modifications for the portaudit itself were rather small.  The real
reason why I care about two seconds of portaudit life is that I am
running portaudit plugin for Nagios [1] and it sometimes time out in the
innocent situations and seem to create visible load on the monitored
server.

I had not looked into the sequence of auditing the individual package at
the port compilation time -- this is not yet showed as much problems as
auditing of the whole list of installed packages.

[1] ports/net-mgmt/nagios-portaudit

>How-To-Repeat:

Try 'time portaudit'.  You'll surely get a real time of a second or
more.  Try my patches -- the real time should be dropped significantly.

Tests for the sufficiently new machine (Core 2 E8200) show 1.5 seconds
vs 0.1 seconds respectively.  But for the dual-CPU P3 1GHz host I have
8.4/0.85 seconds.  And this is not the slowest system that can run
FreeBSD.

>Fix:

First two patches add the needed functionality to pkg_install bundle.
I am not completely sure that the new match_* functions belong to
lib/match.c, may be they deserve the separate file.

--- 0001-Add-functions-for-traversing-package-database-and-ma.patch begins here --
>From ce2fbab57ffb6f91d0a13a07cc82ec5a97cb06ff Mon Sep 17 00:00:00 2001
From: Eygene Ryabinkin <rea-fbsd at codelabs.ru>
Date: Tue, 26 Aug 2008 14:59:29 +0400

This functions will help one who wants to search package database for
the given package names or name globs and do something more than just
collecting the names of the matched packages.  They are skeleton-like
functions and are supposed to be used as package database iterator
and a handy match function:
-----
session = match_begin(MATCH_STYLE);

while (match_next_package(session)) {
	...
	pkg_name = match_get_pkgname(session);
	...
	match_matches(session, template);
	...
}

match_end(session);
-----

Signed-off-by: Eygene Ryabinkin <rea-fbsd at codelabs.ru>
---
 lib/lib.h   |    5 +++
 lib/match.c |  100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 105 insertions(+), 0 deletions(-)

diff --git a/lib/lib.h b/lib/lib.h
index 773278a..2890df6 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -228,6 +228,11 @@ char		**matchbyorigin(const char *, int *);
 char		***matchallbyorigin(const char **, int *);
 int		isinstalledpkg(const char *name);
 int		pattern_match(match_t MatchType, char *pattern, const char *pkgname);
+struct match_session *match_begin(match_t);
+void            match_end(struct match_session *);
+Boolean         match_next_package(struct match_session *);
+int             match_matches(struct match_session *, char *);
+const char      *match_get_pkgname(struct match_session *);
 
 /* Dependencies */
 int		sortdeps(char **);
diff --git a/lib/match.c b/lib/match.c
index 07c136b..01f09ee 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -37,6 +37,16 @@ struct store {
     char **store;
 };
 
+/*
+ * Structure that holds information about package traversal
+ * and matching session.
+ */
+struct match_session {
+	FTS *ftsp;
+	FTSENT *f;
+	match_t match_type;;
+};
+
 static int rex_match(const char *, const char *, int);
 static int csh_match(const char *, const char *, int);
 struct store *storecreate(struct store *);
@@ -416,6 +426,96 @@ errout:
 }
 
 /*
+ * Prepares package database for the traversal.
+ */
+struct match_session *
+match_begin(match_t MatchType)
+{
+	struct match_session *sess;
+	const char *paths[2] = {LOG_DIR, NULL};
+
+	if (!isdir(paths[0]))
+		return NULL;
+
+	sess = (struct match_session *)malloc(sizeof(*sess));
+	if (sess == NULL)
+		return NULL;
+	bzero((void *)sess, sizeof(*sess));
+
+	sess->match_type = MatchType;
+	sess->ftsp = fts_open((char * const *)(uintptr_t)paths,
+	    FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, fname_cmp);
+	if (sess->ftsp == NULL) {
+		free((void *)sess);
+		return NULL;
+	}
+
+	return sess;
+}
+
+/*
+ * Ends package traversal session.
+ */
+void
+match_end(struct match_session *sess)
+{
+	if (sess == NULL)
+		return;
+	if (sess->ftsp != NULL)
+		fts_close(sess->ftsp);
+	free((void *)sess);
+	return;
+}
+
+/*
+ * Proceeds to the next package in the session.
+ * Returns TRUE if package was selected, FALSE otherwise.
+ */
+Boolean
+match_next_package(struct match_session *sess)
+{
+	if (sess == NULL || sess->ftsp == NULL)
+		return FALSE;
+
+	while ((sess->f = fts_read(sess->ftsp)) != NULL) {
+		if (sess->f->fts_info == FTS_D &&
+		    sess->f->fts_level == 1) {
+			fts_set(sess->ftsp, sess->f, FTS_SKIP);
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+/*
+ * Matches the current package name against the given pattern.
+ * Returns 1 if pattern matches, 0 if not matches and -1 for
+ * the error condition.
+ */
+int
+match_matches(struct match_session *sess, char *pattern)
+{
+	if (sess == NULL || sess->ftsp == NULL || sess->f == NULL ||
+	    pattern == NULL)
+		return FALSE;
+
+	return pattern_match(sess->match_type, pattern,
+	    sess->f->fts_name);
+}
+
+/*
+ * Returns name of the current package.
+ */
+const char *
+match_get_pkgname(struct match_session *sess)
+{
+	if (sess == NULL || sess->ftsp == NULL || sess->f == NULL)
+		return NULL;
+	
+	return (const char *)(sess->f->fts_name);
+}
+
+/*
  * Returns 1 if specified pkgname matches RE pattern.
  * Otherwise returns 0 if doesn't match or -1 if RE
  * engine reported an error (usually invalid syntax).
-- 
1.5.6.4
--- 0001-Add-functions-for-traversing-package-database-and-ma.patch ends here --

--- 0002-New-utility-pkg_audit.patch begins here ---
>From 854bc5645c15ae9ae9d72a1e123c5c738d6f2d82 Mon Sep 17 00:00:00 2001
From: Eygene Ryabinkin <rea-fbsd at codelabs.ru>
Date: Tue, 26 Aug 2008 15:08:46 +0400
Subject: [PATCH] New utility: pkg_audit

It is mainly a helper for portupgrade to avoid awk scripting and
numerous calls for pkg_info.  This utility speeds up portaudit by a
factor of 10 on a system with 521 installed ports and the auditfile that
contains 3213 entries:
-----
$ ls -d /var/db/pkg/*  | wc -l
     521

$ tar xOf /var/db/portaudit/auditfile.tbz auditfile | sed -e'/^#/d' | wc -l
    3213

$ time ./portaudit
Affected package: ruby-1.8.6.111_4,1
Type of problem: ruby -- DNS spoofing vulnerability.
Reference: <http://www.FreeBSD.org/ports/portaudit/959d384d-6b59-11dd-9d79-001fc61c2a55.html>

Affected package: ruby-1.8.6.111_4,1
Type of problem: ruby -- DoS vulnerability in WEBrick.
Reference: <http://www.FreeBSD.org/ports/portaudit/f7ba20aa-6b5a-11dd-9d79-001fc61c2a55.html>

Affected package: ruby-1.8.6.111_4,1
Type of problem: ruby -- multiple vulnerabilities in safe level.
Reference: <http://www.FreeBSD.org/ports/portaudit/c329712a-6b5b-11dd-9d79-001fc61c2a55.html>

3 problem(s) in your installed packages found.

You are advised to update or deinstall the affected package(s) immediately.

real    0m0.107s
user    0m0.116s
sys     0m0.012s

$ time portaudit
Affected package: ruby-1.8.6.111_4,1
Type of problem: ruby -- multiple vulnerabilities in safe level.
Reference: <http://www.FreeBSD.org/ports/portaudit/c329712a-6b5b-11dd-9d79-001fc61c2a55.html>

Affected package: ruby-1.8.6.111_4,1
Type of problem: ruby -- DoS vulnerability in WEBrick.
Reference: <http://www.FreeBSD.org/ports/portaudit/f7ba20aa-6b5a-11dd-9d79-001fc61c2a55.html>

Affected package: ruby-1.8.6.111_4,1
Type of problem: ruby -- DNS spoofing vulnerability.
Reference: <http://www.FreeBSD.org/ports/portaudit/959d384d-6b59-11dd-9d79-001fc61c2a55.html>

3 problem(s) in your installed packages found.

You are advised to update or deinstall the affected package(s) immediately.

real    0m1.583s
user    0m0.560s
sys     0m1.057s
-----

Signed-off-by: Eygene Ryabinkin <rea-fbsd at codelabs.ru>
---
 Makefile          |    2 +-
 audit/Makefile    |   14 +++
 audit/audit.h     |   43 +++++++++
 audit/main.c      |  162 +++++++++++++++++++++++++++++++++
 audit/parse.c     |  259 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 audit/pkg_audit.1 |   63 +++++++++++++
 6 files changed, 542 insertions(+), 1 deletions(-)
 create mode 100644 audit/Makefile
 create mode 100644 audit/audit.h
 create mode 100644 audit/main.c
 create mode 100644 audit/parse.c
 create mode 100644 audit/pkg_audit.1

diff --git a/Makefile b/Makefile
index fefbd08..abc1e65 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 
 .include <bsd.own.mk>
 
-SUBDIR=	lib add create delete info updating version
+SUBDIR=	lib add create delete info updating version audit
 
 .include <bsd.subdir.mk>
 
diff --git a/audit/Makefile b/audit/Makefile
new file mode 100644
index 0000000..2ece5f8
--- /dev/null
+++ b/audit/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG=	pkg_audit
+SRCS=	main.c parse.c
+
+CFLAGS+= -I${.CURDIR}/../lib
+
+WARNS?=	6
+WFORMAT?=	1
+
+DPADD=	${LIBINSTALL} ${LIBFETCH} ${LIBMD}
+LDADD=	${LIBINSTALL} -lfetch -lmd
+
+.include <bsd.prog.mk>
diff --git a/audit/audit.h b/audit/audit.h
new file mode 100644
index 0000000..1f0a369
--- /dev/null
+++ b/audit/audit.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Eygene Ryabinkin
+ * 26 August 2008
+ *
+ * Parsing module for pkg_audit, header file.
+ *
+ */
+
+#ifndef __AUDIT_H__
+#define __AUDIT_H__
+
+#include <sys/queue.h>
+
+SLIST_HEAD(audit_contents, audit_entry);
+
+struct audit_entry {
+	char	*pkgglob;	/* Package name glob */
+	char	*url;		/* URL of advisory */
+	char	*descr;		/* Description of vulnerability */
+	size_t	pfx_size;	/* Metacharacter-less glob part size */
+	SLIST_ENTRY(audit_entry) entries;
+};
+
+
+/* Function prototypes */
+int
+parse_auditfile(FILE *_fp, struct audit_contents *_head);
+
+
+#endif /* defined(__AUDIT_H__) */
diff --git a/audit/main.c b/audit/main.c
new file mode 100644
index 0000000..98d4869
--- /dev/null
+++ b/audit/main.c
@@ -0,0 +1,162 @@
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Eygene Ryabinkin
+ * 26 August 2008
+ *
+ * This is the audit module -- fast helper for the portaudit script.
+ *
+ * It is filter-like utility: it reads the audit file from the
+ * standard input, intersects the vulnerable port list with the
+ * packages installed in the system and outputs the entries for
+ * the vulnerable ports that are present in the system to the
+ * standard output.
+ *
+ * The installed package can be listed multiple times, since it
+ * can be vulnerable to more than one bug at a time.  But the
+ * whole output entries will be unique -- package name and
+ * vulnerability details should produce unique entry.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#ifdef PROFILING
+#include <sys/time.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <getopt.h>
+#include <err.h>
+#include <stdio.h>
+
+#include "lib.h"
+#include "audit.h"
+
+static inline void
+audit_package(const char *_pkgname, struct audit_contents *_head,
+    struct match_session *_msess, FILE *_fp);
+
+int
+main(int argc, char *argv[])
+{
+	char freebsd[sizeof("FreeBSD-XXYYZZXXYYZZ")];
+	unsigned long reldate;
+	size_t reldate_size = sizeof(reldate);
+	int mib[2];
+
+	FILE *in = stdin, *out = stdout;
+	struct match_session *msess;
+	struct audit_entry *item;
+#ifdef PROFILING
+	struct timeval t1, t2;
+	double dt;
+#endif
+
+	/* Make compiler happy */
+	if (argv[argc] == NULL) {};
+
+	struct audit_contents head =
+	    SLIST_HEAD_INITIALIZER(head);
+
+	mib[0] = CTL_KERN;
+	mib[1] = KERN_OSRELDATE;
+	if (sysctl(mib, 2, (void *)&reldate, &reldate_size, NULL, 0) != 0)
+		errx(1, "Unable to get kern.osreldate");
+	snprintf(freebsd, sizeof(freebsd), "%s-%lu", "FreeBSD", reldate);
+	
+	SLIST_INIT(&head);
+#ifdef PROFILING
+	gettimeofday(&t1, NULL);
+#endif
+	if (parse_auditfile(in, &head) != 0) {
+		errx(1, "Failed to parse audit entries");
+	}
+#ifdef PROFILING
+	gettimeofday(&t2, NULL);
+	dt = t2.tv_sec - t1.tv_sec + 1e-6 * (t2.tv_usec - t1.tv_usec);
+	fprintf(stderr, "parse_auditfile(): %.6lf sec\n", dt);
+#endif
+
+	msess = match_begin(MATCH_GLOB);
+	if (msess == NULL)
+		return 1;
+	
+#ifdef PROFILING
+	gettimeofday(&t1, NULL);
+#endif
+
+	/* Special check: FreeBSD itself */
+	SLIST_FOREACH (item, &head, entries) {
+		if (strncmp(item->pkgglob,
+		    "FreeBSD", sizeof("FreeBSD") - 1) == 0 &&
+		    pattern_match(MATCH_GLOB, item->pkgglob, freebsd)) {
+			fprintf(out, "%s|%s|%s\n",
+			    freebsd, item->url, item->descr);
+		}
+	}
+
+	/* Installed packages */
+	while (match_next_package(msess))
+		audit_package(match_get_pkgname(msess), &head, msess, out);
+
+#ifdef PROFILING
+	gettimeofday(&t2, NULL);
+	dt = t2.tv_sec - t1.tv_sec + 1e-6 * (t2.tv_usec - t1.tv_usec);
+	fprintf(stderr, "match loop: %.6lf sec\n", dt);
+#endif
+
+	match_end(msess);
+
+	SLIST_FOREACH (item, &head, entries) {
+		free((void *)item->pkgglob);
+		free((void *)item);
+	}
+
+	return 0;
+}
+
+void
+cleanup(int sig)
+{
+	sig = 0;
+	return;
+}
+
+/*
+ * Loops over audit file contents and checks each entry in turn.
+ *
+ * The great speedup is to test the package prefix at the first
+ * place and only if it matches perform full match -- match_matches
+ * uses slow matching routines without precompilation and other
+ * tricks.  For hundreds of installed ports and a couple of thousands
+ * audit entries this slows things down very well.
+ */
+static inline void
+audit_package(const char *pkgname, struct audit_contents *head,
+    struct match_session *msess, FILE *fp)
+{
+	struct audit_entry *item;
+
+	SLIST_FOREACH (item, head, entries) {
+		if (strncmp(pkgname, item->pkgglob,
+		    item->pfx_size) == 0 &&
+		    match_matches(msess, item->pkgglob)) {
+			fprintf(fp, "%s|%s|%s\n",
+			    pkgname, item->url, item->descr);
+		}
+	}
+}
diff --git a/audit/parse.c b/audit/parse.c
new file mode 100644
index 0000000..fb33f7c
--- /dev/null
+++ b/audit/parse.c
@@ -0,0 +1,259 @@
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Eygene Ryabinkin
+ * 26 August 2008
+ *
+ * Parsing module for pkg_audit.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <getopt.h>
+#include <err.h>
+#include <stdio.h>
+
+#include "lib.h"
+#include "audit.h"
+
+/* Simple exponentially-growing buffer. */
+
+struct dyn_buffer {
+	char *buf;
+	size_t size;
+};
+
+/* Prototypes */
+static int
+parse_audit_entry(struct dyn_buffer *_b, struct audit_entry *_e);
+
+static int
+read_line(FILE *_fp, struct dyn_buffer *_b);
+
+static struct dyn_buffer *
+buf_init(size_t _size);
+static void
+buf_destroy(struct dyn_buffer *_b);
+static int
+buf_grow(struct dyn_buffer *_b);
+
+/*
+ * Parses audit file to the linked list of single entries.
+ *
+ * Return values:
+ * 0 -- file was successfully parsed;
+ * 1 -- parsing or read error occured;
+ * -1 -- bad arguments, memory allocation problems, etc.
+ */
+int
+parse_auditfile(FILE *fp, struct audit_contents *head)
+{
+	struct audit_entry *e;
+	struct dyn_buffer *b;
+	int errcode;
+
+	b = buf_init(256);
+	if (b == NULL)
+		return 1;
+
+	while ((errcode = read_line(fp, b)) == 0) {
+		if (b->buf[0] == '#')
+			continue;
+		e = (struct audit_entry *)malloc(sizeof(*e));
+		if (e == NULL) {
+			buf_destroy(b);
+			return -1;
+		}
+		bzero((void *)e, sizeof(*e));
+		if ((errcode = parse_audit_entry(b, e)) != 0) {
+			buf_destroy(b);
+			return errcode;
+		}
+		SLIST_INSERT_HEAD(head, e, entries);
+	}
+
+	buf_destroy(b);
+
+	if (errcode != 1)
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * Helpers for audit file parsing routine.
+ */
+
+static struct dyn_buffer *
+buf_init(size_t size)
+{
+	struct dyn_buffer *b;
+
+	if (size <= 0)
+		return NULL;
+
+	b = (struct dyn_buffer *)malloc(sizeof(*b));
+	if (b == NULL)
+		return NULL;
+
+	bzero((void *)b, sizeof(*b));
+	b->size = size;
+	b->buf = (char *)malloc(b->size * sizeof(b->buf[0]));
+	if (b->buf == NULL) {
+		free((void *)b);
+		return NULL;
+	}
+
+	bzero((void *)b->buf, b->size);
+	return b;
+}
+
+static void
+buf_destroy(struct dyn_buffer *b)
+{
+	if (b == NULL)
+		return;
+	
+	if (b->buf != NULL)
+		free((void *)b->buf);
+	free((void *)b);
+	return;
+}
+
+static int
+buf_grow(struct dyn_buffer *b)
+{
+	char *newbuf;
+
+	if (b == NULL || b->buf == NULL || b->size <= 0)
+		return -1;
+
+	newbuf = (char *)malloc(2 * b->size * sizeof(newbuf));
+	if (newbuf == NULL)
+		return 1;
+
+	bzero(newbuf, 2 * b->size);
+	bcopy((void *)b->buf, (void *)newbuf, b->size);
+	b->buf = newbuf;
+	b->size *= 2;
+
+	return 0;
+}
+
+/*
+ * fgets()-like function that reads the whole input line
+ * Returns 0 on the successful read, 1 for the end-of-file
+ * condition, -1 for any error.
+ *
+ * Terminating '\n' is removed from the line.
+ */
+static int
+read_line(FILE *fp, struct dyn_buffer *b)
+{
+	size_t offset = 0, len = 0;
+
+	if (fp == NULL || b == NULL)
+		return -1;
+
+	if (feof(fp))
+		return 1;
+
+	/* We need at least two-character buffer */
+	if (b->size == 1 && buf_grow(b) != 0)
+		return -1;
+
+	b->buf[b->size - 1] = '\0';
+	offset = 0;
+	while (fgets(b->buf + offset, b->size - offset, fp) != NULL) {
+		len = strlen(b->buf);
+		/*
+		 * Read zero characters or buffer even shrinked?
+		 * Strange, let's indicate error.
+		 */
+		if (len <= offset)
+			return -1;
+		if (b->buf[len - 1] == '\n') {
+			b->buf[len - 1] = '\0';
+			return 0;
+		}
+
+		offset = len;
+		if (buf_grow(b) != 0)
+			return -1;
+
+		/* Should not happen, but who knows */
+		if (offset >= b->size)
+			return -1;
+	}
+
+	if (feof(fp)) {
+		/*
+		 * If we read no characters, if means that we were
+		 * at the EOF, but it was detected only by fgets(),
+		 * not the first feof().
+		 */
+		if (len == 0)
+			return 1;
+		else
+			return 0;
+	} else {
+		return -1;
+	}
+}
+
+/*
+ * Parses single audit line and places it to the structure.
+ * Calculates length of the package name suffix that is free
+ * from metacharacters -- it is used for the quick matches
+ * against port name.
+ */
+static int
+parse_audit_entry(struct dyn_buffer *b, struct audit_entry *e)
+{
+	size_t len;
+	char *string = NULL, *d1 = NULL, *d2 = NULL;
+	static const char globset[] = "{*?><=!";
+
+	/*
+	 * At least 5 characters:
+	 * two delimiters and three non-empty fields.
+	 */
+	len = strlen(b->buf);
+	if (len < 5)
+		return 1;
+
+	/* Locate delimiters. */
+	d1 = strchr(b->buf, '|');
+	if (d1 == NULL)
+		return 1;
+	d2 = strchr(d1 + 1, '|');
+	if (d2 == NULL)
+		return 1;
+
+	string = (char *)malloc((len + 1) * sizeof(string[0]));
+	if (string == NULL)
+		return -1;
+
+	bcopy((void *)b->buf, (void *)string, (len + 1) * sizeof(string[0]));
+	string[d1 - b->buf] = '\0';
+	string[d2 - b->buf] = '\0';
+	e->pkgglob = string;
+	e->url = string + (d1 - b->buf) + 1;
+	e->descr = string + (d2 - b->buf) + 1;
+	e->pfx_size = strcspn(e->pkgglob, globset);
+
+	return 0;
+}
diff --git a/audit/pkg_audit.1 b/audit/pkg_audit.1
new file mode 100644
index 0000000..cd4abbc
--- /dev/null
+++ b/audit/pkg_audit.1
@@ -0,0 +1,63 @@
+.\"
+.\" FreeBSD install - a package for the installation and maintenance
+.\" of non-core utilities.
+.\"
+.\" 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.
+.\"
+.\" Eygene Ryabinkin
+.\"
+.\"
+.\"     @(#)pkg_audit.1
+.\" $FreeBSD$
+.\"
+.Dd Aug 26, 2008
+.Dt PKG_AUDIT 1
+.Os
+.Sh NAME
+.Nm pkg_audit
+.Nd lists vulnerable ports installed in the system
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+command is used to extract vulnerability information from the audit
+file and list vulnerable packages that are present in the system.
+It is main purpose to help
+.Xr portaudit 1
+utility to avoid time-consuming scripting.
+.Nm
+reads vulnerability information from the standard input and writes
+the list of vulnerable ports to the standard output.
+Format of the output lines is the same as for the audit file, but
+package matching globs are substituted with the actual package names.
+.Sh TECHNICAL DETAILS
+First the audit file is parsed to the internal representation
+(currently it is linked list).
+Then we are traversing installed packages database and trying to
+match the package name against each audit entry.
+The crucial step for the speeding up the process is to first
+match the package prefix that has no CSH-like metacharacters
+and perform full comparison only if match is found.
+One more name is tested prior to the installed packages: it is
+.Qo FreeBSD-`sysctl -n kern.osreldate` Qc ,
+the version of
+.Fx
+the current system is running.
+.Sh SEE ALSO
+.Xr portaudit 1 ,
+.Xr pkg_add 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_version 1 .
+.Sh AUTHORS
+.An Eygene Ryabinkin Aq rea-fbsd at codelabs.ru
+.Sh BUGS
+Sure to be some.
-- 
1.5.6.4
--- 0002-New-utility-pkg_audit.patch ends here ---

And the above patch teaches portaudit(1) to use the new utility
if it is available.

--- 0001-Modify-portaudit-to-make-use-of-pkg_audit-utility.patch begins here ---
>From c982693afce2543b60b1937d13ae18ec2a2240d0 Mon Sep 17 00:00:00 2001
From: Eygene Ryabinkin <rea-fbsd at codelabs.ru>
Date: Tue, 26 Aug 2008 15:26:02 +0400
Subject: [PATCH] Modify portaudit to make use of pkg_audit utility

Signed-off-by: Eygene Ryabinkin <rea-fbsd at codelabs.ru>
---
 portaudit |   43 ++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 42 insertions(+), 1 deletions(-)

diff --git a/portaudit b/portaudit
index ee1613e..6cf7039 100755
--- a/portaudit
+++ b/portaudit
@@ -173,6 +173,43 @@ audit_installed()
 	return $rc
 }
 
+audit_installed_pkgaudit ()
+{
+	local rc=0
+	local osversion=`sysctl -n kern.osreldate`
+
+	fixedre=`echo -n $portaudit_fixed | tr -c '[:alnum:]- \t\n' 'x' | tr -s ' \t\n' '|'`
+
+	extract_auditfile | pkg_audit | awk -F\| "$PRINTAFFECTED_AWK"'
+		BEGIN { vul=0; fixedre="'"$fixedre"'" }
+		$2 !~ /'"$opt_restrict"'/ { next }
+		$1 ~ /^FreeBSD[<=>!]/ {
+			if (fixedre && $2 ~ fixedre) next
+			print_affected("FreeBSD-'"$osversion"'", \
+				"To disable this check add the uuid to \`portaudit_fixed'"'"' in /usr/local/etc/portaudit.conf")
+			next
+		}
+		{
+			vul++
+			print_affected($0, "")
+		}
+		END {
+			if ("'$opt_quiet'" == "false") {
+				print vul " problem(s) in your installed packages found."
+			}
+			if (vul > 0) {
+				if ("'$opt_quiet'" == "false") {
+					print "\nYou are advised to update or deinstall" \
+						" the affected package(s) immediately."
+				}
+				exit(1)
+			}
+		}
+	' || rc=$?
+
+	return $rc
+}
+
 audit_file()
 {
 	local rc=0
@@ -461,7 +498,11 @@ fi
 
 if $opt_audit; then
 	portaudit_prerequisites
-	audit_installed || ret=$?
+	if which pkg_audit > /dev/null 2>&1; then
+		audit_installed_pkgaudit || ret=$?
+	else
+		audit_installed || ret=$?
+	fi
 fi
 
 if $opt_auditcwd; then
-- 
1.5.6.4
--- 0001-Modify-portaudit-to-make-use-of-pkg_audit-utility.patch ends here ---
>Release-Note:
>Audit-Trail:
>Unformatted:



More information about the freebsd-ports-bugs mailing list