xref: /netbsd-src/external/bsd/pkg_install/dist/lib/vulnerabilities-file.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /*	$NetBSD: vulnerabilities-file.c,v 1.3 2018/03/25 04:04:36 sevan Exp $	*/
2 
3 /*-
4  * Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg@NetBSD.org>.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #if HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35 
36 #include <nbcompat.h>
37 
38 #if HAVE_SYS_CDEFS_H
39 #include <sys/cdefs.h>
40 #endif
41 __RCSID("$NetBSD: vulnerabilities-file.c,v 1.3 2018/03/25 04:04:36 sevan Exp $");
42 
43 #if HAVE_SYS_STAT_H
44 #include <sys/stat.h>
45 #endif
46 #if HAVE_SYS_WAIT_H
47 #include <sys/wait.h>
48 #endif
49 #ifndef BOOTSTRAP
50 #include <archive.h>
51 #endif
52 #include <ctype.h>
53 #if HAVE_ERR_H
54 #include <err.h>
55 #endif
56 #include <errno.h>
57 #include <fcntl.h>
58 #include <limits.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #ifndef NETBSD
62 #include <nbcompat/sha1.h>
63 #include <nbcompat/sha2.h>
64 #else
65 #include <sha1.h>
66 #include <sha2.h>
67 #endif
68 #include <unistd.h>
69 
70 #include "lib.h"
71 
72 static struct pkg_vulnerabilities *read_pkg_vulnerabilities_archive(struct archive *, int);
73 static struct pkg_vulnerabilities *parse_pkg_vuln(const char *, size_t, int);
74 
75 static const char pgp_msg_start[] = "-----BEGIN PGP SIGNED MESSAGE-----\n";
76 static const char pgp_msg_end[] = "-----BEGIN PGP SIGNATURE-----\n";
77 static const char pkcs7_begin[] = "-----BEGIN PKCS7-----\n";
78 static const char pkcs7_end[] = "-----END PKCS7-----\n";
79 
80 static struct archive *
81 prepare_raw_file(void)
82 {
83 	struct archive *a = archive_read_new();
84 	if (a == NULL)
85 		errx(EXIT_FAILURE, "memory allocation failed");
86 
87 	archive_read_support_filter_gzip(a);
88 	archive_read_support_filter_bzip2(a);
89 	archive_read_support_filter_xz(a);
90 	archive_read_support_format_raw(a);
91 	return a;
92 }
93 
94 static void
95 verify_signature_pkcs7(const char *input)
96 {
97 #ifdef HAVE_SSL
98 	const char *begin_pkgvul, *end_pkgvul, *begin_sig, *end_sig;
99 
100 	if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
101 		begin_pkgvul = input + strlen(pgp_msg_start);
102 		if ((end_pkgvul = strstr(begin_pkgvul, pgp_msg_end)) == NULL)
103 			errx(EXIT_FAILURE, "Invalid PGP signature");
104 		if ((begin_sig = strstr(end_pkgvul, pkcs7_begin)) == NULL)
105 			errx(EXIT_FAILURE, "No PKCS7 signature");
106 	} else {
107 		begin_pkgvul = input;
108 		if ((begin_sig = strstr(begin_pkgvul, pkcs7_begin)) == NULL)
109 			errx(EXIT_FAILURE, "No PKCS7 signature");
110 		end_pkgvul = begin_sig;
111 	}
112 	if ((end_sig = strstr(begin_sig, pkcs7_end)) == NULL)
113 		errx(EXIT_FAILURE, "Invalid PKCS7 signature");
114 	end_sig += strlen(pkcs7_end);
115 
116 	if (easy_pkcs7_verify(begin_pkgvul, end_pkgvul - begin_pkgvul,
117 	    begin_sig, end_sig - begin_sig, certs_pkg_vulnerabilities, 0))
118 		errx(EXIT_FAILURE, "Unable to verify PKCS7 signature");
119 #else
120 	errx(EXIT_FAILURE, "OpenSSL support is not compiled in");
121 #endif
122 }
123 
124 static void
125 verify_signature(const char *input, size_t input_len)
126 {
127 	gpg_verify(input, input_len, gpg_keyring_pkgvuln, NULL, 0);
128 	if (certs_pkg_vulnerabilities != NULL)
129 		verify_signature_pkcs7(input);
130 }
131 
132 static void *
133 sha512_hash_init(void)
134 {
135 	static SHA512_CTX hash_ctx;
136 
137 	SHA512_Init(&hash_ctx);
138 	return &hash_ctx;
139 }
140 
141 static void
142 sha512_hash_update(void *ctx, const void *data, size_t len)
143 {
144 	SHA512_CTX *hash_ctx = ctx;
145 
146 	SHA512_Update(hash_ctx, data, len);
147 }
148 
149 static const char *
150 sha512_hash_finish(void *ctx)
151 {
152 	static char hash[SHA512_DIGEST_STRING_LENGTH];
153 	unsigned char digest[SHA512_DIGEST_LENGTH];
154 	SHA512_CTX *hash_ctx = ctx;
155 	int i;
156 
157 	SHA512_Final(digest, hash_ctx);
158 	for (i = 0; i < SHA512_DIGEST_LENGTH; ++i) {
159 		unsigned char c;
160 
161 		c = digest[i] / 16;
162 		if (c < 10)
163 			hash[2 * i] = '0' + c;
164 		else
165 			hash[2 * i] = 'a' - 10 + c;
166 
167 		c = digest[i] % 16;
168 		if (c < 10)
169 			hash[2 * i + 1] = '0' + c;
170 		else
171 			hash[2 * i + 1] = 'a' - 10 + c;
172 	}
173 	hash[2 * i] = '\0';
174 
175 	return hash;
176 }
177 
178 static void *
179 sha1_hash_init(void)
180 {
181 	static SHA1_CTX hash_ctx;
182 
183 	SHA1Init(&hash_ctx);
184 	return &hash_ctx;
185 }
186 
187 static void
188 sha1_hash_update(void *ctx, const void *data, size_t len)
189 {
190 	SHA1_CTX *hash_ctx = ctx;
191 
192 	SHA1Update(hash_ctx, data, len);
193 }
194 
195 static const char *
196 sha1_hash_finish(void *ctx)
197 {
198 	static char hash[SHA1_DIGEST_STRING_LENGTH];
199 	SHA1_CTX *hash_ctx = ctx;
200 
201 	SHA1End(hash_ctx, hash);
202 
203 	return hash;
204 }
205 
206 static const struct hash_algorithm {
207 	const char *name;
208 	size_t name_len;
209 	void * (*init)(void);
210 	void (*update)(void *, const void *, size_t);
211 	const char * (* finish)(void *);
212 } hash_algorithms[] = {
213 	{ "SHA512", 6, sha512_hash_init, sha512_hash_update,
214 	  sha512_hash_finish },
215 	{ "SHA1", 4, sha1_hash_init, sha1_hash_update,
216 	  sha1_hash_finish },
217 	{ NULL, 0, NULL, NULL, NULL }
218 };
219 
220 static void
221 verify_hash(const char *input, const char *hash_line)
222 {
223 	const struct hash_algorithm *hash;
224 	void *ctx;
225 	const char *last_start, *next, *hash_value;
226 	int in_pgp_msg;
227 
228 	for (hash = hash_algorithms; hash->name != NULL; ++hash) {
229 		if (strncmp(hash_line, hash->name, hash->name_len))
230 			continue;
231 		if (isspace((unsigned char)hash_line[hash->name_len]))
232 			break;
233 	}
234 	if (hash->name == NULL) {
235 		const char *end_name;
236 		for (end_name = hash_line; *end_name != '\0'; ++end_name) {
237 			if (!isalnum((unsigned char)*end_name))
238 				break;
239 		}
240 		warnx("Unsupported hash algorithm: %.*s",
241 		    (int)(end_name - hash_line), hash_line);
242 		return;
243 	}
244 
245 	hash_line += hash->name_len;
246 	if (!isspace((unsigned char)*hash_line))
247 		errx(EXIT_FAILURE, "Invalid #CHECKSUM");
248 	while (isspace((unsigned char)*hash_line) && *hash_line != '\n')
249 		++hash_line;
250 
251 	if (*hash_line == '\n')
252 		errx(EXIT_FAILURE, "Invalid #CHECKSUM");
253 
254 	ctx = (*hash->init)();
255 	if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
256 		input += strlen(pgp_msg_start);
257 		in_pgp_msg = 1;
258 	} else {
259 		in_pgp_msg = 0;
260 	}
261 	for (last_start = input; *input != '\0'; input = next) {
262 		if ((next = strchr(input, '\n')) == NULL)
263 			errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
264 		++next;
265 		if (in_pgp_msg && strncmp(input, pgp_msg_end, strlen(pgp_msg_end)) == 0)
266 			break;
267 		if (!in_pgp_msg && strncmp(input, pkcs7_begin, strlen(pkcs7_begin)) == 0)
268 			break;
269 		if (*input == '\n' ||
270 		    strncmp(input, "Hash:", 5) == 0 ||
271 		    strncmp(input, "# $NetBSD", 9) == 0 ||
272 		    strncmp(input, "#CHECKSUM", 9) == 0) {
273 			(*hash->update)(ctx, last_start, input - last_start);
274 			last_start = next;
275 		}
276 	}
277 	(*hash->update)(ctx, last_start, input - last_start);
278 	hash_value = (*hash->finish)(ctx);
279 	if (strncmp(hash_line, hash_value, strlen(hash_value)))
280 		errx(EXIT_FAILURE, "%s hash doesn't match", hash->name);
281 	hash_line += strlen(hash_value);
282 
283 	while (isspace((unsigned char)*hash_line) && *hash_line != '\n')
284 		++hash_line;
285 
286 	if (!isspace((unsigned char)*hash_line))
287 		errx(EXIT_FAILURE, "Invalid #CHECKSUM");
288 }
289 
290 static void
291 add_vulnerability(struct pkg_vulnerabilities *pv, size_t *allocated, const char *line)
292 {
293 	size_t len_pattern, len_class, len_url;
294 	const char *start_pattern, *start_class, *start_url;
295 
296 	start_pattern = line;
297 
298 	start_class = line;
299 	while (*start_class != '\0' && !isspace((unsigned char)*start_class))
300 		++start_class;
301 	len_pattern = start_class - line;
302 
303 	while (*start_class != '\n' && isspace((unsigned char)*start_class))
304 		++start_class;
305 
306 	if (*start_class == '0' || *start_class == '\n')
307 		errx(EXIT_FAILURE, "Input error: missing classification");
308 
309 	start_url = start_class;
310 	while (*start_url != '\0' && !isspace((unsigned char)*start_url))
311 		++start_url;
312 	len_class = start_url - start_class;
313 
314 	while (*start_url != '\n' && isspace((unsigned char)*start_url))
315 		++start_url;
316 
317 	if (*start_url == '0' || *start_url == '\n')
318 		errx(EXIT_FAILURE, "Input error: missing URL");
319 
320 	line = start_url;
321 	while (*line != '\0' && !isspace((unsigned char)*line))
322 		++line;
323 	len_url = line - start_url;
324 
325 	if (pv->entries == *allocated) {
326 		if (*allocated == 0)
327 			*allocated = 16;
328 		else if (*allocated <= SSIZE_MAX / 2)
329 			*allocated *= 2;
330 		else
331 			errx(EXIT_FAILURE, "Too many vulnerabilities");
332 		pv->vulnerability = xrealloc(pv->vulnerability,
333 		    sizeof(char *) * *allocated);
334 		pv->classification = xrealloc(pv->classification,
335 		    sizeof(char *) * *allocated);
336 		pv->advisory = xrealloc(pv->advisory,
337 		    sizeof(char *) * *allocated);
338 	}
339 
340 	pv->vulnerability[pv->entries] = xmalloc(len_pattern + 1);
341 	memcpy(pv->vulnerability[pv->entries], start_pattern, len_pattern);
342 	pv->vulnerability[pv->entries][len_pattern] = '\0';
343 	pv->classification[pv->entries] = xmalloc(len_class + 1);
344 	memcpy(pv->classification[pv->entries], start_class, len_class);
345 	pv->classification[pv->entries][len_class] = '\0';
346 	pv->advisory[pv->entries] = xmalloc(len_url + 1);
347 	memcpy(pv->advisory[pv->entries], start_url, len_url);
348 	pv->advisory[pv->entries][len_url] = '\0';
349 
350 	++pv->entries;
351 }
352 
353 struct pkg_vulnerabilities *
354 read_pkg_vulnerabilities_memory(void *buf, size_t len, int check_sum)
355 {
356 #ifdef BOOTSTRAP
357 	errx(EXIT_FAILURE, "Audit functions are unsupported during bootstrap");
358 #else
359 	struct archive *a;
360 	struct pkg_vulnerabilities *pv;
361 
362 	a = prepare_raw_file();
363 	if (archive_read_open_memory(a, buf, len) != ARCHIVE_OK)
364 		errx(EXIT_FAILURE, "Cannot open pkg_vulnerabilies buffer: %s",
365 		    archive_error_string(a));
366 
367 	pv = read_pkg_vulnerabilities_archive(a, check_sum);
368 
369 	return pv;
370 #endif
371 }
372 
373 struct pkg_vulnerabilities *
374 read_pkg_vulnerabilities_file(const char *path, int ignore_missing, int check_sum)
375 {
376 #ifdef BOOTSTRAP
377 	errx(EXIT_FAILURE, "Audit functions are unsupported during bootstrap");
378 #else
379 	struct archive *a;
380 	struct pkg_vulnerabilities *pv;
381 	int fd;
382 
383 	if ((fd = open(path, O_RDONLY)) == -1) {
384 		if (errno == ENOENT && ignore_missing)
385 			return NULL;
386 		err(EXIT_FAILURE, "Cannot open %s", path);
387 	}
388 
389 	a = prepare_raw_file();
390 	if (archive_read_open_fd(a, fd, 65536) != ARCHIVE_OK)
391 		errx(EXIT_FAILURE, "Cannot open ``%s'': %s", path,
392 		    archive_error_string(a));
393 
394 	pv = read_pkg_vulnerabilities_archive(a, check_sum);
395 	close(fd);
396 
397 	return pv;
398 #endif
399 }
400 
401 #ifndef BOOTSTRAP
402 static struct pkg_vulnerabilities *
403 read_pkg_vulnerabilities_archive(struct archive *a, int check_sum)
404 {
405 	struct archive_entry *ae;
406 	struct pkg_vulnerabilities *pv;
407 	char *buf;
408 	size_t buf_len, off;
409 	ssize_t r;
410 
411 	if (archive_read_next_header(a, &ae) != ARCHIVE_OK)
412 		errx(EXIT_FAILURE, "Cannot read pkg_vulnerabilities: %s",
413 		    archive_error_string(a));
414 
415 	off = 0;
416 	buf_len = 65536;
417 	buf = xmalloc(buf_len + 1);
418 
419 	for (;;) {
420 		r = archive_read_data(a, buf + off, buf_len - off);
421 		if (r <= 0)
422 			break;
423 		off += r;
424 		if (off == buf_len) {
425 			buf_len *= 2;
426 			if (buf_len < off)
427 				errx(EXIT_FAILURE, "pkg_vulnerabilties too large");
428 			buf = xrealloc(buf, buf_len + 1);
429 		}
430 	}
431 
432 	if (r != ARCHIVE_OK)
433 		errx(EXIT_FAILURE, "Cannot read pkg_vulnerabilities: %s",
434 		    archive_error_string(a));
435 
436 	archive_read_close(a);
437 
438 	buf[off] = '\0';
439 	pv = parse_pkg_vuln(buf, off, check_sum);
440 	free(buf);
441 	return pv;
442 }
443 
444 static struct pkg_vulnerabilities *
445 parse_pkg_vuln(const char *input, size_t input_len, int check_sum)
446 {
447 	struct pkg_vulnerabilities *pv;
448 	long version;
449 	char *end;
450 	const char *iter, *next;
451 	size_t allocated_vulns;
452 	int in_pgp_msg;
453 
454 	pv = xmalloc(sizeof(*pv));
455 
456 	allocated_vulns = pv->entries = 0;
457 	pv->vulnerability = NULL;
458 	pv->classification = NULL;
459 	pv->advisory = NULL;
460 
461 	if (strlen(input) != input_len)
462 		errx(1, "Invalid input (NUL character found)");
463 
464 	if (check_sum)
465 		verify_signature(input, input_len);
466 
467 	if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
468 		iter = input + strlen(pgp_msg_start);
469 		in_pgp_msg = 1;
470 	} else {
471 		iter = input;
472 		in_pgp_msg = 0;
473 	}
474 
475 	for (; *iter; iter = next) {
476 		if ((next = strchr(iter, '\n')) == NULL)
477 			errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
478 		++next;
479 		if (*iter == '\0' || *iter == '\n')
480 			continue;
481 		if (strncmp(iter, "Hash:", 5) == 0)
482 			continue;
483 		if (strncmp(iter, "# $NetBSD", 9) == 0)
484 			continue;
485 		if (*iter == '#' && isspace((unsigned char)iter[1])) {
486 			for (++iter; iter != next; ++iter) {
487 				if (!isspace((unsigned char)*iter))
488 					errx(EXIT_FAILURE, "Invalid header");
489 			}
490 			continue;
491 		}
492 
493 		if (strncmp(iter, "#FORMAT", 7) != 0)
494 			errx(EXIT_FAILURE, "Input header is malformed");
495 
496 		iter += 7;
497 		if (!isspace((unsigned char)*iter))
498 			errx(EXIT_FAILURE, "Invalid #FORMAT");
499 		++iter;
500 		version = strtol(iter, &end, 10);
501 		if (iter == end || version != 1 || *end != '.')
502 			errx(EXIT_FAILURE, "Input #FORMAT");
503 		iter = end + 1;
504 		version = strtol(iter, &end, 10);
505 		if (iter == end || version != 1 || *end != '.')
506 			errx(EXIT_FAILURE, "Input #FORMAT");
507 		iter = end + 1;
508 		version = strtol(iter, &end, 10);
509 		if (iter == end || version != 0)
510 			errx(EXIT_FAILURE, "Input #FORMAT");
511 		for (iter = end; iter != next; ++iter) {
512 			if (!isspace((unsigned char)*iter))
513 				errx(EXIT_FAILURE, "Input #FORMAT");
514 		}
515 		break;
516 	}
517 	if (*iter == '\0')
518 		errx(EXIT_FAILURE, "Missing #CHECKSUM or content");
519 
520 	for (iter = next; *iter; iter = next) {
521 		if ((next = strchr(iter, '\n')) == NULL)
522 			errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
523 		++next;
524 		if (*iter == '\0' || *iter == '\n')
525 			continue;
526 		if (in_pgp_msg && strncmp(iter, pgp_msg_end, strlen(pgp_msg_end)) == 0)
527 			break;
528 		if (!in_pgp_msg && strncmp(iter, pkcs7_begin, strlen(pkcs7_begin)) == 0)
529 			break;
530 		if (*iter == '#' &&
531 		    (iter[1] == '\0' || iter[1] == '\n' || isspace((unsigned char)iter[1])))
532 			continue;
533 		if (strncmp(iter, "#CHECKSUM", 9) == 0) {
534 			iter += 9;
535 			if (!isspace((unsigned char)*iter))
536 				errx(EXIT_FAILURE, "Invalid #CHECKSUM");
537 			while (isspace((unsigned char)*iter))
538 				++iter;
539 			verify_hash(input, iter);
540 			continue;
541 		}
542 		if (*iter == '#') {
543 			/*
544 			 * This should really be an error,
545 			 * but it is still used.
546 			 */
547 			/* errx(EXIT_FAILURE, "Invalid data line starting with #"); */
548 			continue;
549 		}
550 		add_vulnerability(pv, &allocated_vulns, iter);
551 	}
552 
553 	if (pv->entries != allocated_vulns) {
554 		pv->vulnerability = xrealloc(pv->vulnerability,
555 		    sizeof(char *) * pv->entries);
556 		pv->classification = xrealloc(pv->classification,
557 		    sizeof(char *) * pv->entries);
558 		pv->advisory = xrealloc(pv->advisory,
559 		    sizeof(char *) * pv->entries);
560 	}
561 
562 	return pv;
563 }
564 #endif
565 
566 void
567 free_pkg_vulnerabilities(struct pkg_vulnerabilities *pv)
568 {
569 	size_t i;
570 
571 	for (i = 0; i < pv->entries; ++i) {
572 		free(pv->vulnerability[i]);
573 		free(pv->classification[i]);
574 		free(pv->advisory[i]);
575 	}
576 	free(pv->vulnerability);
577 	free(pv->classification);
578 	free(pv->advisory);
579 	free(pv);
580 }
581 
582 static int
583 check_ignored_entry(struct pkg_vulnerabilities *pv, size_t i)
584 {
585 	const char *iter, *next;
586 	size_t entry_len, url_len;
587 
588 	if (ignore_advisories == NULL)
589 		return 0;
590 
591 	url_len = strlen(pv->advisory[i]);
592 
593 	for (iter = ignore_advisories; *iter; iter = next) {
594 		if ((next = strchr(iter, '\n')) == NULL) {
595 			entry_len = strlen(iter);
596 			next = iter + entry_len;
597 		} else {
598 			entry_len = next - iter;
599 			++next;
600 		}
601 		if (url_len != entry_len)
602 			continue;
603 		if (strncmp(pv->advisory[i], iter, entry_len) == 0)
604 			return 1;
605 	}
606 	return 0;
607 }
608 
609 int
610 audit_package(struct pkg_vulnerabilities *pv, const char *pkgname,
611     const char *limit_vul_types, int include_ignored, int output_type)
612 {
613 	FILE *output = output_type == 1 ? stdout : stderr;
614 	size_t i;
615 	int retval, do_eol, ignored;
616 
617 	retval = 0;
618 
619 	do_eol = (strcasecmp(check_eol, "yes") == 0);
620 
621 	for (i = 0; i < pv->entries; ++i) {
622 		ignored = check_ignored_entry(pv, i);
623 		if (ignored && !include_ignored)
624 			continue;
625 		if (limit_vul_types != NULL &&
626 		    strcmp(limit_vul_types, pv->classification[i]))
627 			continue;
628 		if (!pkg_match(pv->vulnerability[i], pkgname))
629 			continue;
630 		if (strcmp("eol", pv->classification[i]) == 0) {
631 			if (!do_eol)
632 				continue;
633 			retval = 1;
634 			if (output_type == 0) {
635 				puts(pkgname);
636 				continue;
637 			}
638 			fprintf(output,
639 			    "Package %s has reached end-of-life (eol), "
640 			    "see %s/eol-packages\n", pkgname,
641 			    tnf_vulnerability_base);
642 			continue;
643 		}
644 		retval = 1;
645 		if (output_type == 0) {
646 			fprintf(stdout, "%s%s\n",
647 				pkgname, ignored ? " (ignored)" : "");
648 		} else {
649 			fprintf(output,
650 			    "Package %s has a%s %s vulnerability, see %s\n",
651 			    pkgname, ignored ? "n ignored" : "",
652 			    pv->classification[i], pv->advisory[i]);
653 		}
654 	}
655 	return retval;
656 }
657