From 8212a7a0faa51c2969f3beecc221df5aef433ce5 Mon Sep 17 00:00:00 2001 From: Wietse Venema Date: Sat, 9 Jan 2021 00:00:00 -0500 Subject: [PATCH] postfix-3.6-20210109 --- postfix/HISTORY | 29 ++ postfix/README_FILES/COMPATIBILITY_README | 16 +- postfix/README_FILES/TLS_README | 6 +- postfix/RELEASE_NOTES | 14 + postfix/US_PATENT_6321267 | 5 + postfix/WISHLIST | 3 + postfix/html/COMPATIBILITY_README.html | 15 +- postfix/html/TLS_README.html | 4 +- postfix/html/postalias.1.html | 17 +- postfix/html/postconf.5.html | 25 +- postfix/html/postmap.1.html | 17 +- postfix/man/man1/postalias.1 | 15 +- postfix/man/man1/postmap.1 | 15 +- postfix/man/man5/postconf.5 | 25 +- postfix/proto/COMPATIBILITY_README.html | 15 +- postfix/proto/TLS_README.html | 4 +- postfix/proto/postconf.proto | 25 +- postfix/src/global/Makefile.in | 38 +- postfix/src/global/compat_level.c | 457 ++++++++++++++++++++ postfix/src/global/compat_level.h | 43 ++ postfix/src/global/compat_level_convert.in | 22 + postfix/src/global/compat_level_convert.ref | 29 ++ postfix/src/global/compat_level_expand.in | 27 ++ postfix/src/global/compat_level_expand.ref | 55 +++ postfix/src/global/mail_params.c | 28 +- postfix/src/global/mail_params.h | 33 +- postfix/src/global/mail_version.h | 2 +- postfix/src/master/Makefile.in | 1 + postfix/src/master/master_ent.c | 5 +- postfix/src/postalias/postalias.c | 15 +- postfix/src/postconf/Makefile.in | 1 + postfix/src/postconf/postconf.c | 6 + postfix/src/postfix/Makefile.in | 1 + postfix/src/postfix/postfix.c | 7 +- postfix/src/postmap/postmap.c | 15 +- postfix/src/util/Makefile.in | 3 + postfix/src/util/alldig.c | 25 ++ postfix/src/util/mac_expand.c | 199 ++++++++- postfix/src/util/mac_expand.h | 26 ++ postfix/src/util/mac_expand.in | 18 + postfix/src/util/mac_expand.ref | 35 ++ postfix/src/util/stringops.h | 1 + 42 files changed, 1235 insertions(+), 107 deletions(-) create mode 100644 postfix/src/global/compat_level.c create mode 100644 postfix/src/global/compat_level.h create mode 100644 postfix/src/global/compat_level_convert.in create mode 100644 postfix/src/global/compat_level_convert.ref create mode 100644 postfix/src/global/compat_level_expand.in create mode 100644 postfix/src/global/compat_level_expand.ref diff --git a/postfix/HISTORY b/postfix/HISTORY index 1edb0d0e9..1d5d5d479 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -25315,3 +25315,32 @@ Apologies for any names omitted. Thienemann. See RELEASE_NOTES for caveats. Files: proto/postconf.proto, bounce/bounce_notify_tester.c, many test data files to exercise corner cases. + +20201220 + + Infrastructure: support to add custom comparison operators + for Postfix configuration files. This will be used to implement + custom comparison operators for compatibility_level values + that contain both the Postfix major and minor version and + maybe patchlevel. Files: util/alldig.c, util/stringops.h, + util/mac_expand.[hc] and test files. + +20210102 + + Infrastructure: support for the <=level, = 3.6 and the ccoommppaattiibbiilliittyy__lleevveell set to 3 or -higher. With Postfix <= 3.5, the default algorithm is mmdd55. The best-practice +algorithm is sshhaa225566 with Postfix >= 3.6 and the ccoommppaattiibbiilliittyy__lleevveell set to 3.6 +or higher. With Postfix <= 3.5, the default algorithm is mmdd55. The best-practice algorithm is now sshhaa225566. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in favor of sha256. However, as long as there are no known "second pre-image" attacks against the older algorithms, @@ -1122,7 +1122,7 @@ or multiple match attributes can be employed. The ":" character is not used as a delimiter as it occurs between each pair of fingerprint (hexadecimal) digits. The default algorithm is sshhaa225566 with Postfix >= 3.6 and the ccoommppaattiibbiilliittyy__lleevveell -set to 3 or higher; with Postfix <= 3.5, the default algorithm is mmdd55. The +set to 3.6 or higher; with Postfix <= 3.5, the default algorithm is mmdd55. The best-practice algorithm is now sshhaa225566. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in favor of sha256. However, as long as there are no known "second pre-image" attacks against the diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 606df671f..71e75f0ff 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -25,6 +25,20 @@ more recent Eclipse Public License 2.0. Recipients can choose to take the software under the license of their choice. Those who are more comfortable with the IPL can continue with that license. +Major changes with snapshot 20220109 +==================================== + +Starting with Postfix version 3.6, the "latest" compatibility level +is the Postfix version that introduced the last incompatible change. +The level is formatted as 'major.minor.patch', where 'patch' is +usually omitted and defaults to zero. Earlier compatibility levels +are 0, 1 and 2. + +This also introduces support for the <=level,

As long as the smtpd_tls_fingerprint_digest parameter is left at its implicit default value, and the compatibility_level setting is less than -3, Postfix logs a warning each time a client certificate or public key +3.6, Postfix logs a warning each time a client certificate or public key fingerprint is (potentially) used for access control:

@@ -437,7 +437,7 @@ the expected server certificates or public keys.

As long as the smtp_tls_fingerprint_digest (or LMTP equivalent) parameter is left at its implicit default value, and the -compatibility_level setting is less than 3, Postfix logs a warning each +compatibility_level setting is less than 3.6, Postfix logs a warning each time the "fingerprint" security level is used to specify matching "md5" digests of trusted server certificates or public keys:

@@ -485,6 +485,17 @@ warning: To disable backwards compatibility use "postconf compatibility_level = 9999" at their own risk.

+

Starting with Postfix version 3.6, the compatibility level in +the above warning message is the Postfix version that introduced +the last incompatible change. The level is formatted as +major.minor.patch, where patch is usually omitted and +defaults to zero. Earlier compatibility levels are 0, 1 and 2.

+ +

NOTE: this also introduces support for the "<level", +"<=level", and other operators to compare compatibility levels. +With the standard operators "<", "<=", etc., compatibility +level "3.10" would be smaller than "3.9" which is undesirable.

+ diff --git a/postfix/html/TLS_README.html b/postfix/html/TLS_README.html index 915beded9..4c8762f80 100644 --- a/postfix/html/TLS_README.html +++ b/postfix/html/TLS_README.html @@ -784,7 +784,7 @@ table.

The digest algorithm used to compute the client certificate fingerprints is specified with the main.cf smtpd_tls_fingerprint_digest parameter. The default algorithm is sha256 with Postfix ≥ -3.6 and the compatibility_level set to 3 or higher. With +3.6 and the compatibility_level set to 3.6 or higher. With Postfix ≤ 3.5, the default algorithm is md5. The best-practice algorithm is now sha256. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in @@ -1503,7 +1503,7 @@ delimiter as it occurs between each pair of fingerprint (hexadecimal) digits.

The default algorithm is sha256 with Postfix ≥ 3.6 -and the compatibility_level set to 3 or higher; with Postfix +and the compatibility_level set to 3.6 or higher; with Postfix ≤ 3.5, the default algorithm is md5. The best-practice algorithm is now sha256. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in diff --git a/postfix/html/postalias.1.html b/postfix/html/postalias.1.html index 6cd5eeaad..e8710adaf 100644 --- a/postfix/html/postalias.1.html +++ b/postfix/html/postalias.1.html @@ -128,12 +128,18 @@ POSTALIAS(1) POSTALIAS(1) file_name.dir. This is available on systems with support for dbm databases. + fail A table that reliably fails all requests. The lookup ta- + ble name is used for logging only. This table exists to + simplify Postfix error tests. + hash The output is a hashed file, named file_name.db. This is available on systems with support for db databases. - fail A table that reliably fails all requests. The lookup ta- - ble name is used for logging only. This table exists to - simplify Postfix error tests. + lmdb The output is a btree-based file, named file_name.lmdb. + lmdb supports concurrent writes and reads from different + processes, unlike other supported file-based tables. + This is available on systems with support for lmdb data- + bases. sdbm The output consists of two files, named file_name.pag and file_name.dir. This is available on systems with support @@ -207,6 +213,11 @@ POSTALIAS(1) POSTALIAS(1) A prefix that is prepended to the process name in syslog records, so that, for example, "smtpd" becomes "prefix/smtpd". + Available in Postfix 2.11 and later: + + lmdb_map_size (16777216) + The initial OpenLDAP LMDB database size limit in bytes. + STANDARDS RFC 822 (ARPA Internet Text Messages) diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index cf5ecf30c..68316e7ab 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -1625,6 +1625,17 @@ warning: To disable backwards compatibility use "postconf

+

Starting with Postfix version 3.6, the compatibility level in +the above warning message is the Postfix version that introduced +the last incompatible change. The level is formatted as +major.minor.patch, where patch is usually omitted and +defaults to zero. Earlier compatibility levels are 0, 1 and 2.

+ +

NOTE: this also introduces support for the "<level", +"<=level", and other operators to compare compatibility levels. +With the standard operators "<", "<=", etc., compatibility +level "3.10" would be smaller than "3.9" which is undesirable.

+

This feature is available in Postfix 3.0 and later.

@@ -3335,7 +3346,7 @@ address.

(default: no)

Enable non-delivery notifications (bounce messages) that link -to the original message by including a References: and In-Reply_to: +to the original message by including a References: and In-Reply-To: header with the original Message-ID value. There are advantages and disadvantages to consider.

@@ -5819,7 +5830,7 @@ The fingerprint digest algorithm is configurable via the Postfix version 2.5).
The default algorithm is sha256 with Postfix ≥ 3.6 -and the compatibility_level set to 3 or higher. With Postfix +and the compatibility_level set to 3.6 or higher. With Postfix ≤ 3.5, the default algorithm is md5. The best-practice algorithm is now sha256. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in favor of @@ -9726,7 +9737,7 @@ feature. The fingerprint digest algorithm is configurable via the Postfix version 2.5).

The default algorithm is sha256 with Postfix ≥ 3.6 -and the compatibility_level set to 3 or higher. With Postfix +and the compatibility_level set to 3.6 or higher. With Postfix ≤ 3.5, the default algorithm is md5. The best-practice algorithm is now sha256. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in favor of @@ -12815,7 +12826,7 @@ attacks, it is not feasible to create a new public key and a matching certificate (or public/private key-pair) that has the same fingerprint.

The default algorithm is sha256 with Postfix ≥ 3.6 -and the compatibility_level set to 3 or higher. With Postfix +and the compatibility_level set to 3.6 or higher. With Postfix ≤ 3.5, the default algorithm is md5.

The best-practice algorithm is now sha256. Recent advances in hash @@ -14455,7 +14466,7 @@ Postfix version 2.5). This feature requires "compatibility_level set to 3 or higher. With Postfix +and the compatibility_level set to 3.6 or higher. With Postfix ≤ 3.5, the default algorithm is md5. The best-practice algorithm is now sha256. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in favor of @@ -14588,7 +14599,7 @@ Postfix version 2.5). This feature requires "

The default algorithm is sha256 with Postfix ≥ 3.6 -and the compatibility_level set to 3 or higher. With Postfix +and the compatibility_level set to 3.6 or higher. With Postfix ≤ 3.5, the default algorithm is md5. The best-practice algorithm is now sha256. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in favor of @@ -17658,7 +17669,7 @@ fingerprints or public key fingerprints (Postfix 2.9 and later) for check_ccert_access and permit_tls_clientcerts.

The default algorithm is sha256 with Postfix ≥ 3.6 -and the compatibility_level set to 3 or higher. With Postfix +and the compatibility_level set to 3.6 or higher. With Postfix ≤ 3.5, the default algorithm is md5.

The best-practice algorithm is now sha256. Recent advances in hash diff --git a/postfix/html/postmap.1.html b/postfix/html/postmap.1.html index 853fd67dc..27380f2f3 100644 --- a/postfix/html/postmap.1.html +++ b/postfix/html/postmap.1.html @@ -204,13 +204,19 @@ POSTMAP(1) POSTMAP(1) file_name.dir. This is available on systems with support for dbm databases. + fail A table that reliably fails all requests. The lookup ta- + ble name is used for logging only. This table exists to + simplify Postfix error tests. + hash The output file is a hashed file, named file_name.db. This is available on systems with support for db data- bases. - fail A table that reliably fails all requests. The lookup ta- - ble name is used for logging only. This table exists to - simplify Postfix error tests. + lmdb The output is a btree-based file, named file_name.lmdb. + lmdb supports concurrent writes and reads from different + processes, unlike other supported file-based tables. + This is available on systems with support for lmdb data- + bases. sdbm The output consists of two files, named file_name.pag and file_name.dir. This is available on systems with support @@ -277,6 +283,11 @@ POSTMAP(1) POSTMAP(1) A prefix that is prepended to the process name in syslog records, so that, for example, "smtpd" becomes "prefix/smtpd". + Available in Postfix 2.11 and later: + + lmdb_map_size (16777216) + The initial OpenLDAP LMDB database size limit in bytes. + SEE ALSO postalias(1), create/update/query alias database postconf(1), supported database types diff --git a/postfix/man/man1/postalias.1 b/postfix/man/man1/postalias.1 index 0f47fe404..08ea13ddf 100644 --- a/postfix/man/man1/postalias.1 +++ b/postfix/man/man1/postalias.1 @@ -132,13 +132,18 @@ This is available on systems with support for \fBcdb\fR databases. The output consists of two files, named \fIfile_name\fB.pag\fR and \fIfile_name\fB.dir\fR. This is available on systems with support for \fBdbm\fR databases. -.IP \fBhash\fR -The output is a hashed file, named \fIfile_name\fB.db\fR. -This is available on systems with support for \fBdb\fR databases. .IP \fBfail\fR A table that reliably fails all requests. The lookup table name is used for logging only. This table exists to simplify Postfix error tests. +.IP \fBhash\fR +The output is a hashed file, named \fIfile_name\fB.db\fR. +This is available on systems with support for \fBdb\fR databases. +.IP \fBlmdb\fR +The output is a btree\-based file, named \fIfile_name\fB.lmdb\fR. +\fBlmdb\fR supports concurrent writes and reads from different +processes, unlike other supported file\-based tables. +This is available on systems with support for \fBlmdb\fR databases. .IP \fBsdbm\fR The output consists of two files, named \fIfile_name\fB.pag\fR and \fIfile_name\fB.dir\fR. @@ -208,6 +213,10 @@ The syslog facility of Postfix logging. .IP "\fBsyslog_name (see 'postconf -d' output)\fR" A prefix that is prepended to the process name in syslog records, so that, for example, "smtpd" becomes "prefix/smtpd". +.PP +Available in Postfix 2.11 and later: +.IP "\fBlmdb_map_size (16777216)\fR" +The initial OpenLDAP LMDB database size limit in bytes. .SH "STANDARDS" .na .nf diff --git a/postfix/man/man1/postmap.1 b/postfix/man/man1/postmap.1 index 7f08fd8af..e4dd20e7b 100644 --- a/postfix/man/man1/postmap.1 +++ b/postfix/man/man1/postmap.1 @@ -220,13 +220,18 @@ This is available on systems with support for \fBcdb\fR databases. The output consists of two files, named \fIfile_name\fB.pag\fR and \fIfile_name\fB.dir\fR. This is available on systems with support for \fBdbm\fR databases. -.IP \fBhash\fR -The output file is a hashed file, named \fIfile_name\fB.db\fR. -This is available on systems with support for \fBdb\fR databases. .IP \fBfail\fR A table that reliably fails all requests. The lookup table name is used for logging only. This table exists to simplify Postfix error tests. +.IP \fBhash\fR +The output file is a hashed file, named \fIfile_name\fB.db\fR. +This is available on systems with support for \fBdb\fR databases. +.IP \fBlmdb\fR +The output is a btree\-based file, named \fIfile_name\fB.lmdb\fR. +\fBlmdb\fR supports concurrent writes and reads from different +processes, unlike other supported file\-based tables. +This is available on systems with support for \fBlmdb\fR databases. .IP \fBsdbm\fR The output consists of two files, named \fIfile_name\fB.pag\fR and \fIfile_name\fB.dir\fR. @@ -291,6 +296,10 @@ The syslog facility of Postfix logging. .IP "\fBsyslog_name (see 'postconf -d' output)\fR" A prefix that is prepended to the process name in syslog records, so that, for example, "smtpd" becomes "prefix/smtpd". +.PP +Available in Postfix 2.11 and later: +.IP "\fBlmdb_map_size (16777216)\fR" +The initial OpenLDAP LMDB database size limit in bytes. .SH "SEE ALSO" .na .nf diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index c3223e98a..d40fe566a 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -982,6 +982,17 @@ warning: To disable backwards compatibility use "postconf .ft R .in -4 .PP +Starting with Postfix version 3.6, the compatibility level in +the above warning message is the Postfix version that introduced +the last incompatible change. The level is formatted as +\fImajor.minor.patch\fR, where \fIpatch\fR is usually omitted and +defaults to zero. Earlier compatibility levels are 0, 1 and 2. +.PP +NOTE: this also introduces support for the "= 3.6 -and the \fBcompatibility_level\fR set to 3 or higher. With Postfix +and the \fBcompatibility_level\fR set to 3.6 or higher. With Postfix <= 3.5, the default algorithm is \fBmd5\fR. The best\-practice algorithm is now \fBsha256\fR. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in favor of @@ -6056,7 +6067,7 @@ smtpd_tls_fingerprint_digest parameter (hard\-coded as md5 prior to Postfix version 2.5). .PP The default algorithm is \fBsha256\fR with Postfix >= 3.6 -and the \fBcompatibility_level\fR set to 3 or higher. With Postfix +and the \fBcompatibility_level\fR set to 3.6 or higher. With Postfix <= 3.5, the default algorithm is \fBmd5\fR. The best\-practice algorithm is now \fBsha256\fR. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in favor of @@ -8307,7 +8318,7 @@ attacks, it is not feasible to create a new public key and a matching certificate (or public/private key\-pair) that has the same fingerprint. .PP The default algorithm is \fBsha256\fR with Postfix >= 3.6 -and the \fBcompatibility_level\fR set to 3 or higher. With Postfix +and the \fBcompatibility_level\fR set to 3.6 or higher. With Postfix <= 3.5, the default algorithm is \fBmd5\fR. .PP The best\-practice algorithm is now \fBsha256\fR. Recent advances in hash @@ -9722,7 +9733,7 @@ Postfix version 2.5). This feature requires "smtpd_tls_ask_ccert 2.2 and later. .br The default algorithm is \fBsha256\fR with Postfix >= 3.6 -and the \fBcompatibility_level\fR set to 3 or higher. With Postfix +and the \fBcompatibility_level\fR set to 3.6 or higher. With Postfix <= 3.5, the default algorithm is \fBmd5\fR. The best\-practice algorithm is now \fBsha256\fR. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in favor of @@ -9841,7 +9852,7 @@ Postfix version 2.5). This feature requires "smtpd_tls_ask_ccert = yes" and is available with Postfix version 2.2 and later. .br The default algorithm is \fBsha256\fR with Postfix >= 3.6 -and the \fBcompatibility_level\fR set to 3 or higher. With Postfix +and the \fBcompatibility_level\fR set to 3.6 or higher. With Postfix <= 3.5, the default algorithm is \fBmd5\fR. The best\-practice algorithm is now \fBsha256\fR. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in favor of @@ -12290,7 +12301,7 @@ fingerprints or public key fingerprints (Postfix 2.9 and later) for \fBcheck_ccert_access\fR and \fBpermit_tls_clientcerts\fR. .PP The default algorithm is \fBsha256\fR with Postfix >= 3.6 -and the \fBcompatibility_level\fR set to 3 or higher. With Postfix +and the \fBcompatibility_level\fR set to 3.6 or higher. With Postfix <= 3.5, the default algorithm is \fBmd5\fR. .PP The best\-practice algorithm is now \fBsha256\fR. Recent advances in hash diff --git a/postfix/proto/COMPATIBILITY_README.html b/postfix/proto/COMPATIBILITY_README.html index 25b6f6c65..ec483ddf6 100644 --- a/postfix/proto/COMPATIBILITY_README.html +++ b/postfix/proto/COMPATIBILITY_README.html @@ -391,7 +391,7 @@ key.

As long as the smtpd_tls_fingerprint_digest parameter is left at its implicit default value, and the compatibility_level setting is less than -3, Postfix logs a warning each time a client certificate or public key +3.6, Postfix logs a warning each time a client certificate or public key fingerprint is (potentially) used for access control:

@@ -437,7 +437,7 @@ the expected server certificates or public keys.

As long as the smtp_tls_fingerprint_digest (or LMTP equivalent) parameter is left at its implicit default value, and the -compatibility_level setting is less than 3, Postfix logs a warning each +compatibility_level setting is less than 3.6, Postfix logs a warning each time the "fingerprint" security level is used to specify matching "md5" digests of trusted server certificates or public keys:

@@ -485,6 +485,17 @@ warning: To disable backwards compatibility use "postconf compatibility_level= Sites that don't care about backwards compatibility may set "compatibility_level = 9999" at their own risk.

+

Starting with Postfix version 3.6, the compatibility level in +the above warning message is the Postfix version that introduced +the last incompatible change. The level is formatted as +major.minor.patch, where patch is usually omitted and +defaults to zero. Earlier compatibility levels are 0, 1 and 2.

+ +

NOTE: this also introduces support for the "<level", +"<=level", and other operators to compare compatibility levels. +With the standard operators "<", "<=", etc., compatibility +level "3.10" would be smaller than "3.9" which is undesirable.

+ diff --git a/postfix/proto/TLS_README.html b/postfix/proto/TLS_README.html index badd59ad1..e86caad20 100644 --- a/postfix/proto/TLS_README.html +++ b/postfix/proto/TLS_README.html @@ -784,7 +784,7 @@ table.

The digest algorithm used to compute the client certificate fingerprints is specified with the main.cf smtpd_tls_fingerprint_digest parameter. The default algorithm is sha256 with Postfix ≥ -3.6 and the compatibility_level set to 3 or higher. With +3.6 and the compatibility_level set to 3.6 or higher. With Postfix ≤ 3.5, the default algorithm is md5. The best-practice algorithm is now sha256. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in @@ -1503,7 +1503,7 @@ delimiter as it occurs between each pair of fingerprint (hexadecimal) digits.

The default algorithm is sha256 with Postfix ≥ 3.6 -and the compatibility_level set to 3 or higher; with Postfix +and the compatibility_level set to 3.6 or higher; with Postfix ≤ 3.5, the default algorithm is md5. The best-practice algorithm is now sha256. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index 0fbe21134..cccd0a283 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -5123,7 +5123,7 @@ Postfix version 2.5). This feature requires "smtpd_tls_ask_ccert 2.2 and later.

The default algorithm is sha256 with Postfix ≥ 3.6 -and the compatibility_level set to 3 or higher. With Postfix +and the compatibility_level set to 3.6 or higher. With Postfix ≤ 3.5, the default algorithm is md5. The best-practice algorithm is now sha256. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in favor of @@ -5257,7 +5257,7 @@ Postfix version 2.5). This feature requires "smtpd_tls_ask_ccert = yes" and is available with Postfix version 2.2 and later.
The default algorithm is sha256 with Postfix ≥ 3.6 -and the compatibility_level set to 3 or higher. With Postfix +and the compatibility_level set to 3.6 or higher. With Postfix ≤ 3.5, the default algorithm is md5. The best-practice algorithm is now sha256. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in favor of @@ -9304,7 +9304,7 @@ smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to Postfix version 2.5).
The default algorithm is sha256 with Postfix ≥ 3.6 -and the compatibility_level set to 3 or higher. With Postfix +and the compatibility_level set to 3.6 or higher. With Postfix ≤ 3.5, the default algorithm is md5. The best-practice algorithm is now sha256. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in favor of @@ -9749,7 +9749,7 @@ smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to Postfix version 2.5).

The default algorithm is sha256 with Postfix ≥ 3.6 -and the compatibility_level set to 3 or higher. With Postfix +and the compatibility_level set to 3.6 or higher. With Postfix ≤ 3.5, the default algorithm is md5. The best-practice algorithm is now sha256. Recent advances in hash function cryptanalysis have led to md5 and sha1 being deprecated in favor of @@ -12427,7 +12427,7 @@ attacks, it is not feasible to create a new public key and a matching certificate (or public/private key-pair) that has the same fingerprint.

The default algorithm is sha256 with Postfix ≥ 3.6 -and the compatibility_level set to 3 or higher. With Postfix +and the compatibility_level set to 3.6 or higher. With Postfix ≤ 3.5, the default algorithm is md5.

The best-practice algorithm is now sha256. Recent advances in hash @@ -12570,7 +12570,7 @@ fingerprints or public key fingerprints (Postfix 2.9 and later) for check_ccert_access and permit_tls_clientcerts.

The default algorithm is sha256 with Postfix ≥ 3.6 -and the compatibility_level set to 3 or higher. With Postfix +and the compatibility_level set to 3.6 or higher. With Postfix ≤ 3.5, the default algorithm is md5.

The best-practice algorithm is now sha256. Recent advances in hash @@ -16753,6 +16753,17 @@ warning: To disable backwards compatibility use "postconf +

Starting with Postfix version 3.6, the compatibility level in +the above warning message is the Postfix version that introduced +the last incompatible change. The level is formatted as +major.minor.patch, where patch is usually omitted and +defaults to zero. Earlier compatibility levels are 0, 1 and 2.

+ +

NOTE: this also introduces support for the "<level", +"<=level", and other operators to compare compatibility levels. +With the standard operators "<", "<=", etc., compatibility +level "3.10" would be smaller than "3.9" which is undesirable.

+

This feature is available in Postfix 3.0 and later.

%PARAM message_drop_headers bcc, content-length, resent-bcc, return-path @@ -17979,7 +17990,7 @@ of the null sender address. %PARAM enable_threaded_bounces no

Enable non-delivery notifications (bounce messages) that link -to the original message by including a References: and In-Reply_to: +to the original message by including a References: and In-Reply-To: header with the original Message-ID value. There are advantages and disadvantages to consider.

diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index 80702b03a..0fab4ceb6 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -37,7 +37,7 @@ SRCS = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \ mail_addr_form.c quote_flags.c maillog_client.c \ normalize_mailhost_addr.c map_search.c reject_deliver_request.c \ info_log_addr_form.c sasl_mech_filter.c login_sender_match.c \ - test_main.c + test_main.c compat_level.c OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \ clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \ @@ -76,7 +76,7 @@ OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ $(NON_PLUGIN_MAP_OBJ) mail_addr_form.o quote_flags.o maillog_client.o \ normalize_mailhost_addr.o map_search.o reject_deliver_request.o \ info_log_addr_form.o sasl_mech_filter.o login_sender_match.o \ - test_main.o + test_main.o compat_level.o # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf. # When hard-linking these maps, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ), # otherwise it sets the PLUGIN_* macros. @@ -112,7 +112,7 @@ HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \ attr_override.h mail_parm_split.h midna_adomain.h mail_addr_form.h \ maillog_client.h normalize_mailhost_addr.h map_search.h \ info_log_addr_form.h sasl_mech_filter.h login_sender_match.h \ - test_main.h + test_main.h compat_level.h TESTSRC = rec2stream.c stream2rec.c recdump.c DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) @@ -128,7 +128,8 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \ data_redirect addr_match_list safe_ultostr verify_sender_addr \ mail_version mail_dict server_acl uxtext mail_parm_split \ 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 LIBS = ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX) LIB_DIR = ../../lib @@ -402,6 +403,9 @@ delivered_hdr: delivered_hdr.c $(LIB) $(LIBS) login_sender_match: login_sender_match.c $(LIB) $(LIBS) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) +compat_level: compat_level.c $(LIB) $(LIBS) + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + tests: tok822_test mime_tests strip_addr_test tok822_limit_test \ xtext_test scache_multi_test ehlo_mask_test \ namadr_list_test mail_conf_time_test header_body_checks_tests \ @@ -410,7 +414,7 @@ tests: tok822_test mime_tests strip_addr_test tok822_limit_test \ smtp_reply_footer_test off_cvt_test mail_addr_crunch_test \ mail_addr_find_test mail_addr_map_test quote_822_local_test \ normalize_mailhost_addr_test haproxy_srvr_test map_search_test \ - delivered_hdr_test login_sender_match_test + delivered_hdr_test login_sender_match_test compat_level_test 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 @@ -748,6 +752,20 @@ login_sender_match_test: update login_sender_match login_sender_match.ref diff login_sender_match.ref login_sender_match.tmp rm -f login_sender_match.tmp +compat_level_test: compat_level_expand_test compat_level_convert_test + +compat_level_expand_test: update compat_level compat_level_expand.in \ + compat_level_expand.ref + -$(SHLIB_ENV) $(VALGRIND) ./compat_level -x compat_level_expand.tmp 2>&1 + diff compat_level_expand.ref compat_level_expand.tmp + rm -f compat_level_expand.tmp + +compat_level_convert_test: update compat_level compat_level_convert.in \ + compat_level_convert.ref + -$(SHLIB_ENV) $(VALGRIND) ./compat_level -c compat_level_convert.tmp 2>&1 + diff compat_level_convert.ref compat_level_convert.tmp + rm -f compat_level_convert.tmp + printfck: $(OBJS) $(PROG) rm -rf printfck mkdir printfck @@ -950,6 +968,15 @@ clnt_stream.o: clnt_stream.c clnt_stream.o: clnt_stream.h clnt_stream.o: mail_params.h clnt_stream.o: mail_proto.h +compat_level.o: ../../include/check_arg.h +compat_level.o: ../../include/mac_expand.h +compat_level.o: ../../include/mac_parse.h +compat_level.o: ../../include/msg.h +compat_level.o: ../../include/sys_defs.h +compat_level.o: ../../include/vbuf.h +compat_level.o: ../../include/vstring.h +compat_level.o: compat_level.c +compat_level.o: compat_level.h conv_time.o: ../../include/msg.h conv_time.o: ../../include/sys_defs.h conv_time.o: conv_time.c @@ -1872,6 +1899,7 @@ mail_params.o: ../../include/vbuf.h mail_params.o: ../../include/vstream.h mail_params.o: ../../include/vstring.h mail_params.o: ../../include/vstring_vstream.h +mail_params.o: compat_level.h mail_params.o: mail_conf.h mail_params.o: mail_params.c mail_params.o: mail_params.h diff --git a/postfix/src/global/compat_level.c b/postfix/src/global/compat_level.c new file mode 100644 index 000000000..7cb8d010a --- /dev/null +++ b/postfix/src/global/compat_level.c @@ -0,0 +1,457 @@ +/*++ +/* NAME +/* compat_level 3 +/* SUMMARY +/* compatibility_level support +/* SYNOPSIS +/* #include +/* +/* void compat_level_relop_register(void) +/* +/* long compat_level_from_string( +/* const char *str, +/* void PRINTFLIKE(1, 2) (*msg_fn)(const char *,...)) +/* +/* long compat_level_from_numbers( +/* long major, +/* long minor, +/* long patch, +/* void PRINTFLIKE(1, 2) (*msg_fn)(const char *,...)) +/* +/* const char *compat_level_to_string( +/* long compat_level, +/* void PRINTFLIKE(1, 2) (*msg_fn)(const char *,...)) +/* AUXULIARY FUNCTIONS +/* long compat_level_from_major_minor( +/* long major, +/* long minor, +/* void PRINTFLIKE(1, 2) (*msg_fn)(const char *,...)) +/* +/* long compat_level_from_major( +/* long major, +/* void PRINTFLIKE(1, 2) (*msg_fn)(const char *,...)) +/* DESCRIPTION +/* This module supports compatibility level syntax with +/* "major.minor.patch" but will also accept the shorter forms +/* "major.minor" and "major" (missing members default to zero). +/* Compatibility levels with multiple numbers cannot be compared +/* as strings or as floating-point numbers (for example, "3.10" +/* would be smaller than "3.9"). +/* +/* The major number can range from [0..2047] inclusive (11 +/* bits) or more, while the minor and patch numbers can range +/* from [0..1023] inclusive (10 bits). +/* +/* compat_level_from_string() converts a compatibility level +/* from string form to numerical form for easy comparison. +/* Valid input results in a non-negative result. In case of +/* error, compat_level_from_string() reports the problem with +/* the provided function, and returns -1 if that function does +/* not terminate execution. +/* +/* compat_level_from_numbers() creates an internal-form +/* compatibility level from distinct numbers. Valid input +/* results in a non-negative result. In case of error, +/* compat_level_from_numbers() reports the problem with the +/* provided function, and returns -1 if that function does not +/* terminate execution. +/* +/* The functions compat_level_from_major_minor() and +/* compat_level_from_major() are helpers that default the missing +/* information to zeroes. +/* +/* compat_level_to_string() converts a compatibility level +/* from numerical form to canonical string form. Valid input +/* results in a non-null result. In case of error, +/* compat_level_to_string() reports the problem with the +/* provided function, and returns a null pointer if that +/* function does not terminate execution. +/* +/* compat_level_relop_register() registers a mac_expand() callback +/* that registers operators such as <=level, >level, that compare +/* compatibility levels. This function should be called before +/* loading parameter settings from main.cf. +/* DIAGNOSTICS +/* info, .., panic: bad compatibility_level syntax. +/* BUGS +/* The patch and minor fields range from 0..1023 (10 bits) while +/* the major field ranges from 0..COMPAT_MAJOR_SHIFT47 or more +/* (11 bits or more). +/* +/* This would be a great use case for fucntions returning +/* StatusOr or StatusOr, but is it a bit +/* late for a port to C++. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include +#include +#include +#include + + /* + * Utility library. + */ +#include +#include + + /* + * For easy comparison we convert a three-number compatibility level into + * just one number, using different bit ranges for the major version, minor + * version, and patch level. + * + * We use long integers because standard C guarantees that long has at last 32 + * bits instead of int which may have only 16 bits (though it is unlikely + * that Postfix would run on such systems). That gives us 11 or more bits + * for the major version, and 10 bits for minor the version and patchlevel. + * + * Below are all the encoding details in one place. This is easier to verify + * than wading through code. + */ +#define COMPAT_MAJOR_SHIFT \ + (COMPAT_MINOR_SHIFT + COMPAT_MINOR_WIDTH) + +#define COMPAT_MINOR_SHIFT COMPAT_PATCH_WIDTH +#define COMPAT_MINOR_BITS 0x3ff +#define COMPAT_MINOR_WIDTH 10 + +#define COMPAT_PATCH_BITS 0x3ff +#define COMPAT_PATCH_WIDTH 10 + +#define GOOD_MAJOR(m) ((m) >= 0 && (m) <= (LONG_MAX >> COMPAT_MAJOR_SHIFT)) +#define GOOD_MINOR(m) ((m) >= 0 && (m) <= COMPAT_MINOR_BITS) +#define GOOD_PATCH(p) ((p) >= 0 && (p) <= COMPAT_PATCH_BITS) + +#define ENCODE_MAJOR(m) ((m) << COMPAT_MAJOR_SHIFT) +#define ENCODE_MINOR(m) ((m) << COMPAT_MINOR_SHIFT) +#define ENCODE_PATCH(p) (p) + +#define DECODE_MAJOR(l) ((l) >> COMPAT_MAJOR_SHIFT) +#define DECODE_MINOR(l) (((l) >> COMPAT_MINOR_SHIFT) & COMPAT_MINOR_BITS) +#define DECODE_PATCH(l) ((l) & COMPAT_PATCH_BITS) + + /* + * Global library. + */ +#include + +/* compat_level_from_string - convert major[.minor] to comparable type */ + +long compat_level_from_string(const char *str, + void PRINTFLIKE(1, 2) (*msg_fn) (const char *,...)) +{ + long major, minor, patch, res = 0; + const char *start; + char *remainder; + + start = str; + major = strtol(start, &remainder, 10); + if (start < remainder && (*remainder == 0 || *remainder == '.') + && errno != ERANGE && GOOD_MAJOR(major)) { + res = ENCODE_MAJOR(major); + if (*remainder == 0) + return res; + start = remainder + 1; + minor = strtol(start, &remainder, 10); + if (start < remainder && (*remainder == 0 || *remainder == '.') + && errno != ERANGE && GOOD_MINOR(minor)) { + res |= ENCODE_MINOR(minor); + if (*remainder == 0) + return (res); + start = remainder + 1; + patch = strtol(start, &remainder, 10); + if (start < remainder && *remainder == 0 && errno != ERANGE + && GOOD_PATCH(patch)) { + return (res | ENCODE_PATCH(patch)); + } + } + } + msg_fn("malformed compatibility level syntax: \"%s\"", str); + return (-1); +} + +/* compat_level_from_numbers - internal form from numbers */ + +long compat_level_from_numbers(long major, long minor, long patch, + void PRINTFLIKE(1, 2) (*msg_fn) (const char *,...)) +{ + const char myname[] = "compat_level_from_numbers"; + + /* + * Sanity checks. + */ + if (!GOOD_MAJOR(major)) { + msg_fn("%s: bad major version: %ld", myname, major); + return (-1); + } + if (!GOOD_MINOR(minor)) { + msg_fn("%s: bad minor version: %ld", myname, minor); + return (-1); + } + if (!GOOD_PATCH(patch)) { + msg_fn("%s: bad patch level: %ld", myname, patch); + return (-1); + } + + /* + * Conversion. + */ + return (ENCODE_MAJOR(major) | ENCODE_MINOR(minor) | ENCODE_PATCH(patch)); +} + +/* compat_level_to_string - pretty-print a compatibility level */ + +const char *compat_level_to_string(long compat_level, + void PRINTFLIKE(1, 2) (*msg_fn) (const char *,...)) +{ + const char myname[] = "compat_level_to_string"; + static VSTRING *buf; + long major; + long minor; + long patch; + + /* + * Sanity check. + */ + if (compat_level < 0) { + msg_fn("%s: bad compatibility level: %ld", myname, compat_level); + return (0); + } + + /* + * Compatibility levels 0..2 have no minor or patch level. + */ + if (buf == 0) + buf = vstring_alloc(10); + major = DECODE_MAJOR(compat_level); + if (!GOOD_MAJOR(major)) { + msg_fn("%s: bad compatibility major level: %ld", myname, compat_level); + return (0); + } + vstring_sprintf(buf, "%ld", major); + if (major > 2) { + + /* + * Expect that major.minor will be common. + */ + minor = DECODE_MINOR(compat_level); + vstring_sprintf_append(buf, ".%ld", minor); + + /* + * Expect that major.minor.patch will be rare. + */ + patch = DECODE_PATCH(compat_level); + if (patch) + vstring_sprintf_append(buf, ".%ld", patch); + } + return (vstring_str(buf)); +} + +/* compat_relop_eval - mac_expand callback */ + +static MAC_EXP_OP_RES compat_relop_eval(const char *left_str, int relop, + const char *rite_str) +{ + const char myname[] = "compat_relop_eval"; + long left_val, rite_val; + + /* + * Negative result means error. + */ + if ((left_val = compat_level_from_string(left_str, msg_warn)) < 0 + || (rite_val = compat_level_from_string(rite_str, msg_warn)) < 0) + return (MAC_EXP_OP_RES_ERROR); + + /* + * Valid result. The difference between non-negative numbers will no + * overflow. + */ + long delta = left_val - rite_val; + + switch (relop) { + case MAC_EXP_OP_TOK_EQ: + return (mac_exp_op_res_bool[delta == 0]); + case MAC_EXP_OP_TOK_NE: + return (mac_exp_op_res_bool[delta != 0]); + case MAC_EXP_OP_TOK_LT: + return (mac_exp_op_res_bool[delta < 0]); + case MAC_EXP_OP_TOK_LE: + return (mac_exp_op_res_bool[delta <= 0]); + case MAC_EXP_OP_TOK_GE: + return (mac_exp_op_res_bool[delta >= 0]); + case MAC_EXP_OP_TOK_GT: + return (mac_exp_op_res_bool[delta > 0]); + default: + msg_panic("%s: unknown operator: %d", + myname, relop); + } +} + +/* compat_level_register - register comparison operators */ + +void compat_level_relop_register(void) +{ + int compat_level_relops[] = { + MAC_EXP_OP_TOK_EQ, MAC_EXP_OP_TOK_NE, + MAC_EXP_OP_TOK_GT, MAC_EXP_OP_TOK_GE, + MAC_EXP_OP_TOK_LT, MAC_EXP_OP_TOK_LE, + 0, + }; + + mac_expand_add_relop(compat_level_relops, "level", compat_relop_eval); +} + +#ifdef TEST +#include + +#include +#include +#include +#include +#include +#include + +static const char *lookup(const char *name, int unused_mode, void *context) +{ + HTABLE *table = (HTABLE *) context; + + return (htable_find(table, name)); +} + +static void test_expand(void) +{ + VSTRING *buf = vstring_alloc(100); + VSTRING *result = vstring_alloc(100); + char *cp; + char *name; + char *value; + HTABLE *table; + int stat; + + /* + * Add relops that compare string lengths instead of content. + */ + compat_level_relop_register(); + + /* + * Loop over the inputs. + */ + while (!vstream_feof(VSTREAM_IN)) { + + table = htable_create(0); + + /* + * Read a block of definitions, terminated with an empty line. + */ + while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) { + vstream_printf("<< %s\n", vstring_str(buf)); + vstream_fflush(VSTREAM_OUT); + if (VSTRING_LEN(buf) == 0) + break; + cp = vstring_str(buf); + name = mystrtok(&cp, CHARS_SPACE "="); + value = mystrtok(&cp, CHARS_SPACE "="); + htable_enter(table, name, value ? mystrdup(value) : 0); + } + + /* + * Read a block of patterns, terminated with an empty line or EOF. + */ + while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) { + vstream_printf("<< %s\n", vstring_str(buf)); + vstream_fflush(VSTREAM_OUT); + if (VSTRING_LEN(buf) == 0) + break; + VSTRING_RESET(result); + stat = mac_expand(result, vstring_str(buf), MAC_EXP_FLAG_NONE, + (char *) 0, lookup, (void *) table); + vstream_printf("stat=%d result=%s\n", stat, vstring_str(result)); + vstream_fflush(VSTREAM_OUT); + } + htable_free(table, myfree); + vstream_printf("\n"); + } + + /* + * Clean up. + */ + vstring_free(buf); + vstring_free(result); +} + +static void test_convert(void) +{ + VSTRING *buf = vstring_alloc(100); + long compat_level; + const char *as_string; + + /* + * Read compatibility level. + */ + while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) { + if ((compat_level = compat_level_from_string(vstring_str(buf), + msg_warn)) < 0) + continue; + msg_info("%s -> 0x%lx", vstring_str(buf), compat_level); + if ((as_string = compat_level_to_string(compat_level, + msg_warn)) == 0) + continue; + msg_info("0x%lx->%s", compat_level, as_string); + } + vstring_free(buf); +} + +static NORETURN usage(char **argv) +{ + msg_fatal("usage: %s option\n-c (convert)\n-c (expand)", argv[0]); +} + +int main(int argc, char **argv) +{ + int ch; + int mode = 0; + +#define MODE_EXPAND (1<<0) +#define MODE_CONVERT (1<<1) + + while ((ch = GETOPT(argc, argv, "cx")) > 0) { + switch (ch) { + case 'c': + mode |= MODE_CONVERT; + break; + case 'v': + msg_verbose++; + break; + case 'x': + mode |= MODE_EXPAND; + break; + default: + usage(argv); + } + } + switch (mode) { + case MODE_CONVERT: + test_convert(); + break; + case MODE_EXPAND: + test_expand(); + break; + default: + usage(argv); + } + exit(0); +} + +#endif diff --git a/postfix/src/global/compat_level.h b/postfix/src/global/compat_level.h new file mode 100644 index 000000000..076e887e2 --- /dev/null +++ b/postfix/src/global/compat_level.h @@ -0,0 +1,43 @@ +#ifndef _COMPAT_LEVEL_H_INCLUDED_ +#define _COMPAT_LEVEL_H_INCLUDED_ + +/*++ +/* NAME +/* compat_level 3h +/* SUMMARY +/* compatibility_level support +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern void compat_level_relop_register(void); +extern long compat_level_from_string(const char *, + void PRINTFLIKE(1, 2) (*) (const char *,...)); +extern long compat_level_from_numbers(long, long, long, + void PRINTFLIKE(1, 2) (*) (const char *,...)); +extern const char *compat_level_to_string(long, + void PRINTFLIKE(1, 2) (*) (const char *,...)); + +#define compat_level_from_major(major, msg_fn) \ + compat_level_from_major_minor((major), 0, (msg_fn)) +#define compat_level_from_major_minor(major, minor, msg_fn) \ + compat_level_from_numbers((major), (minor), 0, (msg_fn)) + +# + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +#endif diff --git a/postfix/src/global/compat_level_convert.in b/postfix/src/global/compat_level_convert.in new file mode 100644 index 000000000..e7cec78b5 --- /dev/null +++ b/postfix/src/global/compat_level_convert.in @@ -0,0 +1,22 @@ +3.2.1 +3.2.1.0 +3a.2.1 +a3.2.1 +3.a2.1 +3.2a.1 +3.2a.a1 +3.2a.1a +3 +3. +3.2. +3.1.1. +2 +1 +0 +3.1023 +3.1024 +3.2.1023 +3.2.1024 +-3 +3.-2.1 +3.2.-1 diff --git a/postfix/src/global/compat_level_convert.ref b/postfix/src/global/compat_level_convert.ref new file mode 100644 index 000000000..17f29dca4 --- /dev/null +++ b/postfix/src/global/compat_level_convert.ref @@ -0,0 +1,29 @@ +unknown: 3.2.1 -> 0x300801 +unknown: 0x300801->3.2.1 +unknown: warning: malformed compatibility level syntax: "3.2.1.0" +unknown: warning: malformed compatibility level syntax: "3a.2.1" +unknown: warning: malformed compatibility level syntax: "a3.2.1" +unknown: warning: malformed compatibility level syntax: "3.a2.1" +unknown: warning: malformed compatibility level syntax: "3.2a.1" +unknown: warning: malformed compatibility level syntax: "3.2a.a1" +unknown: warning: malformed compatibility level syntax: "3.2a.1a" +unknown: 3 -> 0x300000 +unknown: 0x300000->3.0 +unknown: warning: malformed compatibility level syntax: "3." +unknown: warning: malformed compatibility level syntax: "3.2." +unknown: warning: malformed compatibility level syntax: "3.1.1." +unknown: 2 -> 0x200000 +unknown: 0x200000->2 +unknown: 1 -> 0x100000 +unknown: 0x100000->1 +unknown: 0 -> 0x0 +unknown: 0x0->0 +unknown: 3.1023 -> 0x3ffc00 +unknown: 0x3ffc00->3.1023 +unknown: warning: malformed compatibility level syntax: "3.1024" +unknown: 3.2.1023 -> 0x300bff +unknown: 0x300bff->3.2.1023 +unknown: warning: malformed compatibility level syntax: "3.2.1024" +unknown: warning: malformed compatibility level syntax: "-3" +unknown: warning: malformed compatibility level syntax: "3.-2.1" +unknown: warning: malformed compatibility level syntax: "3.2.-1" diff --git a/postfix/src/global/compat_level_expand.in b/postfix/src/global/compat_level_expand.in new file mode 100644 index 000000000..0f5208bfa --- /dev/null +++ b/postfix/src/global/compat_level_expand.in @@ -0,0 +1,27 @@ +compatibility_level = 3 + +${ {$compatibility_level} ==level {3.0} ? {good} : {bad} } +${ {$compatibility_level} !=level {3.0} ? {bad} : {good} } +${ {$compatibility_level} ==level {3.10} ? {bad} : {good} } +${ {$compatibility_level} !=level {3.10} ? {good} : {bad} } +${ {$compatibility_level} level {4} ? {bad} : {good} } +${ {$compatibility_level} >=level {4} ? {bad} : {good} } +${ {$compatibility_level} >level {2} ? {good} : {bad} } +${ {$compatibility_level} >=level {2} ? {good} : {bad} } +${ {$compatibility_level} >=level {3.0} ? {good} : {bad} } +${ {$compatibility_level} >=level {3A} ? {error} : {error} } +${ {$compatibility_level} >=level {3.} ? {error} : {error} } +${ {$compatibility_level} >=level {.1} ? {error} : {error} } +${ {3} > {3.2} ? {bad} : {good} } +${ {3} >level {3.2} ? {bad} : {good} } +${ {3} < {3.2} ? {good} : {bad} } +${ {3} {3.2} ? {bad} : {good} } +${ {3.10} >level {3.2} ? {good} : {bad} } +${ {3.10} < {3.2} ? {good} : {bad} } +${ {3.10} level {4} ? {bad} : {good} } +stat=0 result=good +<< ${ {$compatibility_level} >=level {4} ? {bad} : {good} } +stat=0 result=good +<< ${ {$compatibility_level} >level {2} ? {good} : {bad} } +stat=0 result=good +<< ${ {$compatibility_level} >=level {2} ? {good} : {bad} } +stat=0 result=good +<< ${ {$compatibility_level} >=level {3.0} ? {good} : {bad} } +stat=0 result=good +<< ${ {$compatibility_level} >=level {3A} ? {error} : {error} } +unknown: warning: malformed compatibility level syntax: "3A" +stat=1 result= +<< ${ {$compatibility_level} >=level {3.} ? {error} : {error} } +unknown: warning: malformed compatibility level syntax: "3." +stat=1 result= +<< ${ {$compatibility_level} >=level {.1} ? {error} : {error} } +unknown: warning: malformed compatibility level syntax: ".1" +stat=1 result= +<< ${ {3} > {3.2} ? {bad} : {good} } +stat=0 result=good +<< ${ {3} >level {3.2} ? {bad} : {good} } +stat=0 result=good +<< ${ {3} < {3.2} ? {good} : {bad} } +stat=0 result=good +<< ${ {3} {3.2} ? {bad} : {good} } +stat=0 result=good +<< ${ {3.10} >level {3.2} ? {good} : {bad} } +stat=0 result=good +<< ${ {3.10} < {3.2} ? {good} : {bad} } +stat=0 result=good +<< ${ {3.10} #include #include +#include /* * Special configuration variables. @@ -357,7 +360,7 @@ int var_smtputf8_enable; int var_strict_smtputf8; char *var_smtputf8_autoclass; int var_idna2003_compat; -int var_compat_level; +char *var_compatibility_level; char *var_drop_hdrs; char *var_info_log_addr_form; bool var_enable_orcpt; @@ -394,6 +397,11 @@ int warn_compat_break_smtputf8_enable; int warn_compat_break_chroot; int warn_compat_break_relay_restrictions; + /* + * Parsed from var_compatibility_level; + */ +long compat_level; + /* check_myhostname - lookup hostname and validate */ static const char *check_myhostname(void) @@ -635,9 +643,9 @@ static void check_legacy_defaults(void) /* * Look for specific parameters whose default changed when the - * compatibility level changed to 3. + * compatibility level changed to 3.6. */ - if (var_compat_level < 3) { + if (compat_level < compat_level_from_string(COMPAT_LEVEL_3_6, msg_panic)) { if (mail_conf_lookup(VAR_SMTPD_TLS_FPT_DGST) == 0) warn_compat_break_smtpd_tls_fpt_dgst = 1; if (mail_conf_lookup(VAR_SMTP_TLS_FPT_DGST) == 0) @@ -654,7 +662,7 @@ static void check_legacy_defaults(void) * Look for specific parameters whose default changed when the * compatibility level changed to 2. */ - if (var_compat_level < 2) { + if (compat_level < compat_level_from_string(COMPAT_LEVEL_2, msg_panic)) { if (mail_conf_lookup(VAR_RELAY_DOMAINS) == 0) { warn_compat_break_relay_domains = 1; if (mail_conf_lookup(VAR_FFLUSH_DOMAINS) == 0) @@ -673,7 +681,7 @@ static void check_legacy_defaults(void) * Look for specific parameters whose default changed when the * compatibility level changed from 0 to 1. */ - if (var_compat_level < 1) { + if (compat_level < compat_level_from_string(COMPAT_LEVEL_1, msg_panic)) { if (mail_conf_lookup(VAR_APP_DOT_MYDOMAIN) == 0) warn_compat_break_app_dot_mydomain = 1; @@ -703,8 +711,8 @@ static void check_legacy_defaults(void) void mail_params_init() { - static const CONFIG_INT_TABLE first_int_defaults[] = { - VAR_COMPAT_LEVEL, DEF_COMPAT_LEVEL, &var_compat_level, 0, 0, + static const CONFIG_STR_TABLE compat_level_defaults[] = { + VAR_COMPAT_LEVEL, DEF_COMPAT_LEVEL, &var_compatibility_level, 0, 0, 0, }; static const CONFIG_STR_TABLE first_str_defaults[] = { @@ -866,7 +874,9 @@ void mail_params_init() * Extract compatibility level first, so that we can determine what * parameters of interest are left at their legacy defaults. */ - get_mail_conf_int_table(first_int_defaults); + compat_level_relop_register(); + get_mail_conf_str_table(compat_level_defaults); + compat_level = compat_level_from_string(var_compatibility_level, msg_fatal); check_legacy_defaults(); /* diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index b8a2269dc..046252be9 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -47,13 +47,18 @@ extern bool var_show_unk_rcpt_table; /* * Compatibility level and migration support. Update postconf(5), - * COMPATIBILITY_README, and conf/main.cf when updating the current - * compatibility level. + * COMPATIBILITY_README, global/mail_params.[hc] and conf/main.cf when + * updating the current compatibility level. */ +#define COMPAT_LEVEL_0 "0" +#define COMPAT_LEVEL_1 "1" +#define COMPAT_LEVEL_2 "2" +#define COMPAT_LEVEL_3_6 "3.6" +#define LAST_COMPAT_LEVEL COMPAT_LEVEL_3_6 + #define VAR_COMPAT_LEVEL "compatibility_level" -#define DEF_COMPAT_LEVEL 0 -#define CUR_COMPAT_LEVEL 3 -extern int var_compat_level; +#define DEF_COMPAT_LEVEL COMPAT_LEVEL_0 +extern char *var_compatibility_level; extern int warn_compat_break_app_dot_mydomain; extern int warn_compat_break_smtputf8_enable; @@ -68,6 +73,8 @@ extern int warn_compat_break_smtpd_tls_fpt_dgst; extern int warn_compat_break_smtp_tls_fpt_dgst; extern int warn_compat_break_lmtp_tls_fpt_dgst; +extern long compat_level; + /* * What problem classes should be reported to the postmaster via email. * Default is bad problems only. See mail_error(3). Even when mail notices @@ -527,7 +534,7 @@ extern bool var_swap_bangpath; extern bool var_append_at_myorigin; #define VAR_APP_DOT_MYDOMAIN "append_dot_mydomain" -#define DEF_APP_DOT_MYDOMAIN "${{$compatibility_level} < {1} ? " \ +#define DEF_APP_DOT_MYDOMAIN "${{$compatibility_level} #include #include +#include /* Local stuff. */ @@ -490,7 +491,9 @@ MASTER_SERV *get_master_ent() * XXX Chroot cannot imply unprivileged service (for example, the pickup * service runs chrooted but needs privileges to open files as the user). */ - chroot = get_bool_ent(&bufp, "chroot", var_compat_level < 1 ? "y" : "n"); + chroot = get_bool_ent(&bufp, "chroot", compat_level + < compat_level_from_string(COMPAT_LEVEL_1, msg_panic) ? + "y" : "n"); /* * Wakeup timer. XXX should we require that var_proc_limit == 1? Right diff --git a/postfix/src/postalias/postalias.c b/postfix/src/postalias/postalias.c index c48751591..635338a71 100644 --- a/postfix/src/postalias/postalias.c +++ b/postfix/src/postalias/postalias.c @@ -126,13 +126,18 @@ /* The output consists of two files, named \fIfile_name\fB.pag\fR and /* \fIfile_name\fB.dir\fR. /* This is available on systems with support for \fBdbm\fR databases. -/* .IP \fBhash\fR -/* The output is a hashed file, named \fIfile_name\fB.db\fR. -/* This is available on systems with support for \fBdb\fR databases. /* .IP \fBfail\fR /* A table that reliably fails all requests. The lookup table /* name is used for logging only. This table exists to simplify /* Postfix error tests. +/* .IP \fBhash\fR +/* The output is a hashed file, named \fIfile_name\fB.db\fR. +/* This is available on systems with support for \fBdb\fR databases. +/* .IP \fBlmdb\fR +/* The output is a btree-based file, named \fIfile_name\fB.lmdb\fR. +/* \fBlmdb\fR supports concurrent writes and reads from different +/* processes, unlike other supported file-based tables. +/* This is available on systems with support for \fBlmdb\fR databases. /* .IP \fBsdbm\fR /* The output consists of two files, named \fIfile_name\fB.pag\fR and /* \fIfile_name\fB.dir\fR. @@ -196,6 +201,10 @@ /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR" /* A prefix that is prepended to the process name in syslog /* records, so that, for example, "smtpd" becomes "prefix/smtpd". +/* .PP +/* Available in Postfix 2.11 and later: +/* .IP "\fBlmdb_map_size (16777216)\fR" +/* The initial OpenLDAP LMDB database size limit in bytes. /* STANDARDS /* RFC 822 (ARPA Internet Text Messages) /* SEE ALSO diff --git a/postfix/src/postconf/Makefile.in b/postfix/src/postconf/Makefile.in index 33598bc02..83b4953ed 100644 --- a/postfix/src/postconf/Makefile.in +++ b/postfix/src/postconf/Makefile.in @@ -995,6 +995,7 @@ depend: $(MAKES) # do not edit below this line - it is generated by 'make depend' postconf.o: ../../include/argv.h postconf.o: ../../include/check_arg.h +postconf.o: ../../include/compat_level.h postconf.o: ../../include/dict.h postconf.o: ../../include/htable.h postconf.o: ../../include/mail_conf.h diff --git a/postfix/src/postconf/postconf.c b/postfix/src/postconf/postconf.c index 295e52f85..e52dbc1eb 100644 --- a/postfix/src/postconf/postconf.c +++ b/postfix/src/postconf/postconf.c @@ -608,6 +608,7 @@ #include #include #include +#include /* Application-specific. */ @@ -933,6 +934,11 @@ int main(int argc, char **argv) } } + /* + * For consistency with mail_params_init(). + */ + compat_level_relop_register(); + /* * We don't enforce import_environment consistency in this program. * diff --git a/postfix/src/postfix/Makefile.in b/postfix/src/postfix/Makefile.in index 56ff96b0b..dc3bf4309 100644 --- a/postfix/src/postfix/Makefile.in +++ b/postfix/src/postfix/Makefile.in @@ -65,6 +65,7 @@ depend: $(MAKES) postfix.o: ../../include/argv.h postfix.o: ../../include/check_arg.h postfix.o: ../../include/clean_env.h +postfix.o: ../../include/compat_level.h postfix.o: ../../include/mail_conf.h postfix.o: ../../include/mail_params.h postfix.o: ../../include/mail_parm_split.h diff --git a/postfix/src/postfix/postfix.c b/postfix/src/postfix/postfix.c index b2306fb60..4a63f4ed2 100644 --- a/postfix/src/postfix/postfix.c +++ b/postfix/src/postfix/postfix.c @@ -434,6 +434,7 @@ #include #include #include +#include /* Additional installation parameters. */ @@ -577,14 +578,14 @@ int main(int argc, char **argv) * Alert the sysadmin that the backwards-compatible settings are still in * effect. */ - if (var_compat_level < CUR_COMPAT_LEVEL) { + if (compat_level < compat_level_from_string(LAST_COMPAT_LEVEL, msg_panic)) { msg_info("Postfix is running with backwards-compatible default " "settings"); msg_info("See http://www.postfix.org/COMPATIBILITY_README.html " "for details"); msg_info("To disable backwards compatibility use \"postconf " - VAR_COMPAT_LEVEL "=%d\" and \"postfix reload\"", - CUR_COMPAT_LEVEL); + VAR_COMPAT_LEVEL "=%s\" and \"postfix reload\"", + LAST_COMPAT_LEVEL); } check_setenv("PATH", ROOT_PATH); /* sys_defs.h */ check_setenv(CONF_ENV_PATH, var_config_dir);/* mail_conf.h */ diff --git a/postfix/src/postmap/postmap.c b/postfix/src/postmap/postmap.c index 247242e44..6d4f2413e 100644 --- a/postfix/src/postmap/postmap.c +++ b/postfix/src/postmap/postmap.c @@ -210,13 +210,18 @@ /* The output consists of two files, named \fIfile_name\fB.pag\fR and /* \fIfile_name\fB.dir\fR. /* This is available on systems with support for \fBdbm\fR databases. -/* .IP \fBhash\fR -/* The output file is a hashed file, named \fIfile_name\fB.db\fR. -/* This is available on systems with support for \fBdb\fR databases. /* .IP \fBfail\fR /* A table that reliably fails all requests. The lookup table /* name is used for logging only. This table exists to simplify /* Postfix error tests. +/* .IP \fBhash\fR +/* The output file is a hashed file, named \fIfile_name\fB.db\fR. +/* This is available on systems with support for \fBdb\fR databases. +/* .IP \fBlmdb\fR +/* The output is a btree-based file, named \fIfile_name\fB.lmdb\fR. +/* \fBlmdb\fR supports concurrent writes and reads from different +/* processes, unlike other supported file-based tables. +/* This is available on systems with support for \fBlmdb\fR databases. /* .IP \fBsdbm\fR /* The output consists of two files, named \fIfile_name\fB.pag\fR and /* \fIfile_name\fB.dir\fR. @@ -275,6 +280,10 @@ /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR" /* A prefix that is prepended to the process name in syslog /* records, so that, for example, "smtpd" becomes "prefix/smtpd". +/* .PP +/* Available in Postfix 2.11 and later: +/* .IP "\fBlmdb_map_size (16777216)\fR" +/* The initial OpenLDAP LMDB database size limit in bytes. /* SEE ALSO /* postalias(1), create/update/query alias database /* postconf(1), supported database types diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index 6eb41bbd1..1649c475a 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -1955,6 +1955,8 @@ load_file.o: vbuf.h load_file.o: vstream.h load_file.o: warn_stat.h load_lib.o: load_lib.c +load_lib.o: load_lib.h +load_lib.o: msg.h load_lib.o: sys_defs.h logwriter.o: check_arg.h logwriter.o: iostuff.h @@ -1980,6 +1982,7 @@ lstat_as.o: set_eugid.h lstat_as.o: sys_defs.h lstat_as.o: warn_stat.h mac_expand.o: check_arg.h +mac_expand.o: htable.h mac_expand.o: mac_expand.c mac_expand.o: mac_expand.h mac_expand.o: mac_parse.h diff --git a/postfix/src/util/alldig.c b/postfix/src/util/alldig.c index c4815f68c..cabe5c37b 100644 --- a/postfix/src/util/alldig.c +++ b/postfix/src/util/alldig.c @@ -8,8 +8,14 @@ /* /* int alldig(string) /* const char *string; +/* +/* int allalnum(string) +/* const char *string; /* DESCRIPTION /* alldig() determines if its argument is an all-numerical string. +/* +/* allalnum() determines if its argument is an all-alphanumerical +/* string. /* SEE ALSO /* An alldig() routine appears in Brian W. Kernighan, P.J. Plauger: /* "Software Tools", Addison-Wesley 1976. @@ -22,6 +28,11 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA /*--*/ /* System library. */ @@ -46,3 +57,17 @@ int alldig(const char *string) return (0); return (1); } + +/* allalnum - return true if string is all alphanum */ + +int allalnum(const char *string) +{ + const char *cp; + + if (*string == 0) + return (0); + for (cp = string; *cp != 0; cp++) + if (!ISALNUM(*cp)) + return (0); + return (1); +} diff --git a/postfix/src/util/mac_expand.c b/postfix/src/util/mac_expand.c index 8ba0071bc..03dc2d8da 100644 --- a/postfix/src/util/mac_expand.c +++ b/postfix/src/util/mac_expand.c @@ -13,6 +13,18 @@ /* const char *filter; /* const char *lookup(const char *key, int mode, void *context) /* void *context; +/* AUXILIARY FUNCTIONS +/* typedef MAC_EXP_OP_RES (*MAC_EXPAND_RELOP_FN) ( +/* const char *left, +/* int tok_val, +/* const char *rite) +/* +/* void mac_expand_add_relop( +/* int *tok_list, +/* const char *suffix, +/* MAC_EXPAND_RELOP_FN relop_eval) +/* +/* MAC_EXP_OP_RES mac_exp_op_res_bool[2]; /* DESCRIPTION /* This module implements parameter-less named attribute /* expansions, both conditional and unconditional. As of Postfix @@ -98,6 +110,29 @@ /* result value means that the requested attribute was not defined. /* .IP context /* Caller context that is passed on to the attribute lookup routine. +/* .PP +/* mac_expand_add_relop() registers a function that implements +/* support for custom relational operators. Custom operator names +/* such as "==xxx" have two parts: a prefix that is identical to +/* a built-in operator such as "==", and an application-specified +/* suffix such as "xxx". +/* +/* Arguments: +/* .IP tok_list +/* A null-terminated list of MAC_EXP_OP_TOK_* values that support +/* the custom operator suffix. +/* .IP suffix +/* A null-terminated alphanumeric string that specifies the custom +/* operator suffix. +/* .IP relop_eval +/* A function that compares two strings according to the +/* MAC_EXP_OP_TOK_* value specified with the tok_val argument, +/* and that returns non-zero if the custom operator evaluates to +/* true, zero otherwise. +/* +/* mac_exp_op_res_bool provides an array that converts a boolean +/* value (0 or 1) to the corresponding MAX_EXP_OP_RES_TRUE or +/* MAX_EXP_OP_RES_FALSE value. /* DIAGNOSTICS /* Fatal errors: out of memory. Warnings: syntax errors, unreasonable /* recursion depth. @@ -137,6 +172,7 @@ /* Utility library. */ #include +#include #include #include #include @@ -144,6 +180,14 @@ #include #include + /* + * Simplifies the return of common relational operator results. + */ +MAC_EXP_OP_RES mac_exp_op_res_bool[2] = { + MAC_EXP_OP_RES_FALSE, + MAC_EXP_OP_RES_TRUE +}; + /* * Little helper structure. */ @@ -180,7 +224,8 @@ typedef struct { #define MAC_EXP_BVAL_FALSE "" /* - * Relational operators. + * Relational operators. The MAC_EXP_OP_TOK_* are defined in the header + * file. */ #define MAC_EXP_OP_STR_EQ "==" #define MAC_EXP_OP_STR_NE "!=" @@ -195,14 +240,6 @@ typedef struct { "\" or \"" MAC_EXP_OP_STR_GE "\"" \ "\" or \"" MAC_EXP_OP_STR_GT "\"" -#define MAC_EXP_OP_TOK_NONE 0 -#define MAC_EXP_OP_TOK_EQ 1 -#define MAC_EXP_OP_TOK_NE 2 -#define MAC_EXP_OP_TOK_LT 3 -#define MAC_EXP_OP_TOK_LE 4 -#define MAC_EXP_OP_TOK_GE 5 -#define MAC_EXP_OP_TOK_GT 6 - static const NAME_CODE mac_exp_op_table[] = { MAC_EXP_OP_STR_EQ, MAC_EXP_OP_TOK_EQ, @@ -219,6 +256,17 @@ static const NAME_CODE mac_exp_op_table[] = */ #define MAC_EXP_WHITESPACE CHARS_SPACE + /* + * Support for operator extensions. + */ +static HTABLE *mac_exp_ext_table; +static VSTRING *mac_exp_ext_key; + + /* + * SLMs. + */ +#define STR(x) vstring_str(x) + /* atol_or_die - convert or die */ static long atol_or_die(const char *strval) @@ -234,8 +282,8 @@ static long atol_or_die(const char *strval) /* mac_exp_eval - evaluate binary expression */ -static int mac_exp_eval(const char *left, int tok_val, - const char *rite) +static MAC_EXP_OP_RES mac_exp_eval(const char *left, int tok_val, + const char *rite) { static const char myname[] = "mac_exp_eval"; long delta; @@ -250,17 +298,17 @@ static int mac_exp_eval(const char *left, int tok_val, } switch (tok_val) { case MAC_EXP_OP_TOK_EQ: - return (delta == 0); + return (mac_exp_op_res_bool[delta == 0]); case MAC_EXP_OP_TOK_NE: - return (delta != 0); + return (mac_exp_op_res_bool[delta != 0]); case MAC_EXP_OP_TOK_LT: - return (delta < 0); + return (mac_exp_op_res_bool[delta < 0]); case MAC_EXP_OP_TOK_LE: - return (delta <= 0); + return (mac_exp_op_res_bool[delta <= 0]); case MAC_EXP_OP_TOK_GE: - return (delta >= 0); + return (mac_exp_op_res_bool[delta >= 0]); case MAC_EXP_OP_TOK_GT: - return (delta > 0); + return (mac_exp_op_res_bool[delta > 0]); default: msg_panic("%s: unknown operator: %d", myname, tok_val); @@ -360,6 +408,9 @@ static int mac_exp_parse_relational(MAC_EXP_CONTEXT *mc, const char **lookup, int op_tokval; int op_result; size_t tmp_len; + char *type_pos; + size_t type_len; + MAC_EXPAND_RELOP_FN relop_eval; /* * Left operand. The caller is expected to skip leading whitespace before @@ -381,6 +432,24 @@ static int mac_exp_parse_relational(MAC_EXP_CONTEXT *mc, const char **lookup, MAC_EXP_OP_STR_ANY, left_op_strval, cp); cp += op_len; + /* + * Custom operator suffix. + */ + if (mac_exp_ext_table && ISALNUM(*cp)) { + type_pos = cp; + for (type_len = 1; ISALNUM(cp[type_len]); type_len++) + /* void */ ; + cp += type_len; + vstring_sprintf(mac_exp_ext_key, "%.*s", + (int) (op_len + type_len), op_pos); + if ((relop_eval = (MAC_EXPAND_RELOP_FN) htable_find(mac_exp_ext_table, + STR(mac_exp_ext_key))) == 0) + MAC_EXP_ERR_RETURN(mc, "bad operator suffix at: \"...%.*s>>>%.*s\"", + (int) op_len, op_pos, (int) type_len, type_pos); + } else { + relop_eval = mac_exp_eval; + } + /* * Right operand. Todo: syntax may depend on operator. */ @@ -400,8 +469,10 @@ static int mac_exp_parse_relational(MAC_EXP_CONTEXT *mc, const char **lookup, mc->status |= mac_expand(rite_op_buf = vstring_alloc(100), rite_op_strval, mc->flags, mc->filter, mc->lookup, mc->context); - op_result = mac_exp_eval(vstring_str(left_op_buf), op_tokval, - vstring_str(rite_op_buf)); + if ((mc->flags & MAC_EXP_FLAG_SCAN) == 0 + && (op_result = relop_eval(vstring_str(left_op_buf), op_tokval, + vstring_str(rite_op_buf))) == MAC_EXP_OP_RES_ERROR) + mc->status |= MAC_PARSE_ERROR; vstring_free(left_op_buf); vstring_free(rite_op_buf); if (mc->status & MAC_PARSE_ERROR) @@ -412,11 +483,57 @@ static int mac_exp_parse_relational(MAC_EXP_CONTEXT *mc, const char **lookup, * for compatibility with the historical code that looks named parameter * values. */ - *lookup = (op_result ? MAC_EXP_BVAL_TRUE : MAC_EXP_BVAL_FALSE); + if (mc->flags & MAC_EXP_FLAG_SCAN) { + *lookup = 0; + } else { + switch (op_result) { + case MAC_EXP_OP_RES_TRUE: + *lookup = MAC_EXP_BVAL_TRUE; + break; + case MAC_EXP_OP_RES_FALSE: + *lookup = MAC_EXP_BVAL_FALSE; + break; + default: + msg_panic("mac_expand: unexpected operator result: %d", op_result); + } + } *bp = cp; return (0); } +/* mac_expand_add_relop - register operator extensions */ + +void mac_expand_add_relop(int *tok_list, const char *suffix, + MAC_EXPAND_RELOP_FN relop_eval) +{ + const char myname[] = "mac_expand_add_relop"; + const char *tok_name; + int *tp; + + /* + * Sanity checks. + */ + if (!allalnum(suffix)) + msg_panic("%s: bad operator suffix: %s", myname, suffix); + + /* + * One-time initialization. + */ + if (mac_exp_ext_table == 0) { + mac_exp_ext_table = htable_create(10); + mac_exp_ext_key = vstring_alloc(10); + } + for (tp = tok_list; *tp; tp++) { + if ((tok_name = str_name_code(mac_exp_op_table, *tp)) == 0) + msg_panic("%s: unknown token code: %d", myname, *tp); + vstring_sprintf(mac_exp_ext_key, "%s%s", tok_name, suffix); + if (htable_locate(mac_exp_ext_table, STR(mac_exp_ext_key)) != 0) + msg_panic("%s: duplicate key: %s", myname, STR(mac_exp_ext_key)); + (void) htable_enter(mac_exp_ext_table, + STR(mac_exp_ext_key), (void *) relop_eval); + } +} + /* mac_expand_callback - callback for mac_parse */ static int mac_expand_callback(int type, VSTRING *buf, void *ptr) @@ -621,7 +738,6 @@ int mac_expand(VSTRING *result, const char *pattern, int flags, /* * This code certainly deserves a stand-alone test program. */ -#include #include #include #include @@ -634,7 +750,32 @@ static const char *lookup(const char *name, int unused_mode, void *context) return (htable_find(table, name)); } -int main(int unused_argc, char **unused_argv) +static MAC_EXP_OP_RES length_relop_eval(const char *left, int relop, + const char *rite) +{ + const char myname[] = "length_relop_eval"; + ssize_t delta = strlen(left) - strlen(rite); + + switch (relop) { + case MAC_EXP_OP_TOK_EQ: + return (mac_exp_op_res_bool[delta == 0]); + case MAC_EXP_OP_TOK_NE: + return (mac_exp_op_res_bool[delta != 0]); + case MAC_EXP_OP_TOK_LT: + return (mac_exp_op_res_bool[delta < 0]); + case MAC_EXP_OP_TOK_LE: + return (mac_exp_op_res_bool[delta <= 0]); + case MAC_EXP_OP_TOK_GE: + return (mac_exp_op_res_bool[delta >= 0]); + case MAC_EXP_OP_TOK_GT: + return (mac_exp_op_res_bool[delta > 0]); + default: + msg_panic("%s: unknown operator: %d", + myname, relop); + } +} + +int main(int unused_argc, char **argv) { VSTRING *buf = vstring_alloc(100); VSTRING *result = vstring_alloc(100); @@ -643,7 +784,21 @@ int main(int unused_argc, char **unused_argv) char *value; HTABLE *table; int stat; + int length_relops[] = { + MAC_EXP_OP_TOK_EQ, MAC_EXP_OP_TOK_NE, + MAC_EXP_OP_TOK_GT, MAC_EXP_OP_TOK_GE, + MAC_EXP_OP_TOK_LT, MAC_EXP_OP_TOK_LE, + 0, + }; + /* + * Add relops that compare string lengths instead of content. + */ + mac_expand_add_relop(length_relops, "length", length_relop_eval); + + /* + * Loop over the inputs. + */ while (!vstream_feof(VSTREAM_IN)) { table = htable_create(0); diff --git a/postfix/src/util/mac_expand.h b/postfix/src/util/mac_expand.h index e37d6ea95..4a2398218 100644 --- a/postfix/src/util/mac_expand.h +++ b/postfix/src/util/mac_expand.h @@ -26,6 +26,30 @@ #define MAC_EXP_FLAG_SCAN (1<<2) #define MAC_EXP_FLAG_PRINTABLE (1<<3) + /* + * Token codes, public so tha they are available to mac_expand_add_relop() + */ +#define MAC_EXP_OP_TOK_NONE 0 /* Sentinel */ +#define MAC_EXP_OP_TOK_EQ 1 /* == */ +#define MAC_EXP_OP_TOK_NE 2 /* != */ +#define MAC_EXP_OP_TOK_LT 3 /* < */ +#define MAC_EXP_OP_TOK_LE 4 /* <= */ +#define MAC_EXP_OP_TOK_GE 5 /* >= */ +#define MAC_EXP_OP_TOK_GT 6 /* > */ + + /* + * Relational operator results. An enum to discourage asuming that 0 is + * false, !0 is true. + */ +typedef enum MAC_EXP_OP_RES { + MAC_EXP_OP_RES_TRUE, + MAC_EXP_OP_RES_FALSE, + MAC_EXP_OP_RES_ERROR, +} MAC_EXP_OP_RES; + + +extern MAC_EXP_OP_RES mac_exp_op_res_bool[2]; + /* * Real lookup or just a test? */ @@ -33,8 +57,10 @@ #define MAC_EXP_MODE_USE (1) typedef const char *(*MAC_EXP_LOOKUP_FN) (const char *, int, void *); +typedef MAC_EXP_OP_RES (*MAC_EXPAND_RELOP_FN) (const char *, int, const char *); extern int mac_expand(VSTRING *, const char *, int, const char *, MAC_EXP_LOOKUP_FN, void *); +void mac_expand_add_relop(int *, const char *, MAC_EXPAND_RELOP_FN); /* LICENSE /* .ad diff --git a/postfix/src/util/mac_expand.in b/postfix/src/util/mac_expand.in index dbc583c0f..1d6190654 100644 --- a/postfix/src/util/mac_expand.in +++ b/postfix/src/util/mac_expand.in @@ -85,3 +85,21 @@ ${{a} < {b}} ${{a} <= {b}} ${{a} >= {b}} ${{a} > {b}} + +name1 = foo + +${{$name1} >=blah {bar}} +${{aaa} == {bbb}} +${{aaa} ==length {bbb}} +${{aaa} <=length {bbb}} +${{aaa} >=length {bbb}} +${{aaa} != {bbb}} +${{aaa} !=length {bbb}} +${{aaa} > {bb}} +${{aaa} >length {bb}} +${{aaa} >= {bb}} +${{aaa} >=length {bb}} +${{aaa} < {bb}} +${{aaa} {b}} stat=0 result= +<< + +<< name1 = foo +<< +<< ${{$name1} >=blah {bar}} +unknown: warning: bad operator suffix at: "...>=>>>blah" +stat=1 result= +<< ${{aaa} == {bbb}} +stat=0 result= +<< ${{aaa} ==length {bbb}} +stat=0 result=true +<< ${{aaa} <=length {bbb}} +stat=0 result=true +<< ${{aaa} >=length {bbb}} +stat=0 result=true +<< ${{aaa} != {bbb}} +stat=0 result=true +<< ${{aaa} !=length {bbb}} +stat=0 result= +<< ${{aaa} > {bb}} +stat=0 result= +<< ${{aaa} >length {bb}} +stat=0 result=true +<< ${{aaa} >= {bb}} +stat=0 result= +<< ${{aaa} >=length {bb}} +stat=0 result=true +<< ${{aaa} < {bb}} +stat=0 result=true +<< ${{aaa}