Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/libpkg/pkg_audit.c
2065 views
1
/*-
2
* Copyright (c) 2011-2012 Julien Laffaye <[email protected]>
3
* Copyright (c) 2014 Matthew Seaman <[email protected]>
4
* Copyright (c) 2014 Vsevolod Stakhov <[email protected]>
5
* Copyright (c) 2020 Baptiste Daroussin <[email protected]>
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
}
133
LL_FOREACH_SAFE(e->cve, cve, cve_tmp) {
134
free(cve->cvename);
135
free(cve);
136
}
137
free(e->url);
138
free(e->desc);
139
free(e->id);
140
}
141
free(e);
142
}
143
144
static void
145
pkg_audit_free_list(struct pkg_audit_entry *h)
146
{
147
struct pkg_audit_entry *e;
148
149
while (h) {
150
e = h;
151
h = h->next;
152
pkg_audit_free_entry(e);
153
}
154
}
155
156
struct pkg_audit_extract_cbdata {
157
int out;
158
const char *fname;
159
const char *dest;
160
};
161
162
static int
163
pkg_audit_sandboxed_extract(int fd, void *ud)
164
{
165
struct pkg_audit_extract_cbdata *cbdata = ud;
166
int rc = EPKG_OK;
167
struct archive *a = NULL;
168
struct archive_entry *ae = NULL;
169
170
a = archive_read_new();
171
#if ARCHIVE_VERSION_NUMBER < 3000002
172
archive_read_support_compression_all(a);
173
#else
174
archive_read_support_filter_all(a);
175
#endif
176
177
archive_read_support_format_raw(a);
178
179
if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) {
180
pkg_emit_error("archive_read_open_filename(%s) failed: %s",
181
cbdata->fname, archive_error_string(a));
182
rc = EPKG_FATAL;
183
}
184
else {
185
while (archive_read_next_header(a, &ae) == ARCHIVE_OK) {
186
if (archive_read_data_into_fd(a, cbdata->out) != ARCHIVE_OK) {
187
pkg_emit_error("archive_read_data_into_fd(%s) failed: %s",
188
cbdata->dest, archive_error_string(a));
189
break;
190
}
191
}
192
archive_read_close(a);
193
archive_read_free(a);
194
}
195
196
return (rc);
197
}
198
199
int
200
pkg_audit_fetch(const char *src, const char *dest)
201
{
202
int fd = -1, outfd = -1;
203
char tmp[MAXPATHLEN];
204
const char *tmpdir;
205
int retcode = EPKG_FATAL;
206
time_t t = 0;
207
struct stat st;
208
struct pkg_audit_extract_cbdata cbdata;
209
int dfd = -1;
210
struct timespec ts[2] = {
211
{
212
.tv_nsec = 0
213
},
214
{
215
.tv_nsec = 0
216
}
217
};
218
219
if (src == NULL) {
220
src = pkg_object_string(pkg_config_get("VULNXML_SITE"));
221
}
222
223
tmpdir = getenv("TMPDIR");
224
if (tmpdir == NULL)
225
tmpdir = "/tmp";
226
227
strlcpy(tmp, tmpdir, sizeof(tmp));
228
strlcat(tmp, "/vuln.xml.XXXXXXXXXX", sizeof(tmp));
229
230
if (dest != NULL) {
231
if (stat(dest, &st) != -1)
232
t = st.st_mtime;
233
} else {
234
dfd = pkg_get_dbdirfd();
235
if (fstatat(dfd, "vuln.xml", &st, 0) != -1)
236
t = st.st_mtime;
237
}
238
239
switch (pkg_fetch_file_tmp(NULL, src, tmp, t)) {
240
case EPKG_OK:
241
break;
242
case EPKG_UPTODATE:
243
pkg_emit_notice("vulnxml file up-to-date");
244
retcode = EPKG_OK;
245
goto cleanup;
246
default:
247
pkg_emit_error("cannot fetch vulnxml file");
248
goto cleanup;
249
}
250
251
/* Open input fd */
252
fd = open(tmp, O_RDONLY);
253
if (fd == -1)
254
goto cleanup;
255
/* Open out fd */
256
if (dest != NULL) {
257
outfd = open(dest, O_RDWR|O_CREAT|O_TRUNC,
258
S_IRUSR|S_IRGRP|S_IROTH);
259
} else {
260
outfd = openat(dfd, "vuln.xml", O_RDWR|O_CREAT|O_TRUNC,
261
S_IRUSR|S_IRGRP|S_IROTH);
262
}
263
if (outfd == -1) {
264
pkg_emit_errno("pkg_audit_fetch", "open out fd");
265
goto cleanup;
266
}
267
268
cbdata.fname = tmp;
269
cbdata.out = outfd;
270
cbdata.dest = dest;
271
fstat(fd, &st);
272
273
/* Call sandboxed */
274
retcode = pkg_emit_sandbox_call(pkg_audit_sandboxed_extract, fd, &cbdata);
275
ts[0].tv_sec = st.st_mtime;
276
ts[1].tv_sec = st.st_mtime;
277
futimens(outfd, ts);
278
279
cleanup:
280
unlink(tmp);
281
282
if (fd != -1)
283
close(fd);
284
if (outfd != -1)
285
close(outfd);
286
287
return (retcode);
288
}
289
290
/*
291
* Expand multiple names to a set of audit entries
292
*/
293
static void
294
pkg_audit_expand_entry(struct pkg_audit_entry *entry, struct pkg_audit_entry **head)
295
{
296
struct pkg_audit_entry *n;
297
struct pkg_audit_pkgname *ncur;
298
struct pkg_audit_package *pcur;
299
300
/* Set the name of the current entry */
301
if (entry->packages == NULL || entry->packages->names == NULL) {
302
pkg_audit_free_entry(entry);
303
return;
304
}
305
306
LL_FOREACH(entry->packages, pcur) {
307
LL_FOREACH(pcur->names, ncur) {
308
n = xcalloc(1, sizeof(struct pkg_audit_entry));
309
n->pkgname = ncur->pkgname;
310
/* Set new entry as reference entry */
311
n->ref = true;
312
n->cve = entry->cve;
313
n->desc = entry->desc;
314
n->versions = pcur->versions;
315
n->url = entry->url;
316
n->id = entry->id;
317
LL_PREPEND(*head, n);
318
}
319
}
320
LL_PREPEND(*head, entry);
321
}
322
323
enum vulnxml_parse_state {
324
VULNXML_PARSE_INIT = 0,
325
VULNXML_PARSE_VULN,
326
VULNXML_PARSE_TOPIC,
327
VULNXML_PARSE_PACKAGE,
328
VULNXML_PARSE_PACKAGE_NAME,
329
VULNXML_PARSE_RANGE,
330
VULNXML_PARSE_RANGE_GT,
331
VULNXML_PARSE_RANGE_GE,
332
VULNXML_PARSE_RANGE_LT,
333
VULNXML_PARSE_RANGE_LE,
334
VULNXML_PARSE_RANGE_EQ,
335
VULNXML_PARSE_CVE
336
};
337
338
enum vulnxml_parse_attribute_state {
339
VULNXML_ATTR_NONE = 0,
340
VULNXML_ATTR_VID,
341
};
342
343
struct vulnxml_userdata {
344
struct pkg_audit_entry *cur_entry;
345
struct pkg_audit *audit;
346
enum vulnxml_parse_state state;
347
xstring *content;
348
int range_num;
349
enum vulnxml_parse_attribute_state attr;
350
};
351
352
static void
353
vulnxml_start_element(struct vulnxml_userdata *ud, yxml_t *xml)
354
{
355
struct pkg_audit_versions_range *vers;
356
struct pkg_audit_pkgname *name_entry;
357
struct pkg_audit_package *pkg_entry;
358
359
if (ud->state == VULNXML_PARSE_INIT && STRIEQ(xml->elem, "vuln")) {
360
ud->cur_entry = xcalloc(1, sizeof(struct pkg_audit_entry));
361
ud->cur_entry->next = ud->audit->entries;
362
ud->state = VULNXML_PARSE_VULN;
363
}
364
else if (ud->state == VULNXML_PARSE_VULN && STRIEQ(xml->elem, "topic")) {
365
ud->state = VULNXML_PARSE_TOPIC;
366
}
367
else if (ud->state == VULNXML_PARSE_VULN && STRIEQ(xml->elem, "package")) {
368
pkg_entry = xcalloc(1, sizeof(struct pkg_audit_package));
369
LL_PREPEND(ud->cur_entry->packages, pkg_entry);
370
ud->state = VULNXML_PARSE_PACKAGE;
371
}
372
else if (ud->state == VULNXML_PARSE_VULN && STRIEQ(xml->elem, "cvename")) {
373
ud->state = VULNXML_PARSE_CVE;
374
}
375
else if (ud->state == VULNXML_PARSE_PACKAGE && STRIEQ(xml->elem, "name")) {
376
ud->state = VULNXML_PARSE_PACKAGE_NAME;
377
name_entry = xcalloc(1, sizeof(struct pkg_audit_pkgname));
378
LL_PREPEND(ud->cur_entry->packages->names, name_entry);
379
}
380
else if (ud->state == VULNXML_PARSE_PACKAGE && STRIEQ(xml->elem, "range")) {
381
ud->state = VULNXML_PARSE_RANGE;
382
vers = xcalloc(1, sizeof(struct pkg_audit_versions_range));
383
LL_PREPEND(ud->cur_entry->packages->versions, vers);
384
ud->range_num = 0;
385
}
386
else if (ud->state == VULNXML_PARSE_RANGE && STRIEQ(xml->elem, "gt")) {
387
ud->range_num ++;
388
ud->state = VULNXML_PARSE_RANGE_GT;
389
}
390
else if (ud->state == VULNXML_PARSE_RANGE && STRIEQ(xml->elem, "ge")) {
391
ud->range_num ++;
392
ud->state = VULNXML_PARSE_RANGE_GE;
393
}
394
else if (ud->state == VULNXML_PARSE_RANGE && STRIEQ(xml->elem, "lt")) {
395
ud->range_num ++;
396
ud->state = VULNXML_PARSE_RANGE_LT;
397
}
398
else if (ud->state == VULNXML_PARSE_RANGE && STRIEQ(xml->elem, "le")) {
399
ud->range_num ++;
400
ud->state = VULNXML_PARSE_RANGE_LE;
401
}
402
else if (ud->state == VULNXML_PARSE_RANGE && STRIEQ(xml->elem, "eq")) {
403
ud->range_num ++;
404
ud->state = VULNXML_PARSE_RANGE_EQ;
405
}
406
}
407
408
static void
409
vulnxml_end_element(struct vulnxml_userdata *ud, yxml_t *xml)
410
{
411
struct pkg_audit_cve *cve;
412
struct pkg_audit_entry *entry;
413
struct pkg_audit_versions_range *vers;
414
int range_type = -1;
415
416
fflush(ud->content->fp);
417
if (ud->state == VULNXML_PARSE_VULN && STRIEQ(xml->elem, "vuxml")) {
418
pkg_audit_expand_entry(ud->cur_entry, &ud->audit->entries);
419
ud->state = VULNXML_PARSE_INIT;
420
}
421
else if (ud->state == VULNXML_PARSE_TOPIC && STRIEQ(xml->elem, "vuln")) {
422
ud->cur_entry->desc = xstrdup(ud->content->buf);
423
ud->state = VULNXML_PARSE_VULN;
424
}
425
else if (ud->state == VULNXML_PARSE_CVE && STRIEQ(xml->elem, "references")) {
426
entry = ud->cur_entry;
427
cve = xmalloc(sizeof(struct pkg_audit_cve));
428
cve->cvename = xstrdup(ud->content->buf);
429
LL_PREPEND(entry->cve, cve);
430
ud->state = VULNXML_PARSE_VULN;
431
}
432
else if (ud->state == VULNXML_PARSE_PACKAGE && STRIEQ(xml->elem, "affects")) {
433
ud->state = VULNXML_PARSE_VULN;
434
}
435
else if (ud->state == VULNXML_PARSE_PACKAGE_NAME && STRIEQ(xml->elem, "package")) {
436
ud->cur_entry->packages->names->pkgname = xstrdup(ud->content->buf);
437
ud->state = VULNXML_PARSE_PACKAGE;
438
}
439
else if (ud->state == VULNXML_PARSE_RANGE && STRIEQ(xml->elem, "package")) {
440
ud->state = VULNXML_PARSE_PACKAGE;
441
}
442
else if (ud->state == VULNXML_PARSE_RANGE_GT && STRIEQ(xml->elem, "range")) {
443
range_type = GT;
444
ud->state = VULNXML_PARSE_RANGE;
445
}
446
else if (ud->state == VULNXML_PARSE_RANGE_GE && STRIEQ(xml->elem, "range")) {
447
range_type = GTE;
448
ud->state = VULNXML_PARSE_RANGE;
449
}
450
else if (ud->state == VULNXML_PARSE_RANGE_LT && STRIEQ(xml->elem, "range")) {
451
range_type = LT;
452
ud->state = VULNXML_PARSE_RANGE;
453
}
454
else if (ud->state == VULNXML_PARSE_RANGE_LE && STRIEQ(xml->elem, "range")) {
455
range_type = LTE;
456
ud->state = VULNXML_PARSE_RANGE;
457
}
458
else if (ud->state == VULNXML_PARSE_RANGE_EQ && STRIEQ(xml->elem, "range")) {
459
range_type = EQ;
460
ud->state = VULNXML_PARSE_RANGE;
461
}
462
463
if (range_type > 0) {
464
vers = ud->cur_entry->packages->versions;
465
if (ud->range_num == 1) {
466
vers->v1.version = xstrdup(ud->content->buf);
467
vers->v1.type = range_type;
468
}
469
else if (ud->range_num == 2) {
470
vers->v2.version = xstrdup(ud->content->buf);
471
vers->v2.type = range_type;
472
}
473
}
474
xstring_reset(ud->content);
475
}
476
477
static void
478
vulnxml_start_attribute(struct vulnxml_userdata *ud, yxml_t *xml)
479
{
480
if (ud->state != VULNXML_PARSE_VULN)
481
return;
482
483
if (STRIEQ(xml->attr, "vid"))
484
ud->attr = VULNXML_ATTR_VID;
485
}
486
487
static void
488
vulnxml_end_attribute(struct vulnxml_userdata *ud, yxml_t *xml __unused)
489
{
490
fflush(ud->content->fp);
491
if (ud->state == VULNXML_PARSE_VULN && ud->attr == VULNXML_ATTR_VID) {
492
ud->cur_entry->id = xstrdup(ud->content->buf);
493
ud->attr = VULNXML_ATTR_NONE;
494
}
495
xstring_reset(ud->content);
496
}
497
498
static void
499
vulnxml_val_attribute(struct vulnxml_userdata *ud, yxml_t *xml)
500
{
501
if (ud->state == VULNXML_PARSE_VULN && ud->attr == VULNXML_ATTR_VID) {
502
fputs(xml->data, ud->content->fp);
503
}
504
}
505
506
static void
507
vulnxml_handle_data(struct vulnxml_userdata *ud, yxml_t *xml)
508
{
509
510
switch(ud->state) {
511
case VULNXML_PARSE_INIT:
512
case VULNXML_PARSE_VULN:
513
case VULNXML_PARSE_PACKAGE:
514
case VULNXML_PARSE_RANGE:
515
/* On these states we do not need any data */
516
break;
517
case VULNXML_PARSE_TOPIC:
518
case VULNXML_PARSE_PACKAGE_NAME:
519
case VULNXML_PARSE_CVE:
520
case VULNXML_PARSE_RANGE_GT:
521
case VULNXML_PARSE_RANGE_GE:
522
case VULNXML_PARSE_RANGE_LT:
523
case VULNXML_PARSE_RANGE_LE:
524
case VULNXML_PARSE_RANGE_EQ:
525
fputs(xml->data, ud->content->fp);
526
break;
527
}
528
}
529
530
static int
531
pkg_audit_parse_vulnxml(struct pkg_audit *audit)
532
{
533
int ret = EPKG_FATAL;
534
yxml_t x;
535
yxml_ret_t r;
536
char buf[BUFSIZ];
537
char *walk, *end;
538
struct vulnxml_userdata ud;
539
540
yxml_init(&x, buf, BUFSIZ);
541
ud.cur_entry = NULL;
542
ud.audit = audit;
543
ud.range_num = 0;
544
ud.state = VULNXML_PARSE_INIT;
545
ud.content = xstring_new();
546
547
walk = audit->map;
548
end = walk + audit->len;
549
while (walk < end) {
550
r = yxml_parse(&x, *walk++);
551
switch (r) {
552
case YXML_EREF:
553
case YXML_ESTACK:
554
pkg_emit_error("Unexpected EOF while parsing vulnxml");
555
goto out;
556
case YXML_ESYN:
557
pkg_emit_error("Syntax error while parsing vulnxml");
558
goto out;
559
case YXML_ECLOSE:
560
pkg_emit_error("Close tag does not match open tag line %d", x.line);
561
goto out;
562
case YXML_ELEMSTART:
563
vulnxml_start_element(&ud, &x);
564
break;
565
case YXML_ELEMEND:
566
vulnxml_end_element(&ud, &x);
567
break;
568
case YXML_CONTENT:
569
vulnxml_handle_data(&ud, &x);
570
break;
571
case YXML_ATTRVAL:
572
vulnxml_val_attribute(&ud, &x);
573
break;
574
case YXML_ATTRSTART:
575
vulnxml_start_attribute(&ud, &x);
576
break;
577
/* ignore */
578
case YXML_ATTREND:
579
vulnxml_end_attribute(&ud, &x);
580
/* ignore */
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
/*
669
* Determining jump indexes to the next different prefix.
670
* Only non-1 increments are calculated there.
671
*
672
* Due to the current usage that picks only increment for the
673
* first of the non-unique prefixes in a row, we could
674
* calculate only that one and skip calculations for the
675
* succeeding, but for the uniformity and clarity we're
676
* calculating 'em all.
677
*/
678
for (n = 1, tofill = 0; ret[n].e; n++) {
679
if (ret[n - 1].noglob_len != ret[n].noglob_len) {
680
struct pkg_audit_item *base;
681
682
base = ret + n - tofill;
683
for (i = 0; tofill > 1; i++, tofill--)
684
base[i].next_pfx_incr = tofill;
685
tofill = 1;
686
} else if (STREQ(ret[n - 1].e->pkgname, ret[n].e->pkgname)) {
687
tofill++;
688
} else {
689
tofill = 1;
690
}
691
}
692
693
/* Calculate jump indexes for the first byte of the package name */
694
memset(audit_entry_first_byte_idx, '\0', sizeof(audit_entry_first_byte_idx));
695
for (n = 1, i = 0; n < 256; n++) {
696
while (ret[i].e != NULL &&
697
(size_t)(ret[i].e->pkgname[0]) < n)
698
i++;
699
audit_entry_first_byte_idx[n] = i;
700
}
701
702
return (ret);
703
}
704
705
static bool
706
pkg_audit_version_match(const char *pkgversion, struct pkg_audit_version *v)
707
{
708
bool res = false;
709
710
/*
711
* Return true so it is easier for the caller to handle case where there is
712
* only one version to match: the missing one will always match.
713
*/
714
if (v->version == NULL)
715
return (true);
716
717
switch (pkg_version_cmp(pkgversion, v->version)) {
718
case -1:
719
if (v->type == LT || v->type == LTE)
720
res = true;
721
break;
722
case 0:
723
if (v->type == EQ || v->type == LTE || v->type == GTE)
724
res = true;
725
break;
726
case 1:
727
if (v->type == GT || v->type == GTE)
728
res = true;
729
break;
730
}
731
return (res);
732
}
733
734
static void
735
pkg_audit_add_entry(struct pkg_audit_entry *e, struct pkg_audit_issues **ai)
736
{
737
struct pkg_audit_issue *issue;
738
739
if (*ai == NULL)
740
*ai = xcalloc(1, sizeof(**ai));
741
issue = xcalloc(1, sizeof(*issue));
742
issue->audit = e;
743
(*ai)->count++;
744
LL_APPEND((*ai)->issues, issue);
745
}
746
747
bool
748
pkg_audit_is_vulnerable(struct pkg_audit *audit, struct pkg *pkg,
749
struct pkg_audit_issues **ai, bool stop_quick)
750
{
751
struct pkg_audit_entry *e;
752
struct pkg_audit_versions_range *vers;
753
struct pkg_audit_item *a;
754
bool res = false, res1, res2;
755
756
if (!audit->parsed)
757
return false;
758
759
/* check if we decided to ignore that package or not */
760
if (match_ucl_lists(pkg->name,
761
pkg_config_get("AUDIT_IGNORE_GLOB"),
762
pkg_config_get("AUDIT_IGNORE_REGEX")))
763
return (false);
764
765
a = audit->items;
766
a += audit_entry_first_byte_idx[(size_t)pkg->name[0]];
767
768
for (; (e = a->e) != NULL; a += a->next_pfx_incr) {
769
int cmp;
770
size_t i;
771
772
/*
773
* Audit entries are sorted, so if we had found one
774
* that is lexicographically greater than our name,
775
* it and the rest won't match our name.
776
*/
777
cmp = strncmp(pkg->name, e->pkgname, a->noglob_len);
778
if (cmp > 0)
779
continue;
780
else if (cmp < 0)
781
break;
782
783
for (i = 0; i < a->next_pfx_incr; i++) {
784
e = a[i].e;
785
if (fnmatch(e->pkgname, pkg->name, 0) != 0)
786
continue;
787
788
if (pkg->version == NULL) {
789
/*
790
* Assume that all versions should be checked
791
*/
792
res = true;
793
pkg_audit_add_entry(e, ai);
794
}
795
else {
796
LL_FOREACH(e->versions, vers) {
797
res1 = pkg_audit_version_match(pkg->version, &vers->v1);
798
res2 = pkg_audit_version_match(pkg->version, &vers->v2);
799
800
if (res1 && res2) {
801
res = true;
802
pkg_audit_add_entry(e, ai);
803
break;
804
}
805
}
806
}
807
808
if (res && stop_quick)
809
return (res);
810
}
811
}
812
813
return (res);
814
}
815
816
struct pkg_audit *
817
pkg_audit_new(void)
818
{
819
struct pkg_audit *audit;
820
821
audit = xcalloc(1, sizeof(struct pkg_audit));
822
823
return (audit);
824
}
825
826
int
827
pkg_audit_load(struct pkg_audit *audit, const char *fname)
828
{
829
int dfd, fd;
830
void *mem;
831
struct stat st;
832
833
if (fname != NULL) {
834
if ((fd = open(fname, O_RDONLY)) == -1)
835
return (EPKG_FATAL);
836
} else {
837
dfd = pkg_get_dbdirfd();
838
if ((fd = openat(dfd, "vuln.xml", O_RDONLY)) == -1)
839
return (EPKG_FATAL);
840
}
841
842
if (fstat(fd, &st) == -1) {
843
close(fd);
844
return (EPKG_FATAL);
845
}
846
847
if ((mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
848
close(fd);
849
return (EPKG_FATAL);
850
}
851
close(fd);
852
853
audit->map = mem;
854
audit->len = st.st_size;
855
audit->loaded = true;
856
857
return (EPKG_OK);
858
}
859
860
/* This can and should be executed after cap_enter(3) */
861
int
862
pkg_audit_process(struct pkg_audit *audit)
863
{
864
if (geteuid() == 0)
865
return (EPKG_FATAL);
866
867
if (!audit->loaded)
868
return (EPKG_FATAL);
869
870
if (pkg_audit_parse_vulnxml(audit) == EPKG_FATAL)
871
return (EPKG_FATAL);
872
873
audit->items = pkg_audit_preprocess(audit->entries);
874
audit->parsed = true;
875
876
return (EPKG_OK);
877
}
878
879
void
880
pkg_audit_free (struct pkg_audit *audit)
881
{
882
if (audit != NULL) {
883
if (audit->parsed) {
884
pkg_audit_free_list(audit->entries);
885
free(audit->items);
886
}
887
if (audit->loaded) {
888
munmap(audit->map, audit->len);
889
}
890
free(audit);
891
}
892
}
893
894
void
895
pkg_audit_issues_free(struct pkg_audit_issues *issues)
896
{
897
struct pkg_audit_issue *i, *issue;
898
899
if (issues == NULL)
900
return;
901
902
LL_FOREACH_SAFE(issues->issues, issue, i) {
903
LL_DELETE(issues->issues, issue);
904
free(issue);
905
}
906
}
907
908