]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.12-20260602
authorWietse Z Venema <wietse@porcupine.org>
Tue, 2 Jun 2026 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Fri, 5 Jun 2026 10:35:25 +0000 (20:35 +1000)
postfix/HISTORY
postfix/src/global/mail_version.h
postfix/src/smtpd/smtpd.c
postfix/src/util/Makefile.in
postfix/src/util/allprint.c
postfix/src/util/allprint_test.c [new file with mode: 0644]
postfix/src/util/stringops.h

index 1906d84fd4faf6d711286bae0852c556716245d1..abaa94ec51cb48b61644766f290f1e9beed05a17 100644 (file)
@@ -31242,6 +31242,18 @@ Apologies for any names omitted.
        panic() while parsing a TLSA record with length 3. Found
        during code maintenance. File: tls/tls_dane.c.
 
+20260602
+
+       Incompatibility: the Postfix SMTP server now rejects ENVID
+       and ORCPT parameters with an encoded null (+00) sequence.
+       Problem introduced: Postfix 2.3, date: 20050507; reported
+       by Michael Wollner. File: smtpd/smtpd.c.
+
+       Incompatibility: the Postfix SMTP server now rejects ORCPT
+       parameters with encoded characters that are not printable
+       or whitespace. Files: smtpd/smtpd.c, util/allprint.c,
+       util/allprint_test.c.
+
 TODO
 
        Reorganize PTEST_LIB, PMOCK_LIB, TESTLIB, TESTLIBS, etc.
index e619ac82eed643b00d3d15687a573f35ddff25b7..eb4924c979af9d95fcafaac2a8d87d7cb4ee5cc4 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      "20260601"
+#define MAIL_RELEASE_DATE      "20260602"
 #define MAIL_VERSION_NUMBER    "3.12"
 
 #ifdef SNAPSHOT
index cf9da72a4ba806bbefc301037137285114e8a9ce..1f1c785aa6eaa215dc31ddba67161ae3c91f7855 100644 (file)
@@ -2790,7 +2790,8 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
            }
            if (dsn_envid
                || xtext_unquote(state->dsn_buf, arg + 6) == 0
-               || !allprint(STR(state->dsn_buf))) {
+               || !all_isprint_tab(STR(state->dsn_buf))
+               || strlen(STR(state->dsn_buf)) != LEN(state->dsn_buf)) {
                state->error_mask |= MAIL_ERROR_PROTOCOL;
                smtpd_chat_reply(state, "501 5.5.4 Bad ENVID parameter syntax");
                return (-1);
@@ -3137,7 +3138,9 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
                || *(dsn_orcpt_type = STR(state->dsn_orcpt_buf)) == 0
                || (strcasecmp(dsn_orcpt_type, "utf-8") == 0 ?
                    uxtext_unquote(state->dsn_buf, coded_addr) == 0 :
-                   xtext_unquote(state->dsn_buf, coded_addr) == 0)) {
+                   xtext_unquote(state->dsn_buf, coded_addr) == 0)
+               || !all_isprint_tab(STR(state->dsn_buf))
+               || strlen(STR(state->dsn_buf)) != LEN(state->dsn_buf)) {
                state->error_mask |= MAIL_ERROR_PROTOCOL;
                smtpd_chat_reply(state,
                             "501 5.5.4 Error: Bad ORCPT parameter syntax");
index 9720b7da51dbbed7850eecc5406af08f0e0068b2..754dff0bc4163a4896b1bdd1421141393ac59747 100644 (file)
@@ -139,7 +139,7 @@ TESTSRC     = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
        dict_stream_test.c dict_cli.c dict_union_test.c \
        find_inet_service_test.c hash_fnv_test.c known_tcp_ports_test.c \
        msg_output_test.c myaddrinfo_test.c mymalloc_test.c mystrtok_test.c \
-       unescape_test.c
+       unescape_test.c allprint_test.c
 DEFS   = -I. -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
 FILES  = Makefile $(SRCS) $(HDRS)
@@ -163,7 +163,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
        dict_union_test dict_pipe_test myaddrinfo_test \
        clean_env inet_prefix_top printable readlline quote_for_json \
        normalize_ws valid_uri_scheme clean_ascii_cntrl_space \
-       normalize_v4mapped_addr_test ossl_digest_test
+       normalize_v4mapped_addr_test ossl_digest_test allprint_test
 PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX) $(LIB_PREFIX)lmdb$(LIB_SUFFIX) \
        $(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX) \
        $(LIB_PREFIX)db$(LIB_SUFFIX)
@@ -462,6 +462,9 @@ binhash: $(LIB)
 hash_fnv_test: $(LIB) $(TESTLIBS) update
        $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(TESTLIBS) $(SYSLIBS)
 
+allprint_test: $(LIB) $(TESTLIBS) update
+       $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(TESTLIBS) $(SYSLIBS)
+
 unix_recv_fd:  $(LIB)
        mv $@.o junk
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
@@ -662,7 +665,7 @@ tests: update valid_hostname_test mac_expand_test dict_test test_unescape \
        valid_utf8_string_test readlline_test quote_for_json_test \
        normalize_ws_test valid_uri_scheme_test clean_ascii_cntrl_space_test \
        test_normalize_v4mapped_addr test_ossl_digest test_dict_pipe \
-       test_dict_union test_hash_fnv
+       test_dict_union test_hash_fnv test_allprint
  
 dict_tests: dict_test \
        dict_pcre_tests dict_cidr_test dict_thash_test dict_static_test \
@@ -837,6 +840,9 @@ binhash_test: binhash /usr/share/dict/words
 test_hash_fnv: hash_fnv_test
        $(SHLIB_ENV) ${VALGRIND} ./hash_fnv_test
 
+test_allprint: allprint_test
+       $(SHLIB_ENV) ${VALGRIND} ./allprint_test
+
 hex_code_test: hex_code
        $(SHLIB_ENV) ${VALGRIND} ./hex_code
 
@@ -1195,6 +1201,22 @@ allprint.o: stringops.h
 allprint.o: sys_defs.h
 allprint.o: vbuf.h
 allprint.o: vstring.h
+allprint_test.o: ../../include/msg_jmp.h
+allprint_test.o: ../../include/pmock_expect.h
+allprint_test.o: ../../include/ptest.h
+allprint_test.o: ../../include/ptest_main.h
+allprint_test.o: allprint_test.c
+allprint_test.o: argv.h
+allprint_test.o: check_arg.h
+allprint_test.o: msg.h
+allprint_test.o: msg_output.h
+allprint_test.o: msg_vstream.h
+allprint_test.o: myrand.h
+allprint_test.o: stringops.h
+allprint_test.o: sys_defs.h
+allprint_test.o: vbuf.h
+allprint_test.o: vstream.h
+allprint_test.o: vstring.h
 allspace.o: allspace.c
 allspace.o: check_arg.h
 allspace.o: stringops.h
index 6e9c519ad1a8c9c5ec314bc0d42f610ec90073d5..4f44c203037b7e775164d3a22b2e39e9a88250b9 100644 (file)
@@ -6,10 +6,24 @@
 /* SYNOPSIS
 /*     #include <stringops.h>
 /*
+/*     int     all_isprint(buffer)
+/*     const char *buffer;
+/*
+/*     int     all_isprint_tab(buffer)
+/*     const char *buffer;
+/* LEGACY API
 /*     int     allprint(buffer)
 /*     const char *buffer;
 /* DESCRIPTION
-/*     allprint() determines if its argument is an all-printable string.
+/*     all_isprint() determines if its argument contains only isprint()
+/*     characters (letters, digits, punctuation, space).
+/*
+/*     all_isprint_tab() determines if its argument contains only
+/*     isprint() or TAB characters. This function follows the "white
+/*     space" specification in RFC 5322 section 2.2.1.
+/*
+/*     allprint() implements ABI backwards compatibility. For new
+/*     programs, allprint is an alias for all_isprint.
 /*
 /*     Arguments:
 /* .IP buffer
@@ -23,6 +37,9 @@
 /*     IBM T.J. Watson Research
 /*     P.O. Box 704
 /*     Yorktown Heights, NY 10598, USA
+/*
+/*     Wietse Venema
+/*     porcupine.org
 /*--*/
 
 /* System library. */
 
 #include "stringops.h"
 
-/* allprint - return true if string is all printable */
+/* allprint - ABI compatibility */
+
+#undef allprint
 
 int     allprint(const char *string)
+{
+    return (all_isprint(string));
+}
+
+/* all_isprint - return true if string contains only isprint() characters */
+
+int   all_isprint(const char *string)
 {
     const char *cp;
     int     ch;
@@ -48,3 +74,18 @@ int     allprint(const char *string)
            return (0);
     return (1);
 }
+
+/* all_isprint_tab - return true with only isprint() or TAB characters */
+
+int     all_isprint_tab(const char *string)
+{
+    const char *cp;
+    int     ch;
+
+    if (*string == 0)
+       return (0);
+    for (cp = string; (ch = *(unsigned char *) cp) != 0; cp++)
+       if (!ISASCII(ch) || (!ISPRINT(ch) && ch != '\t'))
+           return (0);
+    return (1);
+}
diff --git a/postfix/src/util/allprint_test.c b/postfix/src/util/allprint_test.c
new file mode 100644 (file)
index 0000000..6f880a1
--- /dev/null
@@ -0,0 +1,90 @@
+ /*
+  * Test program to exercise allprint.c. See PTEST_README for documentation.
+  */
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <ctype.h>
+
+ /*
+  * Utility library.
+  */
+#include <stringops.h>
+
+ /*
+  * Test library.
+  */
+#include <ptest.h>
+
+typedef struct PTEST_CASE {
+    const char *testname;              /* identifies test case */
+    void    (*action) (PTEST_CTX *t, const struct PTEST_CASE *tp);
+} PTEST_CASE;
+
+static void test_allprint(PTEST_CTX *t, const PTEST_CASE *tp)
+{
+    if (allprint != all_isprint)
+       ptest_error(t, "allprint %p != all_isprint %p",
+                   (void *) allprint, (void *) all_isprint);
+}
+
+static void test_all_isprint(PTEST_CTX *t, const PTEST_CASE *tp)
+{
+    unsigned char long_buf[256];
+    int     code;
+    int     pos;
+
+    for (pos = 0, code = 1; code < 256; code++) {
+       unsigned char short_buf[] = {code, 0};
+
+       if (ISPRINT(code)) {
+           long_buf[pos++] = code;
+           if (!all_isprint((char *) short_buf)) 
+               ptest_error(t, "all_isprint(\"\\x%x\"): got 0, want 1", code);
+       } else {
+           if (all_isprint((char *) short_buf))
+               ptest_error(t, "all_isprint(\"\\x%x\"): got 1, want 0", code);
+       }
+    }
+    long_buf[pos] = 0;
+    if (!all_isprint((char *) long_buf))
+       ptest_error(t, "all_isprint(t, \"%s\"): got 0, want 1", long_buf);
+}
+
+static void test_all_isprint_tab(PTEST_CTX *t, const PTEST_CASE *tp)
+{
+    unsigned char long_buf[256];
+    int     code;
+    int     pos;
+
+    for (pos = 0, code = 1; code < 256; code++) {
+        unsigned char short_buf[] = {code, 0};
+
+        if (ISPRINT(code) || code == '\t') {
+            long_buf[pos++] = code;
+            if (!all_isprint_tab((char *) short_buf))
+                ptest_error(t, "all_isprint_tab(\"\\x%x\"): got 0, want 1",
+                             code);
+        } else {
+            if (all_isprint_tab((char *) short_buf))
+                ptest_error(t, "all_isprint_tab(\"\\x%x\"): got 1, want 0",
+                            code);
+        }
+    }
+    long_buf[pos] = 0;
+    if (!all_isprint_tab((char *) long_buf))
+        ptest_error(t, "all_isprint(t, \"%s\"): got 0, want 1", long_buf);
+}
+
+ /*
+  * The test cases.
+  */
+static const PTEST_CASE ptestcases[] = {
+    {.testname = "test allprint",.action = test_allprint,},
+    {.testname = "test all_isprint",.action = test_all_isprint,},
+    {.testname = "test all_isprint_tab",.action = test_all_isprint_tab,},
+};
+
+#include <ptest_main.h>
index 3cf2d117b13200094ff2281e88e8f98e2c5d866c..38efd6cbab75b25d93f1abddc0bbdb952b25196a 100644 (file)
@@ -56,6 +56,8 @@ extern int alldig(const char *);
 extern int allalnum(const char *);
 extern int allalnumus(const char *);
 extern int allprint(const char *);
+extern int all_isprint(const char *);
+extern int all_isprint_tab(const char *);
 extern int allspace(const char *);
 extern int allascii_len(const char *, ssize_t);
 extern const char *WARN_UNUSED_RESULT split_nameval(char *, char **, char **);
@@ -97,6 +99,11 @@ extern char *normalize_ws(char *);
 #define strncasecmp_utf8(s1, s2, l) \
     strncasecmp_utf8x(util_utf8_enable ? CASEF_FLAG_UTF8 : 0, (s1), (s2), (l))
 
+ /*
+  * API compatibility wrappers.
+  */
+#define allprint all_isprint
+
  /*
   * Use STRREF(x) instead of x, to shut up compiler warnings when the operand
   * is a string literal.