]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.12-20260110
authorWietse Z Venema <wietse@porcupine.org>
Sat, 10 Jan 2026 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Mon, 12 Jan 2026 14:04:12 +0000 (01:04 +1100)
postfix/HISTORY
postfix/Makefile.in
postfix/mantools/check-proxy-type-table [new file with mode: 0755]
postfix/proto/stop.double-history
postfix/src/cleanup/Makefile.in
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/proxymap/Makefile.in
postfix/src/proxymap/proxymap.c

index 93eba0284ee5212107010765c394cecc4fdfe26b..ad770cb08f36ecbe1b39842cf2a0c1fa3fe258b3 100644 (file)
@@ -30303,3 +30303,30 @@ Apologies for any names omitted.
 
        Documentation: fixed wrong default in INSTALL and postconf(5).
        Files: proto/INSTALL.html, proto/postconf.proto.
+
+20260105
+
+       Cleanup: added missing entries to the proxy_read_maps default
+       value for more complete coverage. File: global/mail_params.h.
+
+20260106
+
+       Cleanup: added more missing entries to the proxy_read_maps
+       default value, based on documentation that parameters support
+       type:table inputs. Files: mantools/check-proxy-type-table,
+       global/mail_params.h
+
+20260107
+
+       Cleanup: added missing initialization for parameters with
+       a non-empty default value that specifies a lookup table.
+       File: proxymap/proxymap.c.
+
+       Cleanup: the proxymap service now detects table references
+       in /file/name and in restriction_classes. File: proxymap/proxymap.c
+
+       Feature: export the proxymap authorization tables, for
+       testing, and for potential use by other tools. File:
+       proxymap/proxymap.c
+
+       Cleanup: re-run 'make depend'. File: cleanup/Makefile.in.
index fc7be73865a67490060c146744dc926d8670f099..a436f732588db18f1b0f7038a62a7a0ed6de17a8 100644 (file)
@@ -136,6 +136,7 @@ postconf-unimplemented-check:
 
 missing-proxy-read-maps-check:
        $(SHLIB_ENV) mantools/missing-proxy-read-maps | diff /dev/null -
+       $(SHLIB_ENV) mantools/check-proxy-type-table | diff /dev/null -
 
 typo-check: spell-cc spell-install-proto-text spell-proto-html \
        check-spell-history
diff --git a/postfix/mantools/check-proxy-type-table b/postfix/mantools/check-proxy-type-table
new file mode 100755 (executable)
index 0000000..7637517
--- /dev/null
@@ -0,0 +1,52 @@
+#!/usr/bin/perl
+
+# List the names of parameters that are documented to support "type:table",
+# but that still need to be listed in proxy_read_maps. Some parameters
+# may never use proxymapped tables, but we want to maximize coverage of
+# the proxy_read_maps parameter name list for non-proxymap purposes.
+
+use strict;
+
+my $proto_doc = "proto/postconf.proto";
+my $proxy_read = "bin/postconf -dh proxy_read_maps";
+my %from_proto = ();
+my %from_proxy = ();
+my %proto_stops = ( 
+    "authorized_verp_clients" => 1,    # Obsolete
+    "master_service_disable" => 1,     # False positive
+);
+my $param = "";
+my $debug = 0;
+
+# Get the names of parameters that mention "type:table" in their
+# documentation. This will not find smtpd_mumble_restrictions but those
+# are already covered elsewhere.
+
+open PROTO, $proto_doc || die "Open $proto_doc: $!\n";
+while (<PROTO>) {
+    if (/^%PARAM\s+(\S+)/) { $param = $1; print "got: $param\n" if $debug; }
+    if ($param && /\btype:table\b/ && !/\bnot\b/ && !$proto_stops{$param}) { 
+       print $_ if ($debug);
+       $from_proto{$param} = 1; 
+    }
+}
+close PROTO || die "Read $proto_doc: $!\n";
+
+# Get the names of parameters that appear in proxy_read_maps. We use
+# these as a stop list for the above documentation-based approach.
+
+open PROXY, "$proxy_read|" || die "Run $proxy_read: $!\n";
+for $param (split(/\s+/, <PROXY>)) {
+    $param = substr($param, 1);        # left-hand-side chop()
+    $from_proxy{$param} = 1;
+}
+close PROXY || die "Run $proxy_read: $!\n";
+
+if ($debug) {
+    for $param (sort keys %from_proxy) { print "from proxy: $param\n"; }
+    for $param (sort keys %from_proto) { print "from proto: $param\n"; }
+}
+
+# List parameters with "type:table" that are not listed in proxy_read_maps.
+
+for $param (sort keys %from_proto) { print "$param\n" unless $from_proxy{$param}; }
index f9cc469ba60fd1dcd77f056865cd898027048009..17fbca92cdf8087d24b303ae1d41aea19a959342 100644 (file)
@@ -215,3 +215,6 @@ proto  proto socketmap_table qmqpd qmqpd c tls tls_misc c
  verification in progress File verify verify c 
  virtual virtual c 
  with setgid permissions File postlogd postlogd c 
+ default value with a lookup table File proxymap proxymap c 
+ in file name and in restriction_classes File proxymap proxymap c
+ proxymap proxymap c
index 38fe077437af5041245dc07506b9a80af606d1a8..5430e2f64fe5afb1da4dc361600c50acbd88ce9f 100644 (file)
@@ -1265,6 +1265,41 @@ cleanup_message.o: ../../include/vstream.h
 cleanup_message.o: ../../include/vstring.h
 cleanup_message.o: cleanup.h
 cleanup_message.o: cleanup_message.c
+cleanup_message_test.o: ../../include/argv.h
+cleanup_message_test.o: ../../include/attr.h
+cleanup_message_test.o: ../../include/been_here.h
+cleanup_message_test.o: ../../include/check_arg.h
+cleanup_message_test.o: ../../include/cleanup_user.h
+cleanup_message_test.o: ../../include/dict.h
+cleanup_message_test.o: ../../include/dsn_mask.h
+cleanup_message_test.o: ../../include/header_body_checks.h
+cleanup_message_test.o: ../../include/header_opts.h
+cleanup_message_test.o: ../../include/hfrom_format.h
+cleanup_message_test.o: ../../include/htable.h
+cleanup_message_test.o: ../../include/mail_conf.h
+cleanup_message_test.o: ../../include/mail_params.h
+cleanup_message_test.o: ../../include/mail_stream.h
+cleanup_message_test.o: ../../include/maps.h
+cleanup_message_test.o: ../../include/match_list.h
+cleanup_message_test.o: ../../include/milter.h
+cleanup_message_test.o: ../../include/mime_state.h
+cleanup_message_test.o: ../../include/msg.h
+cleanup_message_test.o: ../../include/msg_vstream.h
+cleanup_message_test.o: ../../include/myflock.h
+cleanup_message_test.o: ../../include/mymalloc.h
+cleanup_message_test.o: ../../include/nvtable.h
+cleanup_message_test.o: ../../include/rec_type.h
+cleanup_message_test.o: ../../include/record.h
+cleanup_message_test.o: ../../include/resolve_clnt.h
+cleanup_message_test.o: ../../include/string_list.h
+cleanup_message_test.o: ../../include/stringops.h
+cleanup_message_test.o: ../../include/sys_defs.h
+cleanup_message_test.o: ../../include/tok822.h
+cleanup_message_test.o: ../../include/vbuf.h
+cleanup_message_test.o: ../../include/vstream.h
+cleanup_message_test.o: ../../include/vstring.h
+cleanup_message_test.o: cleanup.h
+cleanup_message_test.o: cleanup_message_test.c
 cleanup_milter.o: ../../include/argv.h
 cleanup_milter.o: ../../include/attr.h
 cleanup_milter.o: ../../include/been_here.h
index 3a656f828490ffa5d0a840281fd6c3fca55a64fd..5dccfad6538367735681ce161076ed11056dafce 100644 (file)
@@ -2586,7 +2586,32 @@ extern int var_local_rcpt_code;
                                " $" VAR_SMTP_HEAD_CHKS \
                                " $" VAR_SMTP_MIME_CHKS \
                                " $" VAR_SMTP_NEST_CHKS \
-                               " $" VAR_SMTPD_REJECT_FILTER_MAPS
+                               " $" VAR_SMTPD_REJECT_FILTER_MAPS \
+                               " $" VAR_DEBUG_PEER_LIST \
+                               " $" VAR_ETRN_CHECKS \
+                               " $" VAR_FFLUSH_DOMAINS \
+                               " $" VAR_FLUSH_ACL \
+                               " $" VAR_LMTP_CACHE_DEST \
+                               " $" VAR_LOC_RWR_CLIENTS \
+                               " $" VAR_MASQ_EXCEPTIONS \
+                               " $" VAR_PSC_ACL \
+                               " $" VAR_PSC_ALLIST_IF \
+                               " $" VAR_PSC_FORBID_CMDS \
+                               " $" VAR_QMQPD_CLIENTS \
+                               " $" VAR_SHOWQ_ACL \
+                               " $" VAR_SMTP_CACHE_DEST \
+                               " $" VAR_SMTPD_ACL_PERM_LOG \
+                               " $" VAR_SMTPD_FORBID_CMDS \
+                               " $" VAR_SMTPD_HOGGERS \
+                               " $" VAR_SMTPD_SASL_EXCEPTIONS_NETWORKS \
+                               " $" VAR_SMTPD_SASL_MECH_FILTER \
+                               " $" VAR_SMTP_REQTLS_POLICY \
+                               " $" VAR_SMTP_SASL_MECHS \
+                               " $" VAR_SUBMIT_ACL \
+                               " $" VAR_VERP_CLIENTS \
+                               " $" VAR_XCLIENT_HOSTS \
+                               " $" VAR_XFORWARD_HOSTS \
+
 extern char *var_proxy_read_maps;
 
 #define VAR_PROXY_WRITE_MAPS   "proxy_write_maps"
index 0b074de298481208e90cc9f3eec7152601b5c636..2b23134453476eaf67e1a713275ff5a408d37605 100644 (file)
@@ -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      "20251231"
+#define MAIL_RELEASE_DATE      "20260110"
 #define MAIL_VERSION_NUMBER    "3.12"
 
 #ifdef SNAPSHOT
index efe6e0c879e2c3f25a5e2d05eaa15f4999eac12e..dc60edd2f3364e8457ba144535f68a82a8bc46b3 100644 (file)
@@ -24,7 +24,114 @@ Makefile: Makefile.in
 
 test:  $(TESTPROG)
 
-tests:
+tests: empty_proxymap_table empty_proxywrite_table \
+       proxy_read_map_works_for_proxied proxy_read_map_works_for_both \
+       proxy_write_works smtpd_restriction_classes_works file_name_works \
+       ignores_non_dictionary_form
+
+# Create a configuration that references no tables. The default tables
+# are platform-specific and that would complicate tests.
+SETUP_FILES = \
+       echo rm -f main.cf master.cf; \
+       echo alias_maps = >> main.cf; \
+       echo local_recipient_maps = >> main.cf; \
+       echo smtpd_forbidden_commands = >> main.cf; \
+       echo postscreen_cache_map = >> main.cf; \
+       echo address_verify_map = >> main.cf; \
+
+CLEANUP_FILES = rm -f main.cf master.cf
+
+empty_proxymap_table: $(PROG)
+       @echo RUN empty_proxymap_table
+       @$(SETUP_FILES)
+       touch -t 197101010000 main.cf
+       MAIL_CONFIG=`pwd` $(SHLIB_ENV) ${VALGRIND} ./$(PROG) \
+           -SVnexport-all-proxymap | diff /dev/null -
+       @$(CLEANUP_FILES)
+       @echo PASS empty_proxymap_table; echo
+
+empty_proxywrite_table: $(PROG)
+       @echo RUN empty_proxywrite_table
+       @$(SETUP_FILES)
+       touch -t 197101010000 main.cf
+       MAIL_CONFIG=`pwd` $(SHLIB_ENV) ${VALGRIND} ./$(PROG) \
+           -SVnexport-all-proxywrite | diff /dev/null -
+       @$(CLEANUP_FILES)
+       @echo PASS empty_proxywrite_table; echo
+
+proxy_read_map_works_for_proxied: $(PROG)
+       @echo RUN proxy_read_map_works_for_proxied
+       @$(SETUP_FILES)
+       echo local_recipient_maps = proxy:unix:passwd.byname cdb:/some/aliases >> main.cf
+       touch -t 197101010000 main.cf
+       echo unix:passwd.byname > proxy_read_map_works_for_proxied.tmp
+       $(SHLIB_ENV) ${VALGRIND} MAIL_CONFIG=`pwd` ./$(PROG) \
+           -SVnexport-proxy-proxymap | \
+           diff proxy_read_map_works_for_proxied.tmp -
+       @$(CLEANUP_FILES) proxy_read_map_works_for_proxied.tmp
+       @echo PASS proxy_read_map_works_for_proxied; echo
+
+proxy_read_map_works_for_both: $(PROG)
+       @echo RUN proxy_read_map_works_for_both
+       @$(SETUP_FILES)
+       echo local_recipient_maps = proxy:unix:passwd.byname cdb:/some/aliases >> main.cf
+       touch -t 197101010000 main.cf
+       echo cdb:/some/aliases  > proxy_read_map_works_for_both.tmp
+       echo unix:passwd.byname >> proxy_read_map_works_for_both.tmp
+       $(SHLIB_ENV) ${VALGRIND} MAIL_CONFIG=`pwd` ./$(PROG) \
+           -SVnexport-all-proxymap | diff proxy_read_map_works_for_both.tmp -
+       @$(CLEANUP_FILES) proxy_read_map_works_for_both.tmp
+       @echo PASS proxy_read_map_works_for_both; echo
+
+proxy_write_works: $(PROG)
+       @echo RUN proxy_write_works
+       @$(SETUP_FILES)
+       echo postscreen_cache_map = proxy:lmdb:/some/path >> main.cf
+       touch -t 197101010000 main.cf
+       echo lmdb:/some/path > proxy_write_works.tmp
+       $(SHLIB_ENV) ${VALGRIND} MAIL_CONFIG=`pwd` ./$(PROG) \
+           -SVnexport-proxy-proxywrite | \
+           diff proxy_write_works.tmp -
+       @$(CLEANUP_FILES) proxy_write_works.tmp
+       @echo PASS proxy_write_works; echo
+
+smtpd_restriction_classes_works: $(PROG)
+       @echo RUN smtpd_restriction_classes_works
+       @$(SETUP_FILES)
+       echo smtpd_restriction_classes = foo >> main.cf
+       echo 'foo = $$bar' >> main.cf
+       echo bar = proxy:lmdb:/some/file >> main.cf
+       touch -t 197101010000 main.cf
+       echo lmdb:/some/file > smtpd_restriction_classes_works.tmp
+       $(SHLIB_ENV) ${VALGRIND} MAIL_CONFIG=`pwd` ./$(PROG) \
+           -SVnexport-proxy-proxymap | \
+           diff smtpd_restriction_classes_works.tmp -
+       @$(CLEANUP_FILES) smtpd_restriction_classes_works.tmp
+       @echo PASS smtpd_restriction_classes_works; echo
+
+file_name_works: $(PROG)
+       @echo RUN file_name_works
+       @$(SETUP_FILES)
+       echo mynetworks = `pwd`/file_name_works_file >> main.cf
+       echo proxy:lmdb:/some/file > file_name_works_file
+       touch -t 197101010000 main.cf
+       echo lmdb:/some/file > file_name_works.tmp
+       $(SHLIB_ENV) ${VALGRIND} MAIL_CONFIG=`pwd` ./$(PROG) \
+           -SVnexport-proxy-proxymap | \
+           diff file_name_works.tmp -
+       @$(CLEANUP_FILES) file_name_works.tmp file_name_works_file
+       @echo PASS file_name_works; echo
+
+ignores_non_dictionary_form: $(PROG)
+       @echo RUN ignores_non_dictionary_form
+       @$(SETUP_FILES)
+       echo "mynetworks = map:/path [123::456]" >> main.cf
+       touch -t 197101010000 main.cf
+       echo map:/path  > ignores_non_dictionary_form.tmp
+       $(SHLIB_ENV) ${VALGRIND} MAIL_CONFIG=`pwd` ./$(PROG) \
+           -SVnexport-all-proxymap | diff ignores_non_dictionary_form.tmp -
+       @$(CLEANUP_FILES) ignores_non_dictionary_form.tmp
+       @echo PASS ignores_non_dictionary_form; echo
 
 root_tests:
 
@@ -67,6 +174,7 @@ proxymap.o: ../../include/msg.h
 proxymap.o: ../../include/myflock.h
 proxymap.o: ../../include/mymalloc.h
 proxymap.o: ../../include/nvtable.h
+proxymap.o: ../../include/readlline.h
 proxymap.o: ../../include/stringops.h
 proxymap.o: ../../include/sys_defs.h
 proxymap.o: ../../include/vbuf.h
index 4a2c6755a9d2a73b7ef04d3c1fda6c649e884743..a1204bc0f404979f175c74b776deab185715860a 100644 (file)
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <ctype.h>
 
 /* Utility library. */
 
 #include <dict.h>
 #include <dict_pipe.h>
 #include <dict_union.h>
+#include <readlline.h>
 
 /* Global library. */
 
@@ -282,8 +284,11 @@ char   *var_transport_maps;
 char   *var_verify_map;
 char   *var_smtpd_snd_auth_maps;
 char   *var_psc_cache_map;
+char   *var_smtpd_forbid_cmds;
+char   *var_psc_forbid_cmds;
 char   *var_proxy_read_maps;
 char   *var_proxy_write_maps;
+char   *var_rest_classes;
 
  /*
   * The pre-approved, pre-parsed list of maps.
@@ -304,6 +309,24 @@ static VSTRING *map_type_name_flags;
   */
 static int proxy_writer;
 
+ /*
+  * Do we implement the proxymap or proxywrite service, or do we export
+  * type:table information to stdout? The exported info is without the proxy:
+  * prefix.
+  * 
+  * EXPORT_PROXY lists only tables that proxied.
+  * 
+  * EXPORT_ALL lists both proxied and non-proxied tables that Postfix is
+  * configured to use. This requires that proxy_read_maps and
+  * proxy_write_maps list all tables, including those that will never be
+  * proxied.
+  */
+static enum {
+    EXPORT_NONE,                       /* Answer requests */
+    EXPORT_PROXY,                      /* Export proxied type:table */
+    EXPORT_ALL,                                /* Export all type:table */
+}       proxy_exporter = EXPORT_NONE;
+
  /*
   * Silly little macros.
   */
@@ -708,6 +731,8 @@ DICT   *dict_proxy_open(const char *map, int open_flags, int dict_flags)
     return (dict_open(map, open_flags, dict_flags));
 }
 
+static void authorize_file_content(const char *);
+
 /* authorize_proxied_maps - recursively authorize maps */
 
 static void authorize_proxied_maps(char *bp)
@@ -734,6 +759,14 @@ static void authorize_proxied_maps(char *bp)
            if ((type_name = mystrtokq(&type_name, sep, parens)) == 0)
                continue;
        }
+       /* Recurse into /file/name. */
+       if (*type_name == '/') {
+           authorize_file_content(type_name);
+           continue;
+       }
+       /* Skip [ipv6::addr] and other non-table forms. */
+       if (!ISALNUM(*type_name) || strchr(type_name, ':') == 0)
+           continue;
        /* Recurse into nested map (pipemap, unionmap). */
        if ((nested_info = get_nested_dict_name(type_name)) != 0) {
            char   *err;
@@ -750,11 +783,20 @@ static void authorize_proxied_maps(char *bp)
            authorize_proxied_maps(nested_info);
            continue;
        }
-       if (strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN))
-           continue;
-       do {
-           type_name += PROXY_COLON_LEN;
-       } while (!strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN));
+       switch (proxy_exporter) {
+       case EXPORT_NONE:
+       case EXPORT_PROXY:
+           if (strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN))
+               continue;
+           do {
+               type_name += PROXY_COLON_LEN;
+           } while (!strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN));
+           break;
+       case EXPORT_ALL:
+           while (!strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN))
+               type_name += PROXY_COLON_LEN;
+           break;
+       }
        if (strchr(type_name, ':') != 0
            && htable_locate(proxy_auth_maps, type_name) == 0) {
            (void) htable_enter(proxy_auth_maps, type_name, (void *) 0);
@@ -765,12 +807,86 @@ static void authorize_proxied_maps(char *bp)
     }
 }
 
-/* post_jail_init - initialization after privilege drop */
+/* authorize_file_content - authorize table references in /file/name */
+
+static void authorize_file_content(const char *file_name)
+{
+    VSTREAM *fp;
+    VSTRING *buf;
+
+    if ((fp = vstream_fopen(file_name, O_RDONLY, 0)) == 0) {
+       msg_warn("open %s: %m", file_name);
+       return;
+    }
+    buf = vstring_alloc(100);
+    while (readlline(buf, fp, (int *) 0))
+       authorize_proxied_maps(STR(buf));
+    if (vstream_ferror(fp))
+       msg_warn("read %s: %m", file_name);
+    vstring_free(buf);
+    vstream_fclose(fp);
+}
+
+/* authorize_rest_classes - scan restriction classes for table references */
+
+static void authorize_rest_classes(const char *rest_classes)
+{
+    char   *bp, *saved_rest_classes, *class_name, *saved_class_val;
+    const char *class_val;
+
+    bp = saved_rest_classes = mystrdup(rest_classes);
+    while ((class_name = mystrtok(&bp, CHARS_COMMA_SP)) != 0) {
+       if ((class_val = mail_conf_lookup_eval(class_name)) != 0) {
+           saved_class_val = mystrdup(class_val);
+           authorize_proxied_maps(saved_class_val);
+           myfree(saved_class_val);
+       }
+    }
+    myfree(saved_rest_classes);
+}
+
+/* cmp_ht_key - qsort helper for ht_info pointer array */
+
+static int cmp_ht_key(const void *a, const void *b)
+{
+    HTABLE_INFO **ap = (HTABLE_INFO **) a;
+    HTABLE_INFO **bp = (HTABLE_INFO **) b;
+
+    return (strcmp(ap[0]->key, bp[0]->key));
+}
+
+/* export_type_name_entries - serialize table authorizations to stdout */
 
-static void post_jail_init(char *service_name, char **unused_argv)
+static void export_type_name_entries(void)
+{
+    HTABLE_INFO **ht_info, **ht;
+
+    ht_info = htable_list(proxy_auth_maps);
+    qsort((void *) ht_info, proxy_auth_maps->used, sizeof(*ht_info),
+         cmp_ht_key);
+    for (ht = ht_info; *ht; ht++)
+       vstream_printf("%s\n", ht[0]->key);
+    vstream_fflush(VSTREAM_OUT);
+    myfree(ht_info);
+}
+
+/* pre_jail_init - initialization after privilege drop */
+
+static void pre_jail_init(char *service_name, char **unused_argv)
 {
     char   *saved_filter;
 
+    /*
+     * Is this a request for service, or export?
+     */
+    if (strncmp(service_name, "export-all-", 11) == 0) {
+       proxy_exporter = EXPORT_ALL;
+       service_name += 11;
+    } else if (strncmp(service_name, "export-proxy-", 13) == 0) {
+       proxy_exporter = EXPORT_PROXY;
+       service_name += 13;
+    }
+
     /*
      * Are we proxy writer?
      */
@@ -800,6 +916,20 @@ static void post_jail_init(char *service_name, char **unused_argv)
     authorize_proxied_maps(saved_filter);
     myfree(saved_filter);
 
+    /*
+     * Authorize tables in restriction_classes.
+     */
+    if (proxy_writer == 0 && *var_rest_classes)
+       authorize_rest_classes(var_rest_classes);
+
+    /*
+     * If run as exporter, serialize the authorization table to stdout.
+     */
+    if (proxy_exporter != EXPORT_NONE) {
+       export_type_name_entries();
+       exit(0);
+    }
+
     /*
      * Never, ever, get killed by a master signal, as that could corrupt a
      * persistent database when we're in the middle of an update.
@@ -841,7 +971,26 @@ MAIL_VERSION_STAMP_DECLARE;
 
 int     main(int argc, char **argv)
 {
+
+    /*
+     * Respect the proxy_read_maps and proxy_write_maps dependency graphs.
+     * First, initialize the parameters that specify tables in their
+     * non-empty default values, then initialize the parameters that depend
+     * on those parameters, and so on. Only at the end initialize
+     * proxy_read_maps and proxy_write_maps.
+     * 
+     * TODO(wietse) remove parameters below that have empty default values. It
+     * is sufficient to list their $name in the proxy_*_maps default values.
+     * The list below should be checked with a tool that runs during
+     * pre-release-checks.
+     * 
+     * List parameters even if their defaults do not specify proxied queries (as
+     * of Postfix 3.11, only local_recipient_maps does that). The goal is to
+     * implement an authoritative source of truth tat covers all Postfix
+     * table lookups.
+     */
     static const CONFIG_STR_TABLE str_table[] = {
+       /* Dependencies of proxy_read_maps. */
        VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps, 0, 0,
        VAR_LOCAL_RCPT_MAPS, DEF_LOCAL_RCPT_MAPS, &var_local_rcpt_maps, 0, 0,
        VAR_VIRT_ALIAS_MAPS, DEF_VIRT_ALIAS_MAPS, &var_virt_alias_maps, 0, 0,
@@ -854,12 +1003,17 @@ int     main(int argc, char **argv)
        VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps, 0, 0,
        VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocated_maps, 0, 0,
        VAR_TRANSPORT_MAPS, DEF_TRANSPORT_MAPS, &var_transport_maps, 0, 0,
-       VAR_VERIFY_MAP, DEF_VERIFY_MAP, &var_verify_map, 0, 0,
        VAR_SMTPD_SND_AUTH_MAPS, DEF_SMTPD_SND_AUTH_MAPS, &var_smtpd_snd_auth_maps, 0, 0,
+       VAR_SMTPD_FORBID_CMDS, DEF_SMTPD_FORBID_CMDS, &var_smtpd_forbid_cmds, 0, 0,
+       VAR_PSC_FORBID_CMDS, DEF_PSC_FORBID_CMDS, &var_psc_forbid_cmds, 0, 0,
+       /* Dependencies of proxy_write_maps. */
+       VAR_VERIFY_MAP, DEF_VERIFY_MAP, &var_verify_map, 0, 0,
        VAR_PSC_CACHE_MAP, DEF_PSC_CACHE_MAP, &var_psc_cache_map, 0, 0,
        /* The following two must be last for $mapname to work as expected. */
        VAR_PROXY_READ_MAPS, DEF_PROXY_READ_MAPS, &var_proxy_read_maps, 0, 0,
        VAR_PROXY_WRITE_MAPS, DEF_PROXY_WRITE_MAPS, &var_proxy_write_maps, 0, 0,
+       /* Settings that don't affect or depend on proxy_read/write_maps. */
+       VAR_REST_CLASSES, DEF_REST_CLASSES, &var_rest_classes, 0, 0,
        0,
     };
 
@@ -900,7 +1054,7 @@ int     main(int argc, char **argv)
      */
     multi_server_main(argc, argv, proxymap_service,
                      CA_MAIL_SERVER_STR_TABLE(str_table),
-                     CA_MAIL_SERVER_POST_INIT(post_jail_init),
+                     CA_MAIL_SERVER_PRE_INIT(pre_jail_init),
                      CA_MAIL_SERVER_PRE_ACCEPT(pre_accept),
                      CA_MAIL_SERVER_POST_ACCEPT(post_accept),
     /* XXX CA_MAIL_SERVER_SOLITARY if proxywrite */