]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.9-20110124
authorWietse Venema <wietse@porcupine.org>
Mon, 24 Jan 2011 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:37:07 +0000 (06:37 +0000)
postfix/HISTORY
postfix/README_FILES/POSTSCREEN_README
postfix/WISHLIST
postfix/html/POSTSCREEN_README.html
postfix/proto/POSTSCREEN_README.html
postfix/src/global/mail_version.h
postfix/src/global/smtp_stream.c
postfix/src/postscreen/postscreen.c
postfix/src/util/vstream.c
postfix/src/util/vstream.h

index d2f12947a4b7329deabc528d7068f208fd4b10aa..7b1195fbc1a05db41fafc9e1b777967323d3c6ab 100644 (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.
index 294dbda4f3435fd75d0e6b2d98d161bc11e27104..789aa379b1d2ce20817ddf6a41c94fdca84eedcb 100644 (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
index 32833cfa5edeb3e48ee84837e22860846e50325f..80d381fd521de86f726591b6321c8a930ed31a7c 100644 (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.
 
index 5c74bbcae8f7cdc1b3d219b646d9030b662b03d9..3561164b47031245a94af5b1b8451e376c39a085 100644 (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>:
index de46400998263ac07347095ba79de8c5baf01957..e71ebe1a962bbd64f0daec9a7d07d8b310e6fa42 100644 (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:
index e4ceb0dc5eb75f6b981e1536d1a51aa6e08aae51..77e8821a0d257e23ee86e4db35a2111fbbf7707f 100644 (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
index 82d18e513fe3b5134d94ed31435ac1b04d8b0d17..73ad8705418082a3320a208d00239aa32fa4a2fd 100644 (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
 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 */
index 7b3332b61d23e3f6580cff3fdc7b07aec942cdd5..67bfe3caaf647bb55b90490524583f24912d8192 100644 (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,
index ba3e842b1e37a4c1a937ebdd0ddc8cabbb6e33c9..9ee143a815bdbc684fad889701156a8186caf87d 100644 (file)
 /*     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);
        }
index 3c6c16aeaa7b5b8d7cf19cbbc0a48d44b5baf6f6..fb7d05854d826c63220627eeb12bd285a14c74ca 100644 (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 *,...);