LCOV - code coverage report
Current view: top level - libpkg - pkg_audit.c (source / functions) Hit Total Coverage
Test: cov.info Lines: 0 386 0.0 %
Date: 2015-08-15 Functions: 0 20 0.0 %

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

Generated by: LCOV version 1.10