2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-22 01:49:47 +00:00

postfix-2.9-20110124

This commit is contained in:
Wietse Venema 2011-01-24 00:00:00 -05:00 committed by Viktor Dukhovni
parent 506b64785e
commit ee034fdb56
10 changed files with 121 additions and 82 deletions

View File

@ -16514,3 +16514,16 @@ Apologies for any names omitted.
Bugfix: support for the "dunno" command somehow disappeared
from the postscreen_access_list implementation. File:
postscreen/postscreen_access.c.
20110123
Feature: read/write deadlines. Deadlines were introduced
with postscreen's dummy SMTP engine. In the Postfix SMTP
client and server, deadlines limit the total amount of time
to read or write one command line, one response line, or
one line of message content. This reduces the impact of
application exhaustion attacks that trickle data one byte
at a time. Files: util/vstream.[hc], global/smtp_stream.c.
Cleanup: remove #ifdef MIGRATION_WARNING transitional code
from postscreen. File: postscreen/postscreen.c.

View File

@ -505,7 +505,7 @@ mail:
3. Uncomment the new "smtpd pass ... smtpd" service in master.cf, and
duplicate any "-o parameter=value" entries from the smtpd service that was
commented out in step 1.
commented out in the previous step.
/etc/postfix/master.cf:
smtpd pass - - n - - smtpd

View File

@ -6,6 +6,9 @@ Wish list:
Things to do after the stable release:
Don't forget Apple's code donation for fetching mail from
IMAP server.
vstream_peek_len() and vstream_peek_data() to count the
unread data and to access it, respectively. vstream_peek_data()
can access the saved read buffer if a double-buffered stream
@ -35,6 +38,7 @@ Wish list:
means that many tlsproxy_ parameters become postscreen_
parameters, and that tls_server_init() parameters move to
to tls_server_start(). That is a significant API change.
It also means tlsproxy can't open all files before chroot().
anvil rate limit for sasl_username.

View File

@ -701,7 +701,8 @@ that follow. </p>
<li> <p> Uncomment the new "<tt>smtpd pass ... smtpd</tt>" service
in <a href="master.5.html">master.cf</a>, and duplicate any "<tt>-o parameter=value</tt>" entries
from the smtpd service that was commented out in step 1. </p>
from the smtpd service that was commented out in the previous step.
</p>
<pre>
/etc/postfix/<a href="master.5.html">master.cf</a>:

View File

@ -701,7 +701,8 @@ that follow. </p>
<li> <p> Uncomment the new "<tt>smtpd pass ... smtpd</tt>" service
in master.cf, and duplicate any "<tt>-o parameter=value</tt>" entries
from the smtpd service that was commented out in step 1. </p>
from the smtpd service that was commented out in the previous step.
</p>
<pre>
/etc/postfix/master.cf:

View File

@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
#define MAIL_RELEASE_DATE "20110120"
#define MAIL_RELEASE_DATE "20110124"
#define MAIL_VERSION_NUMBER "2.9"
#ifdef SNAPSHOT

View File

@ -52,7 +52,7 @@
/* and write operations described below.
/* This routine alters the behavior of streams as follows:
/* .IP \(bu
/* The read/write timeout is set to the specified value.
/* The read/write total time limit is set to the specified value.
/* .IP \f(bu
/* The stream is configured to use double buffering.
/* .IP \f(bu
@ -151,6 +151,16 @@
static void smtp_timeout_reset(VSTREAM *stream)
{
vstream_clearerr(stream);
/*
* Important: the time limit feature must not introduce any system calls
* when the input is already in the buffer, or when the output still fits
* in the buffer. Such system calls would really hurt when receiving or
* sending body content one line at a time.
*/
vstream_control(stream,
VSTREAM_CTL_TIME_LIMIT, stream->timeout,
VSTREAM_CTL_END);
}
/* smtp_timeout_detect - test the per-stream timeout flag */

View File

@ -416,13 +416,6 @@ int var_psc_post_queue_limit;
int var_psc_pre_queue_limit;
int var_psc_watchdog;
#undef MIGRATION_WARNING
#ifdef MIGRATION_WARNING
char *var_psc_wlist_nets;
char *var_psc_blist_nets;
#endif
char *var_psc_acl;
char *var_psc_blist_action;
@ -495,11 +488,6 @@ HTABLE *psc_client_concurrency; /* per-client concurrency */
/*
* Local variables.
*/
#ifdef MIGRATION_WARNING
static ADDR_MATCH_LIST *psc_wlist_nets; /* permanently whitelisted networks */
static ADDR_MATCH_LIST *psc_blist_nets; /* permanently blacklisted networks */
#endif
static ARGV *psc_acl; /* permanent white/backlist */
static int psc_blist_action; /* PSC_ACT_DROP/ENFORCE/etc */
@ -715,47 +703,6 @@ static void psc_service(VSTREAM *smtp_client_stream,
break;
}
}
#ifdef MIGRATION_WARNING
/*
* The permanent whitelist has highest precedence (never block mail from
* whitelisted sites, and never run tests against those sites).
*/
if (psc_wlist_nets != 0
&& psc_addr_match_list_match(psc_wlist_nets, state->smtp_client_addr)) {
msg_info("WHITELISTED [%s]:%s", PSC_CLIENT_ADDR_PORT(state));
psc_conclude(state);
return;
}
/*
* The permanent blacklist has second precedence. If the client is
* permanently blacklisted, send some generic reply and hang up
* immediately, or run more tests for logging purposes.
*/
if (psc_blist_nets != 0
&& psc_addr_match_list_match(psc_blist_nets, state->smtp_client_addr)) {
msg_info("BLACKLISTED [%s]:%s", PSC_CLIENT_ADDR_PORT(state));
PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_BLIST_FAIL);
switch (psc_blist_action) {
case PSC_ACT_DROP:
PSC_DROP_SESSION_STATE(state,
"521 5.3.2 Service currently unavailable\r\n");
return;
case PSC_ACT_ENFORCE:
PSC_ENFORCE_SESSION_STATE(state,
"550 5.3.2 Service currently unavailable\r\n");
break;
case PSC_ACT_IGNORE:
PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_BLIST_FAIL);
/* Not: PSC_PASS_SESSION_STATE. Repeat this test the next time. */
break;
default:
msg_panic("%s: unknown blacklist action value %d",
myname, psc_blist_action);
}
}
#endif
/*
* The temporary whitelist (i.e. the postscreen cache) has the lowest
@ -787,7 +734,8 @@ static void psc_service(VSTREAM *smtp_client_stream,
}
/*
* Reply with 421 when we can't analyze more connections.
* Reply with 421 when we can't analyze more connections. That also means
* no deep protocol tests when the noforward flag is raised.
*/
if (var_psc_pre_queue_limit > 0
&& psc_check_queue_length - psc_post_queue_length
@ -841,21 +789,6 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
* Open read-only maps before dropping privilege, for consistency with
* other Postfix daemons.
*/
#ifdef MIGRATION_WARNING
if (*var_psc_wlist_nets)
psc_wlist_nets =
addr_match_list_init(MATCH_FLAG_NONE, var_psc_wlist_nets);
if (*var_psc_blist_nets)
psc_blist_nets = addr_match_list_init(MATCH_FLAG_NONE,
var_psc_blist_nets);
if (psc_blist_nets || psc_wlist_nets) {
msg_warn("The %s and %s features will be removed soon. Use %s instead",
VAR_PSC_WLIST_NETS, VAR_PSC_BLIST_NETS, VAR_PSC_ACL);
msg_warn("To stop this warning, specify empty values for %s and %s",
VAR_PSC_WLIST_NETS, VAR_PSC_BLIST_NETS);
}
#endif
psc_acl_pre_jail_init();
if (*var_psc_acl)
psc_acl = psc_acl_parse(var_psc_acl, VAR_PSC_ACL);
@ -1095,10 +1028,6 @@ int main(int argc, char **argv)
VAR_PSC_PIPEL_ACTION, DEF_PSC_PIPEL_ACTION, &var_psc_pipel_action, 1, 0,
VAR_PSC_NSMTP_ACTION, DEF_PSC_NSMTP_ACTION, &var_psc_nsmtp_action, 1, 0,
VAR_PSC_BARLF_ACTION, DEF_PSC_BARLF_ACTION, &var_psc_barlf_action, 1, 0,
#ifdef MIGRATION_WARNING
VAR_PSC_WLIST_NETS, DEF_PSC_WLIST_NETS, &var_psc_wlist_nets, 0, 0,
VAR_PSC_BLIST_NETS, DEF_PSC_BLIST_NETS, &var_psc_blist_nets, 0, 0,
#endif
VAR_PSC_ACL, DEF_PSC_ACL, &var_psc_acl, 0, 0,
VAR_PSC_BLIST_ACTION, DEF_PSC_BLIST_ACTION, &var_psc_blist_action, 1, 0,
VAR_PSC_FORBID_CMDS, DEF_PSC_FORBID_CMDS, &var_psc_forbid_cmds, 0, 0,

View File

@ -304,6 +304,12 @@
/* int. Use an explicit cast to avoid problems on LP64
/* environments and other environments where ssize_t is larger
/* than int.
/* .IP "VSTREAM_CTL_TIME_LIMIT (int)"
/* Specify an upper bound on the total time to complete all
/* subsequent read or write operations. This is different from
/* VSTREAM_CTL_TIMEOUT, which specifies a deadline for each
/* read or write operation. Specify a relative time in seconds,
/* or zero to disable this feature.
/* .PP
/* vstream_fileno() gives access to the file handle associated with
/* a buffered stream. With streams that have separate read/write
@ -522,6 +528,21 @@ VSTREAM vstream_fstd[] = {
#define VSTREAM_FFLUSH_SOME(stream) \
vstream_fflush_some((stream), (stream)->buf.len - (stream)->buf.cnt)
/* Note: this does not change a negative result into a zero result. */
#define VSTREAM_SUB_TIME(x, y, z) \
do { \
(x).tv_sec = (y).tv_sec - (z).tv_sec; \
(x).tv_usec = (y).tv_usec - (z).tv_usec; \
while ((x).tv_usec < 0) { \
(x).tv_usec += 1000000; \
(x).tv_sec -= 1; \
} \
while ((x).tv_usec >= 1000000) { \
(x).tv_usec -= 1000000; \
(x).tv_sec += 1; \
} \
} while (0)
/* vstream_buf_init - initialize buffer */
static void vstream_buf_init(VBUF *bp, int flags)
@ -590,6 +611,9 @@ static int vstream_fflush_some(VSTREAM *stream, ssize_t to_flush)
char *data;
ssize_t len;
ssize_t n;
int timeout;
struct timeval before;
struct timeval elapsed;
/*
* Sanity checks. It is illegal to flush a read-only stream. Otherwise,
@ -630,14 +654,31 @@ static int vstream_fflush_some(VSTREAM *stream, ssize_t to_flush)
* any.
*/
for (data = (char *) bp->data, len = to_flush; len > 0; len -= n, data += n) {
if ((n = stream->write_fn(stream->fd, data, len, stream->timeout, stream->context)) <= 0) {
if (bp->flags & VSTREAM_FLAG_DEADLINE) {
timeout = stream->time_limit.tv_sec + (stream->time_limit.tv_usec > 0);
if (timeout <= 0) {
bp->flags |= (VSTREAM_FLAG_ERR | VSTREAM_FLAG_TIMEOUT);
errno = ETIMEDOUT;
return (VSTREAM_EOF);
}
if (len == to_flush)
GETTIMEOFDAY(&before);
else
before = stream->iotime;
} else
timeout = stream->timeout;
if ((n = stream->write_fn(stream->fd, data, len, timeout, stream->context)) <= 0) {
bp->flags |= VSTREAM_FLAG_ERR;
if (errno == ETIMEDOUT)
bp->flags |= VSTREAM_FLAG_TIMEOUT;
return (VSTREAM_EOF);
}
if (stream->timeout)
if (timeout)
GETTIMEOFDAY(&stream->iotime);
if (bp->flags & VSTREAM_FLAG_DEADLINE) {
VSTREAM_SUB_TIME(elapsed, stream->iotime, before);
VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed);
}
if (msg_verbose > 2 && stream != VSTREAM_ERR && n != to_flush)
msg_info("%s: %d flushed %ld/%ld", myname, stream->fd,
(long) n, (long) to_flush);
@ -698,6 +739,9 @@ static int vstream_buf_get_ready(VBUF *bp)
VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
const char *myname = "vstream_buf_get_ready";
ssize_t n;
struct timeval before;
struct timeval elapsed;
int timeout;
/*
* Detect a change of I/O direction or position. If so, flush any
@ -759,7 +803,17 @@ static int vstream_buf_get_ready(VBUF *bp)
* data as is available right now, whichever is less. Update the cached
* file seek position, if any.
*/
switch (n = stream->read_fn(stream->fd, bp->data, bp->len, stream->timeout, stream->context)) {
if (bp->flags & VSTREAM_FLAG_DEADLINE) {
timeout = stream->time_limit.tv_sec + (stream->time_limit.tv_usec > 0);
if (timeout <= 0) {
bp->flags |= (VSTREAM_FLAG_ERR | VSTREAM_FLAG_TIMEOUT);
errno = ETIMEDOUT;
return (VSTREAM_EOF);
}
GETTIMEOFDAY(&before);
} else
timeout = stream->timeout;
switch (n = stream->read_fn(stream->fd, bp->data, bp->len, timeout, stream->context)) {
case -1:
bp->flags |= VSTREAM_FLAG_ERR;
if (errno == ETIMEDOUT)
@ -769,8 +823,12 @@ static int vstream_buf_get_ready(VBUF *bp)
bp->flags |= VSTREAM_FLAG_EOF;
return (VSTREAM_EOF);
default:
if (stream->timeout)
if (timeout)
GETTIMEOFDAY(&stream->iotime);
if (bp->flags & VSTREAM_FLAG_DEADLINE) {
VSTREAM_SUB_TIME(elapsed, stream->iotime, before);
VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed);
}
if (msg_verbose > 2)
msg_info("%s: fd %d got %ld", myname, stream->fd, (long) n);
bp->cnt = -n;
@ -1082,6 +1140,7 @@ VSTREAM *vstream_fdopen(int fd, int flags)
stream->context = 0;
stream->jbuf = 0;
stream->iotime.tv_sec = stream->iotime.tv_usec = 0;
stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0;
stream->req_bufsize = VSTREAM_BUFSIZE;
return (stream);
}
@ -1227,6 +1286,7 @@ void vstream_control(VSTREAM *stream, int name,...)
int old_fd;
ssize_t req_bufsize = 0;
VSTREAM *stream2;
int time_limit;
#define SWAP(type,a,b) do { type temp = (a); (a) = (b); (b) = (temp); } while (0)
@ -1334,6 +1394,24 @@ void vstream_control(VSTREAM *stream, int name,...)
&& req_bufsize > stream->req_bufsize)
stream->req_bufsize = req_bufsize;
break;
/*
* Make no gettimeofday() etc. system call until we really know
* that we need to do I/O. This avoids a performance hit when
* sending or receiving body content one line at a time.
*/
case VSTREAM_CTL_TIME_LIMIT:
time_limit = va_arg(ap, int);
if (time_limit < 0) {
msg_panic("%s: bad time limit: %d", myname, time_limit);
} else if (time_limit == 0) {
stream->buf.flags &= ~VSTREAM_FLAG_DEADLINE;
} else {
stream->buf.flags |= VSTREAM_FLAG_DEADLINE;
stream->time_limit.tv_sec = time_limit;
stream->time_limit.tv_usec = 0;
}
break;
default:
msg_panic("%s: bad name %d", myname, name);
}

View File

@ -57,6 +57,7 @@ typedef struct VSTREAM {
int timeout; /* read/write timout */
VSTREAM_JMP_BUF *jbuf; /* exception handling */
struct timeval iotime; /* time of last fill/flush */
struct timeval time_limit; /* read/write time limit */
} VSTREAM;
extern VSTREAM vstream_fstd[]; /* pre-defined streams */
@ -76,6 +77,7 @@ extern VSTREAM vstream_fstd[]; /* pre-defined streams */
#define VSTREAM_FLAG_SEEK (1<<10) /* seek info valid */
#define VSTREAM_FLAG_NSEEK (1<<11) /* can't seek this file */
#define VSTREAM_FLAG_DOUBLE (1<<12) /* double buffer */
#define VSTREAM_FLAG_DEADLINE (1<<13) /* deadline active */
#define VSTREAM_PURGE_READ (1<<0) /* flush unread data */
#define VSTREAM_PURGE_WRITE (1<<1) /* flush unwritten data */
@ -133,6 +135,7 @@ extern void vstream_control(VSTREAM *, int,...);
#endif
#define VSTREAM_CTL_BUFSIZE 12
#define VSTREAM_CTL_SWAP_FD 13
#define VSTREAM_CTL_TIME_LIMIT 14
extern VSTREAM *PRINTFLIKE(1, 2) vstream_printf(const char *,...);
extern VSTREAM *PRINTFLIKE(2, 3) vstream_fprintf(VSTREAM *, const char *,...);