1 /* $NetBSD: vulnerabilities-file.c,v 1.1.1.5 2010/06/26 00:14:33 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.1.1.5 2010/06/26 00:14:33 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
;
454 next
= NULL
; /* LSC: Fix -Os compilation: -Werror=maybe-uninitialized */
455 #endif /* defined(__minix) */
456 pv
= xmalloc(sizeof(*pv
));
458 allocated_vulns
= pv
->entries
= 0;
459 pv
->vulnerability
= NULL
;
460 pv
->classification
= NULL
;
463 if (strlen(input
) != input_len
)
464 errx(1, "Invalid input (NUL character found)");
467 verify_signature(input
, input_len
);
469 if (strncmp(input
, pgp_msg_start
, strlen(pgp_msg_start
)) == 0) {
470 iter
= input
+ strlen(pgp_msg_start
);
477 for (; *iter
; iter
= next
) {
478 if ((next
= strchr(iter
, '\n')) == NULL
)
479 errx(EXIT_FAILURE
, "Missing newline in pkg-vulnerabilities");
481 if (*iter
== '\0' || *iter
== '\n')
483 if (strncmp(iter
, "Hash:", 5) == 0)
485 if (strncmp(iter
, "# $NetBSD", 9) == 0)
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");
495 if (strncmp(iter
, "#FORMAT", 7) != 0)
496 errx(EXIT_FAILURE
, "Input header is malformed");
499 if (!isspace((unsigned char)*iter
))
500 errx(EXIT_FAILURE
, "Invalid #FORMAT");
502 version
= strtol(iter
, &end
, 10);
503 if (iter
== end
|| version
!= 1 || *end
!= '.')
504 errx(EXIT_FAILURE
, "Input #FORMAT");
506 version
= strtol(iter
, &end
, 10);
507 if (iter
== end
|| version
!= 1 || *end
!= '.')
508 errx(EXIT_FAILURE
, "Input #FORMAT");
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");
520 errx(EXIT_FAILURE
, "Missing #CHECKSUM or content");
522 for (iter
= next
; *iter
; iter
= next
) {
523 if ((next
= strchr(iter
, '\n')) == NULL
)
524 errx(EXIT_FAILURE
, "Missing newline in pkg-vulnerabilities");
526 if (*iter
== '\0' || *iter
== '\n')
528 if (in_pgp_msg
&& strncmp(iter
, pgp_msg_end
, strlen(pgp_msg_end
)) == 0)
530 if (!in_pgp_msg
&& strncmp(iter
, pkcs7_begin
, strlen(pkcs7_begin
)) == 0)
533 (iter
[1] == '\0' || iter
[1] == '\n' || isspace((unsigned char)iter
[1])))
535 if (strncmp(iter
, "#CHECKSUM", 9) == 0) {
537 if (!isspace((unsigned char)*iter
))
538 errx(EXIT_FAILURE
, "Invalid #CHECKSUM");
539 while (isspace((unsigned char)*iter
))
541 verify_hash(input
, iter
);
546 * This should really be an error,
547 * but it is still used.
549 /* errx(EXIT_FAILURE, "Invalid data line starting with #"); */
552 add_vulnerability(pv
, &allocated_vulns
, iter
);
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
);
569 free_pkg_vulnerabilities(struct pkg_vulnerabilities
*pv
)
573 for (i
= 0; i
< pv
->entries
; ++i
) {
574 free(pv
->vulnerability
[i
]);
575 free(pv
->classification
[i
]);
576 free(pv
->advisory
[i
]);
578 free(pv
->vulnerability
);
579 free(pv
->classification
);
585 check_ignored_entry(struct pkg_vulnerabilities
*pv
, size_t i
)
587 const char *iter
, *next
;
588 size_t entry_len
, url_len
;
590 if (ignore_advisories
== NULL
)
593 url_len
= strlen(pv
->advisory
[i
]);
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
;
600 entry_len
= next
- iter
;
603 if (url_len
!= entry_len
)
605 if (strncmp(pv
->advisory
[i
], iter
, entry_len
) == 0)
612 audit_package(struct pkg_vulnerabilities
*pv
, const char *pkgname
,
613 const char *limit_vul_types
, int output_type
)
615 FILE *output
= output_type
== 1 ? stdout
: stderr
;
621 do_eol
= (strcasecmp(check_eol
, "yes") == 0);
623 for (i
= 0; i
< pv
->entries
; ++i
) {
624 if (check_ignored_entry(pv
, i
))
626 if (limit_vul_types
!= NULL
&&
627 strcmp(limit_vul_types
, pv
->classification
[i
]))
629 if (!pkg_match(pv
->vulnerability
[i
], pkgname
))
631 if (strcmp("eol", pv
->classification
[i
]) == 0) {
635 if (output_type
== 0) {
640 "Package %s has reached end-of-life (eol), "
641 "see %s/eol-packages\n", pkgname
,
642 tnf_vulnerability_base
);
646 if (output_type
== 0) {
650 "Package %s has a %s vulnerability, see %s\n",
651 pkgname
, pv
->classification
[i
], pv
->advisory
[i
]);