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