No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / pkg_install / dist / admin / audit.c
blob2d7c9332ceff3a459979be62e0b5a2ac131dfed2
1 /* $NetBSD: audit.c,v 1.14 2009/08/02 17:56:44 joerg Exp $ */
3 #if HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 #include <nbcompat.h>
7 #if HAVE_SYS_CDEFS_H
8 #include <sys/cdefs.h>
9 #endif
10 __RCSID("$NetBSD: audit.c,v 1.14 2009/08/02 17:56:44 joerg Exp $");
12 /*-
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
18 * are met:
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
25 * distribution.
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
38 * SUCH DAMAGE.
41 #if HAVE_SYS_TYPES_H
42 #include <sys/types.h>
43 #endif
44 #if HAVE_SYS_STAT_H
45 #include <sys/stat.h>
46 #endif
47 #if HAVE_ERR_H
48 #include <err.h>
49 #endif
50 #if HAVE_ERRNO_H
51 #include <errno.h>
52 #endif
53 #if HAVE_FCNTL_H
54 #include <fcntl.h>
55 #endif
56 #if HAVE_SIGNAL_H
57 #include <signal.h>
58 #endif
59 #if HAVE_STDIO_H
60 #include <stdio.h>
61 #endif
62 #if HAVE_STRING_H
63 #include <string.h>
64 #endif
65 #ifdef NETBSD
66 #include <unistd.h>
67 #else
68 #include <nbcompat/unistd.h>
69 #endif
71 #include <fetch.h>
73 #include "admin.h"
74 #include "lib.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:";
85 static void
86 parse_options(int argc, char **argv, const char *options)
88 int ch;
90 optreset = 1;
92 * optind == 0 is interpreted as partial reset request
93 * by GNU getopt, so compensate against this and cleanup
94 * at the end.
96 optind = 1;
97 ++argc;
98 --argv;
100 while ((ch = getopt(argc, argv, options)) != -1) {
101 switch (ch) {
102 case 'e':
103 check_eol = 1;
104 break;
105 case 's':
106 check_signature = 1;
107 break;
108 case 't':
109 limit_vul_types = optarg;
110 break;
111 case 'u':
112 update_pkg_vuln = 1;
113 break;
114 default:
115 usage();
116 /* NOTREACHED */
120 --optind; /* See above comment. */
123 static int
124 check_exact_pkg(const char *pkg)
126 return audit_package(pv, pkg, limit_vul_types, check_eol,
127 quiet ? 0 : 1);
130 static int
131 check_batch_exact_pkgs(const char *fname)
133 FILE *f;
134 char buf[4096], *line, *eol;
135 int ret;
137 ret = 0;
138 if (strcmp(fname, "-") == 0)
139 f = stdin;
140 else {
141 f = fopen(fname, "r");
142 if (f == NULL)
143 err(EXIT_FAILURE, "Failed to open input file %s",
144 fname);
146 while ((line = fgets(buf, sizeof(buf), f)) != NULL) {
147 eol = line + strlen(line);
148 if (eol == line)
149 continue;
150 --eol;
151 if (*eol == '\n') {
152 if (eol == line)
153 continue;
154 *eol = '\0';
156 ret |= check_exact_pkg(line);
158 if (f != stdin)
159 fclose(f);
161 return ret;
164 static int
165 check_one_installed_pkg(const char *pkg, void *cookie)
167 int *ret = cookie;
169 *ret |= check_exact_pkg(pkg);
170 return 0;
173 static int
174 check_installed_pattern(const char *pattern)
176 int ret = 0;
178 match_installed_pkgs(pattern, check_one_installed_pkg, &ret);
180 return ret;
183 static void
184 check_and_read_pkg_vulnerabilities(void)
186 struct stat st;
187 time_t now;
189 if (pkg_vulnerabilities_file == NULL)
190 errx(EXIT_FAILURE, "PKG_VULNERABILITIES is not set");
192 if (verbose >= 1) {
193 if (stat(pkg_vulnerabilities_file, &st) == -1) {
194 if (errno == ENOENT)
195 errx(EXIT_FAILURE,
196 "pkg-vulnerabilities not found, run %s -d",
197 getprogname());
198 errx(EXIT_FAILURE, "pkg-vulnerabilities not readable");
200 now = time(NULL);
201 now -= st.st_mtime;
202 if (now < 0)
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);
215 void
216 audit_pkgdb(int argc, char **argv)
218 int rv;
220 parse_options(argc, argv, audit_options);
221 argv += optind;
223 check_and_read_pkg_vulnerabilities();
225 rv = 0;
226 if (*argv == NULL)
227 rv |= check_installed_pattern("*");
228 else {
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);
239 void
240 audit_pkg(int argc, char **argv)
242 int rv;
244 parse_options(argc, argv, audit_options);
245 argv += optind;
247 check_and_read_pkg_vulnerabilities();
248 rv = 0;
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);
259 void
260 audit_batch(int argc, char **argv)
262 int rv;
264 parse_options(argc, argv, audit_options);
265 argv += optind;
267 check_and_read_pkg_vulnerabilities();
268 rv = 0;
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);
278 void
279 check_pkg_vulnerabilities(int argc, char **argv)
281 parse_options(argc, argv, "s");
282 if (argc != optind + 1)
283 usage();
285 pv = read_pkg_vulnerabilities(argv[optind], 0, check_signature);
286 free_pkg_vulnerabilities(pv);
289 void
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;
295 ssize_t cur_fetched;
296 struct url *url;
297 struct url_stat st;
298 fetchIO *f;
299 int fd;
300 struct stat sb;
301 char my_flags[20];
302 const char *flags;
304 parse_options(argc, argv, "su");
305 if (argc != optind)
306 usage();
308 if (verbose >= 2)
309 fprintf(stderr, "Fetching %s\n", pkg_vulnerabilities_url);
311 url = fetchParseURL(pkg_vulnerabilities_url);
312 if (url == NULL)
313 errx(EXIT_FAILURE,
314 "Could not parse location of pkg_vulnerabilities: %s",
315 fetchLastErrString);
317 flags = fetch_flags;
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",
323 fetch_flags);
324 flags = my_flags;
325 } else
326 update_pkg_vuln = 0;
327 if (fd != -1)
328 close(fd);
331 f = fetchXGet(url, &st, flags);
332 if (f == NULL && update_pkg_vuln &&
333 fetchLastErrCode == FETCH_UNCHANGED) {
334 if (verbose >= 1)
335 fprintf(stderr, "%s is not newer\n",
336 pkg_vulnerabilities_url);
337 exit(EXIT_SUCCESS);
340 if (f == NULL)
341 errx(EXIT_FAILURE, "Could not fetch vulnerability file: %s",
342 fetchLastErrString);
344 if (st.size > SSIZE_MAX - 1)
345 errx(EXIT_FAILURE, "pkg-vulnerabilities is too large");
347 buf_len = st.size;
348 buf = xmalloc(buf_len + 1);
349 buf_fetched = 0;
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)
355 errx(EXIT_FAILURE,
356 "Truncated pkg-vulnerabilities received");
357 else if (cur_fetched == -1)
358 errx(EXIT_FAILURE,
359 "IO error while fetching pkg-vulnerabilities: %s",
360 fetchLastErrString);
361 buf_fetched += cur_fetched;
364 buf[buf_len] = '\0';
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);
371 } else {
372 pv_check = parse_pkg_vulnerabilities(buf, buf_len,
373 check_signature);
375 free_pkg_vulnerabilities(pv_check);
377 fd = open(pkg_vulnerabilities_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
378 if (fd == -1)
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");
384 if (close(fd) == -1)
385 err(EXIT_FAILURE, "Cannot close pkg-vulnerability file after write");
387 free(buf);
389 exit(EXIT_SUCCESS);
392 static int
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",
400 pattern);
401 if ((delim = strchr(pattern, '>')) != NULL ||
402 (delim = strchr(pattern, '<')) != NULL)
403 errx(EXIT_FAILURE,
404 "Mixed relational and wildcard patterns in %s",
405 pattern);
406 } else if ((delim = strchr(pattern, '>')) != NULL) {
407 end_base = delim;
408 if ((delim = strchr(pattern, '<')) != NULL && delim < end_base)
409 errx(EXIT_FAILURE, "Inverted operators in %s",
410 pattern);
411 } else if ((delim = strchr(pattern, '<')) != NULL) {
412 end_base = delim;
413 } else if ((end_base = strrchr(pattern, '-')) == NULL) {
414 errx(EXIT_FAILURE, "Missing - in absolute pattern %s",
415 pattern);
418 if (strncmp(pkg, pattern, end_base - pattern) != 0)
419 return 0;
420 if (pkg[end_base - pattern] != '\0')
421 return 0;
423 return 1;
426 static int
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;
431 char *expanded_pkg;
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",
437 pattern);
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",
443 pattern);
445 while ((inner_brace = strchr(open_brace + 1, '{')) != NULL) {
446 if (inner_brace >= close_brace)
447 break;
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);
458 ++open_brace;
460 do {
461 iter = strchr(open_brace, ',');
462 if (iter == NULL || iter > close_brace)
463 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,
468 suffix_len);
469 if (check_pkg_history1(pkg, expanded_pkg)) {
470 free(expanded_pkg);
471 return 1;
473 open_brace = iter + 1;
474 } while (iter < close_brace);
476 free(expanded_pkg);
477 return 0;
480 static void
481 check_pkg_history(const char *pkg)
483 size_t i;
485 for (i = 0; i < pv->entries; ++i) {
486 if (!quick_pkg_match(pv->vulnerability[i], pkg))
487 continue;
488 if (strcmp("eol", pv->classification[i]) == 0)
489 continue;
490 if (check_pkg_history1(pkg, pv->vulnerability[i]) == 0)
491 continue;
493 printf("%s %s %s\n", pv->vulnerability[i],
494 pv->classification[i], pv->advisory[i]);
498 void
499 audit_history(int argc, char **argv)
501 parse_options(argc, argv, "st:");
502 argv += optind;
504 check_and_read_pkg_vulnerabilities();
505 for (; *argv != NULL; ++argv)
506 check_pkg_history(*argv);
508 free_pkg_vulnerabilities(pv);
509 exit(EXIT_SUCCESS);