Home | History | Annotate | Line # | Download | only in lib
      1 /*	$NetBSD: vulnerabilities-file.c,v 1.5 2021/04/10 19:49:59 nia Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg (at) 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.5 2021/04/10 19:49:59 nia 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 #ifndef BOOTSTRAP
     81 static struct archive *
     82 prepare_raw_file(void)
     83 {
     84 	struct archive *a = archive_read_new();
     85 	if (a == NULL)
     86 		errx(EXIT_FAILURE, "memory allocation failed");
     87 
     88 	archive_read_support_filter_gzip(a);
     89 	archive_read_support_filter_bzip2(a);
     90 	archive_read_support_filter_xz(a);
     91 	archive_read_support_format_raw(a);
     92 	return a;
     93 }
     94 #endif
     95 
     96 static void
     97 verify_signature_pkcs7(const char *input)
     98 {
     99 #ifdef HAVE_SSL
    100 	const char *begin_pkgvul, *end_pkgvul, *begin_sig, *end_sig;
    101 
    102 	if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
    103 		begin_pkgvul = input + strlen(pgp_msg_start);
    104 		if ((end_pkgvul = strstr(begin_pkgvul, pgp_msg_end)) == NULL)
    105 			errx(EXIT_FAILURE, "Invalid PGP signature");
    106 		if ((begin_sig = strstr(end_pkgvul, pkcs7_begin)) == NULL)
    107 			errx(EXIT_FAILURE, "No PKCS7 signature");
    108 	} else {
    109 		begin_pkgvul = input;
    110 		if ((begin_sig = strstr(begin_pkgvul, pkcs7_begin)) == NULL)
    111 			errx(EXIT_FAILURE, "No PKCS7 signature");
    112 		end_pkgvul = begin_sig;
    113 	}
    114 	if ((end_sig = strstr(begin_sig, pkcs7_end)) == NULL)
    115 		errx(EXIT_FAILURE, "Invalid PKCS7 signature");
    116 	end_sig += strlen(pkcs7_end);
    117 
    118 	if (easy_pkcs7_verify(begin_pkgvul, end_pkgvul - begin_pkgvul,
    119 	    begin_sig, end_sig - begin_sig, certs_pkg_vulnerabilities, 0))
    120 		errx(EXIT_FAILURE, "Unable to verify PKCS7 signature");
    121 #else
    122 	errx(EXIT_FAILURE, "OpenSSL support is not compiled in");
    123 #endif
    124 }
    125 
    126 static void
    127 verify_signature(const char *input, size_t input_len)
    128 {
    129 	gpg_verify(input, input_len, gpg_keyring_pkgvuln, NULL, 0);
    130 	if (certs_pkg_vulnerabilities != NULL)
    131 		verify_signature_pkcs7(input);
    132 }
    133 
    134 static void *
    135 sha512_hash_init(void)
    136 {
    137 	static SHA512_CTX hash_ctx;
    138 
    139 	SHA512_Init(&hash_ctx);
    140 	return &hash_ctx;
    141 }
    142 
    143 static void
    144 sha512_hash_update(void *ctx, const void *data, size_t len)
    145 {
    146 	SHA512_CTX *hash_ctx = ctx;
    147 
    148 	SHA512_Update(hash_ctx, data, len);
    149 }
    150 
    151 static const char *
    152 sha512_hash_finish(void *ctx)
    153 {
    154 	static char hash[SHA512_DIGEST_STRING_LENGTH];
    155 	unsigned char digest[SHA512_DIGEST_LENGTH];
    156 	SHA512_CTX *hash_ctx = ctx;
    157 	int i;
    158 
    159 	SHA512_Final(digest, hash_ctx);
    160 	for (i = 0; i < SHA512_DIGEST_LENGTH; ++i) {
    161 		unsigned char c;
    162 
    163 		c = digest[i] / 16;
    164 		if (c < 10)
    165 			hash[2 * i] = '0' + c;
    166 		else
    167 			hash[2 * i] = 'a' - 10 + c;
    168 
    169 		c = digest[i] % 16;
    170 		if (c < 10)
    171 			hash[2 * i + 1] = '0' + c;
    172 		else
    173 			hash[2 * i + 1] = 'a' - 10 + c;
    174 	}
    175 	hash[2 * i] = '\0';
    176 
    177 	return hash;
    178 }
    179 
    180 static void *
    181 sha1_hash_init(void)
    182 {
    183 	static SHA1_CTX hash_ctx;
    184 
    185 	SHA1Init(&hash_ctx);
    186 	return &hash_ctx;
    187 }
    188 
    189 static void
    190 sha1_hash_update(void *ctx, const void *data, size_t len)
    191 {
    192 	SHA1_CTX *hash_ctx = ctx;
    193 
    194 	SHA1Update(hash_ctx, data, len);
    195 }
    196 
    197 static const char *
    198 sha1_hash_finish(void *ctx)
    199 {
    200 	static char hash[SHA1_DIGEST_STRING_LENGTH];
    201 	SHA1_CTX *hash_ctx = ctx;
    202 
    203 	SHA1End(hash_ctx, hash);
    204 
    205 	return hash;
    206 }
    207 
    208 static const struct hash_algorithm {
    209 	const char *name;
    210 	size_t name_len;
    211 	void * (*init)(void);
    212 	void (*update)(void *, const void *, size_t);
    213 	const char * (* finish)(void *);
    214 } hash_algorithms[] = {
    215 	{ "SHA512", 6, sha512_hash_init, sha512_hash_update,
    216 	  sha512_hash_finish },
    217 	{ "SHA1", 4, sha1_hash_init, sha1_hash_update,
    218 	  sha1_hash_finish },
    219 	{ NULL, 0, NULL, NULL, NULL }
    220 };
    221 
    222 static void
    223 verify_hash(const char *input, const char *hash_line)
    224 {
    225 	const struct hash_algorithm *hash;
    226 	void *ctx;
    227 	const char *last_start, *next, *hash_value;
    228 	int in_pgp_msg;
    229 
    230 	for (hash = hash_algorithms; hash->name != NULL; ++hash) {
    231 		if (strncmp(hash_line, hash->name, hash->name_len))
    232 			continue;
    233 		if (isspace((unsigned char)hash_line[hash->name_len]))
    234 			break;
    235 	}
    236 	if (hash->name == NULL) {
    237 		const char *end_name;
    238 		for (end_name = hash_line; *end_name != '\0'; ++end_name) {
    239 			if (!isalnum((unsigned char)*end_name))
    240 				break;
    241 		}
    242 		warnx("Unsupported hash algorithm: %.*s",
    243 		    (int)(end_name - hash_line), hash_line);
    244 		return;
    245 	}
    246 
    247 	hash_line += hash->name_len;
    248 	if (!isspace((unsigned char)*hash_line))
    249 		errx(EXIT_FAILURE, "Invalid #CHECKSUM");
    250 	while (isspace((unsigned char)*hash_line) && *hash_line != '\n')
    251 		++hash_line;
    252 
    253 	if (*hash_line == '\n')
    254 		errx(EXIT_FAILURE, "Invalid #CHECKSUM");
    255 
    256 	ctx = (*hash->init)();
    257 	if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
    258 		input += strlen(pgp_msg_start);
    259 		in_pgp_msg = 1;
    260 	} else {
    261 		in_pgp_msg = 0;
    262 	}
    263 	for (last_start = input; *input != '\0'; input = next) {
    264 		if ((next = strchr(input, '\n')) == NULL)
    265 			errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
    266 		++next;
    267 		if (in_pgp_msg && strncmp(input, pgp_msg_end, strlen(pgp_msg_end)) == 0)
    268 			break;
    269 		if (!in_pgp_msg && strncmp(input, pkcs7_begin, strlen(pkcs7_begin)) == 0)
    270 			break;
    271 		if (*input == '\n' ||
    272 		    strncmp(input, "Hash:", 5) == 0 ||
    273 		    strncmp(input, "# $NetBSD", 9) == 0 ||
    274 		    strncmp(input, "#CHECKSUM", 9) == 0) {
    275 			(*hash->update)(ctx, last_start, input - last_start);
    276 			last_start = next;
    277 		}
    278 	}
    279 	(*hash->update)(ctx, last_start, input - last_start);
    280 	hash_value = (*hash->finish)(ctx);
    281 	if (strncmp(hash_line, hash_value, strlen(hash_value)))
    282 		errx(EXIT_FAILURE, "%s hash doesn't match", hash->name);
    283 	hash_line += strlen(hash_value);
    284 
    285 	while (isspace((unsigned char)*hash_line) && *hash_line != '\n')
    286 		++hash_line;
    287 
    288 	if (!isspace((unsigned char)*hash_line))
    289 		errx(EXIT_FAILURE, "Invalid #CHECKSUM");
    290 }
    291 
    292 static void
    293 add_vulnerability(struct pkg_vulnerabilities *pv, size_t *allocated, const char *line)
    294 {
    295 	size_t len_pattern, len_class, len_url;
    296 	const char *start_pattern, *start_class, *start_url;
    297 
    298 	start_pattern = line;
    299 
    300 	start_class = line;
    301 	while (*start_class != '\0' && !isspace((unsigned char)*start_class))
    302 		++start_class;
    303 	len_pattern = start_class - line;
    304 
    305 	while (*start_class != '\n' && isspace((unsigned char)*start_class))
    306 		++start_class;
    307 
    308 	if (*start_class == '0' || *start_class == '\n')
    309 		errx(EXIT_FAILURE, "Input error: missing classification");
    310 
    311 	start_url = start_class;
    312 	while (*start_url != '\0' && !isspace((unsigned char)*start_url))
    313 		++start_url;
    314 	len_class = start_url - start_class;
    315 
    316 	while (*start_url != '\n' && isspace((unsigned char)*start_url))
    317 		++start_url;
    318 
    319 	if (*start_url == '0' || *start_url == '\n')
    320 		errx(EXIT_FAILURE, "Input error: missing URL");
    321 
    322 	line = start_url;
    323 	while (*line != '\0' && !isspace((unsigned char)*line))
    324 		++line;
    325 	len_url = line - start_url;
    326 
    327 	if (pv->entries == *allocated) {
    328 		if (*allocated == 0)
    329 			*allocated = 16;
    330 		else if (*allocated <= SSIZE_MAX / 2)
    331 			*allocated *= 2;
    332 		else
    333 			errx(EXIT_FAILURE, "Too many vulnerabilities");
    334 		pv->vulnerability = xrealloc(pv->vulnerability,
    335 		    sizeof(char *) * *allocated);
    336 		pv->classification = xrealloc(pv->classification,
    337 		    sizeof(char *) * *allocated);
    338 		pv->advisory = xrealloc(pv->advisory,
    339 		    sizeof(char *) * *allocated);
    340 	}
    341 
    342 	pv->vulnerability[pv->entries] = xmalloc(len_pattern + 1);
    343 	memcpy(pv->vulnerability[pv->entries], start_pattern, len_pattern);
    344 	pv->vulnerability[pv->entries][len_pattern] = '\0';
    345 	pv->classification[pv->entries] = xmalloc(len_class + 1);
    346 	memcpy(pv->classification[pv->entries], start_class, len_class);
    347 	pv->classification[pv->entries][len_class] = '\0';
    348 	pv->advisory[pv->entries] = xmalloc(len_url + 1);
    349 	memcpy(pv->advisory[pv->entries], start_url, len_url);
    350 	pv->advisory[pv->entries][len_url] = '\0';
    351 
    352 	++pv->entries;
    353 }
    354 
    355 struct pkg_vulnerabilities *
    356 read_pkg_vulnerabilities_memory(void *buf, size_t len, int check_sum)
    357 {
    358 #ifdef BOOTSTRAP
    359 	errx(EXIT_FAILURE, "Audit functions are unsupported during bootstrap");
    360 #else
    361 	struct archive *a;
    362 	struct pkg_vulnerabilities *pv;
    363 
    364 	a = prepare_raw_file();
    365 	if (archive_read_open_memory(a, buf, len) != ARCHIVE_OK)
    366 		errx(EXIT_FAILURE, "Cannot open pkg_vulnerabilies buffer: %s",
    367 		    archive_error_string(a));
    368 
    369 	pv = read_pkg_vulnerabilities_archive(a, check_sum);
    370 
    371 	return pv;
    372 #endif
    373 }
    374 
    375 struct pkg_vulnerabilities *
    376 read_pkg_vulnerabilities_file(const char *path, int ignore_missing, int check_sum)
    377 {
    378 #ifdef BOOTSTRAP
    379 	errx(EXIT_FAILURE, "Audit functions are unsupported during bootstrap");
    380 #else
    381 	struct archive *a;
    382 	struct pkg_vulnerabilities *pv;
    383 	int fd;
    384 
    385 	if ((fd = open(path, O_RDONLY)) == -1) {
    386 		if (errno == ENOENT && ignore_missing)
    387 			return NULL;
    388 		err(EXIT_FAILURE, "Cannot open %s", path);
    389 	}
    390 
    391 	a = prepare_raw_file();
    392 	if (archive_read_open_fd(a, fd, 65536) != ARCHIVE_OK)
    393 		errx(EXIT_FAILURE, "Cannot open ``%s'': %s", path,
    394 		    archive_error_string(a));
    395 
    396 	pv = read_pkg_vulnerabilities_archive(a, check_sum);
    397 	close(fd);
    398 
    399 	return pv;
    400 #endif
    401 }
    402 
    403 #ifndef BOOTSTRAP
    404 static struct pkg_vulnerabilities *
    405 read_pkg_vulnerabilities_archive(struct archive *a, int check_sum)
    406 {
    407 	struct archive_entry *ae;
    408 	struct pkg_vulnerabilities *pv;
    409 	char *buf;
    410 	size_t buf_len, off;
    411 	ssize_t r;
    412 
    413 	if (archive_read_next_header(a, &ae) != ARCHIVE_OK)
    414 		errx(EXIT_FAILURE, "Cannot read pkg_vulnerabilities: %s",
    415 		    archive_error_string(a));
    416 
    417 	off = 0;
    418 	buf_len = 65536;
    419 	buf = xmalloc(buf_len + 1);
    420 
    421 	for (;;) {
    422 		r = archive_read_data(a, buf + off, buf_len - off);
    423 		if (r <= 0)
    424 			break;
    425 		off += r;
    426 		if (off == buf_len) {
    427 			buf_len *= 2;
    428 			if (buf_len < off)
    429 				errx(EXIT_FAILURE, "pkg_vulnerabilties too large");
    430 			buf = xrealloc(buf, buf_len + 1);
    431 		}
    432 	}
    433 
    434 	if (r != ARCHIVE_OK)
    435 		errx(EXIT_FAILURE, "Cannot read pkg_vulnerabilities: %s",
    436 		    archive_error_string(a));
    437 
    438 	archive_read_close(a);
    439 
    440 	buf[off] = '\0';
    441 	pv = parse_pkg_vuln(buf, off, check_sum);
    442 	free(buf);
    443 	return pv;
    444 }
    445 
    446 static struct pkg_vulnerabilities *
    447 parse_pkg_vuln(const char *input, size_t input_len, int check_sum)
    448 {
    449 	struct pkg_vulnerabilities *pv;
    450 	long version;
    451 	char *end;
    452 	const char *iter, *next;
    453 	size_t allocated_vulns;
    454 	int in_pgp_msg;
    455 
    456 	pv = xmalloc(sizeof(*pv));
    457 
    458 	allocated_vulns = pv->entries = 0;
    459 	pv->vulnerability = NULL;
    460 	pv->classification = NULL;
    461 	pv->advisory = NULL;
    462 
    463 	if (strlen(input) != input_len)
    464 		errx(1, "Invalid input (NUL character found)");
    465 
    466 	if (check_sum)
    467 		verify_signature(input, input_len);
    468 
    469 	if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
    470 		iter = input + strlen(pgp_msg_start);
    471 		in_pgp_msg = 1;
    472 	} else {
    473 		iter = input;
    474 		in_pgp_msg = 0;
    475 	}
    476 
    477 	for (; *iter; iter = next) {
    478 		if ((next = strchr(iter, '\n')) == NULL)
    479 			errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
    480 		++next;
    481 		if (*iter == '\0' || *iter == '\n')
    482 			continue;
    483 		if (strncmp(iter, "Hash:", 5) == 0)
    484 			continue;
    485 		if (strncmp(iter, "# $NetBSD", 9) == 0)
    486 			continue;
    487 		if (*iter == '#' && isspace((unsigned char)iter[1])) {
    488 			for (++iter; iter != next; ++iter) {
    489 				if (!isspace((unsigned char)*iter))
    490 					errx(EXIT_FAILURE, "Invalid header");
    491 			}
    492 			continue;
    493 		}
    494 
    495 		if (strncmp(iter, "#FORMAT", 7) != 0)
    496 			errx(EXIT_FAILURE, "Input header is malformed");
    497 
    498 		iter += 7;
    499 		if (!isspace((unsigned char)*iter))
    500 			errx(EXIT_FAILURE, "Invalid #FORMAT");
    501 		++iter;
    502 		version = strtol(iter, &end, 10);
    503 		if (iter == end || version != 1 || *end != '.')
    504 			errx(EXIT_FAILURE, "Input #FORMAT");
    505 		iter = end + 1;
    506 		version = strtol(iter, &end, 10);
    507 		if (iter == end || version != 1 || *end != '.')
    508 			errx(EXIT_FAILURE, "Input #FORMAT");
    509 		iter = end + 1;
    510 		version = strtol(iter, &end, 10);
    511 		if (iter == end || version != 0)
    512 			errx(EXIT_FAILURE, "Input #FORMAT");
    513 		for (iter = end; iter != next; ++iter) {
    514 			if (!isspace((unsigned char)*iter))
    515 				errx(EXIT_FAILURE, "Input #FORMAT");
    516 		}
    517 		break;
    518 	}
    519 	if (*iter == '\0')
    520 		errx(EXIT_FAILURE, "Missing #CHECKSUM or content");
    521 
    522 	for (iter = next; *iter; iter = next) {
    523 		if ((next = strchr(iter, '\n')) == NULL)
    524 			errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
    525 		++next;
    526 		if (*iter == '\0' || *iter == '\n')
    527 			continue;
    528 		if (in_pgp_msg && strncmp(iter, pgp_msg_end, strlen(pgp_msg_end)) == 0)
    529 			break;
    530 		if (!in_pgp_msg && strncmp(iter, pkcs7_begin, strlen(pkcs7_begin)) == 0)
    531 			break;
    532 		if (*iter == '#' &&
    533 		    (iter[1] == '\0' || iter[1] == '\n' || isspace((unsigned char)iter[1])))
    534 			continue;
    535 		if (strncmp(iter, "#CHECKSUM", 9) == 0) {
    536 			iter += 9;
    537 			if (!isspace((unsigned char)*iter))
    538 				errx(EXIT_FAILURE, "Invalid #CHECKSUM");
    539 			while (isspace((unsigned char)*iter))
    540 				++iter;
    541 			verify_hash(input, iter);
    542 			continue;
    543 		}
    544 		if (*iter == '#') {
    545 			/*
    546 			 * This should really be an error,
    547 			 * but it is still used.
    548 			 */
    549 			/* errx(EXIT_FAILURE, "Invalid data line starting with #"); */
    550 			continue;
    551 		}
    552 		add_vulnerability(pv, &allocated_vulns, iter);
    553 	}
    554 
    555 	if (pv->entries != allocated_vulns) {
    556 		pv->vulnerability = xrealloc(pv->vulnerability,
    557 		    sizeof(char *) * pv->entries);
    558 		pv->classification = xrealloc(pv->classification,
    559 		    sizeof(char *) * pv->entries);
    560 		pv->advisory = xrealloc(pv->advisory,
    561 		    sizeof(char *) * pv->entries);
    562 	}
    563 
    564 	return pv;
    565 }
    566 #endif
    567 
    568 void
    569 free_pkg_vulnerabilities(struct pkg_vulnerabilities *pv)
    570 {
    571 	size_t i;
    572 
    573 	for (i = 0; i < pv->entries; ++i) {
    574 		free(pv->vulnerability[i]);
    575 		free(pv->classification[i]);
    576 		free(pv->advisory[i]);
    577 	}
    578 	free(pv->vulnerability);
    579 	free(pv->classification);
    580 	free(pv->advisory);
    581 	free(pv);
    582 }
    583 
    584 static int
    585 check_ignored_entry(struct pkg_vulnerabilities *pv, size_t i)
    586 {
    587 	const char *iter, *next;
    588 	size_t entry_len, url_len;
    589 
    590 	if (ignore_advisories == NULL)
    591 		return 0;
    592 
    593 	url_len = strlen(pv->advisory[i]);
    594 
    595 	for (iter = ignore_advisories; *iter; iter = next) {
    596 		if ((next = strchr(iter, '\n')) == NULL) {
    597 			entry_len = strlen(iter);
    598 			next = iter + entry_len;
    599 		} else {
    600 			entry_len = next - iter;
    601 			++next;
    602 		}
    603 		if (url_len != entry_len)
    604 			continue;
    605 		if (strncmp(pv->advisory[i], iter, entry_len) == 0)
    606 			return 1;
    607 	}
    608 	return 0;
    609 }
    610 
    611 int
    612 audit_package(struct pkg_vulnerabilities *pv, const char *pkgname,
    613     const char *limit_vul_types, int include_ignored, int output_type)
    614 {
    615 	FILE *output = output_type == 1 ? stdout : stderr;
    616 	size_t i;
    617 	int retval, do_eol, ignored;
    618 
    619 	retval = 0;
    620 
    621 	do_eol = (strcasecmp(check_eol, "yes") == 0);
    622 
    623 	for (i = 0; i < pv->entries; ++i) {
    624 		ignored = check_ignored_entry(pv, i);
    625 		if (ignored && !include_ignored)
    626 			continue;
    627 		if (limit_vul_types != NULL &&
    628 		    strcmp(limit_vul_types, pv->classification[i]))
    629 			continue;
    630 		if (!pkg_match(pv->vulnerability[i], pkgname))
    631 			continue;
    632 		if (strcmp("eol", pv->classification[i]) == 0) {
    633 			if (!do_eol)
    634 				continue;
    635 			retval = 1;
    636 			if (output_type == 0) {
    637 				puts(pkgname);
    638 				continue;
    639 			}
    640 			fprintf(output,
    641 			    "Package %s has reached end-of-life (eol), "
    642 			    "see %s/eol-packages\n", pkgname,
    643 			    tnf_vulnerability_base);
    644 			continue;
    645 		}
    646 		retval = 1;
    647 		if (output_type == 0) {
    648 			fprintf(stdout, "%s%s\n",
    649 				pkgname, ignored ? " (ignored)" : "");
    650 		} else {
    651 			fprintf(output,
    652 			    "Package %s has a%s %s vulnerability, see %s\n",
    653 			    pkgname, ignored ? "n ignored" : "",
    654 			    pv->classification[i], pv->advisory[i]);
    655 		}
    656 	}
    657 	return retval;
    658 }
    659