etc/protocols - sync with NetBSD-8
[minix.git] / external / bsd / pkg_install / dist / admin / audit.c
blob7f4e6a745657e66cc221fe18ba3264d8191faf8c
1 /* $NetBSD: audit.c,v 1.1.1.9 2011/02/18 22:32:28 aymeric 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.1.1.9 2011/02/18 22:32:28 aymeric 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_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:";
84 static void
85 parse_options(int argc, char **argv, const char *options)
87 int ch;
89 optreset = 1;
91 * optind == 0 is interpreted as partial reset request
92 * by GNU getopt, so compensate against this and cleanup
93 * at the end.
95 optind = 1;
96 ++argc;
97 --argv;
99 while ((ch = getopt(argc, argv, options)) != -1) {
100 switch (ch) {
101 case 'e':
102 check_eol = "yes";
103 break;
104 case 's':
105 check_signature = 1;
106 break;
107 case 't':
108 limit_vul_types = optarg;
109 break;
110 case 'u':
111 update_pkg_vuln = 1;
112 break;
113 default:
114 usage();
115 /* NOTREACHED */
119 --optind; /* See above comment. */
122 static int
123 check_exact_pkg(const char *pkg)
125 return audit_package(pv, pkg, limit_vul_types, quiet ? 0 : 1);
128 static int
129 check_batch_exact_pkgs(const char *fname)
131 FILE *f;
132 char buf[4096], *line, *eol;
133 int ret;
135 ret = 0;
136 if (strcmp(fname, "-") == 0)
137 f = stdin;
138 else {
139 f = fopen(fname, "r");
140 if (f == NULL)
141 err(EXIT_FAILURE, "Failed to open input file %s",
142 fname);
144 while ((line = fgets(buf, sizeof(buf), f)) != NULL) {
145 eol = line + strlen(line);
146 if (eol == line)
147 continue;
148 --eol;
149 if (*eol == '\n') {
150 if (eol == line)
151 continue;
152 *eol = '\0';
154 ret |= check_exact_pkg(line);
156 if (f != stdin)
157 fclose(f);
159 return ret;
162 static int
163 check_one_installed_pkg(const char *pkg, void *cookie)
165 int *ret = cookie;
167 *ret |= check_exact_pkg(pkg);
168 return 0;
171 static int
172 check_installed_pattern(const char *pattern)
174 int ret = 0;
176 match_installed_pkgs(pattern, check_one_installed_pkg, &ret);
178 return ret;
181 static void
182 check_and_read_pkg_vulnerabilities(void)
184 struct stat st;
185 time_t now;
187 if (pkg_vulnerabilities_file == NULL)
188 errx(EXIT_FAILURE, "PKG_VULNERABILITIES is not set");
190 if (verbose >= 1) {
191 if (stat(pkg_vulnerabilities_file, &st) == -1) {
192 if (errno == ENOENT)
193 errx(EXIT_FAILURE,
194 "pkg-vulnerabilities not found, run %s -d",
195 getprogname());
196 errx(EXIT_FAILURE, "pkg-vulnerabilities not readable");
198 now = time(NULL);
199 now -= st.st_mtime;
200 if (now < 0)
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);
213 void
214 audit_pkgdb(int argc, char **argv)
216 int rv;
218 parse_options(argc, argv, audit_options);
219 argv += optind;
221 check_and_read_pkg_vulnerabilities();
223 rv = 0;
224 if (*argv == NULL)
225 rv |= check_installed_pattern("*");
226 else {
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);
237 void
238 audit_pkg(int argc, char **argv)
240 int rv;
242 parse_options(argc, argv, audit_options);
243 argv += optind;
245 check_and_read_pkg_vulnerabilities();
246 rv = 0;
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);
257 void
258 audit_batch(int argc, char **argv)
260 int rv;
262 parse_options(argc, argv, audit_options);
263 argv += optind;
265 check_and_read_pkg_vulnerabilities();
266 rv = 0;
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);
276 void
277 check_pkg_vulnerabilities(int argc, char **argv)
279 parse_options(argc, argv, "s");
280 if (argc != optind + 1)
281 usage();
283 pv = read_pkg_vulnerabilities_file(argv[optind], 0, check_signature);
284 free_pkg_vulnerabilities(pv);
287 void
288 fetch_pkg_vulnerabilities(int argc, char **argv)
290 struct pkg_vulnerabilities *pv_check;
291 char *buf;
292 size_t buf_len, buf_fetched;
293 ssize_t cur_fetched;
294 struct url *url;
295 struct url_stat st;
296 fetchIO *f;
297 int fd;
298 struct stat sb;
299 char my_flags[20];
300 const char *flags;
302 parse_options(argc, argv, "su");
303 if (argc != optind)
304 usage();
306 if (verbose >= 2)
307 fprintf(stderr, "Fetching %s\n", pkg_vulnerabilities_url);
309 url = fetchParseURL(pkg_vulnerabilities_url);
310 if (url == NULL)
311 errx(EXIT_FAILURE,
312 "Could not parse location of pkg_vulnerabilities: %s",
313 fetchLastErrString);
315 flags = fetch_flags;
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",
321 fetch_flags);
322 flags = my_flags;
323 } else
324 update_pkg_vuln = 0;
325 if (fd != -1)
326 close(fd);
329 f = fetchXGet(url, &st, flags);
330 if (f == NULL && update_pkg_vuln &&
331 fetchLastErrCode == FETCH_UNCHANGED) {
332 if (verbose >= 1)
333 fprintf(stderr, "%s is not newer\n",
334 pkg_vulnerabilities_url);
335 exit(EXIT_SUCCESS);
338 if (f == NULL)
339 errx(EXIT_FAILURE, "Could not fetch vulnerability file: %s",
340 fetchLastErrString);
342 if (st.size > SSIZE_MAX - 1)
343 errx(EXIT_FAILURE, "pkg-vulnerabilities is too large");
345 buf_len = st.size;
346 buf = xmalloc(buf_len + 1);
347 buf_fetched = 0;
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)
353 errx(EXIT_FAILURE,
354 "Truncated pkg-vulnerabilities received");
355 else if (cur_fetched == -1)
356 errx(EXIT_FAILURE,
357 "IO error while fetching pkg-vulnerabilities: %s",
358 fetchLastErrString);
359 buf_fetched += cur_fetched;
362 buf[buf_len] = '\0';
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);
368 if (fd == -1)
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");
374 if (close(fd) == -1)
375 err(EXIT_FAILURE, "Cannot close pkg-vulnerability file after write");
377 free(buf);
379 exit(EXIT_SUCCESS);
382 static int
383 check_pkg_history_pattern(const char *pkg, const char *pattern)
385 const char *delim, *end_base;
387 if (strpbrk(pattern, "*[") != NULL) {
388 end_base = NULL;
389 for (delim = pattern;
390 *delim != '\0' && *delim != '['; delim++) {
391 if (*delim == '-')
392 end_base = delim;
395 if (end_base == NULL)
396 errx(EXIT_FAILURE, "Missing - in wildcard pattern %s",
397 pattern);
398 if ((delim = strchr(pattern, '>')) != NULL ||
399 (delim = strchr(pattern, '<')) != NULL)
400 errx(EXIT_FAILURE,
401 "Mixed relational and wildcard patterns in %s",
402 pattern);
403 } else if ((delim = strchr(pattern, '>')) != NULL) {
404 end_base = delim;
405 if ((delim = strchr(pattern, '<')) != NULL && delim < end_base)
406 errx(EXIT_FAILURE, "Inverted operators in %s",
407 pattern);
408 } else if ((delim = strchr(pattern, '<')) != NULL) {
409 end_base = delim;
410 } else if ((end_base = strrchr(pattern, '-')) == NULL) {
411 errx(EXIT_FAILURE, "Missing - in absolute pattern %s",
412 pattern);
415 if (strncmp(pkg, pattern, end_base - pattern) != 0)
416 return 0;
417 if (pkg[end_base - pattern] != '\0')
418 return 0;
420 return 1;
423 static int
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;
428 char *expanded_pkg;
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",
434 pattern);
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",
440 pattern);
442 while ((inner_brace = strchr(open_brace + 1, '{')) != NULL) {
443 if (inner_brace >= close_brace)
444 break;
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);
455 ++open_brace;
457 do {
458 iter = strchr(open_brace, ',');
459 if (iter == NULL || iter > close_brace)
460 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,
465 suffix_len);
466 if (check_pkg_history1(pkg, expanded_pkg)) {
467 free(expanded_pkg);
468 return 1;
470 open_brace = iter + 1;
471 } while (iter < close_brace);
473 free(expanded_pkg);
474 return 0;
477 static void
478 check_pkg_history(const char *pkg)
480 size_t i;
482 for (i = 0; i < pv->entries; ++i) {
483 if (!quick_pkg_match(pv->vulnerability[i], pkg))
484 continue;
485 if (strcmp("eol", pv->classification[i]) == 0)
486 continue;
487 if (check_pkg_history1(pkg, pv->vulnerability[i]) == 0)
488 continue;
490 printf("%s %s %s\n", pv->vulnerability[i],
491 pv->classification[i], pv->advisory[i]);
495 void
496 audit_history(int argc, char **argv)
498 parse_options(argc, argv, "st:");
499 argv += optind;
501 check_and_read_pkg_vulnerabilities();
502 for (; *argv != NULL; ++argv)
503 check_pkg_history(*argv);
505 free_pkg_vulnerabilities(pv);
506 exit(EXIT_SUCCESS);