Skip to content

Commit 5cad124

Browse files
committed
Tighten debdb MD5 backend error handling
The debdb loader now depends on add_file_to_backend_by_md5 to distinguish per-file dpkg drift from backend snapshot failures. The minimal regression fix made positive returns nonfatal, but the helper still used magic integers and classified allocation, digest, and formatting failures the same way as expected file skips. Define explicit MD5 backend result values and return MD5_BACKEND_SKIPPED only for per-file conditions such as missing files, non-regular files, and MD5 mismatches. Treat invalid backend state, hash-generation failure, allocation failure, and memfd write failure as MD5_BACKEND_FATAL, and update the debdb caller to test that state directly. The duplicate key is now allocated as a complete string instead of using a fixed-size stack buffer. Add md5_backend_test under the Debian test block to cover hash mismatch skips, missing-file skips, successful snapshot records, and fatal memfd write failures.
1 parent 5fb2574 commit 5cad124

5 files changed

Lines changed: 356 additions & 46 deletions

File tree

src/library/deb-backend.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ static int do_deb_load_list(const conf_t *conf)
168168
: namenode->name;
169169
if (hash != NULL) {
170170
if (add_file_to_backend_by_md5(path, hash, hashtable_ptr, SRC_DEB,
171-
&deb_backend) < 0) {
171+
&deb_backend) == MD5_BACKEND_FATAL) {
172172
rc = 1;
173173
goto out;
174174
}
@@ -244,4 +244,3 @@ static int deb_destroy_backend(void)
244244
modstatdb_shutdown();
245245
return 0;
246246
}
247-

src/library/md5-backend.c

Lines changed: 62 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include <errno.h>
2626
#include <fcntl.h>
2727
#include <stdio.h>
28+
#include <stdlib.h>
29+
#include <string.h>
2830
#include <unistd.h>
2931
#include <openssl/md5.h>
3032
#include <openssl/sha.h>
@@ -44,7 +46,7 @@
4446
* Dpkg does not provide sha256 sums or file sizes to verify against.
4547
* The only source for verification is MD5. The logic implemented is:
4648
* 1) Calculate the MD5 sum and compare to the expected hash. If it does
47-
* not match, abort.
49+
* not match, skip the file.
4850
* 2) Calculate the SHA256 and file size on the local files.
4951
* 3) Add to database.
5052
*
@@ -54,15 +56,23 @@
5456
* This function would compute a sha256 and file size on the attackers
5557
* crafted file so they do not secure this backend.
5658
*
57-
* Returns 0 when the file was added, 1 when the file was skipped
58-
* (benign per-file condition), and -1 on a fatal error that leaves
59-
* the backend snapshot unusable.
59+
* Returns MD5_BACKEND_ADDED when the file was recorded or already present,
60+
* MD5_BACKEND_SKIPPED for per-file conditions expected from dpkg state drift,
61+
* and MD5_BACKEND_FATAL when the backend snapshot is unsafe to use.
6062
*/
61-
int add_file_to_backend_by_md5(const char *path, const char *expected_md5,
63+
md5_backend_result_t add_file_to_backend_by_md5(const char *path,
64+
const char *expected_md5,
6265
struct _hash_record **hashtable,
6366
trust_src_t trust_src, backend *dstbackend)
6467
{
6568

69+
if (path == NULL || expected_md5 == NULL || hashtable == NULL ||
70+
dstbackend == NULL || dstbackend->memfd < 0) {
71+
msg(LOG_ERR, "Invalid MD5 backend destination for %s",
72+
path ? path : "(null)");
73+
return MD5_BACKEND_FATAL;
74+
}
75+
6676
#ifdef DEBUG
6777
msg(LOG_DEBUG, "Adding %s", path);
6878
msg(LOG_DEBUG, "\tExpected MD5: %s", expected_md5);
@@ -72,23 +82,23 @@ int add_file_to_backend_by_md5(const char *path, const char *expected_md5,
7282
struct stat path_stat;
7383
if (fd < 0) {
7484
if (errno != ELOOP) // Don't report symlinks as a warning
75-
msg(LOG_WARNING, "Could not open %si, %s", path,
85+
msg(LOG_WARNING, "Could not open %s, %s", path,
7686
strerror(errno));
77-
return 1;
87+
return MD5_BACKEND_SKIPPED;
7888
}
7989

8090
if (fstat(fd, &path_stat)) {
8191
close(fd);
8292
msg(LOG_WARNING, "fstat file %s failed %s", path,
8393
strerror(errno));
84-
return 1;
94+
return MD5_BACKEND_SKIPPED;
8595
}
8696

8797
// If its not a regular file, skip.
8898
if (!S_ISREG(path_stat.st_mode)) {
8999
close(fd);
90100
msg(LOG_DEBUG, "Not regular file %s", path);
91-
return 1;
101+
return MD5_BACKEND_SKIPPED;
92102
}
93103

94104
size_t file_size = path_stat.st_size;
@@ -102,14 +112,14 @@ int add_file_to_backend_by_md5(const char *path, const char *expected_md5,
102112
if (md5_digest == NULL) {
103113
close(fd);
104114
msg(LOG_ERR, "MD5 digest returned NULL");
105-
return 1;
115+
return MD5_BACKEND_FATAL;
106116
}
107117
if (strcmp(md5_digest, expected_md5) != 0) {
108118
msg(LOG_WARNING, "Skipping %s: hash mismatch. Got %s, expected %s",
109119
path, md5_digest, expected_md5);
110120
close(fd);
111121
free(md5_digest);
112-
return 1;
122+
return MD5_BACKEND_SKIPPED;
113123
}
114124
free(md5_digest);
115125

@@ -120,41 +130,54 @@ int add_file_to_backend_by_md5(const char *path, const char *expected_md5,
120130

121131
if (sha_digest == NULL) {
122132
msg(LOG_ERR, "Sha digest returned NULL");
123-
return 1;
133+
return MD5_BACKEND_FATAL;
124134
}
125135

126136
char *data;
127137
if (asprintf(&data, DATA_FORMAT, trust_src, file_size, sha_digest) == -1) {
128-
data = NULL;
138+
msg(LOG_ERR, "Out of memory formatting trust data for %s", path);
139+
free(sha_digest);
140+
return MD5_BACKEND_FATAL;
129141
}
130142
free(sha_digest);
131143

132-
if (data) {
133-
// Getting rid of the duplicates.
134-
struct _hash_record *rcd = NULL;
135-
char key[kMaxKeyLength];
136-
snprintf(key, kMaxKeyLength - 1, "%s %s", path, data);
137-
138-
HASH_FIND_STR(*hashtable, key, rcd);
139-
140-
if (!rcd) {
141-
rcd = (struct _hash_record *)malloc(
142-
sizeof(struct _hash_record));
143-
rcd->key = strdup(key);
144-
HASH_ADD_KEYPTR(hh, *hashtable, rcd->key,
145-
strlen(rcd->key), rcd);
146-
if (dprintf(dstbackend->memfd, "%s %s\n",
147-
path, data) < 0) {
148-
msg(LOG_ERR,
149-
"dprintf failed writing %s to memfd (%s)",
150-
path, strerror(errno));
151-
free((void *)data);
152-
return -1;
153-
}
154-
}
144+
// Getting rid of the duplicates.
145+
struct _hash_record *rcd = NULL;
146+
char *key = NULL;
147+
148+
if (asprintf(&key, "%s %s", path, data) == -1) {
149+
msg(LOG_ERR, "Out of memory tracking %s", path);
155150
free((void *)data);
156-
return 0;
151+
return MD5_BACKEND_FATAL;
157152
}
158-
return 1;
159-
}
160153

154+
HASH_FIND_STR(*hashtable, key, rcd);
155+
156+
if (!rcd) {
157+
rcd = (struct _hash_record *)malloc(
158+
sizeof(struct _hash_record));
159+
if (rcd == NULL) {
160+
msg(LOG_ERR, "Out of memory tracking %s", path);
161+
free(key);
162+
free((void *)data);
163+
return MD5_BACKEND_FATAL;
164+
}
165+
rcd->key = key;
166+
if (dprintf(dstbackend->memfd, "%s %s\n",
167+
path, data) < 0) {
168+
msg(LOG_ERR,
169+
"dprintf failed writing %s to memfd (%s)",
170+
path, strerror(errno));
171+
free(key);
172+
free(rcd);
173+
free((void *)data);
174+
return MD5_BACKEND_FATAL;
175+
}
176+
HASH_ADD_KEYPTR(hh, *hashtable, rcd->key,
177+
strlen(rcd->key), rcd);
178+
} else {
179+
free(key);
180+
}
181+
free((void *)data);
182+
return MD5_BACKEND_ADDED;
183+
}

src/library/md5-backend.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,16 @@ struct _hash_record {
3232
UT_hash_handle hh;
3333
};
3434

35-
static const int kMaxKeyLength = 4096;
3635
static const int kMd5HexSize = 32;
3736

38-
int add_file_to_backend_by_md5(const char *path, const char *expected_md5,
39-
struct _hash_record **hashtable,
40-
trust_src_t trust_src, backend *dstbackend);
37+
typedef enum {
38+
MD5_BACKEND_FATAL = -1,
39+
MD5_BACKEND_ADDED = 0,
40+
MD5_BACKEND_SKIPPED = 1,
41+
} md5_backend_result_t;
42+
43+
md5_backend_result_t add_file_to_backend_by_md5(const char *path,
44+
const char *expected_md5,
45+
struct _hash_record **hashtable,
46+
trust_src_t trust_src, backend *dstbackend);
4147
#endif

src/tests/Makefile.am

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ rpm_backend_test_LDADD = ${top_builddir}/src/.libs/libfapolicyd.la
140140
endif
141141

142142
if WITH_DEB
143-
check_PROGRAMS += deb_test
143+
check_PROGRAMS += deb_test md5_backend_test
144144
LIBS += -ldpkg -lmd
145145
deb_test_CFLAGS = -std=gnu11 -fPIE -DPIE -pthread -g -W -Wall -Wshadow -Wundef -Wno-unused-result -Wno-unused-parameter -D_GNU_SOURCE -DLIBDPKG_VOLATILE_API
146146
deb_test_LDFLAGS = -pie -Wl,-z,relro -Wl,-z,now
@@ -151,6 +151,10 @@ deb_test_SOURCES = \
151151
${top_srcdir}/src/library/file.c \
152152
${top_srcdir}/src/library/backend-manager.c \
153153
${top_srcdir}/src/library/deb-backend.c
154+
md5_backend_test_CFLAGS = $(deb_test_CFLAGS)
155+
md5_backend_test_LDFLAGS = $(deb_test_LDFLAGS)
156+
md5_backend_test_LDADD = ${top_builddir}/src/.libs/libfapolicyd.la -ldpkg -lmd
157+
md5_backend_test_SOURCES = md5_backend_test.c test-stubs.c
154158
endif
155159

156160
TESTS = $(check_PROGRAMS)

0 commit comments

Comments
 (0)