Hash algorithms (particularly weak ones like md5) are susceptible to
hash collisions -- a condition where two files/strings results in the
same hash. This can be exploited, e.g. to defeat digital signatures, by
padding malicious files with garbage data to coerce an identical hash
to that of legitimately signed files. Such exploits are significantly
harder when file size cannot change.
This change adds a file size constraint to pkg_verify() before checking
hashes: Downloaded IPKs must have the same file size that's encoded in
feed index (Packages file).
This is particularly useful when using opkg with signed feeds, I.e.
feeds with signed Packages files of otherwise unsigned IPKs. It's much
harder to defeat package hashes of a signed feed.
Signed-off-by: Haris Okanovic <
haris.o...@ni.com>
---
libopkg/pkg.c | 28 ++++++++++++++++++++++++----
1 file changed, 24 insertions(+), 4 deletions(-)
diff --git a/libopkg/pkg.c b/libopkg/pkg.c
index ca5ff08..daa7f94 100644
--- a/libopkg/pkg.c
+++ b/libopkg/pkg.c
@@ -1504,12 +1504,32 @@ int pkg_write_changed_filelists(void)
int pkg_verify(pkg_t * pkg)
{
int err;
+ struct stat pkg_stat;
char *local_sig_filename = NULL;
- /* Exit if the package doesn't exist locally as the caller may be about to
- * download it. */
- if (!file_exists(pkg->local_filename))
- return 1;
+ err = xlstat(pkg->local_filename, &pkg_stat);
+ if (err) {
+ if (errno == ENOENT) {
+ /* Exit with soft error 1 if the package doesn't exist.
+ * This allows the caller to download it without nasty
+ * messages in the error log.
+ */
+ return 1;
+ }
+ else {
+ opkg_msg(ERROR, "Failed to stat %s: %s\n",
+ pkg->local_filename, strerror(errno));
+ goto fail;
+ }
+ }
+
+ /* Check size to mitigate hash collisions. */
+ if (pkg_stat.st_size < 1 || pkg_stat.st_size != pkg->size) {
+ err = -1;
+ opkg_msg(ERROR, "File size mismatch: %s is %lld bytes, expecting %lu bytes\n",
+ pkg->local_filename, (long long int)pkg_stat.st_size, pkg->size);
+ goto fail;
+ }
#ifdef HAVE_SHA256
if (pkg->sha256sum) {
--
2.20.0