Skip to content

Commit 13985b8

Browse files
committed
Fix dump-db empty active trust DB handling
fapolicyd-cli --dump-db used LMDB environment statistics to decide whether the trust database was empty. The current generation-based layout can have metadata and named database records in the environment while the active trust database itself has no entries, so the command treated MDB_NOTFOUND from MDB_FIRST as a database error. The dump command now reads the active DB name from trust metadata with stricter error handling, opens that DB, and checks mdb_stat() on the active DBI before cursor iteration. Empty active databases now return success, while present but unreadable metadata is reported as a trust database error instead of silently falling back to a stale DB name.
1 parent 93b8c91 commit 13985b8

1 file changed

Lines changed: 100 additions & 22 deletions

File tree

src/cli/fapolicyd-cli.c

Lines changed: 100 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
bool verbose = false;
6767
static bool lint_rules = false;
6868
static bool assume_yes = false;
69+
#define CLI_TRUST_DB_NAME_SIZE 64
6970

7071
static int send_state_report_signal(unsigned int pid, report_intent_t intent,
7172
char *reason, size_t reason_len)
@@ -180,6 +181,79 @@ static int do_delete_db(void)
180181
return CLI_EXIT_SUCCESS;
181182
}
182183

184+
/*
185+
* dump_parse_metadata - read the active trust DB name from metadata.
186+
* @data: NUL-terminated metadata value.
187+
* @name: Destination for the active DB name.
188+
*
189+
* Returns 0 on success, ENOMEM on allocation failure, or EINVAL when the
190+
* metadata does not contain the fields needed to select an active DB.
191+
*/
192+
static int dump_parse_metadata(const char *data,
193+
char name[CLI_TRUST_DB_NAME_SIZE])
194+
{
195+
char *copy, *line, *save = NULL;
196+
unsigned long ignored_generation;
197+
int have_generation = 0, have_name = 0;
198+
199+
copy = strdup(data);
200+
if (copy == NULL)
201+
return ENOMEM;
202+
203+
name[0] = 0;
204+
for (line = strtok_r(copy, "\n", &save); line;
205+
line = strtok_r(NULL, "\n", &save)) {
206+
if (sscanf(line, "generation=%lu", &ignored_generation) == 1) {
207+
have_generation = 1;
208+
} else if (strncmp(line, "name=", 5) == 0) {
209+
snprintf(name, CLI_TRUST_DB_NAME_SIZE, "%s", line + 5);
210+
have_name = name[0] != 0;
211+
}
212+
}
213+
214+
free(copy);
215+
return have_generation && have_name ? 0 : EINVAL;
216+
}
217+
218+
/*
219+
* dump_active_db_name - find the named DB that fapolicyd-cli -D should read.
220+
* @txn: Read transaction for the trust DB LMDB environment.
221+
* @name: Destination for the active DB name.
222+
*
223+
* Older trust databases did not have generation metadata, so absent metadata
224+
* falls back to DB_NAME. Present but unreadable metadata is treated as an
225+
* error because otherwise the CLI may inspect a stale generation.
226+
*/
227+
static int dump_active_db_name(MDB_txn *txn,
228+
char name[CLI_TRUST_DB_NAME_SIZE])
229+
{
230+
MDB_dbi metadata_dbi;
231+
MDB_val meta_key, meta_value;
232+
char data[4096];
233+
int rc;
234+
235+
snprintf(name, CLI_TRUST_DB_NAME_SIZE, "%s", DB_NAME);
236+
237+
rc = mdb_dbi_open(txn, TRUST_DB_METADATA_NAME, 0, &metadata_dbi);
238+
if (rc == MDB_NOTFOUND)
239+
return 0;
240+
if (rc)
241+
return rc;
242+
243+
meta_key.mv_data = (void *)TRUST_DB_METADATA_KEY;
244+
meta_key.mv_size = sizeof(TRUST_DB_METADATA_KEY) - 1;
245+
rc = mdb_get(txn, metadata_dbi, &meta_key, &meta_value);
246+
if (rc == MDB_NOTFOUND)
247+
return 0;
248+
if (rc)
249+
return rc;
250+
if (meta_value.mv_size >= sizeof(data))
251+
return EINVAL;
252+
253+
memcpy(data, meta_value.mv_data, meta_value.mv_size);
254+
data[meta_value.mv_size] = 0;
255+
return dump_parse_metadata(data, name);
256+
}
183257

184258
// This function opens the trust db and iterates over the entries.
185259
// It returns CLI_EXIT_SUCCESS on success and CLI_EXIT_DB_ERROR on failure
@@ -191,12 +265,10 @@ static int do_dump_db(void)
191265
MDB_env *env;
192266
MDB_txn *txn;
193267
MDB_dbi dbi;
194-
MDB_dbi metadata_dbi;
195268
MDB_stat status;
196269
MDB_cursor *cursor;
197270
MDB_val key, val;
198-
const char *active_name = DB_NAME;
199-
char metadata_name[64];
271+
char active_name[CLI_TRUST_DB_NAME_SIZE];
200272

201273
rc = mdb_env_create(&env);
202274
if (rc) {
@@ -235,23 +307,12 @@ static int do_dump_db(void)
235307
* records the active name in metadata. Older databases did not have this
236308
* metadata, so fall back to DB_NAME for compatibility.
237309
*/
238-
rc = mdb_dbi_open(txn, TRUST_DB_METADATA_NAME, 0, &metadata_dbi);
239-
if (rc == 0) {
240-
MDB_val meta_key, meta_value;
241-
242-
meta_key.mv_data = (void *)TRUST_DB_METADATA_KEY;
243-
meta_key.mv_size = sizeof(TRUST_DB_METADATA_KEY) - 1;
244-
if (mdb_get(txn, metadata_dbi, &meta_key, &meta_value) == 0 &&
245-
meta_value.mv_size < 4096) {
246-
char data[4096];
247-
unsigned long ignored_generation;
248-
249-
memcpy(data, meta_value.mv_data, meta_value.mv_size);
250-
data[meta_value.mv_size] = 0;
251-
if (sscanf(data, "generation=%lu\nname=%63s",
252-
&ignored_generation, metadata_name) == 2)
253-
active_name = metadata_name;
254-
}
310+
rc = dump_active_db_name(txn, active_name);
311+
if (rc) {
312+
fprintf(stderr, "trust metadata read failed, error %d %s\n",
313+
rc, mdb_strerror(rc));
314+
exit_rc = CLI_EXIT_DB_ERROR;
315+
goto txn_abort;
255316
}
256317
rc = mdb_dbi_open(txn, active_name, MDB_DUPSORT, &dbi);
257318
if (rc) {
@@ -260,19 +321,34 @@ static int do_dump_db(void)
260321
exit_rc = CLI_EXIT_DB_ERROR;
261322
goto txn_abort;
262323
}
324+
rc = mdb_stat(txn, dbi, &status);
325+
if (rc) {
326+
fprintf(stderr, "mdb_stat failed, error %d %s\n", rc,
327+
mdb_strerror(rc));
328+
exit_rc = CLI_EXIT_DB_ERROR;
329+
goto dbi_close;
330+
}
331+
if (status.ms_entries == 0) {
332+
printf("Trust database is empty\n");
333+
goto dbi_close;
334+
}
263335
rc = mdb_cursor_open(txn, dbi, &cursor);
264336
if (rc) {
265337
fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc,
266338
mdb_strerror(rc));
267339
exit_rc = CLI_EXIT_DB_ERROR;
268-
goto txn_abort;
340+
goto dbi_close;
269341
}
270342
rc = mdb_cursor_get(cursor, &key, &val, MDB_FIRST);
271343
if (rc) {
344+
if (rc == MDB_NOTFOUND) {
345+
printf("Trust database is empty\n");
346+
goto cursor_close;
347+
}
272348
fprintf(stderr, "mdb_cursor_get failed, error %d %s\n", rc,
273349
mdb_strerror(rc));
274350
exit_rc = CLI_EXIT_DB_ERROR;
275-
goto txn_abort;
351+
goto cursor_close;
276352
}
277353
do {
278354
char *path = NULL, *data = NULL, sha[FILE_DIGEST_STRING_MAX];
@@ -311,7 +387,9 @@ static int do_dump_db(void)
311387

312388
if (rc != MDB_NOTFOUND)
313389
exit_rc = CLI_EXIT_DB_ERROR;
390+
cursor_close:
314391
mdb_cursor_close(cursor);
392+
dbi_close:
315393
mdb_close(env, dbi);
316394
txn_abort:
317395
mdb_txn_abort(txn);

0 commit comments

Comments
 (0)