was comparing memory addresses instead of queue file names.
It now properly compares strings. Reported by Mehmet Avcioglu.
File: global/record.c.
+
+20210811
+
+ Bitrot: OpenSSL 3.x requires const. File: tls/tls_misc.c.
+
+20210925
+
+ Bugfix (bug introduced: Postfix 2.10): postconf -x produced
+ incorrect output, because different functions were implicitly
+ sharing a buffer for intermediate results. Reported
+ by raf, root cause analysis by Viktor Dukhovni. File:
+ postconf/postconf_builtin.c.
+
+20211022
+
+ Bugfix (introduced: Postfix 3.6): the known_tcp_ports setting
+ had no effect. Reported by Peter. The feature wasn't fully
+ implemented. Files: config_known_tcp_ports.c, mail_params.c,
+ posttls-finger/posttls-finger.c, smtp/smtp_connect.c,
+ util/find_inet.c, util/myaddrinfo.c.
+
+20211025
+
+ Bugfix (introduced: Postfix 3.6): mangled warning where a
+ hostname and warning message run together. Viktor Dukhovni.
+ File: tls/tls_dane.c.
+
+20211030
+
+ Bugfix (problem introduced: Postfix 2.11): check_ccert_access
+ worked as expected, but produced a spurious warning when
+ Postfix was built without SASL support. Fix by Brad Barden.
+ File: smtpd/smtpd_check.c.
+
+20211105
+
+ Bugfix (introduced: Postfix 2.4): queue file corruption
+ after a Milter (for example, MIMEDefang) made a request to
+ replace the message body with a copy of that message body
+ plus additional text (for example, a SpamAssassin report).
+
+ The most likely impacts were a) the queue manager reporting
+ a fatal error resulting in email delivery delays, or b) the
+ queue manager reporting the corruption and moving the message
+ to the corrupt queue for damaged messages.
+
+ However, a determined adversary could craft an email message
+ that would trigger the bug, and insert a content filter
+ destination or a redirect email address into its queue file.
+ Postfix would then deliver the message headers there, in
+ most cases without delivering the message body. With enough
+ experimentation, an attacker could make Postfix deliver
+ both the message headers and body.
+
+ The details of a successful attack depend on the Milter
+ implementation, and on the Postfix and Milter configuration
+ details; these can be determined remotely through
+ experimentation. Failed experiments may be detected when
+ the queue manager terminates with a fatal error, or when
+ the queue manager moves damaged files to the "corrupt" queue
+ as evidence.
+
+ Technical details: when Postfix executes a "replace body"
+ Milter request it will reuse queue file storage that was
+ used by the existing email message body. If the new body
+ is larger, Postfix will append body content to the end of
+ the queue file. The corruption happened when a Milter (for
+ example, MIMEDefang) made a request to replace the body of
+ a message with a new body that contained a copy of the
+ original body plus some new text, and the original body
+ contained a line longer than $line_length_limit bytes (for
+ example, an image encoded in base64 without hard or soft
+ line breaks). In queue files, Postfix stores a long text
+ line as multiple records with up to $line_length_limit bytes
+ each. Unfortunately, Postfix's "replace body" support did
+ not account for the additional queue file space needed to
+ store the second etc. record headers. And thus, the last
+ record(s) of a long text line could overwrite one or more
+ queue file records immediately after the space that was
+ previously occupied by the original message body.
+
+ Problem report by BenoƮt Panizzon.
/*
* Finally, output the queue file record.
*/
- CLEANUP_OUT_BUF(state, REC_TYPE_NORM, buf);
+ CLEANUP_OUT_BUF(state, rec_type, buf);
curr_rp->write_offs = vstream_ftell(state->dst);
return (0);
/* cleanup_repl_body - replace message body */
-static const char *cleanup_repl_body(void *context, int cmd, VSTRING *buf)
+static const char *cleanup_repl_body(void *context, int cmd, int rec_type,
+ VSTRING *buf)
{
const char *myname = "cleanup_repl_body";
CLEANUP_STATE *state = (CLEANUP_STATE *) context;
*/
switch (cmd) {
case MILTER_BODY_LINE:
- if (cleanup_body_edit_write(state, REC_TYPE_NORM, buf) < 0)
+ if (cleanup_body_edit_write(state, rec_type, buf) < 0)
return (cleanup_milter_error(state, errno));
break;
case MILTER_BODY_START:
ARGV *association;
char **cpp;
+ clear_known_tcp_ports();
+
/*
* The settings is in the form of associations separated by comma. Split
* it into separate associations.
#include <own_inet_addr.h>
#include <mail_params.h>
#include <compat_level.h>
+#include <config_known_tcp_ports.h>
/*
* Special configuration variables.
#endif
util_utf8_enable = var_smtputf8_enable;
+ /*
+ * Configure the known TCP port mappings.
+ */
+ config_known_tcp_ports(VAR_KNOWN_TCP_PORTS, var_known_tcp_ports);
+
/*
* What protocols should we attempt to support? The result is stored in
* the global inet_proto_table variable.
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20210724"
-#define MAIL_VERSION_NUMBER "3.6.2"
+#define MAIL_RELEASE_DATE "20211107"
+#define MAIL_VERSION_NUMBER "3.6.3"
#ifdef SNAPSHOT
#define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE
typedef const char *(*MILTER_EDIT_FROM_FN) (void *, const char *, const char *);
typedef const char *(*MILTER_EDIT_RCPT_FN) (void *, const char *);
typedef const char *(*MILTER_EDIT_RCPT_PAR_FN) (void *, const char *, const char *);
-typedef const char *(*MILTER_EDIT_BODY_FN) (void *, int, VSTRING *);
+typedef const char *(*MILTER_EDIT_BODY_FN) (void *, int, int, VSTRING *);
typedef struct MILTERS {
MILTER *milter_list; /* linked list of Milters */
if (edit_resp == 0 && LEN(body_line_buf) > 0)
edit_resp = parent->repl_body(parent->chg_context,
MILTER_BODY_LINE,
+ REC_TYPE_NORM,
body_line_buf);
if (edit_resp == 0)
edit_resp = parent->repl_body(parent->chg_context,
MILTER_BODY_END,
+ /* unused*/ 0,
(VSTRING *) 0);
body_edit_lockout = 1;
vstring_free(body_line_buf);
body_line_buf = vstring_alloc(var_line_limit);
edit_resp = parent->repl_body(parent->chg_context,
MILTER_BODY_START,
+ /* unused */ 0,
(VSTRING *) 0);
}
/* Extract lines from the on-the-wire CRLF format. */
LEN(body_line_buf) - 1);
edit_resp = parent->repl_body(parent->chg_context,
MILTER_BODY_LINE,
+ REC_TYPE_NORM,
body_line_buf);
VSTRING_RESET(body_line_buf);
} else {
+ /* Preserves \r if not followed by \n. */
+ if (LEN(body_line_buf) == var_line_limit) {
+ edit_resp = parent->repl_body(parent->chg_context,
+ MILTER_BODY_LINE,
+ REC_TYPE_CONT,
+ body_line_buf);
+ VSTRING_RESET(body_line_buf);
+ }
VSTRING_ADDCH(body_line_buf, ch);
}
}
static const char *pcf_mynetworks(void)
{
static const char *networks;
+ VSTRING *exp_buf;
const char *junk;
/*
if (networks)
return (networks);
+ exp_buf = vstring_alloc(100);
+
if (var_inet_interfaces == 0) {
if ((pcf_cmd_mode & PCF_SHOW_DEFS)
|| (junk = mail_conf_lookup_eval(VAR_INET_INTERFACES)) == 0)
- junk = pcf_expand_parameter_value((VSTRING *) 0, pcf_cmd_mode,
+ junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode,
DEF_INET_INTERFACES,
(PCF_MASTER_ENT *) 0);
var_inet_interfaces = mystrdup(junk);
if (var_mynetworks_style == 0) {
if ((pcf_cmd_mode & PCF_SHOW_DEFS)
|| (junk = mail_conf_lookup_eval(VAR_MYNETWORKS_STYLE)) == 0)
- junk = pcf_expand_parameter_value((VSTRING *) 0, pcf_cmd_mode,
+ junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode,
DEF_MYNETWORKS_STYLE,
(PCF_MASTER_ENT *) 0);
var_mynetworks_style = mystrdup(junk);
if (var_inet_protocols == 0) {
if ((pcf_cmd_mode & PCF_SHOW_DEFS)
|| (junk = mail_conf_lookup_eval(VAR_INET_PROTOCOLS)) == 0)
- junk = pcf_expand_parameter_value((VSTRING *) 0, pcf_cmd_mode,
+ junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode,
DEF_INET_PROTOCOLS,
(PCF_MASTER_ENT *) 0);
var_inet_protocols = mystrdup(junk);
(void) inet_proto_init(VAR_INET_PROTOCOLS, var_inet_protocols);
}
+ vstring_free(exp_buf);
return (networks = mystrdup(mynetworks()));
}
/*
* Convert service to port number, network byte order.
*/
+ service = (char *) filter_known_tcp_port(service);
if (alldig(service)) {
if ((port = atoi(service)) >= 65536 || port == 0)
- msg_fatal("bad network port in destination: %s", destination);
+ msg_fatal("bad network port: %s for destination: %s",
+ service, destination);
*portp = htons(port);
} else {
- if ((sp = getservbyname(filter_known_tcp_port(service), protocol)) != 0)
+ if ((sp = getservbyname(service, protocol)) != 0)
*portp = sp->s_port;
else if (strcmp(service, "smtp") == 0)
*portp = htons(25);
/*
* Convert service to port number, network byte order.
*/
+ service = (char *) filter_known_tcp_port(service);
if (alldig(service)) {
if ((port = atoi(service)) >= 65536 || port == 0)
- msg_fatal("bad network port in destination: %s", destination);
+ msg_fatal("bad network port: %s for destination: %s",
+ service, destination);
*portp = htons(port);
} else {
- if ((sp = getservbyname(filter_known_tcp_port(service), protocol)) == 0)
+ if ((sp = getservbyname(service, protocol)) == 0)
msg_fatal("unknown service: %s/%s", service, protocol);
*portp = sp->s_port;
}
}
} else if (is_map_command(state, name, CHECK_CCERT_ACL, &cpp)) {
status = check_ccert_access(state, *cpp, def_acl);
-#ifdef USE_SASL_AUTH
} else if (is_map_command(state, name, CHECK_SASL_ACL, &cpp)) {
+#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable) {
if (state->sasl_username && state->sasl_username[0])
status = check_sasl_access(state, *cpp, def_acl);
vstring_sprintf(top, "...");
}
- msg_warn("%s%s%s%s: %u %u %u %s%s%s", s1, s2, s3, s4, u, s, m, STR(top),
+ msg_warn("%s%s%s %s: %u %u %u %s%s%s", s1, s2, s3, s4, u, s, m, STR(top),
dlen > MAX_DUMP_BYTES ? "..." : "",
dlen > MAX_DUMP_BYTES ? STR(bot) : "");
}
continue;
}
if (ret == 0) {
- tlsa_carp(TLScontext->namaddr, ": ", "", "unusable TLSA RR",
+ tlsa_carp(TLScontext->namaddr, ":", "", "unusable TLSA RR",
tp->usage, tp->selector, tp->mtype, tp->data,
tp->length);
continue;
}
/* Internal problem in OpenSSL */
- tlsa_carp(TLScontext->namaddr, ": ", "", "error loading trust settings",
+ tlsa_carp(TLScontext->namaddr, ":", "", "error loading trust settings",
tp->usage, tp->selector, tp->mtype, tp->data, tp->length);
tls_print_errors();
return (-1);
EVP_PKEY *peer_pkey = 0;
#ifndef OPENSSL_NO_EC
- EC_KEY *eckey;
+ const EC_KEY *eckey;
#endif
struct servent *sp;
int port;
+ service = filter_known_tcp_port(service);
if (alldig(service) && (port = atoi(service)) != 0) {
if (port < 0 || port > 65535)
msg_fatal("bad port number: %s", service);
return (htons(port));
} else {
- if ((sp = getservbyname(filter_known_tcp_port(service), protocol)) == 0)
+ if ((sp = getservbyname(service, protocol)) == 0)
msg_fatal("unknown service: %s/%s", service, protocol);
return (sp->s_port);
}
const char *proto;
unsigned port;
+ service = filter_known_tcp_port(service);
if (alldig(service)) {
port = atoi(service);
return (port < 65536 ? htons(port) : -1);
} else {
return (-1);
}
- if ((sp = getservbyname(filter_known_tcp_port(service), proto)) != 0) {
+ if ((sp = getservbyname(service, proto)) != 0) {
return (sp->s_port);
} else {
return (-1);
}
#endif
}
- err = getaddrinfo(hostname, filter_known_tcp_port(service), &hints, res);
+ if (service) {
+ service = filter_known_tcp_port(service);
+ if (alldig(service))
+ hints.ai_flags |= AI_NUMERICSERV;
+ }
+ err = getaddrinfo(hostname, service, &hints, res);
#if defined(BROKEN_AI_NULL_SERVICE)
if (service == 0 && err == 0) {
struct addrinfo *r;
}
#endif
}
- err = getaddrinfo(hostaddr, filter_known_tcp_port(service), &hints, res);
+ if (service) {
+ service = filter_known_tcp_port(service);
+ if (alldig(service))
+ hints.ai_flags |= AI_NUMERICSERV;
+ }
+ err = getaddrinfo(hostaddr, service, &hints, res);
#if defined(BROKEN_AI_NULL_SERVICE)
if (service == 0 && err == 0) {
struct addrinfo *r;