with a pathname that was already wiped and free()d, but not
yet reused. Reported by Qualys, assisted by Claude Mythos
Preview. File: postdrop.c.
+
+20260806
+
+ Bug (defect introduced: Postfix 2.10, date: 20120617):
+ uninitialized memory read in postscreen HaProxy client after
+ remote I/O exception, causing garbage to be logged. Reported
+ by Qualys, assisted by Claude Mythos Preview. File:
+ postscreen_haproxy.c.
+
+ Latent bug (defect introduced: Postfix 2.7, date: 20090618):
+ uninitialized memory read after dnsblog(8) returns a string
+ that is not an IPv4 address. Reported by Qualys, assisted
+ by Claude Mythos Preview. File: postscreen_dnsbl.c.
+
+ Bug (defect introduced: Postfix 2.8, date: 20100914):
+ read-after-free in the PSC_CALL_BACK_NOTIFY() macro. This
+ had no effect on program execution, because myfree() wiped
+ memory, and that memory was not yet reused. Problem reported
+ by Qualys, assisted by Claude Mythos Preview. File:
+ postscreen_dnsbl.c.
+
+ Bug (defect introduced: before Postfix alpha, date 19970424):
+ the DNS client could read up to two bytes past the end of
+ an MX record, before discovering that the record was too
+ short. This behavior was later copied with SRV records,
+ potentially over-reading up to six bytes. Problem reported
+ by Qualys, assisted by Claude Mythos Preview. File:
+ dns_lookup.c.
+
+ Bug (defect introduced: before Postfix alpha, date: 19971106):
+ 'int' over-shift, in the queue file record-length parser.
+ Postfix programs do not generate such records, but an
+ attacker could cause postdrop to reject input or panic().
+ Reported by Qualys, assisted by Claude Mythos Preview. File:
+ record.c.
+
+ Bug (defect introduced: Postfix 2.2, date: 20050117):
+ non-transitive comparison of IPv4 addresses. Reported by
+ Qualys, assisted by Claude Mythos Preview. File: sock_addr.c.
+
+ Read after free (no privilege escalation) in debug logging
+ (defect introduced: Postfix 2.2, date: 20050117). Reported
+ by Qualys, assisted by Claude Mythos Preview. File:
+ util/inet_connect.c.
+
+ Bug (defect introduced: Postfix 2.4, date: 20051222): null
+ pointer read crash while parsing a malformed Dovecot AUTH
+ server response. Reported by Qualys, assisted by Claude
+ Mythos Preview. File: xsasl_dovecot_server.c.
+
+ Bug (defect introduced: Postfix 1.0, date: 20000928): the
+ fast flush server, used by the SMTP command "ETRN", and by
+ the commands "postqueue -s site" and "postqueue -i queue_id"
+ (and their sendmail(1) equivalents), used the wrong duplicate
+ suppression API, resulting in too many queue scans by the
+ queue manager. Reported by Qualys, assisted by Claude Mythos
+ Preview. File: flush.c.
+
+ Bug (defect introduced: Postfix 1,1, date: 20010524): the
+ postsuper command under-read a very short queue filename.
+ No crash, information leak, or privilege escalation. Reported
+ by Qualys, assisted by Claude Mythos Preview. Files:
+ postsuper.c, mail_queue.h.
+
+ Bug (defect introduced: Postfix 3.4, date: 20180805): missing
+ SMTP server reset of RCPT TO state after a BDAT command
+ error. A crafted remote SMTP client could then send a DATA
+ command without MAIL FROM or RCPT TO, and crash a Postfix
+ SMTP daemon process with a null pointer read error. Reported
+ by OpenAI Security. File: smtpd/smtpd.c.
+
+ Bug (introduced: Postfix 2.2, date: 20041102): missing SMTP
+ server resets of MAIL FROM and RCPT TO command state after
+ smtpd_end_of_data_restrictions rejected a message. This
+ resulted in SMTP protocol state desynchronization between
+ the remote SMTP client and the Postfix SMTP server.
+
+ A crafted remote SMTP client could then send RCPT TO and
+ DATA without MAIL FROM, and deliver a second message. Then,
+ smtpd_end_of_data_restrictions skipped check_recipient_access
+ constraints, because a recipient counter was > 1. Reported
+ by OpenAI Security. File: smtpd/smtpd.c.
+
+ As reported by OpenAI Security, the failure to reset MAIL
+ FROM and RCPT TO state also affected Milter support (added
+ in Postfix 2.3). Here, after a Milter replied with "accept
+ this message" based on the message envelope, and
+ smtpd_end_of_data_restrictions rejected the message, the
+ Postfix SMTP server as before accepted RCPT TO and DATA
+ without MAIL FROM, and smtpd_end_of_data_restrictions as
+ before skipped check_recipient_access constraints for the
+ second message. Under these conditions, the Postfix Milter
+ client remained in the "accept this message" state, skipping
+ Milter policy enforcement for the second message.
+
+ Bug (defect introduced: Postfix 3.4, date: 20180805): SMTP
+ server command history memory exhaustion with a large number
+ of very small BDAT requests. Reported by OpenAI security.
+ File: smtpd.c.
+
+ Bug (defect introduced: Postfix 1.1, date: 20021116): address
+ verification cache poisoning. A local user could use the
+ postdrop command to submit an address verification probe
+ with envelope or message content that Postfix rejected
+ later, resulting in a negative address verification cache
+ entry for that address. On systems that enable address
+ verification, the negative address verification cache entry
+ would force the Postfix SMTP server to reject a message
+ that it should accept (denial of service). Problem reported
+ by OpenAI Security. File: postdrop.c.
+
+ Queue hygiene: the postdrop command accepted the null record
+ type which the rest of Postfix ingnores. Reported by OpenAI
+ Security. File: postdrop.c.
data_len = strlen(temp) + 1;
break;
case T_SRV:
+ if (fixed->length < 3 * NS_INT16SZ)
+ return (DNS_RETRY);
GETSHORT(pref, pos);
GETSHORT(weight, pos);
GETSHORT(port, pos);
data_len = strlen(temp) + 1;
break;
case T_MX:
+ if (fixed->length < NS_INT16SZ)
+ return (DNS_RETRY);
GETSHORT(pref, pos);
if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0)
return (DNS_RETRY);
STR(queue_id), path);
continue;
}
+ /* 202606 Qualys+Mythos: use htable_locate(), not htable_find(). */
if (dup_filter->used >= FLUSH_DUP_FILTER_SIZE
- || htable_find(dup_filter, STR(queue_id)) == 0) {
+ || htable_locate(dup_filter, STR(queue_id)) == 0) {
if (msg_verbose)
msg_info("%s: logfile %s: update queue file %s time stamps",
myname, path, STR(queue_id));
(((cp) = strrchr((path), MQID_LG_INUM_SEP)) != 0 \
&& ((cp) - (path) >= MQID_LG_TIME_PAD))
+/* 202607 Qualys+Mythos: skip short paths. */
#define MQID_GET_INUM(path, inum, long_form, error) do { \
char *_cp; \
if (((long_form) = MQID_FIND_LG_INUM_SEPARATOR(_cp, (path))) != 0) { \
MQID_LG_DECODE_INUM(_cp + 1, (inum), (error)); \
} else { \
- MQID_SH_DECODE_INUM((path) + MQID_SH_USEC_PAD, (inum), (error)); \
+ if (strlen(path) > MQID_SH_USEC_PAD) \
+ MQID_SH_DECODE_INUM((path) + MQID_SH_USEC_PAD, (inum), (error)); \
+ else { (errno) = EINVAL; error = 1; } \
} \
} while (0)
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20260706"
-#define MAIL_VERSION_NUMBER "3.11.5"
+#define MAIL_RELEASE_DATE "20260809"
+#define MAIL_VERSION_NUMBER "3.11.6"
#ifdef SNAPSHOT
#define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE
const char *myname = "rec_get";
int type;
ssize_t len;
- int len_byte;
+ ssize_t len_byte;
unsigned shift;
/*
* limit.
*/
for (len = 0, shift = 0; /* void */ ; shift += 7) {
- if (shift >= (int) (NBBY * sizeof(int))) {
+ if (shift +7 >= (int) (NBBY * sizeof(int))) {
msg_warn("%s: too many length bits, record type %d",
VSTREAM_PATH(stream), type);
return (REC_TYPE_ERROR);
postdrop.o: ../../include/check_arg.h
postdrop.o: ../../include/clean_env.h
postdrop.o: ../../include/cleanup_user.h
+postdrop.o: ../../include/deliver_request.h
postdrop.o: ../../include/dict.h
+postdrop.o: ../../include/dsn.h
postdrop.o: ../../include/htable.h
postdrop.o: ../../include/iostuff.h
postdrop.o: ../../include/login_sender_match.h
postdrop.o: ../../include/mail_version.h
postdrop.o: ../../include/maillog_client.h
postdrop.o: ../../include/msg.h
+postdrop.o: ../../include/msg_stats.h
postdrop.o: ../../include/msg_vstream.h
postdrop.o: ../../include/myflock.h
postdrop.o: ../../include/mymalloc.h
postdrop.o: ../../include/nvtable.h
postdrop.o: ../../include/rec_attr_map.h
postdrop.o: ../../include/rec_type.h
+postdrop.o: ../../include/recipient_list.h
postdrop.o: ../../include/record.h
postdrop.o: ../../include/stringops.h
postdrop.o: ../../include/sys_defs.h
/* Global library. */
+#include <deliver_request.h>
#include <mail_proto.h>
#include <mail_queue.h>
#include <mail_params.h>
}
if (rec_type == REC_TYPE_ERROR)
msg_fatal("uid=%ld: malformed input", (long) uid);
- if (strchr(*expected, rec_type) == 0)
+ if (rec_type == 0 || strchr(*expected, rec_type) == 0)
msg_fatal("uid=%ld: unexpected record type: %d", (long) uid, rec_type);
if (rec_type == **expected)
expected++;
}
#define STREQ(x,y) (strcmp(x,y) == 0)
+ /* 202607 OpenAI: allow only sendmail '-v' and '-bv' tracing. */
+ if (STREQ(attr_name, MAIL_ATTR_TRACE_FLAGS)) {
+ int tflags = atoi(attr_value);
+
+ if (tflags == DEL_REQ_FLAG_USR_VRFY
+ || tflags == DEL_REQ_FLAG_RECORD)
+ rec_fprintf(dst->stream, REC_TYPE_ATTR, "%s=%d",
+ attr_name, tflags);
+ else
+ msg_warn("uid=%ld: ignoring unexpected trace flags: %.200s",
+ (long) uid, attr_value);
+ continue;
+ }
if ((STREQ(attr_name, MAIL_ATTR_ENCODING)
&& (STREQ(attr_value, MAIL_ATTR_ENC_7BIT)
|| STREQ(attr_value, MAIL_ATTR_ENC_8BIT)
|| rec_attr_map(attr_name)
|| (STREQ(attr_name, MAIL_ATTR_RWR_CONTEXT)
&& (STREQ(attr_value, MAIL_ATTR_RWR_LOCAL)
- || STREQ(attr_value, MAIL_ATTR_RWR_REMOTE)))
- || STREQ(attr_name, MAIL_ATTR_TRACE_FLAGS)) { /* XXX */
+ || STREQ(attr_value, MAIL_ATTR_RWR_REMOTE)))) {
rec_fprintf(dst->stream, REC_TYPE_ATTR, "%s=%s",
attr_name, attr_value);
} else {
_cb_->context = (ctx); \
} while (0)
+/* 20260606 Qualys+Mythos: read after free, neutralized by memset(0xff). */
#define PSC_CALL_BACK_NOTIFY(sp, ev) do { \
PSC_CALL_BACK_ENTRY *_cb_; \
- for (_cb_ = (sp)->table; _cb_ < (sp)->table + (sp)->index; _cb_++) \
+ PSC_CALL_BACK_ENTRY *_end_ = (sp)->table + (sp)->index; \
+ int _todo_ = (sp)->refcount; \
+ for (_cb_ = (sp)->table; _todo_ > 0 && _cb_ < _end_; _cb_++) \
if (_cb_->callback != 0) \
- _cb_->callback((ev), _cb_->context); \
+ { _cb_->callback((ev), _cb_->context); _todo_ -= 1; } \
} while (0)
#define PSC_NULL_EVENT (0)
if (inet_pton(AF_INET, *cpp, addr_buf) != 1)
msg_warn("address conversion error for %s -- ignoring this reply",
*cpp);
- if (ip_match_execute(filter, addr_buf))
+ /* Qualys+Mythos: skip ip_match_execute() after inet_pton() failure. */
+ else if (ip_match_execute(filter, addr_buf))
return (1);
}
return (0);
int non_proxy = 0;
switch (event) {
+ /* Qualys+Mythos: handle I/O exception as error. */
+ default:
+ msg_warn("haproxy read: unexpected event %d", event);
+ status = -1;
+ break;
case EVENT_TIME:
msg_warn("haproxy read: time limit exceeded");
status = -1;
* number.
*/
if ((action & ACTION_STRUCT)
+ /* 202607 Qualys+Mythos: skip short paths. */
+ && strlen(path) > SUFFIX_LEN
&& strcmp(path + (strlen(path) - SUFFIX_LEN), SUFFIX) == 0) {
path[strlen(path) - SUFFIX_LEN] = 0; /* XXX */
if (!mail_queue_id_ok(path)) {
&& SMTPD_STAND_ALONE(state) == 0
&& (err = smtpd_check_eod(state)) != 0) {
smtpd_chat_reply(state, "%s", err);
- if (proxy) {
- smtpd_proxy_close(state);
- } else {
- mail_stream_cleanup(state->dest);
- state->dest = 0;
- state->cleanup = 0;
- }
+ /* 202607 OpenAI: reset state like normal end-of-data. */
+ chat_reset(state, var_smtpd_hist_thrsh);
+ mail_reset(state);
+ rcpt_reset(state);
return (-1);
}
/*
* Reset state, or drop subsequent BDAT payloads until BDAT LAST or RSET.
*/
- if (final_chunk)
+ if (final_chunk) {
+ chat_reset(state, var_smtpd_hist_thrsh);
mail_reset(state);
- else
+ /* 202607 OpenAI: also reset recipient state. */
+ rcpt_reset(state);
+ } else
state->bdat_state = SMTPD_BDAT_STAT_ERROR;
return (-1);
}
for (;;) {
if (state->flags & SMTPD_FLAG_HANGUP)
break;
+ /* Flush the command history if it becomes large. */
+ chat_reset(state, var_smtpd_hist_thrsh);
smtp_stream_setup(state->client, var_smtpd_tmout,
var_smtpd_req_deadline, 0);
if (state->error_count >= var_smtpd_hard_erlim) {
* Safety net.
*/
if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
+ /* 202606 Qualys+Mythos: myfree() above frees 'host' and 'port'. */
msg_info("skipping address family %d for host %s",
- res->ai_family, host);
+ res->ai_family, addr);
continue;
}
found++;
* sequence would invalidate the use of memcmp().
*/
if (sa->sa_family == AF_INET) {
- return (SOCK_ADDR_IN_ADDR(sa).s_addr - SOCK_ADDR_IN_ADDR(sb).s_addr);
+ /* Qualys+Mythos 202606: (int=unsigned-unsigned) is non-transitive. */
+ return (memcmp((void *) &SOCK_ADDR_IN_ADDR(sa).s_addr,
+ (void *) &SOCK_ADDR_IN_ADDR(sb).s_addr,
+ sizeof(SOCK_ADDR_IN_ADDR(sa).s_addr)));
#ifdef HAS_IPV6
} else if (sa->sa_family == AF_INET6) {
return (memcmp((void *) &(SOCK_ADDR_IN6_ADDR(sa)),
cmd = line;
line = split_at(line, '\t');
- if (strcmp(cmd, "VERSION") == 0) {
+ if (strcmp(cmd, "VERSION") == 0 && line != NULL) {
if (sscanf(line, "%u\t%u", &major_version, &minor_version) != 2) {
msg_warn("SASL: Protocol version error");
break;