/ libpkg / pkg_audit.c
pkg_audit.c
  1  /*-
  2   * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
  3   * Copyright (c) 2014 Matthew Seaman <matthew@FreeBSD.org>
  4   * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
  5   * Copyright (c) 2020 Baptiste Daroussin <bapt@FreeBSD.org>
  6   * All rights reserved.
  7   *
  8   * Redistribution and use in source and binary forms, with or without
  9   * modification, are permitted provided that the following conditions
 10   * are met:
 11   * 1. Redistributions of source code must retain the above copyright
 12   *    notice, this list of conditions and the following disclaimer
 13   *    in this position and unchanged.
 14   * 2. Redistributions in binary form must reproduce the above copyright
 15   *    notice, this list of conditions and the following disclaimer in the
 16   *    documentation and/or other materials provided with the distribution.
 17   *
 18   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 19   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 20   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 21   * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 22   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 23   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 24   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 25   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 26   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 27   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28   */
 29  
 30  #include <sys/mman.h>
 31  
 32  #include <archive.h>
 33  #include <err.h>
 34  #include <fcntl.h>
 35  #include <fnmatch.h>
 36  #include <stdio.h>
 37  #include <string.h>
 38  #include <utlist.h>
 39  #include <xstring.h>
 40  
 41  #include <yxml.h>
 42  
 43  #ifdef __linux__
 44  # ifdef __GLIBC__
 45  #  include <sys/time.h>
 46  # endif
 47  #endif
 48  
 49  #include "pkg.h"
 50  #include "pkg/audit.h"
 51  #include "private/pkg.h"
 52  #include "private/event.h"
 53  
 54  /*
 55   * The _sorted stuff.
 56   *
 57   * We are using the optimized search based on the following observations:
 58   *
 59   * - number of VuXML entries is more likely to be far greater than
 60   *   the number of installed ports; thus we should try to optimize
 61   *   the walk through all entries for a given port;
 62   *
 63   * - fnmatch() is good and fast, but if we will compare the audit entry
 64   *   name prefix without globbing characters to the prefix of port name
 65   *   of the same length and they are different, there is no point to
 66   *   check the rest;
 67   *
 68   * - (most important bit): if parsed VuXML entries are lexicographically
 69   *   sorted per the largest prefix with no globbing characters and we
 70   *   know how many succeeding entries have the same prefix we can
 71   *
 72   *   a. skip the rest of the entries once the non-globbing prefix is
 73   *      lexicographically larger than the port name prefix of the
 74   *      same length: all successive prefixes will be larger as well;
 75   *
 76   *   b. if we have non-globbing prefix that is lexicographically smaller
 77   *      than port name prefix, we can skip all succeeding entries with
 78   *      the same prefix; and as some port names tend to repeat due to
 79   *      multiple vulnerabilities, it could be a large win.
 80   */
 81  struct pkg_audit_item {
 82  	struct pkg_audit_entry *e;	/* Entry itself */
 83  	size_t noglob_len;	/* Prefix without glob characters */
 84  	size_t next_pfx_incr;	/* Index increment for the entry with
 85  				   different prefix */
 86  };
 87  
 88  struct pkg_audit {
 89  	struct pkg_audit_entry *entries;
 90  	struct pkg_audit_item *items;
 91  	bool parsed;
 92  	bool loaded;
 93  	char **ignore_globs;
 94  	char **ignore_regexp;
 95  	void *map;
 96  	size_t len;
 97  };
 98  
 99  
100  /*
101   * Another small optimization to skip the beginning of the
102   * VuXML entry array, if possible.
103   *
104   * audit_entry_first_byte_idx[ch] represents the index
105   * of the first VuXML entry in the sorted array that has
106   * its non-globbing prefix that is started with the character
107   * 'ch'.  It allows to skip entries from the beginning of the
108   * VuXML array that aren't relevant for the checked port name.
109   */
110  static size_t audit_entry_first_byte_idx[256];
111  
112  static void
113  pkg_audit_free_entry(struct pkg_audit_entry *e)
114  {
115  	struct pkg_audit_package *ppkg, *ppkg_tmp;
116  	struct pkg_audit_versions_range *vers, *vers_tmp;
117  	struct pkg_audit_cve *cve, *cve_tmp;
118  	struct pkg_audit_pkgname *pname, *pname_tmp;
119  
120  	if (!e->ref) {
121  		LL_FOREACH_SAFE(e->packages, ppkg, ppkg_tmp) {
122  			LL_FOREACH_SAFE(ppkg->versions, vers, vers_tmp) {
123  				free(vers->v1.version);
124  				free(vers->v2.version);
125  				free(vers);
126  			}
127  
128  			LL_FOREACH_SAFE(ppkg->names, pname, pname_tmp) {
129  				free(pname->pkgname);
130  				free(pname);
131  			}
132  			free(ppkg);
133  		}
134  		LL_FOREACH_SAFE(e->cve, cve, cve_tmp) {
135  			free(cve->cvename);
136  			free(cve);
137  		}
138  			free(e->url);
139  			free(e->desc);
140  			free(e->id);
141  	}
142  	free(e);
143  }
144  
145  static void
146  pkg_audit_free_list(struct pkg_audit_entry *h)
147  {
148  	struct pkg_audit_entry *e;
149  
150  	while (h) {
151  		e = h;
152  		h = h->next;
153  		pkg_audit_free_entry(e);
154  	}
155  }
156  
157  struct pkg_audit_extract_cbdata {
158  	int out;
159  	const char *fname;
160  	const char *dest;
161  };
162  
163  static int
164  pkg_audit_sandboxed_extract(int fd, void *ud)
165  {
166  	struct pkg_audit_extract_cbdata *cbdata = ud;
167  	int rc = EPKG_OK;
168  	struct archive *a = NULL;
169  	struct archive_entry *ae = NULL;
170  
171  	a = archive_read_new();
172  #if ARCHIVE_VERSION_NUMBER < 3000002
173  	archive_read_support_compression_all(a);
174  #else
175  	archive_read_support_filter_all(a);
176  #endif
177  
178  	archive_read_support_format_raw(a);
179  
180  	if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) {
181  		pkg_emit_error("archive_read_open_filename(%s) failed: %s",
182  				cbdata->fname, archive_error_string(a));
183  		rc = EPKG_FATAL;
184  	}
185  	else {
186  		while (archive_read_next_header(a, &ae) == ARCHIVE_OK) {
187  			if (archive_read_data_into_fd(a, cbdata->out) != ARCHIVE_OK) {
188  				pkg_emit_error("archive_read_data_into_fd(%s) failed: %s",
189  						cbdata->dest, archive_error_string(a));
190  				break;
191  			}
192  		}
193  		archive_read_close(a);
194  		archive_read_free(a);
195  	}
196  
197  	return (rc);
198  }
199  
200  int
201  pkg_audit_fetch(const char *src, const char *dest)
202  {
203  	int fd = -1, outfd = -1;
204  	char tmp[MAXPATHLEN];
205  	const char *tmpdir;
206  	int retcode = EPKG_FATAL;
207  	time_t t = 0;
208  	struct stat st;
209  	struct pkg_audit_extract_cbdata cbdata;
210  	int dfd = -1;
211  	struct timespec ts[2] = {
212  		{
213  		.tv_nsec = 0
214  		},
215  		{
216  		.tv_nsec = 0
217  		}
218  	};
219  
220  	if (src == NULL) {
221  		src = pkg_object_string(pkg_config_get("VULNXML_SITE"));
222  	}
223  
224  	tmpdir = getenv("TMPDIR");
225  	if (tmpdir == NULL)
226  		tmpdir = "/tmp";
227  
228  	strlcpy(tmp, tmpdir, sizeof(tmp));
229  	strlcat(tmp, "/vuln.xml.XXXXXXXXXX", sizeof(tmp));
230  
231  	if (dest != NULL) {
232  		if (stat(dest, &st) != -1)
233  			t = st.st_mtime;
234  	} else {
235  		dfd = pkg_get_dbdirfd();
236  		if (fstatat(dfd, "vuln.xml", &st, 0) != -1)
237  			t = st.st_mtime;
238  	}
239  
240  	switch (pkg_fetch_file_tmp(NULL, src, tmp, t, &fd)) {
241  	case EPKG_OK:
242  		break;
243  	case EPKG_UPTODATE:
244  		pkg_emit_notice("vulnxml file up-to-date");
245  		retcode = EPKG_OK;
246  		goto cleanup;
247  	default:
248  		pkg_emit_error("cannot fetch vulnxml file");
249  		goto cleanup;
250  	}
251  	/* Open out fd */
252  	if (dest != NULL) {
253  		outfd = open(dest, O_RDWR|O_CREAT|O_TRUNC,
254  		    S_IRUSR|S_IRGRP|S_IROTH);
255  	} else {
256  		outfd = openat(dfd, "vuln.xml", O_RDWR|O_CREAT|O_TRUNC,
257  		    S_IRUSR|S_IRGRP|S_IROTH);
258  	}
259  	if (outfd == -1) {
260  		pkg_emit_errno("pkg_audit_fetch", "open out fd");
261  		goto cleanup;
262  	}
263  
264  	cbdata.fname = tmp;
265  	cbdata.out = outfd;
266  	cbdata.dest = dest;
267  	fstat(fd, &st);
268  
269  	/* Call sandboxed */
270  	retcode = pkg_emit_sandbox_call(pkg_audit_sandboxed_extract, fd, &cbdata);
271  	ts[0].tv_sec = st.st_mtime;
272  	ts[1].tv_sec = st.st_mtime;
273  	futimens(outfd, ts);
274  
275  cleanup:
276  	if (fd != -1)
277  		close(fd);
278  	if (outfd != -1)
279  		close(outfd);
280  
281  	return (retcode);
282  }
283  
284  /*
285   * Expand multiple names to a set of audit entries
286   */
287  static void
288  pkg_audit_expand_entry(struct pkg_audit_entry *entry, struct pkg_audit_entry **head)
289  {
290  	struct pkg_audit_entry *n;
291  	struct pkg_audit_pkgname *ncur;
292  	struct pkg_audit_package *pcur;
293  
294  	/* Set the name of the current entry */
295  	if (entry->packages == NULL || entry->packages->names == NULL) {
296  		pkg_audit_free_entry(entry);
297  		return;
298  	}
299  
300  	LL_FOREACH(entry->packages, pcur) {
301  		LL_FOREACH(pcur->names, ncur) {
302  			n = xcalloc(1, sizeof(struct pkg_audit_entry));
303  			n->pkgname = ncur->pkgname;
304  			/* Set new entry as reference entry */
305  			n->ref = true;
306  			n->cve = entry->cve;
307  			n->desc = entry->desc;
308  			n->versions = pcur->versions;
309  			n->url = entry->url;
310  			n->id = entry->id;
311  			LL_PREPEND(*head, n);
312  		}
313  	}
314  	LL_PREPEND(*head, entry);
315  }
316  
317  enum vulnxml_parse_state {
318  	VULNXML_PARSE_INIT = 0,
319  	VULNXML_PARSE_VULN,
320  	VULNXML_PARSE_TOPIC,
321  	VULNXML_PARSE_PACKAGE,
322  	VULNXML_PARSE_PACKAGE_NAME,
323  	VULNXML_PARSE_RANGE,
324  	VULNXML_PARSE_RANGE_GT,
325  	VULNXML_PARSE_RANGE_GE,
326  	VULNXML_PARSE_RANGE_LT,
327  	VULNXML_PARSE_RANGE_LE,
328  	VULNXML_PARSE_RANGE_EQ,
329  	VULNXML_PARSE_CVE
330  };
331  
332  enum vulnxml_parse_attribute_state {
333  	VULNXML_ATTR_NONE = 0,
334  	VULNXML_ATTR_VID,
335  };
336  
337  struct vulnxml_userdata {
338  	struct pkg_audit_entry *cur_entry;
339  	struct pkg_audit *audit;
340  	enum vulnxml_parse_state state;
341  	xstring *content;
342  	int range_num;
343  	enum vulnxml_parse_attribute_state attr;
344  };
345  
346  static void
347  vulnxml_start_element(struct vulnxml_userdata *ud, yxml_t *xml)
348  {
349  	struct pkg_audit_versions_range *vers;
350  	struct pkg_audit_pkgname *name_entry;
351  	struct pkg_audit_package *pkg_entry;
352  
353  	if (ud->state == VULNXML_PARSE_INIT && STRIEQ(xml->elem, "vuln")) {
354  		ud->cur_entry = xcalloc(1, sizeof(struct pkg_audit_entry));
355  		ud->cur_entry->next = ud->audit->entries;
356  		ud->state = VULNXML_PARSE_VULN;
357  	}
358  	else if (ud->state == VULNXML_PARSE_VULN && STRIEQ(xml->elem, "topic")) {
359  		ud->state = VULNXML_PARSE_TOPIC;
360  	}
361  	else if (ud->state == VULNXML_PARSE_VULN && STRIEQ(xml->elem, "package")) {
362  		pkg_entry = xcalloc(1, sizeof(struct pkg_audit_package));
363  		LL_PREPEND(ud->cur_entry->packages, pkg_entry);
364  		ud->state = VULNXML_PARSE_PACKAGE;
365  	}
366  	else if (ud->state == VULNXML_PARSE_VULN && STRIEQ(xml->elem, "cvename")) {
367  		ud->state = VULNXML_PARSE_CVE;
368  	}
369  	else if (ud->state == VULNXML_PARSE_PACKAGE && STRIEQ(xml->elem, "name")) {
370  		ud->state = VULNXML_PARSE_PACKAGE_NAME;
371  		name_entry = xcalloc(1, sizeof(struct pkg_audit_pkgname));
372  		LL_PREPEND(ud->cur_entry->packages->names, name_entry);
373  	}
374  	else if (ud->state == VULNXML_PARSE_PACKAGE && STRIEQ(xml->elem, "range")) {
375  		ud->state = VULNXML_PARSE_RANGE;
376  		vers = xcalloc(1, sizeof(struct pkg_audit_versions_range));
377  		LL_PREPEND(ud->cur_entry->packages->versions, vers);
378  		ud->range_num = 0;
379  	}
380  	else if (ud->state == VULNXML_PARSE_RANGE && STRIEQ(xml->elem, "gt")) {
381  		ud->range_num ++;
382  		ud->state = VULNXML_PARSE_RANGE_GT;
383  	}
384  	else if (ud->state == VULNXML_PARSE_RANGE && STRIEQ(xml->elem, "ge")) {
385  		ud->range_num ++;
386  		ud->state = VULNXML_PARSE_RANGE_GE;
387  	}
388  	else if (ud->state == VULNXML_PARSE_RANGE && STRIEQ(xml->elem, "lt")) {
389  		ud->range_num ++;
390  		ud->state = VULNXML_PARSE_RANGE_LT;
391  	}
392  	else if (ud->state == VULNXML_PARSE_RANGE && STRIEQ(xml->elem, "le")) {
393  		ud->range_num ++;
394  		ud->state = VULNXML_PARSE_RANGE_LE;
395  	}
396  	else if (ud->state == VULNXML_PARSE_RANGE && STRIEQ(xml->elem, "eq")) {
397  		ud->range_num ++;
398  		ud->state = VULNXML_PARSE_RANGE_EQ;
399  	}
400  }
401  
402  static void
403  vulnxml_end_element(struct vulnxml_userdata *ud, yxml_t *xml)
404  {
405  	struct pkg_audit_cve *cve;
406  	struct pkg_audit_entry *entry;
407  	struct pkg_audit_versions_range *vers;
408  	int range_type = -1;
409  
410  	fflush(ud->content->fp);
411  	if (ud->state == VULNXML_PARSE_VULN && STRIEQ(xml->elem, "vuxml")) {
412  		pkg_audit_expand_entry(ud->cur_entry, &ud->audit->entries);
413  		ud->state = VULNXML_PARSE_INIT;
414  	}
415  	else if (ud->state == VULNXML_PARSE_TOPIC && STRIEQ(xml->elem, "vuln")) {
416  		ud->cur_entry->desc = xstrdup(ud->content->buf);
417  		ud->state = VULNXML_PARSE_VULN;
418  	}
419  	else if (ud->state == VULNXML_PARSE_CVE && STRIEQ(xml->elem, "references")) {
420  		entry = ud->cur_entry;
421  		cve = xmalloc(sizeof(struct pkg_audit_cve));
422  		cve->cvename = xstrdup(ud->content->buf);
423  		LL_PREPEND(entry->cve, cve);
424  		ud->state = VULNXML_PARSE_VULN;
425  	}
426  	else if (ud->state == VULNXML_PARSE_PACKAGE && STRIEQ(xml->elem, "affects")) {
427  		ud->state = VULNXML_PARSE_VULN;
428  	}
429  	else if (ud->state == VULNXML_PARSE_PACKAGE_NAME && STRIEQ(xml->elem, "package")) {
430  		ud->cur_entry->packages->names->pkgname = xstrdup(ud->content->buf);
431  		ud->state = VULNXML_PARSE_PACKAGE;
432  	}
433  	else if (ud->state == VULNXML_PARSE_RANGE && STRIEQ(xml->elem, "package")) {
434  		ud->state = VULNXML_PARSE_PACKAGE;
435  	}
436  	else if (ud->state == VULNXML_PARSE_RANGE_GT && STRIEQ(xml->elem, "range")) {
437  		range_type = GT;
438  		ud->state = VULNXML_PARSE_RANGE;
439  	}
440  	else if (ud->state == VULNXML_PARSE_RANGE_GE && STRIEQ(xml->elem, "range")) {
441  		range_type = GTE;
442  		ud->state = VULNXML_PARSE_RANGE;
443  	}
444  	else if (ud->state == VULNXML_PARSE_RANGE_LT && STRIEQ(xml->elem, "range")) {
445  		range_type = LT;
446  		ud->state = VULNXML_PARSE_RANGE;
447  	}
448  	else if (ud->state == VULNXML_PARSE_RANGE_LE && STRIEQ(xml->elem, "range")) {
449  		range_type = LTE;
450  		ud->state = VULNXML_PARSE_RANGE;
451  	}
452  	else if (ud->state == VULNXML_PARSE_RANGE_EQ && STRIEQ(xml->elem, "range")) {
453  		range_type = EQ;
454  		ud->state = VULNXML_PARSE_RANGE;
455  	}
456  
457  	if (range_type > 0) {
458  		vers = ud->cur_entry->packages->versions;
459  		if (ud->range_num == 1) {
460  			vers->v1.version = xstrdup(ud->content->buf);
461  			vers->v1.type = range_type;
462  		}
463  		else if (ud->range_num == 2) {
464  			vers->v2.version = xstrdup(ud->content->buf);
465  			vers->v2.type = range_type;
466  		}
467  	}
468  	xstring_reset(ud->content);
469  }
470  
471  static void
472  vulnxml_start_attribute(struct vulnxml_userdata *ud, yxml_t *xml)
473  {
474  	if (ud->state != VULNXML_PARSE_VULN)
475  		return;
476  
477  	if (STRIEQ(xml->attr, "vid"))
478  		ud->attr = VULNXML_ATTR_VID;
479  }
480  
481  static void
482  vulnxml_end_attribute(struct vulnxml_userdata *ud, yxml_t *xml __unused)
483  {
484  	fflush(ud->content->fp);
485  	if (ud->state == VULNXML_PARSE_VULN && ud->attr == VULNXML_ATTR_VID) {
486  		ud->cur_entry->id = xstrdup(ud->content->buf);
487  		ud->attr = VULNXML_ATTR_NONE;
488  	}
489  	xstring_reset(ud->content);
490  }
491  
492  static void
493  vulnxml_val_attribute(struct vulnxml_userdata *ud, yxml_t *xml)
494  {
495  	if (ud->state == VULNXML_PARSE_VULN && ud->attr == VULNXML_ATTR_VID) {
496  		fputs(xml->data, ud->content->fp);
497  	}
498  }
499  
500  static void
501  vulnxml_handle_data(struct vulnxml_userdata *ud, yxml_t *xml)
502  {
503  
504  	switch(ud->state) {
505  	case VULNXML_PARSE_INIT:
506  	case VULNXML_PARSE_VULN:
507  	case VULNXML_PARSE_PACKAGE:
508  	case VULNXML_PARSE_RANGE:
509  		/* On these states we do not need any data */
510  		break;
511  	case VULNXML_PARSE_TOPIC:
512  	case VULNXML_PARSE_PACKAGE_NAME:
513  	case VULNXML_PARSE_CVE:
514  	case VULNXML_PARSE_RANGE_GT:
515  	case VULNXML_PARSE_RANGE_GE:
516  	case VULNXML_PARSE_RANGE_LT:
517  	case VULNXML_PARSE_RANGE_LE:
518  	case VULNXML_PARSE_RANGE_EQ:
519  		fputs(xml->data, ud->content->fp);
520  		break;
521  	}
522  }
523  
524  static int
525  pkg_audit_parse_vulnxml(struct pkg_audit *audit)
526  {
527  	int ret = EPKG_FATAL;
528  	yxml_t x;
529  	yxml_ret_t r;
530  	char buf[BUFSIZ];
531  	char *walk, *end;
532  	struct vulnxml_userdata ud;
533  
534  	yxml_init(&x, buf, BUFSIZ);
535  	ud.cur_entry = NULL;
536  	ud.audit = audit;
537  	ud.range_num = 0;
538  	ud.state = VULNXML_PARSE_INIT;
539  	ud.content = xstring_new();
540  
541  	walk = audit->map;
542  	end = walk + audit->len;
543  	while (walk < end) {
544  		r = yxml_parse(&x, *walk++);
545  		switch (r) {
546  		case YXML_EEOF:
547  		case YXML_EREF:
548  		case YXML_ESTACK:
549  			pkg_emit_error("Unexpected EOF while parsing vulnxml");
550  			goto out;
551  		case YXML_ESYN:
552  			pkg_emit_error("Syntax error while parsing vulnxml");
553  			goto out;
554  		case YXML_ECLOSE:
555  			pkg_emit_error("Close tag does not match open tag line %d", x.line);
556  			goto out;
557  		case YXML_ELEMSTART:
558  			vulnxml_start_element(&ud, &x);
559  				break;
560  		case YXML_ELEMEND:
561  			vulnxml_end_element(&ud, &x);
562  			break;
563  		case YXML_CONTENT:
564  			vulnxml_handle_data(&ud, &x);
565  			break;
566  		case YXML_ATTRVAL:
567  			vulnxml_val_attribute(&ud, &x);
568  			break;
569  		case YXML_ATTRSTART:
570  			vulnxml_start_attribute(&ud, &x);
571  			break;
572  			/* ignore */
573  		case YXML_ATTREND:
574  			vulnxml_end_attribute(&ud, &x);
575  			/* ignore */
576  			break;
577  		case YXML_OK:
578  		case YXML_PISTART:
579  		case YXML_PICONTENT:
580  		case YXML_PIEND:
581  			break;
582  		}
583  	}
584  
585  	if (yxml_eof(&x) == YXML_OK)
586  		ret = EPKG_OK;
587  	else
588  		pkg_emit_error("Invalid end of XML");
589  out:
590  	xstring_free(ud.content);
591  
592  	return (ret);
593  }
594  
595  /*
596   * Returns the length of the largest prefix without globbing
597   * characters, as per fnmatch().
598   */
599  static size_t
600  pkg_audit_str_noglob_len(const char *s)
601  {
602  	size_t n;
603  
604  	for (n = 0; s[n] && s[n] != '*' && s[n] != '?' &&
605  	    s[n] != '[' && s[n] != '{' && s[n] != '\\'; n++);
606  
607  	return (n);
608  }
609  
610  /*
611   * Helper for quicksort that lexicographically orders prefixes.
612   */
613  static int
614  pkg_audit_entry_cmp(const void *a, const void *b)
615  {
616  	const struct pkg_audit_item *e1, *e2;
617  	size_t min_len;
618  	int result;
619  
620  	e1 = (const struct pkg_audit_item *)a;
621  	e2 = (const struct pkg_audit_item *)b;
622  
623  	min_len = (e1->noglob_len < e2->noglob_len ?
624  	    e1->noglob_len : e2->noglob_len);
625  	result = strncmp(e1->e->pkgname, e2->e->pkgname, min_len);
626  	/*
627  	 * Additional check to see if some word is a prefix of an
628  	 * another one and, thus, should go before the former.
629  	 */
630  	if (result == 0) {
631  		if (e1->noglob_len < e2->noglob_len)
632  			result = -1;
633  		else if (e1->noglob_len > e2->noglob_len)
634  			result = 1;
635  	}
636  
637  	return (result);
638  }
639  
640  /*
641   * Sorts VuXML entries and calculates increments to jump to the
642   * next distinct prefix.
643   */
644  static struct pkg_audit_item *
645  pkg_audit_preprocess(struct pkg_audit_entry *h)
646  {
647  	struct pkg_audit_entry *e;
648  	struct pkg_audit_item *ret;
649  	size_t i, n, tofill;
650  
651  	n = 0;
652  	LL_FOREACH(h, e)
653  		n++;
654  
655  	ret = xcalloc(n + 1, sizeof(ret[0]));
656  	n = 0;
657  	LL_FOREACH(h, e) {
658  		if (e->pkgname != NULL) {
659  			ret[n].e = e;
660  			ret[n].noglob_len = pkg_audit_str_noglob_len(e->pkgname);
661  			ret[n].next_pfx_incr = 1;
662  			n++;
663  		}
664  	}
665  
666  	qsort(ret, n, sizeof(*ret), pkg_audit_entry_cmp);
667  
668  	if (n < 2)
669  		goto first_byte_idx;
670  
671  	/*
672  	 * Determining jump indexes to the next different prefix.
673  	 * Only non-1 increments are calculated there.
674  	 *
675  	 * Due to the current usage that picks only increment for the
676  	 * first of the non-unique prefixes in a row, we could
677  	 * calculate only that one and skip calculations for the
678  	 * succeeding, but for the uniformity and clarity we're
679  	 * calculating 'em all.
680  	 */
681  	for (n = 1, tofill = 0; ret[n].e; n++) {
682  		if (ret[n - 1].noglob_len != ret[n].noglob_len) {
683  			struct pkg_audit_item *base;
684  
685  			base = ret + n - tofill;
686  			for (i = 0; tofill > 1; i++, tofill--)
687  				base[i].next_pfx_incr = tofill;
688  			tofill = 1;
689  		} else if (STREQ(ret[n - 1].e->pkgname, ret[n].e->pkgname)) {
690  			tofill++;
691  		} else {
692  			tofill = 1;
693  		}
694  	}
695  
696  first_byte_idx:
697  	/* Calculate jump indexes for the first byte of the package name */
698  	memset(audit_entry_first_byte_idx, '\0', sizeof(audit_entry_first_byte_idx));
699  	for (n = 1, i = 0; n < 256; n++) {
700  		while (ret[i].e != NULL &&
701  		    (size_t)(ret[i].e->pkgname[0]) < n)
702  			i++;
703  		audit_entry_first_byte_idx[n] = i;
704  	}
705  
706  	return (ret);
707  }
708  
709  static bool
710  pkg_audit_version_match(const char *pkgversion, struct pkg_audit_version *v)
711  {
712  	bool res = false;
713  
714  	/*
715  	 * Return true so it is easier for the caller to handle case where there is
716  	 * only one version to match: the missing one will always match.
717  	 */
718  	if (v->version == NULL)
719  		return (true);
720  
721  	switch (pkg_version_cmp(pkgversion, v->version)) {
722  	case -1:
723  		if (v->type == LT || v->type == LTE)
724  			res = true;
725  		break;
726  	case 0:
727  		if (v->type == EQ || v->type == LTE || v->type == GTE)
728  			res = true;
729  		break;
730  	case 1:
731  		if (v->type == GT || v->type == GTE)
732  			res = true;
733  		break;
734  	}
735  	return (res);
736  }
737  
738  static void
739  pkg_audit_add_entry(struct pkg_audit_entry *e, struct pkg_audit_issues **ai)
740  {
741  	struct pkg_audit_issue *issue;
742  
743  	if (*ai == NULL)
744  		*ai = xcalloc(1, sizeof(**ai));
745  	issue = xcalloc(1, sizeof(*issue));
746  	issue->audit = e;
747  	(*ai)->count++;
748  	LL_APPEND((*ai)->issues, issue);
749  }
750  
751  bool
752  pkg_audit_is_vulnerable(struct pkg_audit *audit, struct pkg *pkg,
753      struct pkg_audit_issues **ai, bool stop_quick)
754  {
755  	struct pkg_audit_entry *e;
756  	struct pkg_audit_versions_range *vers;
757  	struct pkg_audit_item *a;
758  	bool res = false, res1, res2;
759  
760  	if (!audit->parsed)
761  		return false;
762  
763  	/* check if we decided to ignore that package or not */
764  	if (match_ucl_lists(pkg->name,
765  	    pkg_config_get("AUDIT_IGNORE_GLOB"),
766  	    pkg_config_get("AUDIT_IGNORE_REGEX")))
767  		return (false);
768  
769  	a = audit->items;
770  	a += audit_entry_first_byte_idx[(size_t)pkg->name[0]];
771  
772  	for (; (e = a->e) != NULL; a += a->next_pfx_incr) {
773  		int cmp;
774  		size_t i;
775  
776  		/*
777  		 * Audit entries are sorted, so if we had found one
778  		 * that is lexicographically greater than our name,
779  		 * it and the rest won't match our name.
780  		 */
781  		cmp = strncmp(pkg->name, e->pkgname, a->noglob_len);
782  		if (cmp > 0)
783  			continue;
784  		else if (cmp < 0)
785  			break;
786  
787  		for (i = 0; i < a->next_pfx_incr; i++) {
788  			e = a[i].e;
789  			if (fnmatch(e->pkgname, pkg->name, 0) != 0)
790  				continue;
791  
792  			if (pkg->version == NULL) {
793  				/*
794  				 * Assume that all versions should be checked
795  				 */
796  				res = true;
797  				pkg_audit_add_entry(e, ai);
798  			}
799  			else {
800  				LL_FOREACH(e->versions, vers) {
801  					res1 = pkg_audit_version_match(pkg->version, &vers->v1);
802  					res2 = pkg_audit_version_match(pkg->version, &vers->v2);
803  
804  					if (res1 && res2) {
805  						res = true;
806  						pkg_audit_add_entry(e, ai);
807  						break;
808  					}
809  				}
810  			}
811  
812  			if (res && stop_quick)
813  				return (res);
814  		}
815  	}
816  
817  	return (res);
818  }
819  
820  struct pkg_audit *
821  pkg_audit_new(void)
822  {
823  	struct pkg_audit *audit;
824  
825  	audit = xcalloc(1, sizeof(struct pkg_audit));
826  
827  	return (audit);
828  }
829  
830  int
831  pkg_audit_load(struct pkg_audit *audit, const char *fname)
832  {
833  	int dfd, fd;
834  	void *mem;
835  	struct stat st;
836  
837  	if (fname != NULL) {
838  		if ((fd = open(fname, O_RDONLY)) == -1)
839  			return (EPKG_FATAL);
840  	} else {
841  		dfd = pkg_get_dbdirfd();
842  		if ((fd = openat(dfd, "vuln.xml", O_RDONLY)) == -1)
843  			return (EPKG_FATAL);
844  	}
845  
846  	if (fstat(fd, &st) == -1) {
847  		close(fd);
848  		return (EPKG_FATAL);
849  	}
850  
851  	if ((mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
852  		close(fd);
853  		return (EPKG_FATAL);
854  	}
855  	close(fd);
856  
857  	audit->map = mem;
858  	audit->len = st.st_size;
859  	audit->loaded = true;
860  
861  	return (EPKG_OK);
862  }
863  
864  /* This can and should be executed after cap_enter(3) */
865  int
866  pkg_audit_process(struct pkg_audit *audit)
867  {
868  	if (geteuid() == 0)
869  		return (EPKG_FATAL);
870  
871  	if (!audit->loaded)
872  		return (EPKG_FATAL);
873  
874  	if (pkg_audit_parse_vulnxml(audit) == EPKG_FATAL)
875  		return (EPKG_FATAL);
876  
877  	audit->items = pkg_audit_preprocess(audit->entries);
878  	audit->parsed = true;
879  
880  	return (EPKG_OK);
881  }
882  
883  void
884  pkg_audit_free (struct pkg_audit *audit)
885  {
886  	if (audit != NULL) {
887  		if (audit->parsed) {
888  			pkg_audit_free_list(audit->entries);
889  			free(audit->items);
890  		}
891  		if (audit->loaded) {
892  			munmap(audit->map, audit->len);
893  		}
894  		free(audit);
895  	}
896  }
897  
898  void
899  pkg_audit_issues_free(struct pkg_audit_issues *issues)
900  {
901  	struct pkg_audit_issue *i, *issue;
902  
903  	if (issues == NULL)
904  		return;
905  
906  	LL_FOREACH_SAFE(issues->issues, issue, i) {
907  		LL_DELETE(issues->issues, issue);
908  		free(issue);
909  	}
910  	free(issues);
911  }