Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / bsd / pkg_install / dist / lib / vulnerabilities-file.c
bloba53f73466ff1f0b2e98ae6cc5467fa6f73e3e8d2
1 /* $NetBSD: vulnerabilities-file.c,v 1.5 2009/03/02 14:59:14 joerg Exp $ */
3 /*-
4 * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>.
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
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.
32 #if HAVE_CONFIG_H
33 #include "config.h"
34 #endif
36 #include <nbcompat.h>
38 #if HAVE_SYS_CDEFS_H
39 #include <sys/cdefs.h>
40 #endif
41 __RCSID("$NetBSD: vulnerabilities-file.c,v 1.5 2009/03/02 14:59:14 joerg Exp $");
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 #include <ctype.h>
50 #if HAVE_ERR_H
51 #include <err.h>
52 #endif
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <limits.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #ifndef NETBSD
59 #include <nbcompat/sha1.h>
60 #include <nbcompat/sha2.h>
61 #else
62 #include <sha1.h>
63 #include <sha2.h>
64 #endif
65 #include <unistd.h>
67 #include "lib.h"
69 static const char pgp_msg_start[] = "-----BEGIN PGP SIGNED MESSAGE-----\n";
70 static const char pgp_msg_end[] = "-----BEGIN PGP SIGNATURE-----\n";
71 static const char pkcs7_begin[] = "-----BEGIN PKCS7-----\n";
72 static const char pkcs7_end[] = "-----END PKCS7-----\n";
74 static void
75 verify_signature_pkcs7(const char *input)
77 #ifdef HAVE_SSL
78 const char *begin_pkgvul, *end_pkgvul, *begin_sig, *end_sig;
80 if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
81 begin_pkgvul = input + strlen(pgp_msg_start);
82 if ((end_pkgvul = strstr(begin_pkgvul, pgp_msg_end)) == NULL)
83 errx(EXIT_FAILURE, "Invalid PGP signature");
84 if ((begin_sig = strstr(end_pkgvul, pkcs7_begin)) == NULL)
85 errx(EXIT_FAILURE, "No PKCS7 signature");
86 } else {
87 begin_pkgvul = input;
88 if ((begin_sig = strstr(begin_pkgvul, pkcs7_begin)) == NULL)
89 errx(EXIT_FAILURE, "No PKCS7 signature");
90 end_pkgvul = begin_sig;
92 if ((end_sig = strstr(begin_sig, pkcs7_end)) == NULL)
93 errx(EXIT_FAILURE, "Invalid PKCS7 signature");
94 end_sig += strlen(pkcs7_end);
96 if (easy_pkcs7_verify(begin_pkgvul, end_pkgvul - begin_pkgvul,
97 begin_sig, end_sig - begin_sig, certs_pkg_vulnerabilities, 0))
98 errx(EXIT_FAILURE, "Unable to verify PKCS7 signature");
99 #else
100 errx(EXIT_FAILURE, "OpenSSL support is not compiled in");
101 #endif
104 static void
105 verify_signature(const char *input, size_t input_len)
107 if (gpg_cmd == NULL && certs_pkg_vulnerabilities == NULL)
108 errx(EXIT_FAILURE,
109 "At least GPG or CERTIFICATE_ANCHOR_PKGVULN "
110 "must be configured");
111 if (gpg_cmd != NULL)
112 inline_gpg_verify(input, input_len, gpg_keyring_pkgvuln);
113 if (certs_pkg_vulnerabilities != NULL)
114 verify_signature_pkcs7(input);
117 static void *
118 sha512_hash_init(void)
120 static SHA512_CTX hash_ctx;
122 SHA512_Init(&hash_ctx);
123 return &hash_ctx;
126 static void
127 sha512_hash_update(void *ctx, const void *data, size_t len)
129 SHA512_CTX *hash_ctx = ctx;
131 SHA512_Update(hash_ctx, data, len);
134 static const char *
135 sha512_hash_finish(void *ctx)
137 static char hash[SHA512_DIGEST_STRING_LENGTH];
138 unsigned char digest[SHA512_DIGEST_LENGTH];
139 SHA512_CTX *hash_ctx = ctx;
140 int i;
142 SHA512_Final(digest, hash_ctx);
143 for (i = 0; i < SHA512_DIGEST_LENGTH; ++i) {
144 unsigned char c;
146 c = digest[i] / 16;
147 if (c < 10)
148 hash[2 * i] = '0' + c;
149 else
150 hash[2 * i] = 'a' - 10 + c;
152 c = digest[i] % 16;
153 if (c < 10)
154 hash[2 * i + 1] = '0' + c;
155 else
156 hash[2 * i + 1] = 'a' - 10 + c;
158 hash[2 * i] = '\0';
160 return hash;
163 static void *
164 sha1_hash_init(void)
166 static SHA1_CTX hash_ctx;
168 SHA1Init(&hash_ctx);
169 return &hash_ctx;
172 static void
173 sha1_hash_update(void *ctx, const void *data, size_t len)
175 SHA1_CTX *hash_ctx = ctx;
177 SHA1Update(hash_ctx, data, len);
180 static const char *
181 sha1_hash_finish(void *ctx)
183 static char hash[SHA1_DIGEST_STRING_LENGTH];
184 SHA1_CTX *hash_ctx = ctx;
186 SHA1End(hash_ctx, hash);
188 return hash;
191 static const struct hash_algorithm {
192 const char *name;
193 size_t name_len;
194 void * (*init)(void);
195 void (*update)(void *, const void *, size_t);
196 const char * (* finish)(void *);
197 } hash_algorithms[] = {
198 { "SHA512", 6, sha512_hash_init, sha512_hash_update,
199 sha512_hash_finish },
200 { "SHA1", 4, sha1_hash_init, sha1_hash_update,
201 sha1_hash_finish },
202 { NULL, 0, NULL, NULL, NULL }
205 static void
206 verify_hash(const char *input, const char *hash_line)
208 const struct hash_algorithm *hash;
209 void *ctx;
210 const char *last_start, *next, *hash_value;
211 int in_pgp_msg;
213 for (hash = hash_algorithms; hash->name != NULL; ++hash) {
214 if (strncmp(hash_line, hash->name, hash->name_len))
215 continue;
216 if (isspace((unsigned char)hash_line[hash->name_len]))
217 break;
219 if (hash->name == NULL) {
220 const char *end_name;
221 for (end_name = hash_line; *end_name != '\0'; ++end_name) {
222 if (!isalnum((unsigned char)*end_name))
223 break;
225 warnx("Unsupported hash algorithm: %.*s",
226 (int)(end_name - hash_line), hash_line);
227 return;
230 hash_line += hash->name_len;
231 if (!isspace((unsigned char)*hash_line))
232 errx(EXIT_FAILURE, "Invalid #CHECKSUM");
233 while (isspace((unsigned char)*hash_line) && *hash_line != '\n')
234 ++hash_line;
236 if (*hash_line == '\n')
237 errx(EXIT_FAILURE, "Invalid #CHECKSUM");
239 ctx = (*hash->init)();
240 if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
241 input += strlen(pgp_msg_start);
242 in_pgp_msg = 1;
243 } else {
244 in_pgp_msg = 0;
246 for (last_start = input; *input != '\0'; input = next) {
247 if ((next = strchr(input, '\n')) == NULL)
248 errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
249 ++next;
250 if (in_pgp_msg && strncmp(input, pgp_msg_end, strlen(pgp_msg_end)) == 0)
251 break;
252 if (!in_pgp_msg && strncmp(input, pkcs7_begin, strlen(pkcs7_begin)) == 0)
253 break;
254 if (*input == '\n' ||
255 strncmp(input, "Hash:", 5) == 0 ||
256 strncmp(input, "# $NetBSD", 9) == 0 ||
257 strncmp(input, "#CHECKSUM", 9) == 0) {
258 (*hash->update)(ctx, last_start, input - last_start);
259 last_start = next;
262 (*hash->update)(ctx, last_start, input - last_start);
263 hash_value = (*hash->finish)(ctx);
264 if (strncmp(hash_line, hash_value, strlen(hash_value)))
265 errx(EXIT_FAILURE, "%s hash doesn't match", hash->name);
266 hash_line += strlen(hash_value);
268 while (isspace((unsigned char)*hash_line) && *hash_line != '\n')
269 ++hash_line;
271 if (!isspace((unsigned char)*hash_line))
272 errx(EXIT_FAILURE, "Invalid #CHECKSUM");
275 static void
276 add_vulnerability(struct pkg_vulnerabilities *pv, size_t *allocated, const char *line)
278 size_t len_pattern, len_class, len_url;
279 const char *start_pattern, *start_class, *start_url;
281 start_pattern = line;
283 start_class = line;
284 while (*start_class != '\0' && !isspace((unsigned char)*start_class))
285 ++start_class;
286 len_pattern = start_class - line;
288 while (*start_class != '\n' && isspace((unsigned char)*start_class))
289 ++start_class;
291 if (*start_class == '0' || *start_class == '\n')
292 errx(EXIT_FAILURE, "Input error: missing classification");
294 start_url = start_class;
295 while (*start_url != '\0' && !isspace((unsigned char)*start_url))
296 ++start_url;
297 len_class = start_url - start_class;
299 while (*start_url != '\n' && isspace((unsigned char)*start_url))
300 ++start_url;
302 if (*start_url == '0' || *start_url == '\n')
303 errx(EXIT_FAILURE, "Input error: missing URL");
305 line = start_url;
306 while (*line != '\0' && !isspace((unsigned char)*line))
307 ++line;
308 len_url = line - start_url;
310 if (pv->entries == *allocated) {
311 if (*allocated == 0)
312 *allocated = 16;
313 else if (*allocated <= SSIZE_MAX / 2)
314 *allocated *= 2;
315 else
316 errx(EXIT_FAILURE, "Too many vulnerabilities");
317 pv->vulnerability = xrealloc(pv->vulnerability,
318 sizeof(char *) * *allocated);
319 pv->classification = xrealloc(pv->classification,
320 sizeof(char *) * *allocated);
321 pv->advisory = xrealloc(pv->advisory,
322 sizeof(char *) * *allocated);
325 pv->vulnerability[pv->entries] = xmalloc(len_pattern + 1);
326 memcpy(pv->vulnerability[pv->entries], start_pattern, len_pattern);
327 pv->vulnerability[pv->entries][len_pattern] = '\0';
328 pv->classification[pv->entries] = xmalloc(len_class + 1);
329 memcpy(pv->classification[pv->entries], start_class, len_class);
330 pv->classification[pv->entries][len_class] = '\0';
331 pv->advisory[pv->entries] = xmalloc(len_url + 1);
332 memcpy(pv->advisory[pv->entries], start_url, len_url);
333 pv->advisory[pv->entries][len_url] = '\0';
335 ++pv->entries;
338 struct pkg_vulnerabilities *
339 read_pkg_vulnerabilities(const char *path, int ignore_missing, int check_sum)
341 struct pkg_vulnerabilities *pv;
342 struct stat st;
343 int fd;
344 char *input, *decompressed_input;
345 size_t input_len, decompressed_len;
346 ssize_t bytes_read;
348 if ((fd = open(path, O_RDONLY)) == -1) {
349 if (errno == ENOENT && ignore_missing)
350 return NULL;
351 err(EXIT_FAILURE, "Cannot open %s", path);
354 if (fstat(fd, &st) == -1)
355 err(EXIT_FAILURE, "Cannot stat %s", path);
357 if ((st.st_mode & S_IFMT) != S_IFREG)
358 errx(EXIT_FAILURE, "Input is not regular file");
359 if (st.st_size > SSIZE_MAX - 1)
360 errx(EXIT_FAILURE, "Input too large");
362 input_len = (size_t)st.st_size;
363 if (input_len < 4)
364 err(EXIT_FAILURE, "Input too short for a pkg_vulnerability file");
365 input = xmalloc(input_len + 1);
366 if ((bytes_read = read(fd, input, input_len)) == -1)
367 err(1, "Failed to read input");
368 if (bytes_read != st.st_size)
369 errx(1, "Unexpected short read");
371 close(fd);
373 if (decompress_buffer(input, input_len, &decompressed_input,
374 &decompressed_len)) {
375 free(input);
376 input = decompressed_input;
377 input_len = decompressed_len;
379 pv = parse_pkg_vulnerabilities(input, input_len, check_sum);
380 free(input);
382 return pv;
385 struct pkg_vulnerabilities *
386 parse_pkg_vulnerabilities(const char *input, size_t input_len, int check_sum)
388 struct pkg_vulnerabilities *pv;
389 long version;
390 char *end;
391 const char *iter, *next;
392 size_t allocated_vulns;
393 int in_pgp_msg;
395 pv = xmalloc(sizeof(*pv));
397 allocated_vulns = pv->entries = 0;
398 pv->vulnerability = NULL;
399 pv->classification = NULL;
400 pv->advisory = NULL;
402 if (strlen(input) != input_len)
403 errx(1, "Invalid input (NUL character found)");
405 if (check_sum)
406 verify_signature(input, input_len);
408 if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
409 iter = input + strlen(pgp_msg_start);
410 in_pgp_msg = 1;
411 } else {
412 iter = input;
413 in_pgp_msg = 0;
416 for (; *iter; iter = next) {
417 if ((next = strchr(iter, '\n')) == NULL)
418 errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
419 ++next;
420 if (*iter == '\0' || *iter == '\n')
421 continue;
422 if (strncmp(iter, "Hash:", 5) == 0)
423 continue;
424 if (strncmp(iter, "# $NetBSD", 9) == 0)
425 continue;
426 if (*iter == '#' && isspace((unsigned char)iter[1])) {
427 for (++iter; iter != next; ++iter) {
428 if (!isspace((unsigned char)*iter))
429 errx(EXIT_FAILURE, "Invalid header");
431 continue;
434 if (strncmp(iter, "#FORMAT", 7) != 0)
435 errx(EXIT_FAILURE, "Input header is malformed");
437 iter += 7;
438 if (!isspace((unsigned char)*iter))
439 errx(EXIT_FAILURE, "Invalid #FORMAT");
440 ++iter;
441 version = strtol(iter, &end, 10);
442 if (iter == end || version != 1 || *end != '.')
443 errx(EXIT_FAILURE, "Input #FORMAT");
444 iter = end + 1;
445 version = strtol(iter, &end, 10);
446 if (iter == end || version != 1 || *end != '.')
447 errx(EXIT_FAILURE, "Input #FORMAT");
448 iter = end + 1;
449 version = strtol(iter, &end, 10);
450 if (iter == end || version != 0)
451 errx(EXIT_FAILURE, "Input #FORMAT");
452 for (iter = end; iter != next; ++iter) {
453 if (!isspace((unsigned char)*iter))
454 errx(EXIT_FAILURE, "Input #FORMAT");
456 break;
458 if (*iter == '\0')
459 errx(EXIT_FAILURE, "Missing #CHECKSUM or content");
461 for (iter = next; *iter; iter = next) {
462 if ((next = strchr(iter, '\n')) == NULL)
463 errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
464 ++next;
465 if (*iter == '\0' || *iter == '\n')
466 continue;
467 if (in_pgp_msg && strncmp(iter, pgp_msg_end, strlen(pgp_msg_end)) == 0)
468 break;
469 if (!in_pgp_msg && strncmp(iter, pkcs7_begin, strlen(pkcs7_begin)) == 0)
470 break;
471 if (*iter == '#' &&
472 (iter[1] == '\0' || iter[1] == '\n' || isspace((unsigned char)iter[1])))
473 continue;
474 if (strncmp(iter, "#CHECKSUM", 9) == 0) {
475 iter += 9;
476 if (!isspace((unsigned char)*iter))
477 errx(EXIT_FAILURE, "Invalid #CHECKSUM");
478 while (isspace((unsigned char)*iter))
479 ++iter;
480 verify_hash(input, iter);
481 continue;
483 if (*iter == '#') {
485 * This should really be an error,
486 * but it is still used.
488 /* errx(EXIT_FAILURE, "Invalid data line starting with #"); */
489 continue;
491 add_vulnerability(pv, &allocated_vulns, iter);
494 if (pv->entries != allocated_vulns) {
495 pv->vulnerability = xrealloc(pv->vulnerability,
496 sizeof(char *) * pv->entries);
497 pv->classification = xrealloc(pv->classification,
498 sizeof(char *) * pv->entries);
499 pv->advisory = xrealloc(pv->advisory,
500 sizeof(char *) * pv->entries);
503 return pv;
506 void
507 free_pkg_vulnerabilities(struct pkg_vulnerabilities *pv)
509 size_t i;
511 for (i = 0; i < pv->entries; ++i) {
512 free(pv->vulnerability[i]);
513 free(pv->classification[i]);
514 free(pv->advisory[i]);
516 free(pv->vulnerability);
517 free(pv->classification);
518 free(pv->advisory);
519 free(pv);
522 static int
523 check_ignored_entry(struct pkg_vulnerabilities *pv, size_t i)
525 const char *iter, *next;
526 size_t entry_len, url_len;
528 if (ignore_advisories == NULL)
529 return 0;
531 url_len = strlen(pv->advisory[i]);
533 for (iter = ignore_advisories; *iter; iter = next) {
534 if ((next = strchr(iter, '\n')) == NULL) {
535 entry_len = strlen(iter);
536 next = iter + entry_len;
537 } else {
538 entry_len = next - iter;
539 ++next;
541 if (url_len != entry_len)
542 continue;
543 if (strncmp(pv->advisory[i], iter, entry_len) == 0)
544 return 1;
546 return 0;
550 audit_package(struct pkg_vulnerabilities *pv, const char *pkgname,
551 const char *limit_vul_types, int check_eol, int output_type)
553 FILE *output = output_type == 1 ? stdout : stderr;
554 size_t i;
555 int retval;
557 retval = 0;
559 for (i = 0; i < pv->entries; ++i) {
560 if (check_ignored_entry(pv, i))
561 continue;
562 if (limit_vul_types != NULL &&
563 strcmp(limit_vul_types, pv->classification[i]))
564 continue;
565 if (!pkg_match(pv->vulnerability[i], pkgname))
566 continue;
567 if (strcmp("eol", pv->classification[i]) == 0) {
568 if (!check_eol)
569 continue;
570 if (output_type == 0) {
571 puts(pkgname);
572 continue;
574 fprintf(output,
575 "Package %s has reached end-of-life (eol), "
576 "see %s/eol-packages\n", pkgname,
577 tnf_vulnerability_base);
578 continue;
580 retval = 1;
581 if (output_type == 0) {
582 puts(pkgname);
583 } else {
584 fprintf(output,
585 "Package %s has a %s vulnerability, see %s\n",
586 pkgname, pv->classification[i], pv->advisory[i]);
589 return retval;