From 6f0b3573e05a76dd9772f386323751edba2b3b76 Mon Sep 17 00:00:00 2001 From: Wietse Z Venema Date: Tue, 21 Jul 2026 00:00:00 -0500 Subject: [PATCH] postfix-3.12-20260721 --- postfix/HISTORY | 21 ++++++++++++- postfix/src/global/mail_version.h | 2 +- postfix/src/nbdb_reindexd/nbdb_process.c | 26 +++++++--------- postfix/src/nbdb_reindexd/nbdb_process_test.c | 2 +- postfix/src/testing/mock_servent.c | 1 + postfix/src/util/binhash.c | 31 +++++++++++++++---- postfix/src/util/find_inet_service.c | 1 + 7 files changed, 61 insertions(+), 23 deletions(-) diff --git a/postfix/HISTORY b/postfix/HISTORY index 0163698c0..f935fa78a 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -31771,7 +31771,7 @@ Apologies for any names omitted. exists. Reported by Qualys, assisted by Claude Mythos Preview. File: pipe.c. -20260720: +20260720 Bug (defect introduced: Postfix 1,1, date: 20010524): the postsuper command over-read a very short queue filename. @@ -31804,6 +31804,25 @@ Apologies for any names omitted. service. Files: nbdb_safe.c, nbdb_safe.h, nbdb_safe_test.c, nbdb_process_test.c. +20260721 + + Hardening: in the non-BerkeleyDB migration service, delay + the decision between running postmap or postalias until + after the database file/directory owner/permission checks. + The benefit from making the decision early (better error + messages) was not worth the risk. Qualys, assisted by Claude + Mythos Preview. Files: nbdb_process.c, nbdb_process_test.c. + + Back-port htable fixes to binhash tables: 1) segfault in + the binhash first/next iterator when a table contains one + element; 2) technical debt: allow an element to be deleted + after the binhash table's first/next iterator has started, + but before it has visited the element; 3) don't free null + data pointers. File: util/binhash.c. + + Code health: missing includes. Files: + testing/mock_servent.c, util/find_inet_service.c. + TODO Reorganize PTEST_LIB, PMOCK_LIB, TESTLIB, TESTLIBS, etc. diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index bd20a5f2e..25f9cb49c 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20260720" +#define MAIL_RELEASE_DATE "20260721" #define MAIL_VERSION_NUMBER "3.12" #ifdef SNAPSHOT diff --git a/postfix/src/nbdb_reindexd/nbdb_process.c b/postfix/src/nbdb_reindexd/nbdb_process.c index 2ddcd4bf9..cd312add2 100644 --- a/postfix/src/nbdb_reindexd/nbdb_process.c +++ b/postfix/src/nbdb_reindexd/nbdb_process.c @@ -170,19 +170,6 @@ int nbdb_process(const char *leg_type, const char *source_path, return (NBDB_STAT_ERROR); } - /* - * Should we run postmap or postalias? Open the source file with the same - * (uid, gid) as the postmap or postalias commands would, so that we can - * detect permission errors quickly. - * - * Note: we do this before the file allowlist/owner/permission safety - * checks, so that we can log the concrete postmap or postalias command - * if a safety check fails. - */ - if ((index_cmd = nbdb_get_index_cmd_as(source_path, leg_idx_st.st_uid, - leg_idx_st.st_gid, why)) == 0) - return (NBDB_STAT_ERROR); - /* * Allow indexing as the legacy indexed file owner if it is considered * "safe". @@ -190,7 +177,18 @@ int nbdb_process(const char *leg_type, const char *source_path, if (!nbdb_safe_to_index_as_legacy_index_owner(source_path, &source_st, STR(leg_idx_path), &leg_idx_st, parent_dir, &parent_dir_st, why)) { - status = NBDB_STAT_ERROR; + return (NBDB_STAT_ERROR); + } + + /* + * Should we run postmap or postalias? Open the source file with the same + * (uid, gid) as the postmap or postalias commands would, so that we can + * detect permission errors quickly. 202607 Qualys+Mythos: move this + * after the safety checks. + */ + if ((index_cmd = nbdb_get_index_cmd_as(source_path, leg_idx_st.st_uid, + leg_idx_st.st_gid, why)) == 0) { + return (NBDB_STAT_ERROR); } /* diff --git a/postfix/src/nbdb_reindexd/nbdb_process_test.c b/postfix/src/nbdb_reindexd/nbdb_process_test.c index 6659d2568..1b8c5f946 100644 --- a/postfix/src/nbdb_reindexd/nbdb_process_test.c +++ b/postfix/src/nbdb_reindexd/nbdb_process_test.c @@ -505,7 +505,7 @@ static const TEST_CASE test_cases[] = { .in_new_type = "hash", .in_source_path = "/path/to/file", .want_status = NBDB_STAT_ERROR, - .want_why = "could not execute command 'postmap cdb:/path/to/file': table /path/to/file has an unexpected pathname", + .want_why = "table /path/to/file has an unexpected pathname", }, }, diff --git a/postfix/src/testing/mock_servent.c b/postfix/src/testing/mock_servent.c index bd3d4c090..94514c704 100644 --- a/postfix/src/testing/mock_servent.c +++ b/postfix/src/testing/mock_servent.c @@ -96,6 +96,7 @@ * System library. */ #include +#include #include #include diff --git a/postfix/src/util/binhash.c b/postfix/src/util/binhash.c index 0383a899a..bd2ade502 100644 --- a/postfix/src/util/binhash.c +++ b/postfix/src/util/binhash.c @@ -100,8 +100,7 @@ /* depending on the value of the "how" argument. Specify /* BINHASH_SEQ_FIRST to start a new sequence, BINHASH_SEQ_NEXT /* to continue, and BINHASH_SEQ_STOP to terminate a sequence -/* early. The caller must not delete an element before it is -/* visited. +/* early. /* RESTRICTIONS /* A callback function should not modify the hash table that is /* specified to its caller. @@ -278,7 +277,7 @@ BINHASH_INFO *binhash_locate(BINHASH *table, const void *key, ssize_t key_len) void binhash_delete(BINHASH *table, const void *key, ssize_t key_len, void (*free_fn) (void *)) { if (table != 0) { - BINHASH_INFO *ht; + BINHASH_INFO *ht, **sp; BINHASH_INFO **h = table->data + binhash_hash(key, key_len, table->size); for (ht = *h; ht; ht = ht->next) { @@ -291,9 +290,17 @@ void binhash_delete(BINHASH *table, const void *key, ssize_t key_len, void (* *h = ht->next; table->used--; myfree(ht->key); - if (free_fn) + if (free_fn && ht->value) (*free_fn) (ht->value); myfree((void *) ht); + /* In case the first/next iterator has not yet visited it */ + for (sp = table->seq_element; sp && *sp; sp++) { + if (*sp == ht) { + while ((*sp = sp[1]) != 0) + sp += 1; + break; + } + } return; } } @@ -315,7 +322,7 @@ void binhash_free(BINHASH *table, void (*free_fn) (void *)) for (ht = *h++; ht; ht = next) { next = ht->next; myfree(ht->key); - if (free_fn) + if (free_fn && ht->value) (*free_fn) (ht->value); myfree((void *) ht); } @@ -378,7 +385,7 @@ BINHASH_INFO *binhash_sequence(BINHASH *table, int how) myfree((void *) table->seq_bucket); table->seq_bucket = binhash_list(table); table->seq_element = table->seq_bucket; - return (*(table->seq_element)++); + /* FALLTHROUGH */ case BINHASH_SEQ_NEXT: /* next element */ if (table->seq_element && *table->seq_element) return (*(table->seq_element)++); @@ -439,6 +446,18 @@ int main(int unused_argc, char **unused_argv) msg_panic("%ld entries not deleted", (long) hash->used); myfree((void *) ht_info); binhash_free(hash, (void (*) (void *)) 0); + + /* Iterate one-element table. */ + hash = binhash_create(10); + binhash_enter(hash, "one", 4, "one"); + if( binhash_sequence(hash,BINHASH_SEQ_FIRST) == 0) + msg_panic("one-element iterator BINHASH_SEQ_FIRST"); + if (binhash_sequence(hash,BINHASH_SEQ_NEXT) != 0) + msg_panic("one-element iterator BINHASH_SEQ_NEXT"); + binhash_free(hash, (void (*) (void *)) 0); + + /* TODO: Delete element before it is visited by iterator. */ + vstring_free(buf); return (0); } diff --git a/postfix/src/util/find_inet_service.c b/postfix/src/util/find_inet_service.c index 29948c1b8..2a6aeedd2 100644 --- a/postfix/src/util/find_inet_service.c +++ b/postfix/src/util/find_inet_service.c @@ -32,6 +32,7 @@ /* System libraries. */ #include +#include #include #include #include -- 2.47.3