1 /* $NetBSD: audit.c,v 1.1.1.9 2011/02/18 22:32:28 aymeric Exp $ */
10 __RCSID("$NetBSD: audit.c,v 1.1.1.9 2011/02/18 22:32:28 aymeric 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_signature
= 0;
77 static const char *limit_vul_types
= NULL
;
78 static int update_pkg_vuln
= 0;
80 static struct pkg_vulnerabilities
*pv
;
82 static const char audit_options
[] = "est:";
85 parse_options(int argc
, char **argv
, const char *options
)
91 * optind == 0 is interpreted as partial reset request
92 * by GNU getopt, so compensate against this and cleanup
99 while ((ch
= getopt(argc
, argv
, options
)) != -1) {
108 limit_vul_types
= optarg
;
119 --optind
; /* See above comment. */
123 check_exact_pkg(const char *pkg
)
125 return audit_package(pv
, pkg
, limit_vul_types
, quiet
? 0 : 1);
129 check_batch_exact_pkgs(const char *fname
)
132 char buf
[4096], *line
, *eol
;
136 if (strcmp(fname
, "-") == 0)
139 f
= fopen(fname
, "r");
141 err(EXIT_FAILURE
, "Failed to open input file %s",
144 while ((line
= fgets(buf
, sizeof(buf
), f
)) != NULL
) {
145 eol
= line
+ strlen(line
);
154 ret
|= check_exact_pkg(line
);
163 check_one_installed_pkg(const char *pkg
, void *cookie
)
167 *ret
|= check_exact_pkg(pkg
);
172 check_installed_pattern(const char *pattern
)
176 match_installed_pkgs(pattern
, check_one_installed_pkg
, &ret
);
182 check_and_read_pkg_vulnerabilities(void)
187 if (pkg_vulnerabilities_file
== NULL
)
188 errx(EXIT_FAILURE
, "PKG_VULNERABILITIES is not set");
191 if (stat(pkg_vulnerabilities_file
, &st
) == -1) {
194 "pkg-vulnerabilities not found, run %s -d",
196 errx(EXIT_FAILURE
, "pkg-vulnerabilities not readable");
201 warnx("pkg-vulnerabilities is from the future");
202 else if (now
> 86400 * 7)
203 warnx("pkg-vulnerabilities is out of date (%ld days old)",
204 (long)(now
/ 86400));
205 else if (verbose
>= 2)
206 warnx("pkg-vulnerabilities is %ld day%s old",
207 (long)(now
/ 86400), now
/ 86400 == 1 ? "" : "s");
210 pv
= read_pkg_vulnerabilities_file(pkg_vulnerabilities_file
, 0, check_signature
);
214 audit_pkgdb(int argc
, char **argv
)
218 parse_options(argc
, argv
, audit_options
);
221 check_and_read_pkg_vulnerabilities();
225 rv
|= check_installed_pattern("*");
227 for (; *argv
!= NULL
; ++argv
)
228 rv
|= check_installed_pattern(*argv
);
230 free_pkg_vulnerabilities(pv
);
232 if (rv
== 0 && verbose
>= 1)
233 fputs("No vulnerabilities found\n", stderr
);
234 exit(rv
? EXIT_FAILURE
: EXIT_SUCCESS
);
238 audit_pkg(int argc
, char **argv
)
242 parse_options(argc
, argv
, audit_options
);
245 check_and_read_pkg_vulnerabilities();
247 for (; *argv
!= NULL
; ++argv
)
248 rv
|= check_exact_pkg(*argv
);
250 free_pkg_vulnerabilities(pv
);
252 if (rv
== 0 && verbose
>= 1)
253 fputs("No vulnerabilities found\n", stderr
);
254 exit(rv
? EXIT_FAILURE
: EXIT_SUCCESS
);
258 audit_batch(int argc
, char **argv
)
262 parse_options(argc
, argv
, audit_options
);
265 check_and_read_pkg_vulnerabilities();
267 for (; *argv
!= NULL
; ++argv
)
268 rv
|= check_batch_exact_pkgs(*argv
);
269 free_pkg_vulnerabilities(pv
);
271 if (rv
== 0 && verbose
>= 1)
272 fputs("No vulnerabilities found\n", stderr
);
273 exit(rv
? EXIT_FAILURE
: EXIT_SUCCESS
);
277 check_pkg_vulnerabilities(int argc
, char **argv
)
279 parse_options(argc
, argv
, "s");
280 if (argc
!= optind
+ 1)
283 pv
= read_pkg_vulnerabilities_file(argv
[optind
], 0, check_signature
);
284 free_pkg_vulnerabilities(pv
);
288 fetch_pkg_vulnerabilities(int argc
, char **argv
)
290 struct pkg_vulnerabilities
*pv_check
;
292 size_t buf_len
, buf_fetched
;
302 parse_options(argc
, argv
, "su");
307 fprintf(stderr
, "Fetching %s\n", pkg_vulnerabilities_url
);
309 url
= fetchParseURL(pkg_vulnerabilities_url
);
312 "Could not parse location of pkg_vulnerabilities: %s",
316 if (update_pkg_vuln
) {
317 fd
= open(pkg_vulnerabilities_file
, O_RDONLY
);
318 if (fd
!= -1 && fstat(fd
, &sb
) != -1) {
319 url
->last_modified
= sb
.st_mtime
;
320 snprintf(my_flags
, sizeof(my_flags
), "%si",
329 f
= fetchXGet(url
, &st
, flags
);
330 if (f
== NULL
&& update_pkg_vuln
&&
331 fetchLastErrCode
== FETCH_UNCHANGED
) {
333 fprintf(stderr
, "%s is not newer\n",
334 pkg_vulnerabilities_url
);
339 errx(EXIT_FAILURE
, "Could not fetch vulnerability file: %s",
342 if (st
.size
> SSIZE_MAX
- 1)
343 errx(EXIT_FAILURE
, "pkg-vulnerabilities is too large");
346 buf
= xmalloc(buf_len
+ 1);
349 while (buf_fetched
< buf_len
) {
350 cur_fetched
= fetchIO_read(f
, buf
+ buf_fetched
,
351 buf_len
- buf_fetched
);
352 if (cur_fetched
== 0)
354 "Truncated pkg-vulnerabilities received");
355 else if (cur_fetched
== -1)
357 "IO error while fetching pkg-vulnerabilities: %s",
359 buf_fetched
+= cur_fetched
;
364 pv_check
= read_pkg_vulnerabilities_memory(buf
, buf_len
, check_signature
);
365 free_pkg_vulnerabilities(pv_check
);
367 fd
= open(pkg_vulnerabilities_file
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0644);
369 err(EXIT_FAILURE
, "Cannot create pkg-vulnerability file %s",
370 pkg_vulnerabilities_file
);
372 if (write(fd
, buf
, buf_len
) != (ssize_t
)buf_len
)
373 err(EXIT_FAILURE
, "Cannot write pkg-vulnerability file");
375 err(EXIT_FAILURE
, "Cannot close pkg-vulnerability file after write");
383 check_pkg_history_pattern(const char *pkg
, const char *pattern
)
385 const char *delim
, *end_base
;
387 if (strpbrk(pattern
, "*[") != NULL
) {
389 for (delim
= pattern
;
390 *delim
!= '\0' && *delim
!= '['; delim
++) {
395 if (end_base
== NULL
)
396 errx(EXIT_FAILURE
, "Missing - in wildcard pattern %s",
398 if ((delim
= strchr(pattern
, '>')) != NULL
||
399 (delim
= strchr(pattern
, '<')) != NULL
)
401 "Mixed relational and wildcard patterns in %s",
403 } else if ((delim
= strchr(pattern
, '>')) != NULL
) {
405 if ((delim
= strchr(pattern
, '<')) != NULL
&& delim
< end_base
)
406 errx(EXIT_FAILURE
, "Inverted operators in %s",
408 } else if ((delim
= strchr(pattern
, '<')) != NULL
) {
410 } else if ((end_base
= strrchr(pattern
, '-')) == NULL
) {
411 errx(EXIT_FAILURE
, "Missing - in absolute pattern %s",
415 if (strncmp(pkg
, pattern
, end_base
- pattern
) != 0)
417 if (pkg
[end_base
- pattern
] != '\0')
424 check_pkg_history1(const char *pkg
, const char *pattern
)
426 const char *open_brace
, *close_brace
, *inner_brace
, *suffix
, *iter
;
427 size_t prefix_len
, suffix_len
, middle_len
;
430 open_brace
= strchr(pattern
, '{');
431 if (open_brace
== NULL
) {
432 if ((close_brace
= strchr(pattern
, '}')) != NULL
)
433 errx(EXIT_FAILURE
, "Unbalanced {} in pattern %s",
435 return check_pkg_history_pattern(pkg
, pattern
);
437 close_brace
= strchr(open_brace
, '}');
438 if (strchr(pattern
, '}') != close_brace
)
439 errx(EXIT_FAILURE
, "Unbalanced {} in pattern %s",
442 while ((inner_brace
= strchr(open_brace
+ 1, '{')) != NULL
) {
443 if (inner_brace
>= close_brace
)
445 open_brace
= inner_brace
;
448 expanded_pkg
= xmalloc(strlen(pattern
)); /* {} are going away... */
450 prefix_len
= open_brace
- pattern
;
451 suffix
= close_brace
+ 1;
452 suffix_len
= strlen(suffix
) + 1;
453 memcpy(expanded_pkg
, pattern
, prefix_len
);
458 iter
= strchr(open_brace
, ',');
459 if (iter
== NULL
|| iter
> close_brace
)
462 middle_len
= iter
- open_brace
;
463 memcpy(expanded_pkg
+ prefix_len
, open_brace
, middle_len
);
464 memcpy(expanded_pkg
+ prefix_len
+ middle_len
, suffix
,
466 if (check_pkg_history1(pkg
, expanded_pkg
)) {
470 open_brace
= iter
+ 1;
471 } while (iter
< close_brace
);
478 check_pkg_history(const char *pkg
)
482 for (i
= 0; i
< pv
->entries
; ++i
) {
483 if (!quick_pkg_match(pv
->vulnerability
[i
], pkg
))
485 if (strcmp("eol", pv
->classification
[i
]) == 0)
487 if (check_pkg_history1(pkg
, pv
->vulnerability
[i
]) == 0)
490 printf("%s %s %s\n", pv
->vulnerability
[i
],
491 pv
->classification
[i
], pv
->advisory
[i
]);
496 audit_history(int argc
, char **argv
)
498 parse_options(argc
, argv
, "st:");
501 check_and_read_pkg_vulnerabilities();
502 for (; *argv
!= NULL
; ++argv
)
503 check_pkg_history(*argv
);
505 free_pkg_vulnerabilities(pv
);