1 /* $NetBSD: vulnerabilities-file.c,v 1.6 2010/04/14 18:24:58 joerg Exp $ */
4 * Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg@NetBSD.org>.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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
39 #include <sys/cdefs.h>
41 __RCSID("$NetBSD: vulnerabilities-file.c,v 1.6 2010/04/14 18:24:58 joerg Exp $");
62 #include <nbcompat/sha1.h>
63 #include <nbcompat/sha2.h>
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);
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";
81 verify_signature_pkcs7(const char *input)
84 const char *begin_pkgvul, *end_pkgvul, *begin_sig, *end_sig;
86 if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
87 begin_pkgvul = input + strlen(pgp_msg_start);
88 if ((end_pkgvul = strstr(begin_pkgvul, pgp_msg_end)) == NULL)
89 errx(EXIT_FAILURE, "Invalid PGP signature");
90 if ((begin_sig = strstr(end_pkgvul, pkcs7_begin)) == NULL)
91 errx(EXIT_FAILURE, "No PKCS7 signature");
94 if ((begin_sig = strstr(begin_pkgvul, pkcs7_begin)) == NULL)
95 errx(EXIT_FAILURE, "No PKCS7 signature");
96 end_pkgvul = begin_sig;
98 if ((end_sig = strstr(begin_sig, pkcs7_end)) == NULL)
99 errx(EXIT_FAILURE, "Invalid PKCS7 signature");
100 end_sig += strlen(pkcs7_end);
102 if (easy_pkcs7_verify(begin_pkgvul, end_pkgvul - begin_pkgvul,
103 begin_sig, end_sig - begin_sig, certs_pkg_vulnerabilities, 0))
104 errx(EXIT_FAILURE, "Unable to verify PKCS7 signature");
106 errx(EXIT_FAILURE, "OpenSSL support is not compiled in");
111 verify_signature(const char *input, size_t input_len)
113 if (gpg_cmd == NULL && certs_pkg_vulnerabilities == NULL)
115 "At least GPG or CERTIFICATE_ANCHOR_PKGVULN "
116 "must be configured");
118 inline_gpg_verify(input, input_len, gpg_keyring_pkgvuln);
119 if (certs_pkg_vulnerabilities != NULL)
120 verify_signature_pkcs7(input);
124 sha512_hash_init(void)
126 static SHA512_CTX hash_ctx;
128 SHA512_Init(&hash_ctx);
133 sha512_hash_update(void *ctx, const void *data, size_t len)
135 SHA512_CTX *hash_ctx = ctx;
137 SHA512_Update(hash_ctx, data, len);
141 sha512_hash_finish(void *ctx)
143 static char hash[SHA512_DIGEST_STRING_LENGTH];
144 unsigned char digest[SHA512_DIGEST_LENGTH];
145 SHA512_CTX *hash_ctx = ctx;
148 SHA512_Final(digest, hash_ctx);
149 for (i = 0; i < SHA512_DIGEST_LENGTH; ++i) {
154 hash[2 * i] = '0' + c;
156 hash[2 * i] = 'a' - 10 + c;
160 hash[2 * i + 1] = '0' + c;
162 hash[2 * i + 1] = 'a' - 10 + c;
172 static SHA1_CTX hash_ctx;
179 sha1_hash_update(void *ctx, const void *data, size_t len)
181 SHA1_CTX *hash_ctx = ctx;
183 SHA1Update(hash_ctx, data, len);
187 sha1_hash_finish(void *ctx)
189 static char hash[SHA1_DIGEST_STRING_LENGTH];
190 SHA1_CTX *hash_ctx = ctx;
192 SHA1End(hash_ctx, hash);
197 static const struct hash_algorithm {
200 void * (*init)(void);
201 void (*update)(void *, const void *, size_t);
202 const char * (* finish)(void *);
203 } hash_algorithms[] = {
204 { "SHA512", 6, sha512_hash_init, sha512_hash_update,
205 sha512_hash_finish },
206 { "SHA1", 4, sha1_hash_init, sha1_hash_update,
208 { NULL, 0, NULL, NULL, NULL }
212 verify_hash(const char *input, const char *hash_line)
214 const struct hash_algorithm *hash;
216 const char *last_start, *next, *hash_value;
219 for (hash = hash_algorithms; hash->name != NULL; ++hash) {
220 if (strncmp(hash_line, hash->name, hash->name_len))
222 if (isspace((unsigned char)hash_line[hash->name_len]))
225 if (hash->name == NULL) {
226 const char *end_name;
227 for (end_name = hash_line; *end_name != '\0'; ++end_name) {
228 if (!isalnum((unsigned char)*end_name))
231 warnx("Unsupported hash algorithm: %.*s",
232 (int)(end_name - hash_line), hash_line);
236 hash_line += hash->name_len;
237 if (!isspace((unsigned char)*hash_line))
238 errx(EXIT_FAILURE, "Invalid #CHECKSUM");
239 while (isspace((unsigned char)*hash_line) && *hash_line != '\n')
242 if (*hash_line == '\n')
243 errx(EXIT_FAILURE, "Invalid #CHECKSUM");
245 ctx = (*hash->init)();
246 if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
247 input += strlen(pgp_msg_start);
252 for (last_start = input; *input != '\0'; input = next) {
253 if ((next = strchr(input, '\n')) == NULL)
254 errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
256 if (in_pgp_msg && strncmp(input, pgp_msg_end, strlen(pgp_msg_end)) == 0)
258 if (!in_pgp_msg && strncmp(input, pkcs7_begin, strlen(pkcs7_begin)) == 0)
260 if (*input == '\n' ||
261 strncmp(input, "Hash:", 5) == 0 ||
262 strncmp(input, "# $NetBSD", 9) == 0 ||
263 strncmp(input, "#CHECKSUM", 9) == 0) {
264 (*hash->update)(ctx, last_start, input - last_start);
268 (*hash->update)(ctx, last_start, input - last_start);
269 hash_value = (*hash->finish)(ctx);
270 if (strncmp(hash_line, hash_value, strlen(hash_value)))
271 errx(EXIT_FAILURE, "%s hash doesn't match", hash->name);
272 hash_line += strlen(hash_value);
274 while (isspace((unsigned char)*hash_line) && *hash_line != '\n')
277 if (!isspace((unsigned char)*hash_line))
278 errx(EXIT_FAILURE, "Invalid #CHECKSUM");
282 add_vulnerability(struct pkg_vulnerabilities *pv, size_t *allocated, const char *line)
284 size_t len_pattern, len_class, len_url;
285 const char *start_pattern, *start_class, *start_url;
287 start_pattern = line;
290 while (*start_class != '\0' && !isspace((unsigned char)*start_class))
292 len_pattern = start_class - line;
294 while (*start_class != '\n' && isspace((unsigned char)*start_class))
297 if (*start_class == '0' || *start_class == '\n')
298 errx(EXIT_FAILURE, "Input error: missing classification");
300 start_url = start_class;
301 while (*start_url != '\0' && !isspace((unsigned char)*start_url))
303 len_class = start_url - start_class;
305 while (*start_url != '\n' && isspace((unsigned char)*start_url))
308 if (*start_url == '0' || *start_url == '\n')
309 errx(EXIT_FAILURE, "Input error: missing URL");
312 while (*line != '\0' && !isspace((unsigned char)*line))
314 len_url = line - start_url;
316 if (pv->entries == *allocated) {
319 else if (*allocated <= SSIZE_MAX / 2)
322 errx(EXIT_FAILURE, "Too many vulnerabilities");
323 pv->vulnerability = xrealloc(pv->vulnerability,
324 sizeof(char *) * *allocated);
325 pv->classification = xrealloc(pv->classification,
326 sizeof(char *) * *allocated);
327 pv->advisory = xrealloc(pv->advisory,
328 sizeof(char *) * *allocated);
331 pv->vulnerability[pv->entries] = xmalloc(len_pattern + 1);
332 memcpy(pv->vulnerability[pv->entries], start_pattern, len_pattern);
333 pv->vulnerability[pv->entries][len_pattern] = '\0';
334 pv->classification[pv->entries] = xmalloc(len_class + 1);
335 memcpy(pv->classification[pv->entries], start_class, len_class);
336 pv->classification[pv->entries][len_class] = '\0';
337 pv->advisory[pv->entries] = xmalloc(len_url + 1);
338 memcpy(pv->advisory[pv->entries], start_url, len_url);
339 pv->advisory[pv->entries][len_url] = '\0';
344 struct pkg_vulnerabilities *
345 read_pkg_vulnerabilities_memory(void *buf, size_t len, int check_sum)
348 errx(EXIT_FAILURE, "Audit functions are unsupported during bootstrap");
351 struct pkg_vulnerabilities *pv;
353 if ((a = archive_read_new()) == NULL)
354 errx(EXIT_FAILURE, "memory allocation failed");
356 if (archive_read_support_compression_all(a) != ARCHIVE_OK ||
357 archive_read_support_format_raw(a) != ARCHIVE_OK ||
358 archive_read_open_memory(a, buf, len) != ARCHIVE_OK)
359 errx(EXIT_FAILURE, "Cannot open pkg_vulnerabilies buffer: %s",
360 archive_error_string(a));
362 pv = read_pkg_vulnerabilities_archive(a, check_sum);
368 struct pkg_vulnerabilities *
369 read_pkg_vulnerabilities_file(const char *path, int ignore_missing, int check_sum)
372 errx(EXIT_FAILURE, "Audit functions are unsupported during bootstrap");
375 struct pkg_vulnerabilities *pv;
378 if ((fd = open(path, O_RDONLY)) == -1) {
379 if (errno == ENOENT && ignore_missing)
381 err(EXIT_FAILURE, "Cannot open %s", path);
384 if ((a = archive_read_new()) == NULL)
385 errx(EXIT_FAILURE, "memory allocation failed");
387 if (archive_read_support_compression_all(a) != ARCHIVE_OK ||
388 archive_read_support_format_raw(a) != ARCHIVE_OK ||
389 archive_read_open_fd(a, fd, 65536) != ARCHIVE_OK)
390 errx(EXIT_FAILURE, "Cannot open ``%s'': %s", path,
391 archive_error_string(a));
393 pv = read_pkg_vulnerabilities_archive(a, check_sum);
401 static struct pkg_vulnerabilities *
402 read_pkg_vulnerabilities_archive(struct archive *a, int check_sum)
404 struct archive_entry *ae;
405 struct pkg_vulnerabilities *pv;
410 if (archive_read_next_header(a, &ae) != ARCHIVE_OK)
411 errx(EXIT_FAILURE, "Cannot read pkg_vulnerabilities: %s",
412 archive_error_string(a));
416 buf = xmalloc(buf_len + 1);
419 r = archive_read_data(a, buf + off, buf_len - off);
423 if (off == buf_len) {
426 errx(EXIT_FAILURE, "pkg_vulnerabilties too large");
427 buf = xrealloc(buf, buf_len + 1);
432 errx(EXIT_FAILURE, "Cannot read pkg_vulnerabilities: %s",
433 archive_error_string(a));
435 archive_read_close(a);
438 pv = parse_pkg_vuln(buf, off, check_sum);
443 static struct pkg_vulnerabilities *
444 parse_pkg_vuln(const char *input, size_t input_len, int check_sum)
446 struct pkg_vulnerabilities *pv;
449 const char *iter, *next;
450 size_t allocated_vulns;
453 pv = xmalloc(sizeof(*pv));
455 allocated_vulns = pv->entries = 0;
456 pv->vulnerability = NULL;
457 pv->classification = NULL;
460 if (strlen(input) != input_len)
461 errx(1, "Invalid input (NUL character found)");
464 verify_signature(input, input_len);
466 if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
467 iter = input + strlen(pgp_msg_start);
474 for (; *iter; iter = next) {
475 if ((next = strchr(iter, '\n')) == NULL)
476 errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
478 if (*iter == '\0' || *iter == '\n')
480 if (strncmp(iter, "Hash:", 5) == 0)
482 if (strncmp(iter, "# $NetBSD", 9) == 0)
484 if (*iter == '#' && isspace((unsigned char)iter[1])) {
485 for (++iter; iter != next; ++iter) {
486 if (!isspace((unsigned char)*iter))
487 errx(EXIT_FAILURE, "Invalid header");
492 if (strncmp(iter, "#FORMAT", 7) != 0)
493 errx(EXIT_FAILURE, "Input header is malformed");
496 if (!isspace((unsigned char)*iter))
497 errx(EXIT_FAILURE, "Invalid #FORMAT");
499 version = strtol(iter, &end, 10);
500 if (iter == end || version != 1 || *end != '.')
501 errx(EXIT_FAILURE, "Input #FORMAT");
503 version = strtol(iter, &end, 10);
504 if (iter == end || version != 1 || *end != '.')
505 errx(EXIT_FAILURE, "Input #FORMAT");
507 version = strtol(iter, &end, 10);
508 if (iter == end || version != 0)
509 errx(EXIT_FAILURE, "Input #FORMAT");
510 for (iter = end; iter != next; ++iter) {
511 if (!isspace((unsigned char)*iter))
512 errx(EXIT_FAILURE, "Input #FORMAT");
517 errx(EXIT_FAILURE, "Missing #CHECKSUM or content");
519 for (iter = next; *iter; iter = next) {
520 if ((next = strchr(iter, '\n')) == NULL)
521 errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
523 if (*iter == '\0' || *iter == '\n')
525 if (in_pgp_msg && strncmp(iter, pgp_msg_end, strlen(pgp_msg_end)) == 0)
527 if (!in_pgp_msg && strncmp(iter, pkcs7_begin, strlen(pkcs7_begin)) == 0)
530 (iter[1] == '\0' || iter[1] == '\n' || isspace((unsigned char)iter[1])))
532 if (strncmp(iter, "#CHECKSUM", 9) == 0) {
534 if (!isspace((unsigned char)*iter))
535 errx(EXIT_FAILURE, "Invalid #CHECKSUM");
536 while (isspace((unsigned char)*iter))
538 verify_hash(input, iter);
543 * This should really be an error,
544 * but it is still used.
546 /* errx(EXIT_FAILURE, "Invalid data line starting with #"); */
549 add_vulnerability(pv, &allocated_vulns, iter);
552 if (pv->entries != allocated_vulns) {
553 pv->vulnerability = xrealloc(pv->vulnerability,
554 sizeof(char *) * pv->entries);
555 pv->classification = xrealloc(pv->classification,
556 sizeof(char *) * pv->entries);
557 pv->advisory = xrealloc(pv->advisory,
558 sizeof(char *) * pv->entries);
566 free_pkg_vulnerabilities(struct pkg_vulnerabilities *pv)
570 for (i = 0; i < pv->entries; ++i) {
571 free(pv->vulnerability[i]);
572 free(pv->classification[i]);
573 free(pv->advisory[i]);
575 free(pv->vulnerability);
576 free(pv->classification);
582 check_ignored_entry(struct pkg_vulnerabilities *pv, size_t i)
584 const char *iter, *next;
585 size_t entry_len, url_len;
587 if (ignore_advisories == NULL)
590 url_len = strlen(pv->advisory[i]);
592 for (iter = ignore_advisories; *iter; iter = next) {
593 if ((next = strchr(iter, '\n')) == NULL) {
594 entry_len = strlen(iter);
595 next = iter + entry_len;
597 entry_len = next - iter;
600 if (url_len != entry_len)
602 if (strncmp(pv->advisory[i], iter, entry_len) == 0)
609 audit_package(struct pkg_vulnerabilities *pv, const char *pkgname,
610 const char *limit_vul_types, int check_eol, int output_type)
612 FILE *output = output_type == 1 ? stdout : stderr;
618 for (i = 0; i < pv->entries; ++i) {
619 if (check_ignored_entry(pv, i))
621 if (limit_vul_types != NULL &&
622 strcmp(limit_vul_types, pv->classification[i]))
624 if (!pkg_match(pv->vulnerability[i], pkgname))
626 if (strcmp("eol", pv->classification[i]) == 0) {
629 if (output_type == 0) {
634 "Package %s has reached end-of-life (eol), "
635 "see %s/eol-packages\n", pkgname,
636 tnf_vulnerability_base);
640 if (output_type == 0) {
644 "Package %s has a %s vulnerability, see %s\n",
645 pkgname, pv->classification[i], pv->advisory[i]);