mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-22 09:57:34 +00:00
postfix-3.11-20250323
This commit is contained in:
parent
f8a6a51f22
commit
ba7dbdd88a
@ -29015,3 +29015,32 @@ Apologies for any names omitted.
|
|||||||
Dovecot auth client did not attempt to create a new connection
|
Dovecot auth client did not attempt to create a new connection
|
||||||
after an I/O error on an existing connection. Reported by
|
after an I/O error on an existing connection. Reported by
|
||||||
Oleksandr Kozmenko. File: xsasl/xsasl_dovecot_server.c.
|
Oleksandr Kozmenko. File: xsasl/xsasl_dovecot_server.c.
|
||||||
|
|
||||||
|
20240315
|
||||||
|
|
||||||
|
Code health: two typos canceled each other's effect. Fix
|
||||||
|
by Michael Tokarev. No change in compiler output. File:
|
||||||
|
util/vstring_vstream.c.
|
||||||
|
|
||||||
|
20250316
|
||||||
|
|
||||||
|
Bugfix (defect introduced: date 19991116): when appending a
|
||||||
|
setting to a main.cf or master.cf file that did not end in
|
||||||
|
a newline character, the "postconf -e" command did not add
|
||||||
|
an extra newline character before appending the new setting,
|
||||||
|
causing information to become garbled. Fix by Michael
|
||||||
|
Tokarev. File: postconf/postconf_edit.c.
|
||||||
|
|
||||||
|
20259317
|
||||||
|
|
||||||
|
Documentation: added text to clarify the difference between
|
||||||
|
SMTP connection reuse and TLS session resumption, and that
|
||||||
|
these can be combined together. File: proto/TLSRPT_README.html.
|
||||||
|
|
||||||
|
20250321
|
||||||
|
|
||||||
|
Safety: the SQLite client now logs a warning when a query
|
||||||
|
uses double quotes instead of the Postfix-recommended single
|
||||||
|
quotes. Oscar Bataille reported that the non-recommended
|
||||||
|
form is not protected against SQL injection. Files:
|
||||||
|
global/dict_sqlite.c, global/dict_sqlite_test.c.
|
||||||
|
@ -7,6 +7,7 @@ TTaabbllee ooff CCoonntteennttss
|
|||||||
* Introduction
|
* Introduction
|
||||||
* Building Postfix with TLSRPT support
|
* Building Postfix with TLSRPT support
|
||||||
* Turning on TLSRPT
|
* Turning on TLSRPT
|
||||||
|
* Connection reuse versus session resumption
|
||||||
* TLSRPT Status logging
|
* TLSRPT Status logging
|
||||||
* Delivering TLSRPT summaries via email
|
* Delivering TLSRPT summaries via email
|
||||||
* MTA-STS Support via smtp_tls_policy_maps
|
* MTA-STS Support via smtp_tls_policy_maps
|
||||||
@ -27,7 +28,7 @@ A policy for domain example.com could look like this:
|
|||||||
|
|
||||||
Instead of mailto:, a policy may specify an https: destination.
|
Instead of mailto:, a policy may specify an https: destination.
|
||||||
|
|
||||||
The diagram below shows how Postfix TLS handshake success and failure events
|
The diagram below shows how successful or failed Postfix TLS handshake events
|
||||||
are collected and processed into daily summary reports.
|
are collected and processed into daily summary reports.
|
||||||
|
|
||||||
Postfix SMTP and TLSRPT client TLSRPT collector, Email or HTTP
|
Postfix SMTP and TLSRPT client TLSRPT collector, Email or HTTP
|
||||||
@ -124,6 +125,36 @@ Notes:
|
|||||||
For details on how to run the TLSRPT collection and reporting infrastructure,
|
For details on how to run the TLSRPT collection and reporting infrastructure,
|
||||||
see the documentation at https://github.com/sys4/tlsrpt-reporter.
|
see the documentation at https://github.com/sys4/tlsrpt-reporter.
|
||||||
|
|
||||||
|
CCoonnnneeccttiioonn rreeuussee vveerrssuuss sseessssiioonn rreessuummppttiioonn
|
||||||
|
|
||||||
|
The Postfix SMTP client implements two kinds of reuse:
|
||||||
|
|
||||||
|
* SSMMTTPP CCoonnnneeccttiioonn rreeuussee:: a Postfix SMTP client creates a new SMTP connection,
|
||||||
|
sends one email message, and saves the connection instead of closing it.
|
||||||
|
Later, some SMTP client reuses that connection, sends an email message, and
|
||||||
|
saves or closes the connection depending on whether it has reached some
|
||||||
|
reuse limit. Each connection can be used by only one Postfix SMTP client at
|
||||||
|
a time.
|
||||||
|
|
||||||
|
* TTLLSS SSeessssiioonn rreessuummppttiioonn:: a Postfix SMTP client saves the result from a "new"
|
||||||
|
TLS handshake. Later, one or more SMTP clients create a new SMTP connection
|
||||||
|
and resume the saved TLS session on their new connection.
|
||||||
|
|
||||||
|
Of course there is a third case:
|
||||||
|
|
||||||
|
* CCoommbbiinneedd rreeuussee aanndd rreessuummppttiioonn:: a Postfix SMTP client creates a new SMTP
|
||||||
|
connection, sends one email message, saves the result from a "new" TLS
|
||||||
|
handshake, and also saves the connection instead of closing it. Later, one
|
||||||
|
SMTP client reuses (and saves) that connection, one client at a time, and
|
||||||
|
one or more clients create a new SMTP connection and resume the saved TLS
|
||||||
|
session on their new connection.
|
||||||
|
|
||||||
|
In all cases, there is no TLS handshake when a saved SMTP connection is reused,
|
||||||
|
and there is no "new" TLS handshake when a saved TLS session is resumed.
|
||||||
|
|
||||||
|
As described next, Postfix will by default log and generate only a TLSRPT event
|
||||||
|
for a "new" TLS handshake.
|
||||||
|
|
||||||
TTLLSSRRPPTT SSttaattuuss llooggggiinngg
|
TTLLSSRRPPTT SSttaattuuss llooggggiinngg
|
||||||
|
|
||||||
With TLSRPT support turned on, the Postfix TLSRPT client will not only report
|
With TLSRPT support turned on, the Postfix TLSRPT client will not only report
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
<li> <a href="#intro"> Introduction </a> </li>
|
<li> <a href="#intro"> Introduction </a> </li>
|
||||||
<li> <a href="#building"> Building Postfix with TLSRPT support </a>
|
<li> <a href="#building"> Building Postfix with TLSRPT support </a>
|
||||||
<li> <a href="#using"> Turning on TLSRPT </a> </li>
|
<li> <a href="#using"> Turning on TLSRPT </a> </li>
|
||||||
|
<li> <a href="#reusing"> Connection reuse versus session resumption </a> </li>
|
||||||
<li> <a href="#logging"> TLSRPT Status logging </a> </li>
|
<li> <a href="#logging"> TLSRPT Status logging </a> </li>
|
||||||
<li> <a href="#delivering"> Delivering TLSRPT summaries via email</a> </li>
|
<li> <a href="#delivering"> Delivering TLSRPT summaries via email</a> </li>
|
||||||
<li> <a href="#mta-sts"> MTA-STS Support via smtp_tls_policy_maps </a> </li>
|
<li> <a href="#mta-sts"> MTA-STS Support via smtp_tls_policy_maps </a> </li>
|
||||||
@ -52,8 +53,8 @@ _smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-report@example.c
|
|||||||
<p> Instead of <tt>mailto:</tt>, a policy may specify an <tt>https:</tt>
|
<p> Instead of <tt>mailto:</tt>, a policy may specify an <tt>https:</tt>
|
||||||
destination. </p>
|
destination. </p>
|
||||||
|
|
||||||
<p> The diagram below shows how Postfix TLS handshake success and
|
<p> The diagram below shows how successful or failed Postfix TLS
|
||||||
failure events are collected and processed into daily summary
|
handshake events are collected and processed into daily summary
|
||||||
reports. </p>
|
reports. </p>
|
||||||
|
|
||||||
<blockquote>
|
<blockquote>
|
||||||
@ -203,6 +204,49 @@ programs should create sockets there. </p>
|
|||||||
infrastructure, see the documentation at
|
infrastructure, see the documentation at
|
||||||
<a href="https://github.com/sys4/tlsrpt-reporter">https://github.com/sys4/tlsrpt-reporter</a>.
|
<a href="https://github.com/sys4/tlsrpt-reporter">https://github.com/sys4/tlsrpt-reporter</a>.
|
||||||
|
|
||||||
|
<h2> <a name="reusing"> Connection reuse versus session resumption
|
||||||
|
</a> </h2>
|
||||||
|
|
||||||
|
<p> The Postfix SMTP client implements two kinds of reuse: </p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li> <p> <b> SMTP Connection reuse: </b> a Postfix SMTP client
|
||||||
|
creates a new SMTP connection, sends one email message, and saves
|
||||||
|
the connection instead of closing it. Later, some SMTP client reuses
|
||||||
|
that connection, sends an email message, and saves or closes the
|
||||||
|
connection depending on whether it has reached some reuse limit.
|
||||||
|
Each connection can be used by only one Postfix SMTP client at a
|
||||||
|
time. </p>
|
||||||
|
|
||||||
|
<li> <p> <b> TLS Session resumption: </b> a Postfix SMTP client
|
||||||
|
saves the result from a "new" TLS handshake. Later, one or more
|
||||||
|
SMTP clients create a new SMTP connection and resume the saved TLS
|
||||||
|
session on their new connection. <p>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p> Of course there is a third case: </p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li> <p> <b> Combined reuse and resumption: </b> a Postfix SMTP
|
||||||
|
client creates a new SMTP connection, sends one email message, saves
|
||||||
|
the result from a "new" TLS handshake, and also saves the connection
|
||||||
|
instead of closing it. Later, one SMTP client reuses (and saves)
|
||||||
|
that connection, one client at a time, and one or more clients
|
||||||
|
create a new SMTP connection and resume the saved TLS session on
|
||||||
|
their new connection. <p>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p> In all cases, there is no TLS handshake when a saved SMTP connection
|
||||||
|
is reused, and there is no "new" TLS handshake when a saved TLS session
|
||||||
|
is resumed. </p>
|
||||||
|
|
||||||
|
<p> As described next, Postfix will by default log and generate only a
|
||||||
|
TLSRPT event for a "new" TLS handshake. </p>
|
||||||
|
|
||||||
<h2> <a name="logging"> TLSRPT Status logging </a> </h2>
|
<h2> <a name="logging"> TLSRPT Status logging </a> </h2>
|
||||||
|
|
||||||
<p> With TLSRPT support turned on, the Postfix TLSRPT client will
|
<p> With TLSRPT support turned on, the Postfix TLSRPT client will
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
<li> <a href="#intro"> Introduction </a> </li>
|
<li> <a href="#intro"> Introduction </a> </li>
|
||||||
<li> <a href="#building"> Building Postfix with TLSRPT support </a>
|
<li> <a href="#building"> Building Postfix with TLSRPT support </a>
|
||||||
<li> <a href="#using"> Turning on TLSRPT </a> </li>
|
<li> <a href="#using"> Turning on TLSRPT </a> </li>
|
||||||
|
<li> <a href="#reusing"> Connection reuse versus session resumption </a> </li>
|
||||||
<li> <a href="#logging"> TLSRPT Status logging </a> </li>
|
<li> <a href="#logging"> TLSRPT Status logging </a> </li>
|
||||||
<li> <a href="#delivering"> Delivering TLSRPT summaries via email</a> </li>
|
<li> <a href="#delivering"> Delivering TLSRPT summaries via email</a> </li>
|
||||||
<li> <a href="#mta-sts"> MTA-STS Support via smtp_tls_policy_maps </a> </li>
|
<li> <a href="#mta-sts"> MTA-STS Support via smtp_tls_policy_maps </a> </li>
|
||||||
@ -52,8 +53,8 @@ _smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-report@example.c
|
|||||||
<p> Instead of <tt>mailto:</tt>, a policy may specify an <tt>https:</tt>
|
<p> Instead of <tt>mailto:</tt>, a policy may specify an <tt>https:</tt>
|
||||||
destination. </p>
|
destination. </p>
|
||||||
|
|
||||||
<p> The diagram below shows how Postfix TLS handshake success and
|
<p> The diagram below shows how successful or failed Postfix TLS
|
||||||
failure events are collected and processed into daily summary
|
handshake events are collected and processed into daily summary
|
||||||
reports. </p>
|
reports. </p>
|
||||||
|
|
||||||
<blockquote>
|
<blockquote>
|
||||||
@ -203,6 +204,49 @@ programs should create sockets there. </p>
|
|||||||
infrastructure, see the documentation at
|
infrastructure, see the documentation at
|
||||||
https://github.com/sys4/tlsrpt-reporter.
|
https://github.com/sys4/tlsrpt-reporter.
|
||||||
|
|
||||||
|
<h2> <a name="reusing"> Connection reuse versus session resumption
|
||||||
|
</a> </h2>
|
||||||
|
|
||||||
|
<p> The Postfix SMTP client implements two kinds of reuse: </p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li> <p> <b> SMTP Connection reuse: </b> a Postfix SMTP client
|
||||||
|
creates a new SMTP connection, sends one email message, and saves
|
||||||
|
the connection instead of closing it. Later, some SMTP client reuses
|
||||||
|
that connection, sends an email message, and saves or closes the
|
||||||
|
connection depending on whether it has reached some reuse limit.
|
||||||
|
Each connection can be used by only one Postfix SMTP client at a
|
||||||
|
time. </p>
|
||||||
|
|
||||||
|
<li> <p> <b> TLS Session resumption: </b> a Postfix SMTP client
|
||||||
|
saves the result from a "new" TLS handshake. Later, one or more
|
||||||
|
SMTP clients create a new SMTP connection and resume the saved TLS
|
||||||
|
session on their new connection. <p>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p> Of course there is a third case: </p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li> <p> <b> Combined reuse and resumption: </b> a Postfix SMTP
|
||||||
|
client creates a new SMTP connection, sends one email message, saves
|
||||||
|
the result from a "new" TLS handshake, and also saves the connection
|
||||||
|
instead of closing it. Later, one SMTP client reuses (and saves)
|
||||||
|
that connection, one client at a time, and one or more clients
|
||||||
|
create a new SMTP connection and resume the saved TLS session on
|
||||||
|
their new connection. <p>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p> In all cases, there is no TLS handshake when a saved SMTP connection
|
||||||
|
is reused, and there is no "new" TLS handshake when a saved TLS session
|
||||||
|
is resumed. </p>
|
||||||
|
|
||||||
|
<p> As described next, Postfix will by default log and generate only a
|
||||||
|
TLSRPT event for a "new" TLS handshake. </p>
|
||||||
|
|
||||||
<h2> <a name="logging"> TLSRPT Status logging </a> </h2>
|
<h2> <a name="logging"> TLSRPT Status logging </a> </h2>
|
||||||
|
|
||||||
<p> With TLSRPT support turned on, the Postfix TLSRPT client will
|
<p> With TLSRPT support turned on, the Postfix TLSRPT client will
|
||||||
|
@ -104,3 +104,4 @@ Gueven
|
|||||||
Oemer
|
Oemer
|
||||||
Kozmenko
|
Kozmenko
|
||||||
Oleksandr
|
Oleksandr
|
||||||
|
Bataille
|
||||||
|
@ -131,7 +131,7 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
|
|||||||
fold_addr smtp_reply_footer mail_addr_map normalize_mailhost_addr \
|
fold_addr smtp_reply_footer mail_addr_map normalize_mailhost_addr \
|
||||||
haproxy_srvr map_search delivered_hdr login_sender_match \
|
haproxy_srvr map_search delivered_hdr login_sender_match \
|
||||||
compat_level config_known_tcp_ports hfrom_format rfc2047_code \
|
compat_level config_known_tcp_ports hfrom_format rfc2047_code \
|
||||||
ascii_header_text sendopts_test
|
ascii_header_text sendopts_test dict_sqlite_test
|
||||||
|
|
||||||
LIBS = ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
|
LIBS = ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
|
||||||
LIB_DIR = ../../lib
|
LIB_DIR = ../../lib
|
||||||
@ -408,6 +408,10 @@ ascii_header_text: ascii_header_text.c $(LIB) $(LIBS)
|
|||||||
sendopts_test: sendopts_test.c $(LIB) $(LIBS)
|
sendopts_test: sendopts_test.c $(LIB) $(LIBS)
|
||||||
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
|
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
|
||||||
|
|
||||||
|
dict_sqlite_test: dict_sqlite_test.c dict_sqlite.o $(LIB) $(LIBS)
|
||||||
|
$(CC) $(CFLAGS) -DTEST -o $@ $@.c dict_sqlite.o $(LIB) $(LIBS) \
|
||||||
|
$(SYSLIBS) $(AUXLIBS_SQLITE)
|
||||||
|
|
||||||
config_known_tcp_ports: config_known_tcp_ports.c $(LIB) $(LIBS)
|
config_known_tcp_ports: config_known_tcp_ports.c $(LIB) $(LIBS)
|
||||||
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
|
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
|
||||||
|
|
||||||
@ -421,7 +425,7 @@ tests: tok822_test mime_tests strip_addr_test tok822_limit_test \
|
|||||||
normalize_mailhost_addr_test haproxy_srvr_test map_search_test \
|
normalize_mailhost_addr_test haproxy_srvr_test map_search_test \
|
||||||
delivered_hdr_test login_sender_match_test compat_level_test \
|
delivered_hdr_test login_sender_match_test compat_level_test \
|
||||||
config_known_tcp_ports_test hfrom_format_test rfc2047_code_test \
|
config_known_tcp_ports_test hfrom_format_test rfc2047_code_test \
|
||||||
ascii_header_text_test test_sendopts
|
ascii_header_text_test test_sendopts test_dict_sqlite
|
||||||
|
|
||||||
mime_tests: mime_test mime_nest mime_8bit mime_dom mime_trunc mime_cvt \
|
mime_tests: mime_test mime_nest mime_8bit mime_dom mime_trunc mime_cvt \
|
||||||
mime_cvt2 mime_cvt3 mime_garb1 mime_garb2 mime_garb3 mime_garb4
|
mime_cvt2 mime_cvt3 mime_garb1 mime_garb2 mime_garb3 mime_garb4
|
||||||
@ -794,7 +798,10 @@ ascii_header_text_test: update ascii_header_text
|
|||||||
$(SHLIB_ENV) $(VALGRIND) ./ascii_header_text
|
$(SHLIB_ENV) $(VALGRIND) ./ascii_header_text
|
||||||
|
|
||||||
test_sendopts: update sendopts_test
|
test_sendopts: update sendopts_test
|
||||||
-$(SHLIB_ENV) $(VALGRIND) ./sendopts_test
|
$(SHLIB_ENV) $(VALGRIND) ./sendopts_test
|
||||||
|
|
||||||
|
test_dict_sqlite: update dict_sqlite_test
|
||||||
|
$(SHLIB_ENV) $(VALGRIND) ./dict_sqlite_test
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAPS)
|
rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAPS)
|
||||||
@ -1331,6 +1338,19 @@ dict_sqlite.o: db_common.h
|
|||||||
dict_sqlite.o: dict_sqlite.c
|
dict_sqlite.o: dict_sqlite.c
|
||||||
dict_sqlite.o: dict_sqlite.h
|
dict_sqlite.o: dict_sqlite.h
|
||||||
dict_sqlite.o: string_list.h
|
dict_sqlite.o: string_list.h
|
||||||
|
dict_sqlite_test.o: ../../include/argv.h
|
||||||
|
dict_sqlite_test.o: ../../include/check_arg.h
|
||||||
|
dict_sqlite_test.o: ../../include/dict.h
|
||||||
|
dict_sqlite_test.o: ../../include/msg.h
|
||||||
|
dict_sqlite_test.o: ../../include/msg_vstream.h
|
||||||
|
dict_sqlite_test.o: ../../include/myflock.h
|
||||||
|
dict_sqlite_test.o: ../../include/stringops.h
|
||||||
|
dict_sqlite_test.o: ../../include/sys_defs.h
|
||||||
|
dict_sqlite_test.o: ../../include/vbuf.h
|
||||||
|
dict_sqlite_test.o: ../../include/vstream.h
|
||||||
|
dict_sqlite_test.o: ../../include/vstring.h
|
||||||
|
dict_sqlite_test.o: dict_sqlite.h
|
||||||
|
dict_sqlite_test.o: dict_sqlite_test.c
|
||||||
domain_list.o: ../../include/argv.h
|
domain_list.o: ../../include/argv.h
|
||||||
domain_list.o: ../../include/check_arg.h
|
domain_list.o: ../../include/check_arg.h
|
||||||
domain_list.o: ../../include/match_list.h
|
domain_list.o: ../../include/match_list.h
|
||||||
|
@ -34,9 +34,13 @@
|
|||||||
/* Must be O_RDONLY.
|
/* Must be O_RDONLY.
|
||||||
/* .IP dict_flags
|
/* .IP dict_flags
|
||||||
/* See dict_open(3).
|
/* See dict_open(3).
|
||||||
|
/* DIAGNOSTICS
|
||||||
|
/* dict_sqlite_open() logs a warning when the query parameter value
|
||||||
|
/* does not use the recommended '' quotes to protect against SQL
|
||||||
|
/* injection (bad examples; no quotes or "" quotes).
|
||||||
/* SEE ALSO
|
/* SEE ALSO
|
||||||
/* dict(3) generic dictionary manager
|
/* dict(3) generic dictionary manager
|
||||||
/* sqlite_table(5) sqlite client configuration
|
/* sqlite_table(5) Postfix sqlite client configuration
|
||||||
/* AUTHOR(S)
|
/* AUTHOR(S)
|
||||||
/* Axel Steiner
|
/* Axel Steiner
|
||||||
/* ast@treibsand.com
|
/* ast@treibsand.com
|
||||||
@ -46,12 +50,16 @@
|
|||||||
/* IBM T.J. Watson Research
|
/* IBM T.J. Watson Research
|
||||||
/* P.O. Box 704
|
/* P.O. Box 704
|
||||||
/* Yorktown Heights, NY 10598, USA
|
/* Yorktown Heights, NY 10598, USA
|
||||||
|
/*
|
||||||
|
/* Wietse Venema
|
||||||
|
/* porcupine.org
|
||||||
/*--*/
|
/*--*/
|
||||||
|
|
||||||
/* System library. */
|
/* System library. */
|
||||||
|
|
||||||
#include <sys_defs.h>
|
#include <sys_defs.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#ifdef HAS_SQLITE
|
#ifdef HAS_SQLITE
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
@ -250,6 +258,38 @@ static const char *dict_sqlite_lookup(DICT *dict, const char *name)
|
|||||||
retval : 0);
|
retval : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* flag_non_recommended_query - as the name says. */
|
||||||
|
|
||||||
|
static void flag_non_recommended_query(const char *query,
|
||||||
|
const char *sqlitecf)
|
||||||
|
{
|
||||||
|
const char *cp;
|
||||||
|
int in_quote;
|
||||||
|
const int squote = '\'';
|
||||||
|
const int dquote = '"';
|
||||||
|
|
||||||
|
for (in_quote = 0, cp = query; *cp != 0; cp++) {
|
||||||
|
if (in_quote == 0) {
|
||||||
|
if (*cp == squote || *cp == dquote)
|
||||||
|
in_quote = *cp;
|
||||||
|
} else if (*cp == in_quote) {
|
||||||
|
in_quote = 0;
|
||||||
|
}
|
||||||
|
if (in_quote == squote)
|
||||||
|
continue;
|
||||||
|
if (*cp == '%') {
|
||||||
|
if (cp[1] == '%') {
|
||||||
|
cp += 1;
|
||||||
|
} else if (ISALNUM(cp[1])) {
|
||||||
|
msg_warn("%s:%s: query >%s< contains >%.2s< without the "
|
||||||
|
"recommended '' quotes", DICT_TYPE_SQLITE, sqlitecf,
|
||||||
|
query, cp);
|
||||||
|
cp += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* sqlite_parse_config - parse sqlite configuration file */
|
/* sqlite_parse_config - parse sqlite configuration file */
|
||||||
|
|
||||||
static void sqlite_parse_config(DICT_SQLITE *dict_sqlite, const char *sqlitecf)
|
static void sqlite_parse_config(DICT_SQLITE *dict_sqlite, const char *sqlitecf)
|
||||||
@ -268,6 +308,8 @@ static void sqlite_parse_config(DICT_SQLITE *dict_sqlite, const char *sqlitecf)
|
|||||||
db_common_sql_build_query(buf, dict_sqlite->parser);
|
db_common_sql_build_query(buf, dict_sqlite->parser);
|
||||||
dict_sqlite->query = vstring_export(buf);
|
dict_sqlite->query = vstring_export(buf);
|
||||||
}
|
}
|
||||||
|
/* Flag %[a-zA-Z0-9] if not protected with ''. */
|
||||||
|
flag_non_recommended_query(dict_sqlite->query, sqlitecf);
|
||||||
dict_sqlite->result_format =
|
dict_sqlite->result_format =
|
||||||
cfg_get_str(dict_sqlite->parser, "result_format", "%s", 1, 0);
|
cfg_get_str(dict_sqlite->parser, "result_format", "%s", 1, 0);
|
||||||
dict_sqlite->expansion_limit =
|
dict_sqlite->expansion_limit =
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
extern DICT *dict_sqlite_open(const char *, int, int);
|
extern DICT *dict_sqlite_open(const char *, int, int);
|
||||||
|
|
||||||
|
|
||||||
/* AUTHOR(S)
|
/* AUTHOR(S)
|
||||||
/* Axel Steiner
|
/* Axel Steiner
|
||||||
/* ast@treibsand.com
|
/* ast@treibsand.com
|
||||||
|
247
postfix/src/global/dict_sqlite_test.c
Normal file
247
postfix/src/global/dict_sqlite_test.c
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
/*++
|
||||||
|
/* NAME
|
||||||
|
/* dict_sqlite_test 1t
|
||||||
|
/* SUMMARY
|
||||||
|
/* dict_sqlite unit test
|
||||||
|
/* SYNOPSIS
|
||||||
|
/* ./dict_sqlite_test
|
||||||
|
/* DESCRIPTION
|
||||||
|
/* dict_sqlite_test runs and logs each configured test, reports if
|
||||||
|
/* a test is a PASS or FAIL, and returns an exit status of zero if
|
||||||
|
/* all tests are a PASS.
|
||||||
|
/*
|
||||||
|
/* Each test creates a temporary test database and a corresponding
|
||||||
|
/* Postfix sqlite client configuration file, both having unique
|
||||||
|
/* names. Otherwise, each test is hermetic.
|
||||||
|
/* LICENSE
|
||||||
|
/* .ad
|
||||||
|
/* .fi
|
||||||
|
/* The Secure Mailer license must be distributed with this software.
|
||||||
|
/* AUTHOR(S)
|
||||||
|
/* Wietse Venema porcupine.org
|
||||||
|
/*--*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
/*
|
||||||
|
* System library.
|
||||||
|
*/
|
||||||
|
#include <sys_defs.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Utility library.
|
||||||
|
*/
|
||||||
|
#include <msg.h>
|
||||||
|
#include <msg_vstream.h>
|
||||||
|
#include <stringops.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global library.
|
||||||
|
*/
|
||||||
|
#include <dict_sqlite.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO(wietse) make this a proper VSTREAM interface or test helper API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* vstream_swap - capture output for testing */
|
||||||
|
|
||||||
|
static void vstream_swap(VSTREAM *one, VSTREAM *two)
|
||||||
|
{
|
||||||
|
VSTREAM save;
|
||||||
|
|
||||||
|
save = *one;
|
||||||
|
*one = *two;
|
||||||
|
*two = save;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Override the printable.c module because it may break some tests.
|
||||||
|
*
|
||||||
|
* TODO(wietse) move this to a fake_printable.c module that can override all
|
||||||
|
* printable.c global symbols.
|
||||||
|
*/
|
||||||
|
int util_utf8_enable;
|
||||||
|
|
||||||
|
char *printable_except(char *string, int replacement, const char *except)
|
||||||
|
{
|
||||||
|
return (string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scaffolding for dict_sqlite(3) tests.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* create_and_populate_db - create an empty database and optionally populate */
|
||||||
|
|
||||||
|
static void create_and_populate_db(char *dbpath, const char *commands)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create an empty database file with a unique name. Assume that an
|
||||||
|
* adversary cannot rename or remove the file.
|
||||||
|
*/
|
||||||
|
if ((fd = mkstemp(dbpath)) < 0)
|
||||||
|
msg_fatal("mkstemp(\"%s\"): %m", dbpath);
|
||||||
|
if (close(fd) < 0)
|
||||||
|
msg_fatal("close %s: %m", dbpath);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO(wietse) Open the database file, prepare and execute commands
|
||||||
|
* to populate the database, and close the database.
|
||||||
|
*/
|
||||||
|
if (commands) {
|
||||||
|
msg_fatal("commands are not yet supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create_and_populate_cf - create sqlite_table(5) configuration file */
|
||||||
|
|
||||||
|
static void create_and_populate_cf(char *cfpath, const char *dbpath,
|
||||||
|
const char *cftext)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
VSTREAM *fp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create an empty sqlite_table(5) configuration file with a unique name.
|
||||||
|
* Assume that an adversary cannot rename or remove the file.
|
||||||
|
*/
|
||||||
|
if ((fd = mkstemp(cfpath)) < 0)
|
||||||
|
msg_fatal("mkstemp(\"%s\"): %m", cfpath);
|
||||||
|
if ((fp = vstream_fdopen(fd, O_WRONLY)) == 0)
|
||||||
|
msg_fatal("vstream_fdopen: %m");
|
||||||
|
(void) vstream_fprintf(fp, "%s\ndbpath = %s\n", cftext, dbpath);
|
||||||
|
if (vstream_fclose(fp) != 0)
|
||||||
|
msg_fatal("vstream_fdclose: %m");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test structure. Some tests may come their own.
|
||||||
|
*/
|
||||||
|
typedef struct TEST_CASE {
|
||||||
|
const char *label;
|
||||||
|
int (*action) (const struct TEST_CASE *);
|
||||||
|
const char *commands; /* commands or null */
|
||||||
|
const char *settings; /* sqlite_table(5) */
|
||||||
|
const char *exp_warning; /* substring match or null */
|
||||||
|
} TEST_CASE;
|
||||||
|
|
||||||
|
#define PASS (0)
|
||||||
|
#define FAIL (1)
|
||||||
|
|
||||||
|
#define PATH_TEMPLATE "/tmp/test-XXXXXXX"
|
||||||
|
|
||||||
|
/* test_flag_non_recommended_query - flag non-recommended query payloads */
|
||||||
|
|
||||||
|
static int test_flag_non_recommended_query(const TEST_CASE *tp)
|
||||||
|
{
|
||||||
|
static VSTRING *msg_buf;
|
||||||
|
VSTREAM *memory_stream;
|
||||||
|
const char template[] = PATH_TEMPLATE;
|
||||||
|
char dbpath[sizeof(template)];
|
||||||
|
char cfpath[sizeof(template)];
|
||||||
|
DICT *dict;
|
||||||
|
|
||||||
|
if (msg_buf == 0)
|
||||||
|
msg_buf = vstring_alloc(100);
|
||||||
|
|
||||||
|
/* Prepare scaffolding database and configuration files. */
|
||||||
|
memcpy(dbpath, template, sizeof(dbpath));
|
||||||
|
create_and_populate_db(dbpath, tp->commands);
|
||||||
|
memcpy(cfpath, template, sizeof(cfpath));
|
||||||
|
create_and_populate_cf(cfpath, dbpath, tp->settings);
|
||||||
|
|
||||||
|
/* Run the test with custom STDERR stream. */
|
||||||
|
VSTRING_RESET(msg_buf);
|
||||||
|
VSTRING_TERMINATE(msg_buf);
|
||||||
|
if ((memory_stream = vstream_memopen(msg_buf, O_WRONLY)) == 0)
|
||||||
|
msg_fatal("open memory stream: %m");
|
||||||
|
vstream_swap(VSTREAM_ERR, memory_stream);
|
||||||
|
if ((dict = dict_sqlite_open(cfpath, O_RDONLY, DICT_FLAG_UTF8_REQUEST)) != 0)
|
||||||
|
dict_close(dict);
|
||||||
|
vstream_swap(memory_stream, VSTREAM_ERR);
|
||||||
|
if (vstream_fclose(memory_stream))
|
||||||
|
msg_fatal("close memory stream: %m");
|
||||||
|
|
||||||
|
/* Cleanup scaffolding database and configuration files. */
|
||||||
|
if (unlink(dbpath) < 0)
|
||||||
|
msg_fatal("unlink %s: %m", dbpath);
|
||||||
|
if (unlink(cfpath) < 0)
|
||||||
|
msg_fatal("unlink %s: %m", cfpath);
|
||||||
|
|
||||||
|
/* Verify the results. */
|
||||||
|
if (tp->exp_warning == 0 && VSTRING_LEN(msg_buf) > 0) {
|
||||||
|
msg_warn("got warning ``%s'', want ``null''", vstring_str(msg_buf));
|
||||||
|
return (FAIL);
|
||||||
|
}
|
||||||
|
if (tp->exp_warning != 0
|
||||||
|
&& strstr(vstring_str(msg_buf), tp->exp_warning) == 0) {
|
||||||
|
msg_warn("got warning ``%s'', want ``%s''",
|
||||||
|
vstring_str(msg_buf), tp->exp_warning);
|
||||||
|
return (FAIL);
|
||||||
|
}
|
||||||
|
return (PASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The list of test cases.
|
||||||
|
*/
|
||||||
|
static const TEST_CASE test_cases[] = {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tests to flag non-recommended query forms. These create an empty test
|
||||||
|
* database, and open it with the dict_sqlite client without querying it.
|
||||||
|
*/
|
||||||
|
{.label = "no_dynamic_payload",
|
||||||
|
.action = test_flag_non_recommended_query,
|
||||||
|
.settings = "query = select a from b where c = 5",
|
||||||
|
},
|
||||||
|
{.label = "dynamic_payload_inside_recommended_quotes",
|
||||||
|
.action = test_flag_non_recommended_query,
|
||||||
|
.settings = "query = select a from b where c = 'xx%syy'",
|
||||||
|
},
|
||||||
|
{.label = "dynamic_payload_without_quotes",
|
||||||
|
.action = test_flag_non_recommended_query,
|
||||||
|
.settings = "query = select s from b where c = xx%syy",
|
||||||
|
.exp_warning = "contains >%s< without the recommended '' quotes",
|
||||||
|
},
|
||||||
|
{.label = "payload_inside_double_quotes",
|
||||||
|
.action = test_flag_non_recommended_query,
|
||||||
|
.settings = "query = select s from b where c = \"xx%syy\"",
|
||||||
|
.exp_warning = "contains >%s< without the recommended '' quotes",
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: Tests that actually populate a test database, and that query it
|
||||||
|
* with the dict_sqlite client.
|
||||||
|
*/
|
||||||
|
{0},
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
const TEST_CASE *tp;
|
||||||
|
int pass = 0;
|
||||||
|
int fail = 0;
|
||||||
|
|
||||||
|
msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
|
||||||
|
|
||||||
|
for (tp = test_cases; tp->label != 0; tp++) {
|
||||||
|
int test_failed;
|
||||||
|
|
||||||
|
msg_info("RUN %s", tp->label);
|
||||||
|
test_failed = tp->action(tp);
|
||||||
|
if (test_failed) {
|
||||||
|
msg_info("FAIL %s", tp->label);
|
||||||
|
fail++;
|
||||||
|
} else {
|
||||||
|
msg_info("PASS %s", tp->label);
|
||||||
|
pass++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg_info("PASS=%d FAIL=%d", pass, fail);
|
||||||
|
exit(fail != 0);
|
||||||
|
}
|
@ -20,7 +20,7 @@
|
|||||||
* Patches change both the patchlevel and the release date. Snapshots have no
|
* Patches change both the patchlevel and the release date. Snapshots have no
|
||||||
* patchlevel; they change the release date only.
|
* patchlevel; they change the release date only.
|
||||||
*/
|
*/
|
||||||
#define MAIL_RELEASE_DATE "20250304"
|
#define MAIL_RELEASE_DATE "20250323"
|
||||||
#define MAIL_VERSION_NUMBER "3.11"
|
#define MAIL_VERSION_NUMBER "3.11"
|
||||||
|
|
||||||
#ifdef SNAPSHOT
|
#ifdef SNAPSHOT
|
||||||
|
@ -113,8 +113,13 @@ static char *pcf_find_cf_info(VSTRING *buf, VSTREAM *dst)
|
|||||||
static char *pcf_next_cf_line(VSTRING *buf, VSTREAM *src, VSTREAM *dst, int *lineno)
|
static char *pcf_next_cf_line(VSTRING *buf, VSTREAM *src, VSTREAM *dst, int *lineno)
|
||||||
{
|
{
|
||||||
char *cp;
|
char *cp;
|
||||||
|
int last_char;
|
||||||
|
|
||||||
while (vstring_get(buf, src) != VSTREAM_EOF) {
|
while ((last_char = vstring_get(buf, src)) != VSTREAM_EOF) {
|
||||||
|
if (last_char != '\n') {
|
||||||
|
VSTRING_ADDCH(buf, '\n');
|
||||||
|
VSTRING_TERMINATE(buf);
|
||||||
|
}
|
||||||
if (lineno)
|
if (lineno)
|
||||||
*lineno += 1;
|
*lineno += 1;
|
||||||
if ((cp = pcf_find_cf_info(buf, dst)) != 0)
|
if ((cp = pcf_find_cf_info(buf, dst)) != 0)
|
||||||
|
@ -123,7 +123,7 @@
|
|||||||
/*
|
/*
|
||||||
* Macro to return the last character added to a VSTRING, for consistency.
|
* Macro to return the last character added to a VSTRING, for consistency.
|
||||||
*/
|
*/
|
||||||
#define VSTRING_GET_RESULT(vp, baselen) \
|
#define VSTRING_GET_RESULT(vp, base_len) \
|
||||||
(VSTRING_LEN(vp) > (base_len) ? vstring_end(vp)[-1] : VSTREAM_EOF)
|
(VSTRING_LEN(vp) > (base_len) ? vstring_end(vp)[-1] : VSTREAM_EOF)
|
||||||
|
|
||||||
/* vstring_get_flags - read line from file, keep newline */
|
/* vstring_get_flags - read line from file, keep newline */
|
||||||
@ -142,7 +142,7 @@ int vstring_get_flags(VSTRING *vp, VSTREAM *fp, int flags)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
VSTRING_TERMINATE(vp);
|
VSTRING_TERMINATE(vp);
|
||||||
return (VSTRING_GET_RESULT(vp, baselen));
|
return (VSTRING_GET_RESULT(vp, base_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vstring_get_flags_nonl - read line from file, strip newline */
|
/* vstring_get_flags_nonl - read line from file, strip newline */
|
||||||
@ -158,7 +158,7 @@ int vstring_get_flags_nonl(VSTRING *vp, VSTREAM *fp, int flags)
|
|||||||
while ((c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != '\n')
|
while ((c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != '\n')
|
||||||
VSTRING_ADDCH(vp, c);
|
VSTRING_ADDCH(vp, c);
|
||||||
VSTRING_TERMINATE(vp);
|
VSTRING_TERMINATE(vp);
|
||||||
return (c == '\n' ? c : VSTRING_GET_RESULT(vp, baselen));
|
return (c == '\n' ? c : VSTRING_GET_RESULT(vp, base_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vstring_get_flags_null - read null-terminated string from file */
|
/* vstring_get_flags_null - read null-terminated string from file */
|
||||||
@ -174,7 +174,7 @@ int vstring_get_flags_null(VSTRING *vp, VSTREAM *fp, int flags)
|
|||||||
while ((c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0)
|
while ((c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0)
|
||||||
VSTRING_ADDCH(vp, c);
|
VSTRING_ADDCH(vp, c);
|
||||||
VSTRING_TERMINATE(vp);
|
VSTRING_TERMINATE(vp);
|
||||||
return (c == 0 ? c : VSTRING_GET_RESULT(vp, baselen));
|
return (c == 0 ? c : VSTRING_GET_RESULT(vp, base_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vstring_get_flags_bound - read line from file, keep newline, up to bound */
|
/* vstring_get_flags_bound - read line from file, keep newline, up to bound */
|
||||||
@ -197,7 +197,7 @@ int vstring_get_flags_bound(VSTRING *vp, VSTREAM *fp, int flags,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
VSTRING_TERMINATE(vp);
|
VSTRING_TERMINATE(vp);
|
||||||
return (VSTRING_GET_RESULT(vp, baselen));
|
return (VSTRING_GET_RESULT(vp, base_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vstring_get_flags_nonl_bound - read line from file, strip newline, up to bound */
|
/* vstring_get_flags_nonl_bound - read line from file, strip newline, up to bound */
|
||||||
@ -217,7 +217,7 @@ int vstring_get_flags_nonl_bound(VSTRING *vp, VSTREAM *fp, int flags,
|
|||||||
while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != '\n')
|
while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != '\n')
|
||||||
VSTRING_ADDCH(vp, c);
|
VSTRING_ADDCH(vp, c);
|
||||||
VSTRING_TERMINATE(vp);
|
VSTRING_TERMINATE(vp);
|
||||||
return (c == '\n' ? c : VSTRING_GET_RESULT(vp, baselen));
|
return (c == '\n' ? c : VSTRING_GET_RESULT(vp, base_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vstring_get_flags_null_bound - read null-terminated string from file */
|
/* vstring_get_flags_null_bound - read null-terminated string from file */
|
||||||
@ -237,7 +237,7 @@ int vstring_get_flags_null_bound(VSTRING *vp, VSTREAM *fp, int flags,
|
|||||||
while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0)
|
while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0)
|
||||||
VSTRING_ADDCH(vp, c);
|
VSTRING_ADDCH(vp, c);
|
||||||
VSTRING_TERMINATE(vp);
|
VSTRING_TERMINATE(vp);
|
||||||
return (c == 0 ? c : VSTRING_GET_RESULT(vp, baselen));
|
return (c == 0 ? c : VSTRING_GET_RESULT(vp, base_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TEST
|
#ifdef TEST
|
||||||
|
Loading…
x
Reference in New Issue
Block a user