]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.0.8 v3.0.8
authorWietse Venema <wietse@porcupine.org>
Sun, 1 Jan 2017 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Mon, 2 Jan 2017 18:35:24 +0000 (13:35 -0500)
17 files changed:
postfix/HISTORY
postfix/src/global/mail_version.h
postfix/src/postscreen/postscreen.c
postfix/src/postscreen/postscreen.h
postfix/src/postscreen/postscreen_early.c
postfix/src/postscreen/postscreen_misc.c
postfix/src/postscreen/postscreen_state.c
postfix/src/postscreen/postscreen_tests.c
postfix/src/postsuper/postsuper.c
postfix/src/smtpd/Makefile.in
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_check.h
postfix/src/smtpd/smtpd_milter.c
postfix/src/smtpd/smtpd_resolve.c
postfix/src/smtpd/smtpd_resolve.h
postfix/src/virtual/mailbox.c

index f057cb585fcfe15eb64b1afc4d00cf9333f28b3d..0c5caedf7df640fde80fdff26d1dae383fd744ed 100644 (file)
@@ -21762,6 +21762,32 @@ Apologies for any names omitted.
 
        Portability: OpenBSD 6.0. Files: makedefs, util/sys_defs.h.
 
+20160618
+
+       Bugfix (introduced: 20091121): with the introduction of
+       sender_dependent_default_transport_maps, the SMTP daemon
+       was not updated. This resulted in false rejects with
+       sender-dependent "error" transports. Based on a fix by
+       Russell Yanofsky.  Files: global/resolve_clnt.c,
+       global/resolve_clnt.h, smtpd/smtpd_check.c, smtpd/smtpd_check.h,
+       smtpd/smtpd_milter.c, smtpd/smtpd_resolve.c, smtpd/smtpd_resolve.h.
+
+20160717
+
+       Bugfix (introduced: Postfix 1.1): the virtual(8) delivery
+       agent discarded the error result from vstream_fseek().
+       File: virtual/mailbox.c.
+
+20160730
+
+       Bugfix (introduced: 20090614): with concurrent connections
+       from the same client IP address, and after-220 tests enabled,
+       postscreen could overwrite the cached "all tests completed"
+       result of one connection that completed the after-220 tests,
+       with the "some tests not completed" result of a concurrent
+       connection where the client hung up later, without completing
+       the after-220 tests.
+
 20160819
 
        Bugfix (introduced: Postfix 3.0): the makedefs script ignored
@@ -21796,3 +21822,24 @@ Apologies for any names omitted.
        UTF8-encoded text, specify "option_group = client" in Postfix
        MySQL configuration files.  This will be the default setting
        with Postfix 3.2 and later.
+
+20161105
+
+       Bugfix (introduced: Postfix 1.1): the postsuper command did
+       not count a successful rename operation after error recovery.
+       Problem reported by Markus Schönhaber. File: postsuper/postsuper.c.
+
+20161206
+
+       Bugfix (introduced: Postfix 3.0): when receiving a MAIL
+       FROM...SMTPUTF8 command while smtpd_delay_reject=no, enable
+       SMTPUTF8 support before processing smtpd_sender_restrictions.
+       Problem reported by Viktor Dukhovni. File: smtpd/smtpd.c.
+
+20161220
+
+       Bugfix (introduced: Postfix 2.1.0): the Postfix SMTP daemon
+       did not query sender_canonical_maps when rejecting unknown
+       senders with "smtpd_reject_unlisted_recipient = yes" or
+       with reject_unlisted_sender.  Stephen R. van den Berg (Mr.
+       procmail).  Files: smtpd/smtpd.c, smtpd/smtpd_check.c.
index d1664d5e95b578b547d871726fe6aeaf2832d284..49a683909f147cd493b0ea7a552ef63ec41e2de9 100644 (file)
@@ -20,8 +20,8 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20161001"
-#define MAIL_VERSION_NUMBER    "3.0.7"
+#define MAIL_RELEASE_DATE      "20170101"
+#define MAIL_VERSION_NUMBER    "3.0.8"
 
 #ifdef SNAPSHOT
 #define MAIL_VERSION_DATE      "-" MAIL_RELEASE_DATE
index e4ea94cb6d04bdf3460383de8d87dcbeca875e73..6bfd76d8c6eae07e3e50c8d6cc7b50d5f8eba369 100644 (file)
@@ -760,6 +760,7 @@ static void psc_endpt_lookup_done(int endpt_status,
      * valid.
      */
     if ((state->flags & PSC_STATE_MASK_ANY_FAIL) == 0
+       && state->client_info->concurrency == 1
        && psc_cache_map != 0
        && (stamp_str = psc_cache_lookup(psc_cache_map, state->smtp_client_addr)) != 0) {
        saved_flags = state->flags;
@@ -773,6 +774,13 @@ static void psc_endpt_lookup_done(int endpt_status,
            psc_conclude(state);
            return;
        }
+    } else if (state->client_info->concurrency > 1) {
+       saved_flags = state->flags;
+       psc_todo_tests(state, event_time());
+       state->flags |= saved_flags;
+       if (msg_verbose)
+           msg_info("%s: new + recent flags: %s",
+                    myname, psc_print_state_flags(state->flags, myname));
     } else {
        saved_flags = state->flags;
        psc_new_tests(state);
@@ -824,6 +832,7 @@ static int psc_cache_validator(const char *client_addr,
                                       void *unused_context)
 {
     PSC_STATE dummy;
+    PSC_CLIENT_INFO dummy_client_info;
 
     /*
      * This function is called by the cache cleanup pseudo thread.
@@ -833,6 +842,7 @@ static int psc_cache_validator(const char *client_addr,
      * silly logging we remove the cache entry only after all tests have
      * expired longer ago than the cache retention time.
      */
+    dummy.client_info = &dummy_client_info;
     psc_parse_tests(&dummy, stamp_str, event_time() - var_psc_cache_ret);
     return ((dummy.flags & PSC_STATE_MASK_ANY_TODO) == 0);
 }
index 130b52770e51e61f8dc041cf757257bb726f0498..69971072a2949b48abd7abaa3cb8d1a4e974c09f 100644 (file)
 
 #define PSC_TINDX_BYTNAME(tname) (PSC_TINDX_ ## tname)
 
+ /*
+  * Per-client shared state.
+  */
+typedef struct {
+    int     concurrency;               /* per-client */
+    int     pass_new_count;            /* per-client */
+    time_t  expire_time[PSC_TINDX_COUNT];      /* per-test expiration */
+} PSC_CLIENT_INFO;
+
  /*
   * Per-session state.
   */
@@ -66,13 +75,12 @@ typedef struct {
     char   *smtp_client_port;          /* client port */
     char   *smtp_server_addr;          /* server address */
     char   *smtp_server_port;          /* server port */
-    int     client_concurrency;                /* per-client */
     const char *final_reply;           /* cause for hanging up */
     VSTRING *send_buf;                 /* pending output */
     /* Test context. */
     struct timeval start_time;         /* start of current test */
     const char *test_name;             /* name of current test */
-    time_t  expire_time[PSC_TINDX_COUNT];      /* per-test expiration */
+    PSC_CLIENT_INFO *client_info;      /* shared client state */
     VSTRING *dnsbl_reply;              /* dnsbl reject text */
     int     dnsbl_score;               /* saved DNSBL score */
     const char *dnsbl_name;            /* DNSBL name with largest weight */
@@ -94,11 +102,14 @@ typedef struct {
   * Emulate legacy ad-hoc variables on top of indexable time stamps. This
   * avoids massive scar tissue during initial feature development.
   */
-#define pregr_stamp    expire_time[PSC_TINDX_PREGR]
-#define dnsbl_stamp    expire_time[PSC_TINDX_DNSBL]
-#define pipel_stamp    expire_time[PSC_TINDX_PIPEL]
-#define nsmtp_stamp    expire_time[PSC_TINDX_NSMTP]
-#define barlf_stamp    expire_time[PSC_TINDX_BARLF]
+#define pregr_stamp    client_info->expire_time[PSC_TINDX_PREGR]
+#define dnsbl_stamp    client_info->expire_time[PSC_TINDX_DNSBL]
+#define pipel_stamp    client_info->expire_time[PSC_TINDX_PIPEL]
+#define nsmtp_stamp    client_info->expire_time[PSC_TINDX_NSMTP]
+#define barlf_stamp    client_info->expire_time[PSC_TINDX_BARLF]
+
+ /* Minize the patch size for stable releases. */
+#define client_concurrency client_info->concurrency
 
  /*
   * Special expiration time values.
@@ -489,17 +500,21 @@ extern int psc_dnsbl_request(const char *, void (*) (int, void *), void *);
 #define PSC_INIT_TESTS(dst) do { \
        time_t *_it_stamp_p; \
        (dst)->flags = 0; \
-       for (_it_stamp_p = (dst)->expire_time; \
-           _it_stamp_p < (dst)->expire_time + PSC_TINDX_COUNT; \
+       for (_it_stamp_p = (dst)->client_info->expire_time; \
+           _it_stamp_p < (dst)->client_info->expire_time + PSC_TINDX_COUNT; \
            _it_stamp_p++) \
            *_it_stamp_p = PSC_TIME_STAMP_INVALID; \
     } while (0)
+#define PSC_INIT_TEST_FLAGS_ONLY(dst) do { \
+       (dst)->flags = 0; \
+    } while (0)
 #define PSC_BEGIN_TESTS(state, name) do { \
        (state)->test_name = (name); \
        GETTIMEOFDAY(&(state)->start_time); \
     } while (0)
 extern void psc_new_tests(PSC_STATE *);
 extern void psc_parse_tests(PSC_STATE *, const char *, time_t);
+extern void psc_todo_tests(PSC_STATE *, time_t);
 extern char *psc_print_tests(VSTRING *, PSC_STATE *);
 extern char *psc_print_grey_key(VSTRING *, const char *, const char *,
                                        const char *, const char *);
index e46487bcbcfd8f0758de14fd2830a3cbe2ce052c..267f0825f6c898a0b4f8f5003f491406b36e92c0 100644 (file)
@@ -87,8 +87,8 @@ static void psc_whitelist_non_dnsbl(PSC_STATE *state)
                state->flags |= PSC_STATE_FLAG_BYTINDX_PASS(tindx);
            }
            /* Update expiration even if the test was completed or disabled. */
-           if (state->expire_time[tindx] < now + var_psc_dnsbl_ttl)
-               state->expire_time[tindx] = now + var_psc_dnsbl_ttl;
+           if (state->client_info->expire_time[tindx] < now + var_psc_dnsbl_ttl)
+               state->client_info->expire_time[tindx] = now + var_psc_dnsbl_ttl;
        }
     }
 }
index 57a1bb67ddcf82adce9c1971c4bc6bc6c443856f..d54b24f9df057472e6c5b1d02c9ff4fb0947d1b5 100644 (file)
@@ -105,13 +105,17 @@ void    psc_conclude(PSC_STATE *state)
     if ((state->flags & PSC_STATE_MASK_ANY_PASS) != 0
        && (state->flags & PSC_STATE_MASK_ANY_PASS) ==
        PSC_STATE_FLAGS_TODO_TO_PASS(state->flags & PSC_STATE_MASK_ANY_TODO))
-       msg_info("PASS %s [%s]:%s", (state->flags & PSC_STATE_FLAG_NEW) == 0 ?
+       msg_info("PASS %s [%s]:%s", (state->flags & PSC_STATE_FLAG_NEW) == 0
+                || state->client_info->pass_new_count++ > 0 ?
                 "OLD" : "NEW", PSC_CLIENT_ADDR_PORT(state));
 
     /*
      * Update the postscreen cache. This still supports a scenario where a
      * client gets whitelisted in the course of multiple sessions, as long as
-     * that client does not "fail" any test.
+     * that client does not "fail" any test. Don't try to optimize away cache
+     * updates; we want cached information to be up-to-date even if a test
+     * result is renewed during overlapping SMTP sessions, and even if
+     * 'postfix reload' happens in the middle of that.
      */
     if ((state->flags & PSC_STATE_MASK_ANY_UPDATE) != 0
        && psc_cache_map != 0) {
index 946afd6fa58c5170b5cd4ec16a6584f8d2f98e9a..b84f6656be72c199982adfe441e1964da8a4a2d7 100644 (file)
@@ -61,7 +61,8 @@
 /*     psc_new_session_state() creates a new session state object
 /*     for the specified client stream, and increments the
 /*     psc_check_queue_length counter.  The flags and per-test time
-/*     stamps are initialized with PSC_INIT_TESTS().  The addr and
+/*     stamps are initialized with PSC_INIT_TESTS(), or for concurrent
+/*     sessions, with PSC_INIT_TEST_FLAGS_ONLY().  The addr and
 /*     port arguments are null-terminated strings with the remote
 /*     SMTP client endpoint. The _reply members are set to
 /*     polite "try again" SMTP replies. The protocol member is set
@@ -149,10 +150,8 @@ PSC_STATE *psc_new_session_state(VSTREAM *stream,
                                         const char *server_port)
 {
     PSC_STATE *state;
-    HTABLE_INFO *ht;
 
     state = (PSC_STATE *) mymalloc(sizeof(*state));
-    PSC_INIT_TESTS(state);
     if ((state->smtp_client_stream = stream) != 0)
        psc_check_queue_length++;
     state->smtp_server_fd = (-1);
@@ -188,10 +187,19 @@ PSC_STATE *psc_new_session_state(VSTREAM *stream,
     /*
      * Update the per-client session count.
      */
-    if ((ht = htable_locate(psc_client_concurrency, client_addr)) == 0)
-       ht = htable_enter(psc_client_concurrency, client_addr, (void *) 0);
-    ht->value += 1;
-    state->client_concurrency = CAST_ANY_PTR_TO_INT(ht->value);
+    if ((state->client_info = (PSC_CLIENT_INFO *)
+        htable_find(psc_client_concurrency, client_addr)) == 0) {
+       state->client_info = (PSC_CLIENT_INFO *)
+           mymalloc(sizeof(state->client_info[0]));
+       (void) htable_enter(psc_client_concurrency, client_addr,
+                           (void *) state->client_info);
+       PSC_INIT_TESTS(state);
+       state->client_info->concurrency = 1;
+       state->client_info->pass_new_count = 0;
+    } else {
+       PSC_INIT_TEST_FLAGS_ONLY(state);
+       state->client_info->concurrency += 1;
+    }
 
     return (state);
 }
@@ -210,9 +218,8 @@ void    psc_free_session_state(PSC_STATE *state)
                            state->smtp_client_addr)) == 0)
        msg_panic("%s: unknown client address: %s",
                  myname, state->smtp_client_addr);
-    if (--(ht->value) == 0)
-       htable_delete(psc_client_concurrency, state->smtp_client_addr,
-                     (void (*) (void *)) 0);
+    if (--(state->client_info->concurrency) == 0)
+       htable_delete(psc_client_concurrency, state->smtp_client_addr, myfree);
 
     if (state->smtp_client_stream != 0) {
        event_server_disconnect(state->smtp_client_stream);
index 17b7a3926149a951c637d6a160d72a03c0c6a10c..f8b92a9888ece7200c9733b3147edf4d158074af 100644 (file)
 /*     const char *stamp_text;
 /*     time_t time_value;
 /*
+/*     void    psc_todo_tests(state, time_value)
+/*     PSC_STATE *state;
+/*     const char *stamp_text;
+/*     time_t time_value;
+/*
 /*     char    *psc_print_tests(buffer, state)
 /*     VSTRING *buffer;
 /*     PSC_STATE *state;
 /*     zeroes all the flags bits. These values are not meant to
 /*     be stored into the postscreen(8) cache.
 /*
+/*     PSC_INIT_TEST_FLAGS_ONLY() zeroes all the flag bits.  It
+/*     should be used when the time stamps are already initialized.
+/*
 /*     psc_new_tests() sets all test expiration time stamps to
-/*     PSC_TIME_STAMP_NEW, and overwrites all flags bits. Only
-/*     enabled tests are flagged with PSC_STATE_FLAG_TODO; the
-/*     object is flagged with PSC_STATE_FLAG_NEW.
+/*     PSC_TIME_STAMP_NEW, and invokes psc_todo_tests().
+/*
+/*     psc_parse_tests() parses a cache file record and invokes
+/*     psc_todo_tests().
 /*
-/*     psc_parse_tests() parses a cache file record and overwrites
-/*     all flags bits. Tests are considered "expired" when they
+/*     psc_todo_tests() overwrites all per-session flag bits, and
+/*     populates the flags based on test expiration time stamp
+/*     information.  Tests are considered "expired" when they
 /*     would be expired at the specified time value. Only enabled
 /*     tests are flagged as "expired"; the object is flagged as
 /*     "new" if some enabled tests have "new" time stamps.
 void    psc_new_tests(PSC_STATE *state)
 {
 
-    /*
-     * We know this client is brand new.
-     */
-    state->flags = PSC_STATE_FLAG_NEW;
-
     /*
      * Give all tests a PSC_TIME_STAMP_NEW time stamp, so that we can later
      * recognize cache entries that haven't passed all enabled tests. When we
@@ -142,19 +147,9 @@ void    psc_new_tests(PSC_STATE *state)
     state->barlf_stamp = PSC_TIME_STAMP_NEW;
 
     /*
-     * Don't flag disabled tests as "todo", because there would be no way to
-     * make those bits go away.
+     * Determine what tests need to be completed.
      */
-    if (PSC_PREGR_TEST_ENABLE())
-       state->flags |= PSC_STATE_FLAG_PREGR_TODO;
-    if (PSC_DNSBL_TEST_ENABLE())
-       state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
-    if (var_psc_pipel_enable)
-       state->flags |= PSC_STATE_FLAG_PIPEL_TODO;
-    if (var_psc_nsmtp_enable)
-       state->flags |= PSC_STATE_FLAG_NSMTP_TODO;
-    if (var_psc_barlf_enable)
-       state->flags |= PSC_STATE_FLAG_BARLF_TODO;
+    psc_todo_tests(state, PSC_TIME_STAMP_NEW + 1);
 }
 
 /* psc_parse_tests - parse test results from cache */
@@ -165,29 +160,18 @@ void    psc_parse_tests(PSC_STATE *state,
 {
     const char *start = stamp_str;
     char   *cp;
-    time_t *time_stamps = state->expire_time;
+    time_t *time_stamps = state->client_info->expire_time;
     time_t *sp;
 
-    /*
-     * We don't know what tests have expired or have never passed.
-     */
-    state->flags = 0;
-
     /*
      * Parse the cache entry, and allow for older postscreen versions that
      * implemented fewer tests. We pretend that the newer tests were disabled
      * at the time that the cache entry was written.
-     * 
-     * Flag the cache entry as "new" when the cache entry has fields for all
-     * enabled tests, but the remote SMTP client has not yet passed all those
-     * tests.
      */
     for (sp = time_stamps; sp < time_stamps + PSC_TINDX_COUNT; sp++) {
        *sp = strtoul(start, &cp, 10);
        if (*start == 0 || (*cp != '\0' && *cp != ';') || errno == ERANGE)
            *sp = PSC_TIME_STAMP_DISABLED;
-       if (*sp == PSC_TIME_STAMP_NEW)
-           state->flags |= PSC_STATE_FLAG_NEW;
        if (msg_verbose)
            msg_info("%s -> %lu", start, (unsigned long) *sp);
        if (*cp == ';')
@@ -196,6 +180,34 @@ void    psc_parse_tests(PSC_STATE *state,
            start = cp;
     }
 
+    /*
+     * Determine what tests need to be completed.
+     */
+    psc_todo_tests(state, time_value);
+}
+
+/* psc_todo_tests - determine what tests to perform */
+
+void    psc_todo_tests(PSC_STATE *state, time_t time_value)
+{
+    time_t *time_stamps = state->client_info->expire_time;
+    time_t *sp;
+
+    /*
+     * Reset all per-session flags.
+     */
+    state->flags = 0;
+
+    /*
+     * Flag the tests as "new" when the cache entry has fields for all
+     * enabled tests, but the remote SMTP client has not yet passed all those
+     * tests.
+     */
+    for (sp = time_stamps; sp < time_stamps + PSC_TINDX_COUNT; sp++) {
+       if (*sp == PSC_TIME_STAMP_NEW)
+           state->flags |= PSC_STATE_FLAG_NEW;
+    }
+
     /*
      * Don't flag disabled tests as "todo", because there would be no way to
      * make those bits go away.
index 0e821367d767123960d110788d930caa87ac9c0b..49b2b32bec0c878ef1086361a4fa2a4e44c2cc14 100644 (file)
@@ -430,7 +430,7 @@ static int postrename(const char *old, const char *new)
     if ((ret = sane_rename(old, new)) < 0) {
        if (errno != ENOENT
            || mail_queue_mkdirs(new) < 0
-           || sane_rename(old, new) < 0)
+           || (ret = sane_rename(old, new)) < 0)
            if (errno != ENOENT)
                msg_fatal("rename file %s as %s: %m", old, new);
     } else {
index 91106b742940232934ad35fd04d59ae1bd9757c5..afffa5f93953aa6ca39becd1f8ff289ecfea992b 100644 (file)
@@ -533,6 +533,7 @@ smtpd_resolve.o: ../../include/mymalloc.h
 smtpd_resolve.o: ../../include/nvtable.h
 smtpd_resolve.o: ../../include/resolve_clnt.h
 smtpd_resolve.o: ../../include/rewrite_clnt.h
+smtpd_resolve.o: ../../include/split_at.h
 smtpd_resolve.o: ../../include/stringops.h
 smtpd_resolve.o: ../../include/sys_defs.h
 smtpd_resolve.o: ../../include/vbuf.h
index b65ec648c02aabca55d9d2f94fe66e38490892c9..12360af186d7e2fed442414fbbf0ee9792129729 100644 (file)
@@ -1214,6 +1214,7 @@ char   *var_rest_classes;
 int     var_strict_rfc821_env;
 bool    var_disable_vrfy_cmd;
 char   *var_canonical_maps;
+char   *var_send_canon_maps;
 char   *var_rcpt_canon_maps;
 char   *var_virt_alias_maps;
 char   *var_virt_mailbox_maps;
@@ -2222,7 +2223,9 @@ static int extract_addr(SMTPD_STATE *state, SMTPD_TOKEN *arg,
        if ((STR(state->addr_buf)[0] == 0 && !allow_empty_addr)
            || (strict_rfc821 && STR(state->addr_buf)[0] == '@')
            || (SMTPD_STAND_ALONE(state) == 0
-               && smtpd_check_addr(STR(state->addr_buf), smtputf8) != 0)) {
+            && smtpd_check_addr(strcmp(state->where, SMTPD_CMD_MAIL) == 0 ?
+                                state->recipient : state->sender,
+                                STR(state->addr_buf), smtputf8) != 0)) {
            msg_warn("Illegal address syntax from %s in %s command: %s",
                     state->namaddr, state->where,
                     printable(STR(arg->vstrval), '?'));
@@ -2272,7 +2275,6 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
     char   *verp_delims = 0;
     int     rate;
     int     dsn_envid = 0;
-    int     smtputf8 = 0;
 
     state->flags &= ~SMTPD_FLAG_SMTPUTF8;
     state->encoding = 0;
@@ -2339,13 +2341,15 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        for (narg = 3; narg < argc; narg++) {
            arg = argv[narg].strval;
            if (strcasecmp(arg, "SMTPUTF8") == 0) {     /* RFC 6531 */
-               smtputf8 = 1;
+               /* Fix 20161206: allow UTF8 in smtpd_sender_restrictions. */
+               state->flags |= SMTPD_FLAG_SMTPUTF8;
                break;
            }
        }
     }
     if (extract_addr(state, argv + 2, PERMIT_EMPTY_ADDR,
-                    var_strict_rfc821_env, smtputf8) != 0) {
+                    var_strict_rfc821_env,
+                    state->flags & SMTPD_FLAG_SMTPUTF8) != 0) {
        state->error_mask |= MAIL_ERROR_PROTOCOL;
        smtpd_chat_reply(state, "501 5.1.7 Bad sender address syntax");
        return (-1);
@@ -2545,8 +2549,6 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        state->verp_delims = mystrdup(verp_delims);
     if (dsn_envid)
        state->dsn_envid = mystrdup(STR(state->dsn_buf));
-    if (smtputf8)
-       state->flags |= SMTPD_FLAG_SMTPUTF8;
     if (USE_SMTPD_PROXY(state))
        state->proxy_mail = mystrdup(STR(state->buffer));
     if (var_smtpd_delay_open == 0 && mail_open_stream(state) < 0) {
@@ -3519,6 +3521,7 @@ static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
     const char *err = 0;
     int     rate;
     int     smtputf8 = 0;
+    int     saved_flags;
 
     /*
      * The SMTP standard (RFC 821) disallows unquoted special characters in
@@ -3605,10 +3608,17 @@ static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        }
     }
     /* Use state->addr_buf, with the unquoted result from extract_addr() */
-    if (SMTPD_STAND_ALONE(state) == 0
-       && (err = smtpd_check_rcpt(state, STR(state->addr_buf))) != 0) {
-       smtpd_chat_reply(state, "%s", err);
-       return (-1);
+    if (SMTPD_STAND_ALONE(state) == 0) {
+       /* Fix 20161206: allow UTF8 in smtpd_recipient_restrictions. */
+       saved_flags = state->flags;
+       if (smtputf8)
+           state->flags |= SMTPD_FLAG_SMTPUTF8;
+       err = smtpd_check_rcpt(state, STR(state->addr_buf));
+       state->flags = saved_flags;
+       if (err != 0) {
+           smtpd_chat_reply(state, "%s", err);
+           return (-1);
+       }
     }
 
     /*
@@ -5682,6 +5692,7 @@ int     main(int argc, char **argv)
        VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0,
        VAR_REST_CLASSES, DEF_REST_CLASSES, &var_rest_classes, 0, 0,
        VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps, 0, 0,
+       VAR_SEND_CANON_MAPS, DEF_SEND_CANON_MAPS, &var_send_canon_maps, 0, 0,
        VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps, 0, 0,
        VAR_VIRT_ALIAS_MAPS, DEF_VIRT_ALIAS_MAPS, &var_virt_alias_maps, 0, 0,
        VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps, 0, 0,
index cafc69d1e6503d5a394b2ddb2eddf09f451ffdd4..28ee1287ed8a7686423a4fa68cc4e2d769a9d81b 100644 (file)
@@ -9,7 +9,8 @@
 /*
 /*     void    smtpd_check_init()
 /*
-/*     int     smtpd_check_addr(address, smtputf8)
+/*     int     smtpd_check_addr(sender, address, smtputf8)
+/*     const char *sender;
 /*     const char *address;
 /*     int     smtputf8;
 /*
@@ -57,7 +58,9 @@
 /*     once during the process life time.
 /*
 /*     smtpd_check_addr() sanity checks an email address and returns
-/*     non-zero in case of badness.
+/*     non-zero in case of badness. The sender argument provides sender
+/*     context for address resolution and caching, or a null pointer
+/*     if information is unavailable.
 /*
 /*     smtpd_check_rewrite() should be called before opening a queue
 /*     file or proxy connection, in order to establish the proper
@@ -278,6 +281,7 @@ static CTABLE *smtpd_rbl_byte_cache;
   * trivial-rewrite resolver.
   */
 static MAPS *local_rcpt_maps;
+static MAPS *send_canon_maps;
 static MAPS *rcpt_canon_maps;
 static MAPS *canonical_maps;
 static MAPS *virt_alias_maps;
@@ -346,7 +350,8 @@ static int generic_checks(SMTPD_STATE *, ARGV *, const char *, const char *, con
   */
 static int check_sender_rcpt_maps(SMTPD_STATE *, const char *);
 static int check_recipient_rcpt_maps(SMTPD_STATE *, const char *);
-static int check_rcpt_maps(SMTPD_STATE *, const char *, const char *);
+static int check_rcpt_maps(SMTPD_STATE *, const char *, const char *,
+                                  const char *);
 
  /*
   * Tempfail actions;
@@ -730,6 +735,9 @@ void    smtpd_check_init(void)
     local_rcpt_maps = maps_create(VAR_LOCAL_RCPT_MAPS, var_local_rcpt_maps,
                                  DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
                                  | DICT_FLAG_UTF8_REQUEST);
+    send_canon_maps = maps_create(VAR_SEND_CANON_MAPS, var_send_canon_maps,
+                                 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+                                 | DICT_FLAG_UTF8_REQUEST);
     rcpt_canon_maps = maps_create(VAR_RCPT_CANON_MAPS, var_rcpt_canon_maps,
                                  DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
                                  | DICT_FLAG_UTF8_REQUEST);
@@ -1595,7 +1603,7 @@ static int permit_auth_destination(SMTPD_STATE *state, char *recipient)
     /*
      * Resolve the address.
      */
-    reply = smtpd_resolve_addr(recipient);
+    reply = smtpd_resolve_addr(state->sender, recipient);
     if (reply->flags & RESOLVE_FLAG_FAIL)
        reject_dict_retry(state, recipient);
 
@@ -1890,7 +1898,7 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient,
     /*
      * Resolve the address.
      */
-    reply = smtpd_resolve_addr(recipient);
+    reply = smtpd_resolve_addr(state->sender, recipient);
     if (reply->flags & RESOLVE_FLAG_FAIL)
        reject_dict_retry(state, recipient);
 
@@ -2078,7 +2086,8 @@ static int reject_unknown_address(SMTPD_STATE *state, const char *addr,
     /*
      * Resolve the address.
      */
-    reply = smtpd_resolve_addr(addr);
+    reply = smtpd_resolve_addr(strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
+                              state->recipient : state->sender, addr);
     if (reply->flags & RESOLVE_FLAG_FAIL)
        reject_dict_retry(state, addr);
 
@@ -3130,7 +3139,8 @@ static int check_mail_access(SMTPD_STATE *state, const char *table,
     /*
      * Resolve the address.
      */
-    reply = smtpd_resolve_addr(addr);
+    reply = smtpd_resolve_addr(strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
+                              state->recipient : state->sender, addr);
     if (reply->flags & RESOLVE_FLAG_FAIL)
        reject_dict_retry(state, addr);
 
@@ -3781,7 +3791,7 @@ static int reject_auth_sender_login_mismatch(SMTPD_STATE *state, const char *sen
      * Reject if the client is logged in and does not own the sender address.
      */
     if (smtpd_sender_login_maps && state->sasl_username) {
-       reply = smtpd_resolve_addr(sender);
+       reply = smtpd_resolve_addr(state->recipient, sender);
        if (reply->flags & RESOLVE_FLAG_FAIL)
            reject_dict_retry(state, sender);
        if ((owners = check_mail_addr_find(state, sender, smtpd_sender_login_maps,
@@ -3815,7 +3825,7 @@ static int reject_unauth_sender_login_mismatch(SMTPD_STATE *state, const char *s
      * owner.
      */
     if (smtpd_sender_login_maps && !state->sasl_username) {
-       reply = smtpd_resolve_addr(sender);
+       reply = smtpd_resolve_addr(state->recipient, sender);
        if (reply->flags & RESOLVE_FLAG_FAIL)
            reject_dict_retry(state, sender);
        if (check_mail_addr_find(state, sender, smtpd_sender_login_maps,
@@ -4627,7 +4637,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
 
 /* smtpd_check_addr - address sanity check */
 
-int     smtpd_check_addr(const char *addr, int smtputf8)
+int     smtpd_check_addr(const char *sender, const char *addr, int smtputf8)
 {
     const RESOLVE_REPLY *resolve_reply;
     const char *myname = "smtpd_check_addr";
@@ -4643,7 +4653,7 @@ int     smtpd_check_addr(const char *addr, int smtputf8)
      */
     if (addr == 0 || *addr == 0)
        return (0);
-    resolve_reply = smtpd_resolve_addr(addr);
+    resolve_reply = smtpd_resolve_addr(sender, addr);
     if (resolve_reply->flags & RESOLVE_FLAG_ERROR)
        return (-1);
 
@@ -5053,7 +5063,8 @@ static int check_recipient_rcpt_maps(SMTPD_STATE *state, const char *recipient)
     if (state->warn_if_reject == 0)
        /* We really validate the recipient address. */
        state->recipient_rcptmap_checked = 1;
-    return (check_rcpt_maps(state, recipient, SMTPD_NAME_RECIPIENT));
+    return (check_rcpt_maps(state, state->sender, recipient,
+                           SMTPD_NAME_RECIPIENT));
 }
 
 /* check_sender_rcpt_maps - generic_checks() sender table check */
@@ -5072,24 +5083,26 @@ static int check_sender_rcpt_maps(SMTPD_STATE *state, const char *sender)
     if (state->warn_if_reject == 0)
        /* We really validate the sender address. */
        state->sender_rcptmap_checked = 1;
-    return (check_rcpt_maps(state, sender, SMTPD_NAME_SENDER));
+    return (check_rcpt_maps(state, state->recipient, sender,
+                           SMTPD_NAME_SENDER));
 }
 
 /* check_rcpt_maps - generic_checks() interface for recipient table check */
 
-static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient,
+static int check_rcpt_maps(SMTPD_STATE *state, const char *sender,
+                                  const char *recipient,
                                   const char *reply_class)
 {
     const RESOLVE_REPLY *reply;
     DSN_SPLIT dp;
 
     if (msg_verbose)
-       msg_info(">>> CHECKING RECIPIENT MAPS <<<");
+       msg_info(">>> CHECKING %s VALIDATION MAPS <<<", reply_class);
 
     /*
      * Resolve the address.
      */
-    reply = smtpd_resolve_addr(recipient);
+    reply = smtpd_resolve_addr(sender, recipient);
     if (reply->flags & RESOLVE_FLAG_FAIL)
        reject_dict_retry(state, recipient);
 
@@ -5110,6 +5123,8 @@ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient,
      * domains.
      */
     if (MATCH(rcpt_canon_maps, CONST_STR(reply->recipient))
+       || (strcmp(reply_class, SMTPD_NAME_SENDER) == 0
+           && MATCH(send_canon_maps, CONST_STR(reply->recipient)))
        || MATCH(canonical_maps, CONST_STR(reply->recipient))
        || MATCH(virt_alias_maps, CONST_STR(reply->recipient)))
        return (0);
@@ -5456,6 +5471,7 @@ char   *var_proxy_interfaces;
 char   *var_rcpt_delim;
 char   *var_rest_classes;
 char   *var_alias_maps;
+char   *var_send_canon_maps;
 char   *var_rcpt_canon_maps;
 char   *var_canonical_maps;
 char   *var_virt_alias_maps;
@@ -5506,6 +5522,7 @@ static const STRING_TABLE string_table[] = {
     VAR_RCPT_DELIM, DEF_RCPT_DELIM, &var_rcpt_delim,
     VAR_REST_CLASSES, DEF_REST_CLASSES, &var_rest_classes,
     VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps,
+    VAR_SEND_CANON_MAPS, DEF_SEND_CANON_MAPS, &var_send_canon_maps,
     VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps,
     VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps,
     VAR_VIRT_ALIAS_MAPS, DEF_VIRT_ALIAS_MAPS, &var_virt_alias_maps,
@@ -6057,6 +6074,22 @@ int     main(int argc, char **argv)
                resp = 0;
                break;
            }
+           if (strcasecmp(args->argv[0], VAR_SEND_CANON_MAPS) == 0) {
+               UPDATE_STRING(var_send_canon_maps, args->argv[1]);
+               UPDATE_MAPS(send_canon_maps, VAR_SEND_CANON_MAPS,
+                           var_send_canon_maps, DICT_FLAG_LOCK
+                           | DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST);
+               resp = 0;
+               break;
+           }
+           if (strcasecmp(args->argv[0], VAR_RCPT_CANON_MAPS) == 0) {
+               UPDATE_STRING(var_rcpt_canon_maps, args->argv[1]);
+               UPDATE_MAPS(rcpt_canon_maps, VAR_RCPT_CANON_MAPS,
+                           var_rcpt_canon_maps, DICT_FLAG_LOCK
+                           | DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST);
+               resp = 0;
+               break;
+           }
            if (strcasecmp(args->argv[0], VAR_RBL_REPLY_MAPS) == 0) {
                UPDATE_STRING(var_rbl_reply_maps, args->argv[1]);
                UPDATE_MAPS(rbl_reply_maps, VAR_RBL_REPLY_MAPS,
index a654cd6e09c528bafda9b92a39c657b94e415830..e7f440145331098ea55e4243426a8073a375ccf7 100644 (file)
@@ -13,7 +13,7 @@
   * External interface.
   */
 extern void smtpd_check_init(void);
-extern int smtpd_check_addr(const char *, int);
+extern int smtpd_check_addr(const char *, const char *, int);
 extern char *smtpd_check_rewrite(SMTPD_STATE *);
 extern char *smtpd_check_client(SMTPD_STATE *);
 extern char *smtpd_check_helo(SMTPD_STATE *, char *);
index 833148a7e65bd48a0a6df7eed6770405204628e9..0d46d917ffe1b2bdca0149ed7fc847c6dd660cea 100644 (file)
@@ -160,7 +160,7 @@ const char *smtpd_milter_eval(const char *name, void *ptr)
            return (0);
        if (state->sender[0] == 0)
            return ("");
-       reply = smtpd_resolve_addr(state->sender);
+       reply = smtpd_resolve_addr(state->recipient, state->sender);
        /* Sendmail 8.13 does not externalize the null string. */
        if (STR(reply->recipient)[0])
            quote_821_local(state->expand_buf, STR(reply->recipient));
@@ -171,13 +171,13 @@ const char *smtpd_milter_eval(const char *name, void *ptr)
     if (strcmp(name, S8_MAC_MAIL_HOST) == 0) {
        if (state->sender == 0)
            return (0);
-       reply = smtpd_resolve_addr(state->sender);
+       reply = smtpd_resolve_addr(state->recipient, state->sender);
        return (STR(reply->nexthop));
     }
     if (strcmp(name, S8_MAC_MAIL_MAILER) == 0) {
        if (state->sender == 0)
            return (0);
-       reply = smtpd_resolve_addr(state->sender);
+       reply = smtpd_resolve_addr(state->recipient, state->sender);
        return (STR(reply->transport));
     }
 
@@ -195,7 +195,7 @@ const char *smtpd_milter_eval(const char *name, void *ptr)
            cp = split_at(STR(state->expand_buf), ' ');
            return (cp ? split_at(cp, ' ') : cp);
        }
-       reply = smtpd_resolve_addr(state->recipient);
+       reply = smtpd_resolve_addr(state->sender, state->recipient);
        /* Sendmail 8.13 does not externalize the null string. */
        if (STR(reply->recipient)[0])
            quote_821_local(state->expand_buf, STR(reply->recipient));
@@ -212,7 +212,7 @@ const char *smtpd_milter_eval(const char *name, void *ptr)
            (void) split_at(STR(state->expand_buf), ' ');
            return (STR(state->expand_buf));
        }
-       reply = smtpd_resolve_addr(state->recipient);
+       reply = smtpd_resolve_addr(state->sender, state->recipient);
        return (STR(reply->nexthop));
     }
     if (strcmp(name, S8_MAC_RCPT_MAILER) == 0) {
@@ -220,7 +220,7 @@ const char *smtpd_milter_eval(const char *name, void *ptr)
            return (0);
        if (state->milter_reject_text)
            return (S8_RCPT_MAILER_ERROR);
-       reply = smtpd_resolve_addr(state->recipient);
+       reply = smtpd_resolve_addr(state->sender, state->recipient);
        return (STR(reply->transport));
     }
     return (0);
index 7cd8e4cda03fe47da15c154306f506d57ac7270f..0adf6904e6b59fafa62cd4eeb2047da814901166 100644 (file)
@@ -9,7 +9,8 @@
 /*     void    smtpd_resolve_init(cache_size)
 /*     int     cache_size;
 /*
-/*     const RESOLVE_REPLY *smtpd_resolve_addr(addr)
+/*     const RESOLVE_REPLY *smtpd_resolve_addr(sender, addr)
+/*     const char *sender;
 /*     const char *addr;
 /* DESCRIPTION
 /*     This module maintains a resolve client cache that persists
@@ -26,6 +27,8 @@
 /*     Arguments:
 /* .IP cache_size
 /*     The requested cache size.
+/* .IP sender
+/*     The message sender, or null pointer.
 /* .IP addr
 /*     The address to resolve.
 /* DIAGNOSTICS
@@ -56,6 +59,7 @@
 #include <vstring.h>
 #include <ctable.h>
 #include <stringops.h>
+#include <split_at.h>
 
 /* Global library. */
 
 static CTABLE *smtpd_resolve_cache;
 
 #define STR(x) vstring_str(x)
+#define SENDER_ADDR_JOIN_CHAR '\n'
 
 /* resolve_pagein - page in an address resolver result */
 
-static void *resolve_pagein(const char *addr, void *unused_context)
+static void *resolve_pagein(const char *sender_plus_addr, void *unused_context)
 {
+    const char myname[] = "resolve_pagein";
     static VSTRING *query;
+    static VSTRING *junk;
+    static VSTRING *sender_buf;
     RESOLVE_REPLY *reply;
-    char   *tmp;
+    const char *sender;
+    const char *addr;
 
     /*
      * Initialize on the fly.
      */
-    if (query == 0)
+    if (query == 0) {
        query = vstring_alloc(10);
+       junk = vstring_alloc(10);
+       sender_buf = vstring_alloc(10);
+    }
 
     /*
      * Initialize.
@@ -91,14 +103,22 @@ static void *resolve_pagein(const char *addr, void *unused_context)
     reply = (RESOLVE_REPLY *) mymalloc(sizeof(*reply));
     resolve_clnt_init(reply);
 
+    /*
+     * Split the sender and address.
+     */
+    vstring_strcpy(junk, sender_plus_addr);
+    sender = STR(junk);
+    if ((addr = split_at(STR(junk), SENDER_ADDR_JOIN_CHAR)) == 0)
+       msg_panic("%s: bad search key: \"%s\"", myname, sender_plus_addr);
+
     /*
      * Resolve the address.
      */
+    rewrite_clnt_internal(MAIL_ATTR_RWR_LOCAL, sender, sender_buf);
     rewrite_clnt_internal(MAIL_ATTR_RWR_LOCAL, addr, query);
-    resolve_clnt_query(STR(query), reply);
-    tmp = mystrdup(STR(reply->recipient));
-    casefold(reply->recipient, tmp);           /* XXX */
-    myfree(tmp);
+    resolve_clnt_query_from(STR(sender_buf), STR(query), reply);
+    vstring_strcpy(junk, STR(reply->recipient));
+    casefold(reply->recipient, STR(junk));     /* XXX */
 
     /*
      * Save the result.
@@ -136,10 +156,17 @@ void    smtpd_resolve_init(int cache_size)
                                        resolve_pageout, (void *) 0);
 }
 
-/* smtpd_resolve_addr - resolve cached addres */
+/* smtpd_resolve_addr - resolve cached address */
 
-const RESOLVE_REPLY *smtpd_resolve_addr(const char *addr)
+const RESOLVE_REPLY *smtpd_resolve_addr(const char *sender, const char *addr)
 {
+    static VSTRING *sender_plus_addr_buf;
+
+    /*
+     * Initialize on the fly.
+     */
+    if (sender_plus_addr_buf == 0)
+       sender_plus_addr_buf = vstring_alloc(10);
 
     /*
      * Sanity check.
@@ -150,5 +177,9 @@ const RESOLVE_REPLY *smtpd_resolve_addr(const char *addr)
     /*
      * Reply from the read-through cache.
      */
-    return (const RESOLVE_REPLY *) ctable_locate(smtpd_resolve_cache, addr);
+    vstring_sprintf(sender_plus_addr_buf, "%s%c%s",
+                   sender ? sender : RESOLVE_NULL_FROM,
+                   SENDER_ADDR_JOIN_CHAR, addr);
+    return (const RESOLVE_REPLY *)
+       ctable_locate(smtpd_resolve_cache, STR(sender_plus_addr_buf));
 }
index bfbc494dad0bd7443ebd94c266edbc72e1014f1b..c04c52a7a9157d0895a72092abefd69ff980ac9b 100644 (file)
@@ -17,7 +17,7 @@
   * External interface.
   */
 extern void smtpd_resolve_init(int);
-extern const RESOLVE_REPLY *smtpd_resolve_addr(const char *);
+extern const RESOLVE_REPLY *smtpd_resolve_addr(const char*, const char *);
 
 /* LICENSE
 /* .ad
index e04c42682c1208de33662a48c11dc98b8f04fa0f..51e646de7af1e487d0902745030a39714249e540 100644 (file)
@@ -80,7 +80,6 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
     int     mail_copy_status;
     int     deliver_status;
     int     copy_flags;
-    long    end;
     struct stat st;
 
     /*
@@ -132,7 +131,9 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
            msg_warn("specify \"%s = no\" to ignore mailbox ownership mismatch",
                     VAR_STRICT_MBOX_OWNER);
        } else {
-           end = vstream_fseek(mp->fp, (off_t) 0, SEEK_END);
+           if (vstream_fseek(mp->fp, (off_t) 0, SEEK_END) < 0)
+               msg_fatal("%s: seek queue file %s: %m",
+                         myname, VSTREAM_PATH(mp->fp));
            mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
                                         copy_flags, "\n", why);
        }