1 /* $NetBSD: audit.c,v 1.14 2009/08/02 17:56:44 joerg Exp $ */
10 __RCSID("$NetBSD: audit.c,v 1.14 2009/08/02 17:56:44 joerg Exp $");
13 * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>.
14 * All rights reserved.
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
20 * 1. Redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in
24 * the documentation and/or other materials provided with the
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
30 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
31 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
32 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
33 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
34 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
35 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
36 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
37 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 #include <sys/types.h>
68 #include <nbcompat/unistd.h>
76 static int check_eol
= 0;
77 static int check_signature
= 0;
78 static const char *limit_vul_types
= NULL
;
79 static int update_pkg_vuln
= 0;
81 static struct pkg_vulnerabilities
*pv
;
83 static const char audit_options
[] = "est:";
86 parse_options(int argc
, char **argv
, const char *options
)
92 * optind == 0 is interpreted as partial reset request
93 * by GNU getopt, so compensate against this and cleanup
100 while ((ch
= getopt(argc
, argv
, options
)) != -1) {
109 limit_vul_types
= optarg
;
120 --optind
; /* See above comment. */
124 check_exact_pkg(const char *pkg
)
126 return audit_package(pv
, pkg
, limit_vul_types
, check_eol
,
131 check_batch_exact_pkgs(const char *fname
)
134 char buf
[4096], *line
, *eol
;
138 if (strcmp(fname
, "-") == 0)
141 f
= fopen(fname
, "r");
143 err(EXIT_FAILURE
, "Failed to open input file %s",
146 while ((line
= fgets(buf
, sizeof(buf
), f
)) != NULL
) {
147 eol
= line
+ strlen(line
);
156 ret
|= check_exact_pkg(line
);
165 check_one_installed_pkg(const char *pkg
, void *cookie
)
169 *ret
|= check_exact_pkg(pkg
);
174 check_installed_pattern(const char *pattern
)
178 match_installed_pkgs(pattern
, check_one_installed_pkg
, &ret
);
184 check_and_read_pkg_vulnerabilities(void)
189 if (pkg_vulnerabilities_file
== NULL
)
190 errx(EXIT_FAILURE
, "PKG_VULNERABILITIES is not set");
193 if (stat(pkg_vulnerabilities_file
, &st
) == -1) {
196 "pkg-vulnerabilities not found, run %s -d",
198 errx(EXIT_FAILURE
, "pkg-vulnerabilities not readable");
203 warnx("pkg-vulnerabilities is from the future");
204 else if (now
> 86400 * 7)
205 warnx("pkg-vulnerabilities is out of date (%ld days old)",
206 (long)(now
/ 86400));
207 else if (verbose
>= 2)
208 warnx("pkg-vulnerabilities is %ld day%s old",
209 (long)(now
/ 86400), now
/ 86400 == 1 ? "" : "s");
212 pv
= read_pkg_vulnerabilities(pkg_vulnerabilities_file
, 0, check_signature
);
216 audit_pkgdb(int argc
, char **argv
)
220 parse_options(argc
, argv
, audit_options
);
223 check_and_read_pkg_vulnerabilities();
227 rv
|= check_installed_pattern("*");
229 for (; *argv
!= NULL
; ++argv
)
230 rv
|= check_installed_pattern(*argv
);
232 free_pkg_vulnerabilities(pv
);
234 if (rv
== 0 && verbose
>= 1)
235 fputs("No vulnerabilities found\n", stderr
);
236 exit(rv
? EXIT_FAILURE
: EXIT_SUCCESS
);
240 audit_pkg(int argc
, char **argv
)
244 parse_options(argc
, argv
, audit_options
);
247 check_and_read_pkg_vulnerabilities();
249 for (; *argv
!= NULL
; ++argv
)
250 rv
|= check_exact_pkg(*argv
);
252 free_pkg_vulnerabilities(pv
);
254 if (rv
== 0 && verbose
>= 1)
255 fputs("No vulnerabilities found\n", stderr
);
256 exit(rv
? EXIT_FAILURE
: EXIT_SUCCESS
);
260 audit_batch(int argc
, char **argv
)
264 parse_options(argc
, argv
, audit_options
);
267 check_and_read_pkg_vulnerabilities();
269 for (; *argv
!= NULL
; ++argv
)
270 rv
|= check_batch_exact_pkgs(*argv
);
271 free_pkg_vulnerabilities(pv
);
273 if (rv
== 0 && verbose
>= 1)
274 fputs("No vulnerabilities found\n", stderr
);
275 exit(rv
? EXIT_FAILURE
: EXIT_SUCCESS
);
279 check_pkg_vulnerabilities(int argc
, char **argv
)
281 parse_options(argc
, argv
, "s");
282 if (argc
!= optind
+ 1)
285 pv
= read_pkg_vulnerabilities(argv
[optind
], 0, check_signature
);
286 free_pkg_vulnerabilities(pv
);
290 fetch_pkg_vulnerabilities(int argc
, char **argv
)
292 struct pkg_vulnerabilities
*pv_check
;
293 char *buf
, *decompressed_input
;
294 size_t buf_len
, buf_fetched
, decompressed_len
;
304 parse_options(argc
, argv
, "su");
309 fprintf(stderr
, "Fetching %s\n", pkg_vulnerabilities_url
);
311 url
= fetchParseURL(pkg_vulnerabilities_url
);
314 "Could not parse location of pkg_vulnerabilities: %s",
318 if (update_pkg_vuln
) {
319 fd
= open(pkg_vulnerabilities_file
, O_RDONLY
);
320 if (fd
!= -1 && fstat(fd
, &sb
) != -1) {
321 url
->last_modified
= sb
.st_mtime
;
322 snprintf(my_flags
, sizeof(my_flags
), "%si",
331 f
= fetchXGet(url
, &st
, flags
);
332 if (f
== NULL
&& update_pkg_vuln
&&
333 fetchLastErrCode
== FETCH_UNCHANGED
) {
335 fprintf(stderr
, "%s is not newer\n",
336 pkg_vulnerabilities_url
);
341 errx(EXIT_FAILURE
, "Could not fetch vulnerability file: %s",
344 if (st
.size
> SSIZE_MAX
- 1)
345 errx(EXIT_FAILURE
, "pkg-vulnerabilities is too large");
348 buf
= xmalloc(buf_len
+ 1);
351 while (buf_fetched
< buf_len
) {
352 cur_fetched
= fetchIO_read(f
, buf
+ buf_fetched
,
353 buf_len
- buf_fetched
);
354 if (cur_fetched
== 0)
356 "Truncated pkg-vulnerabilities received");
357 else if (cur_fetched
== -1)
359 "IO error while fetching pkg-vulnerabilities: %s",
361 buf_fetched
+= cur_fetched
;
366 if (decompress_buffer(buf
, buf_len
, &decompressed_input
,
367 &decompressed_len
)) {
368 pv_check
= parse_pkg_vulnerabilities(decompressed_input
,
369 decompressed_len
, check_signature
);
370 free(decompressed_input
);
372 pv_check
= parse_pkg_vulnerabilities(buf
, buf_len
,
375 free_pkg_vulnerabilities(pv_check
);
377 fd
= open(pkg_vulnerabilities_file
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0644);
379 err(EXIT_FAILURE
, "Cannot create pkg-vulnerability file %s",
380 pkg_vulnerabilities_file
);
382 if (write(fd
, buf
, buf_len
) != (ssize_t
)buf_len
)
383 err(EXIT_FAILURE
, "Cannot write pkg-vulnerability file");
385 err(EXIT_FAILURE
, "Cannot close pkg-vulnerability file after write");
393 check_pkg_history_pattern(const char *pkg
, const char *pattern
)
395 const char *delim
, *end_base
;
397 if ((delim
= strchr(pattern
, '*')) != NULL
) {
398 if ((end_base
= strrchr(pattern
, '-')) == NULL
)
399 errx(EXIT_FAILURE
, "Missing - in wildcard pattern %s",
401 if ((delim
= strchr(pattern
, '>')) != NULL
||
402 (delim
= strchr(pattern
, '<')) != NULL
)
404 "Mixed relational and wildcard patterns in %s",
406 } else if ((delim
= strchr(pattern
, '>')) != NULL
) {
408 if ((delim
= strchr(pattern
, '<')) != NULL
&& delim
< end_base
)
409 errx(EXIT_FAILURE
, "Inverted operators in %s",
411 } else if ((delim
= strchr(pattern
, '<')) != NULL
) {
413 } else if ((end_base
= strrchr(pattern
, '-')) == NULL
) {
414 errx(EXIT_FAILURE
, "Missing - in absolute pattern %s",
418 if (strncmp(pkg
, pattern
, end_base
- pattern
) != 0)
420 if (pkg
[end_base
- pattern
] != '\0')
427 check_pkg_history1(const char *pkg
, const char *pattern
)
429 const char *open_brace
, *close_brace
, *inner_brace
, *suffix
, *iter
;
430 size_t prefix_len
, suffix_len
, middle_len
;
433 open_brace
= strchr(pattern
, '{');
434 if (open_brace
== NULL
) {
435 if ((close_brace
= strchr(pattern
, '}')) != NULL
)
436 errx(EXIT_FAILURE
, "Unbalanced {} in pattern %s",
438 return check_pkg_history_pattern(pkg
, pattern
);
440 close_brace
= strchr(open_brace
, '}');
441 if (strchr(pattern
, '}') != close_brace
)
442 errx(EXIT_FAILURE
, "Unbalanced {} in pattern %s",
445 while ((inner_brace
= strchr(open_brace
+ 1, '{')) != NULL
) {
446 if (inner_brace
>= close_brace
)
448 open_brace
= inner_brace
;
451 expanded_pkg
= xmalloc(strlen(pattern
)); /* {} are going away... */
453 prefix_len
= open_brace
- pattern
;
454 suffix
= close_brace
+ 1;
455 suffix_len
= strlen(suffix
) + 1;
456 memcpy(expanded_pkg
, pattern
, prefix_len
);
461 iter
= strchr(open_brace
, ',');
462 if (iter
== NULL
|| iter
> close_brace
)
465 middle_len
= iter
- open_brace
;
466 memcpy(expanded_pkg
+ prefix_len
, open_brace
, middle_len
);
467 memcpy(expanded_pkg
+ prefix_len
+ middle_len
, suffix
,
469 if (check_pkg_history1(pkg
, expanded_pkg
)) {
473 open_brace
= iter
+ 1;
474 } while (iter
< close_brace
);
481 check_pkg_history(const char *pkg
)
485 for (i
= 0; i
< pv
->entries
; ++i
) {
486 if (!quick_pkg_match(pv
->vulnerability
[i
], pkg
))
488 if (strcmp("eol", pv
->classification
[i
]) == 0)
490 if (check_pkg_history1(pkg
, pv
->vulnerability
[i
]) == 0)
493 printf("%s %s %s\n", pv
->vulnerability
[i
],
494 pv
->classification
[i
], pv
->advisory
[i
]);
499 audit_history(int argc
, char **argv
)
501 parse_options(argc
, argv
, "st:");
504 check_and_read_pkg_vulnerabilities();
505 for (; *argv
!= NULL
; ++argv
)
506 check_pkg_history(*argv
);
508 free_pkg_vulnerabilities(pv
);