]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.2.11-RC1 v2.2.11-RC1
authorWietse Venema <wietse@porcupine.org>
Sat, 22 Jul 2006 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Sat, 10 Feb 2018 21:16:10 +0000 (16:16 -0500)
20 files changed:
postfix/HISTORY
postfix/README_FILES/PGSQL_README
postfix/README_FILES/SMTPD_POLICY_README
postfix/RELEASE_NOTES
postfix/html/PGSQL_README.html
postfix/html/SMTPD_POLICY_README.html
postfix/proto/PGSQL_README.html
postfix/proto/SMTPD_POLICY_README.html
postfix/src/global/dict_pgsql.c
postfix/src/global/hold_message.c
postfix/src/global/mail_proto.h
postfix/src/global/mail_version.h
postfix/src/global/tok822_parse.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtp/smtp_reuse.c
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd_check.c
postfix/src/tls/tls_prng_exch.c
postfix/src/util/safe_open.c
postfix/src/util/sys_defs.h

index 0c85ec7f444b48b2343aa29aa2b143d4eb73fbcf..982eb16fe15ec974bdf8fb4ff096be28429c8cf5 100644 (file)
@@ -10920,3 +10920,94 @@ Apologies for any names omitted.
        sick that you would have more serious problems than a file
        descriptor leak.  Found by Coverity.  Files: local/maildir.c,
        virtual/maildir.c.
+
+20060411
+
+       Bugfix: the SMTP server logged no warning when for some
+       reason the TLS engine was unavailable in wrappermode. Victor
+       Duchovni.  File: smtpd/smtpd.c.
+
+20060430
+
+       Bugfix: dangling pointer in a function that has no caller.
+       Found by Coverity. File: tls/tls_prng_exch.c.
+
+       Bugfix: the workaround for CA-2003-07 (Sendmail) did not
+       null terminate the address before logging a warning. Reported
+       by Kris Kennaway. File: global/tok822_parse.c.
+
+20060516
+
+       Portability: __float80 alignment, by Albert Chin.  File:
+       util/sys_defs.h.
+
+20060524
+
+       Cleanup: send ETRN command parameter when using check_policy
+       in the context of an ETRN command. Joshua Goodall. File:
+       smtpd/smtpd_check.c.
+
+20060604
+
+       Bugfix: the HOLD feature in access or header/body_checks
+       tables didn't properly report subdirectory create errors
+       (which don't happen because the hold queue isn't hashed by
+       default). Found with the BEAM code scanner. File:
+       global/hold_message.c.
+
+       Cleanup: minor fluff found with the BEAM source code analyzer.
+       File: util/safe_open.c.
+
+20060611
+
+       Security: the PostgreSQL client was updated after major
+       database API changes in response to PostgreSQL security
+       issues. This breaks support for PGSQL versions prior to
+       8.1.4, 8.0.8, 7.4.13, and 7.3.15. Support for these requires
+       major code changes which are not possible in the time that
+       is left for the Postfix 2.3 stable release.
+
+       Specific PostgreSQL client changes: use connection-aware
+       quoting, and more robust PQexec() result handling.  Previous
+       versions of the dict_pgsql driver didn't check the status
+       of the result pointer, and certain exceptional events can
+       be mis-interpreted as an empty result set.  Fixes by Leandro
+       Santi. File: global/dict_pgsql.c.
+
+20060626
+
+       Workaround: disable SMTP connection cache lookup by server
+       IP address when the tls_per_site policy table is enabled.
+       Different server names may resolve to the same IP address,
+       and we don't want to use the wrong TLS policy.  File:
+       smtp/smtp_reuse.c.
+
+20060706
+
+       Workaround: disable SMTP connection cache lookup by server
+       IP address when the smtp_sasl_password_maps password feature
+       is enabled.  Different server names may resolve to the same
+       IP address, and we don't want to use the wrong SASL
+       credentials. File: smtp/smtp_reuse.c.
+
+       Bugfix: don't reuse a plaintext connection when the TLS
+       policy requires encryption. This can happen because we have
+       to search the connection cache before we know the
+       server-dependent TLS policy. It does not eliminate the
+       problem that we may still be using the wrong policy because
+       the cached server name was obtained in an insecure manner.
+
+20060707
+
+       Robustness: the SMTPD policy client now encodes the
+       ccert_subject and ccert_issuer attributes as xtext.  Some
+       characters are replaced by +XX, where XX is the two-digit
+       hexadecimal code for the character value.  File:
+       smtpd/smtpd_check.c.
+
+20060716
+
+       Bugfix: the Postfix SMTP client enforced Mandatory TLS only
+       when talking to an ESMTP server. Victor Duchovni.  File:
+       src/smtp/smtp_proto.c.
+
index c7d8c279fcd2c6977afcd387107ca6bdd7d1036d..d6ecf4c7ec73a23ab09be65ffd0cac0798b37418 100644 (file)
@@ -102,4 +102,7 @@ C\bCr\bre\bed\bdi\bit\bts\bs
   * Liviu Daia with further refinements from Jose Luis Tallon and Victor
     Duchovni developed the common query, result_format, domain and
     expansion_limit interface for LDAP, MySQL and PosgreSQL.
+  * Leandro Santi updated the PostgreSQL client to reflect PostgreSQL security
+    issues with respect to quoting which resulted in major database API
+    changes, and made PQexec() handling more robust.
 
index 3f0a2cafc9abac170e564416b53f8b68f055a33f..94c05e0c4ad5698217f8e3cee07c893789e57309 100644 (file)
@@ -53,9 +53,10 @@ a delegated SMTPD access policy request:
     sasl_username=you
     sasl_sender=
     ccert_subject=solaris9.porcupine.org
-    ccert_issuer=Wietse Venema
+    ccert_issuer=Wietse+20Venema
     ccert_fingerprint=C2:9D:F4:87:71:73:73:D9:18:E7:C2:F3:C1:DA:6E:04
     size=12345
+    etrn_domain=
     [empty line]
 
 Notes:
@@ -90,7 +91,13 @@ Notes:
     how the client was authenticated via SASL.
 
   * The "ccert_*" attributes (Postfix 2.2 and later) specify information about
-    how the client was authenticated via TLS.
+    how the client was authenticated via TLS. As of Postfix 2.2.11 these
+    attribute values are encoded as xtext: some characters are represented by
+    +XX, where XX is the two-digit hecadecimal representation of the character
+    value.
+
+  * The "etrn_domain" attribute is defined only in the context of the ETRN
+    command, and specifies the ETRN command parameter.
 
 The following is specific to SMTPD delegated policy requests:
 
index e7e2cd84c57e1dfc49384c64f8f64a8074774ae0..6d01a3884fefeb13613ee3999b285c3c111a8ecc 100644 (file)
@@ -11,6 +11,20 @@ instead, a new snapshot is released.
 The mail_release_date configuration parameter (format: yyyymmdd)
 specifies the release date of a stable release or snapshot release.
 
+Incompatible changes with Postfix version 2.2.11
+------------------------------------------------
+
+The SMTPD policy client now encodes the ccert_subject and ccert_issuer
+attributes as xtext. Some characters are represented by +XX, where
+XX is the two-digit hecadecimal representation of the character
+value.
+
+The PostgreSQL client was updated after major database API changes
+in response to PostgreSQL security issues. This breaks support for
+PGSQL versions prior to 8.1.4, 8.0.8, 7.4.13, and 7.3.15. Support
+for these requires major code changes which are not possible in a
+stable release.
+
 Main changes with Postfix version 2.2
 -------------------------------------
 
index 254f580dd3291e55bf3d9ceca2921b0272c053e5..5f46d58d0aaadf62fb7daf8a63e521f24476f305 100644 (file)
@@ -139,6 +139,10 @@ configuration feature.</li>
 Victor Duchovni developed the common query, result_format, domain and
 expansion_limit interface for LDAP, MySQL and PosgreSQL.</li>
 
+<li> Leandro Santi updated the PostgreSQL client to reflect PostgreSQL
+security issues with respect to quoting which resulted in major
+database API changes, and made PQexec() handling more robust. </li>
+
 </ul>
 
 </body>
index 8cb3d4f4be05d5f2a16318c27742b56c434c9cc0..13198555d3aff04f5a1757156c251108a89e2c0b 100644 (file)
@@ -85,9 +85,10 @@ sasl_method=plain
 sasl_username=you
 sasl_sender=
 ccert_subject=solaris9.porcupine.org
-ccert_issuer=Wietse Venema
+ccert_issuer=Wietse+20Venema
 ccert_fingerprint=C2:9D:F4:87:71:73:73:D9:18:E7:C2:F3:C1:DA:6E:04
 size=12345
+etrn_domain=
 [empty line]
 </pre>
 </blockquote>
@@ -133,8 +134,14 @@ size=12345
     </p>
 
     <li> <p> The "ccert_*" attributes (Postfix 2.2 and later) specify
-    information about how the client was authenticated via TLS.
-    </p>
+    information about how the client was authenticated via TLS.  As
+    of Postfix 2.2.11 these attribute values are encoded as xtext:
+    some characters are represented by +XX, where XX is the two-digit
+    hecadecimal representation of the character value.  </p>
+
+    <li> <p> The "etrn_domain" attribute is defined only in the
+    context of the ETRN command, and specifies the ETRN command
+    parameter. </p>
 
 </ul>
 
index 167c45b9520ad35d9cc1f0bd7f8a961fa0b2d780..954c8a0d1595004f98ff9f3c6c54ebe2246cae43 100644 (file)
@@ -139,6 +139,10 @@ configuration feature.</li>
 Victor Duchovni developed the common query, result_format, domain and
 expansion_limit interface for LDAP, MySQL and PosgreSQL.</li>
 
+<li> Leandro Santi updated the PostgreSQL client to reflect PostgreSQL
+security issues with respect to quoting which resulted in major
+database API changes, and made PQexec() handling more robust. </li>
+
 </ul>
 
 </body>
index d745a08fbad29e9e36acc6c72e3203ed75d212f0..40fab3aef689dc0acdad1a1b529fc50184422275 100644 (file)
@@ -85,9 +85,10 @@ sasl_method=plain
 sasl_username=you
 sasl_sender=
 ccert_subject=solaris9.porcupine.org
-ccert_issuer=Wietse Venema
+ccert_issuer=Wietse+20Venema
 ccert_fingerprint=C2:9D:F4:87:71:73:73:D9:18:E7:C2:F3:C1:DA:6E:04
 size=12345
+etrn_domain=
 [empty line]
 </pre>
 </blockquote>
@@ -133,8 +134,14 @@ size=12345
     </p>
 
     <li> <p> The "ccert_*" attributes (Postfix 2.2 and later) specify
-    information about how the client was authenticated via TLS.
-    </p>
+    information about how the client was authenticated via TLS.  As
+    of Postfix 2.2.11 these attribute values are encoded as xtext:
+    some characters are represented by +XX, where XX is the two-digit
+    hecadecimal representation of the character value.  </p>
+
+    <li> <p> The "etrn_domain" attribute is defined only in the
+    context of the ETRN command, and specifies the ETRN command
+    parameter. </p>
 
 </ul>
 
index 9fe6768bfebc592dc1e8197ce674414fc146dccf..11ed847c2f519341008fe7cd009582fbbd3247b6 100644 (file)
@@ -217,6 +217,7 @@ typedef struct {
     char   *table;
     ARGV   *hosts;
     PLPGSQL *pldb;
+    HOST   *active_host;
 } DICT_PGSQL;
 
 
@@ -225,7 +226,8 @@ typedef struct {
 
 /* internal function declarations */
 static PLPGSQL *plpgsql_init(ARGV *);
-static PGSQL_RES *plpgsql_query(PLPGSQL *, const char *, char *, char *, char *);
+static PGSQL_RES *plpgsql_query(DICT_PGSQL *, const char *, VSTRING *, char *,
+                               char *, char *);
 static void plpgsql_dealloc(PLPGSQL *);
 static void plpgsql_close_host(HOST *);
 static void plpgsql_down_host(HOST *);
@@ -235,41 +237,83 @@ DICT   *dict_pgsql_open(const char *, int, int);
 static void dict_pgsql_close(DICT *);
 static HOST *host_init(const char *);
 
-
 /* dict_pgsql_quote - escape SQL metacharacters in input string */
 
-static void dict_pgsql_quote(DICT *unused, const char *name, VSTRING *result)
+static void dict_pgsql_quote(DICT *dict, const char *name, VSTRING *result)
 {
-    const char *sub;
+    DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict;
+    HOST  *active_host = dict_pgsql->active_host;
+    char  *myname = "dict_pgsql_quote";
+    size_t len = strlen(name);
+    size_t buflen = 2*len + 1;
+    int err = 1;
+
+    if (active_host == 0)
+       msg_panic("%s: bogus dict_pgsql->active_host", myname);
 
     /*
-     * XXX We really should be using an escaper that is provided by the PGSQL
-     * library. The code below seems to be over-kill (see RUS-CERT Advisory
-     * 2001-08:01), but it's better to be safe than to be sorry -- Wietse
+     * We won't get arithmetic overflows in 2*len + 1, because Postfix
+     * input keys have reasonable size limits, better safe than sorry.
      */
-     for (sub = name; *sub; sub++) {
-        switch(*sub) {
-       case '\n':
-            vstring_strcat(result, "\\n");
-           break;
-       case '\r':
-           vstring_strcat(result, "\\r");
-           break;
-       case '\'':
-           vstring_strcat(result, "\\'");
-           break;
-       case '"':
-           vstring_strcat(result, "\\\"");
-           break;
-       case 0:
-           vstring_strcat(result, "\\0");
-           break;
-       default:
-           VSTRING_ADDCH(result, *sub);
-           break;
-       }
+    if (buflen <= len)
+       msg_panic("%s: arithmetic overflow in 2*%lu+1", 
+                 myname, (unsigned long) len);
+
+    /*
+     * XXX Workaround: stop further processing when PQescapeStringConn()
+     * (below) fails. A more proper fix requires invasive changes, not
+     * suitable for a stable release.
+     */
+    if (active_host->stat == STATFAIL)
+       return;
+
+    /*
+     * Escape the input string, using PQescapeStringConn(), because
+     * the older PQescapeString() is not safe anymore, as stated by the
+     * documentation.
+     *
+     * From current libpq (8.1.4) documentation:
+     *
+     * PQescapeStringConn writes an escaped version of the from string 
+     * to the to buffer, escaping special characters so that they cannot
+     * cause any harm, and adding a terminating zero byte.
+     *
+     * ...
+     *
+     * The parameter from points to the first character of the string 
+     * that is to be escaped, and the length parameter gives the number 
+     * of bytes in this string. A terminating zero byte is not required,
+     * and should not be counted in length.
+     *
+     * ...
+     *
+     * (The parameter) to shall point to a buffer that is able to hold 
+     * at least one more byte than twice the value of length, otherwise
+     * the behavior is undefined.
+     *
+     * ...
+     *
+     * If the error parameter is not NULL, then *error is set to zero on
+     * success, nonzero on error ... The output string is still generated
+     * on error, but it can be expected that the server will reject it as
+     * malformed. On error, a suitable message is stored in the conn 
+     * object, whether or not error is NULL.
+     */
+    VSTRING_SPACE(result, buflen);
+    PQescapeStringConn(active_host->db, vstring_end(result), name, len, &err);
+    if (err == 0) {
+       VSTRING_SKIP(result);
+    } else {
+       /* 
+        * PQescapeStringConn() failed. According to the docs, we still 
+        * have a valid, null-terminated output string, but we need not
+        * rely on this behavior.
+        */
+       msg_warn("dict pgsql: (host %s) cannot escape input string: %s", 
+                active_host->hostname, PQerrorMessage(active_host->db));
+       active_host->stat = STATFAIL;
+       VSTRING_TERMINATE(result);
     }
-    VSTRING_TERMINATE(result);
 }
 
 /* dict_pgsql_lookup - find database entry */
@@ -315,14 +359,19 @@ static const char *dict_pgsql_lookup(DICT *dict, const char *name)
     }
 
     /*
-     * Suppress the actual lookup if the expansion is empty
+     * Suppress the actual lookup if the expansion is empty.
+     * 
+     * This initial expansion is outside the context of any
+     * specific host connection, we just want to check the
+     * key pre-requisites, so when quoting happens separately
+     * for each connection, we don't bother with quoting...
      */
     if (!db_common_expand(dict_pgsql->ctx, dict_pgsql->query,
-                         name, 0, query, dict_pgsql_quote))
+                         name, 0, query, 0))
        return (0);
     
     /* do the query - set dict_errno & cleanup if there's an error */
-    if ((query_res = plpgsql_query(pldb, vstring_str(query),
+    if ((query_res = plpgsql_query(dict_pgsql, name, query,
                                   dict_pgsql->dbname,
                                   dict_pgsql->username,
                                   dict_pgsql->password)) == 0) {
@@ -457,28 +506,104 @@ static void dict_pgsql_event(int unused_event, char *context)
  *                     close unnecessary active connections
  */
 
-static PGSQL_RES *plpgsql_query(PLPGSQL *PLDB,
-                                       const char *query,
+static PGSQL_RES *plpgsql_query(DICT_PGSQL *dict_pgsql,
+                                       const char *name,
+                                       VSTRING *query,
                                        char *dbname,
                                        char *username,
                                        char *password)
 {
+    PLPGSQL *PLDB = dict_pgsql->pldb;
     HOST   *host;
     PGSQL_RES *res = 0;
+    ExecStatusType status;
 
     while ((host = dict_pgsql_get_active(PLDB, dbname, username, password)) != NULL) {
-       if ((res = PQexec(host->db, query)) == 0) {
-           msg_warn("pgsql query failed: %s", PQerrorMessage(host->db));
+       /*
+        * The active host is used to escape strings in the
+        * context of the active connection's character encoding.
+        */
+       dict_pgsql->active_host = host;
+       VSTRING_RESET(query);
+       VSTRING_TERMINATE(query);
+       db_common_expand(dict_pgsql->ctx, dict_pgsql->query,
+                        name, 0, query, dict_pgsql_quote);
+       dict_pgsql->active_host = 0;
+
+       /* Check for potential dict_pgsql_quote() failure. */
+       if (host->stat == STATFAIL) {
            plpgsql_down_host(host);
+           continue;
+       }
+
+       /* 
+        * Submit a command to the server. Be paranoid when processing
+        * the result set: try to enumerate every successful case, and
+        * reject everything else.
+        *
+        * From PostgreSQL 8.1.4 docs: (PQexec) returns a PGresult
+        * pointer or possibly a null pointer. A non-null pointer will
+        * generally be returned except in out-of-memory conditions or
+        * serious errors such as inability to send the command to the
+        * server.
+        */
+       if ((res = PQexec(host->db, vstring_str(query))) != 0) {
+           /*
+            * XXX Because non-null result pointer does not imply success,
+            * we need to check the command's result status.
+            *
+            * Section 28.3.1: A result of status PGRES_NONFATAL_ERROR
+            * will never be returned directly by PQexec or other query
+            * execution functions; results of this kind are instead 
+            * passed to the notice processor.
+            *
+            * PGRES_EMPTY_QUERY is being sent by the server when the
+            * query string is empty. The sanity-checking done by
+            * the Postfix infrastructure makes this case impossible,
+            * so we need not handle this situation explicitly.
+            */
+           switch ((status = PQresultStatus(res))) {
+           case PGRES_TUPLES_OK:
+           case PGRES_COMMAND_OK:
+               /* Success. */
+               if (msg_verbose)
+                   msg_info("dict_pgsql: successful query from host %s", 
+                            host->hostname);
+               event_request_timer(dict_pgsql_event, (char *) host, 
+                                   IDLE_CONN_INTV);
+               return (res);
+           case PGRES_FATAL_ERROR:
+               msg_warn("pgsql query failed: fatal error from host %s: %s",
+                        host->hostname, PQresultErrorMessage(res));
+               break;
+           case PGRES_BAD_RESPONSE:
+               msg_warn("pgsql query failed: protocol error, host %s",
+                        host->hostname);
+               break;
+           default:
+               msg_warn("pgsql query failed: unknown code 0x%lx from host %s",
+                        (unsigned long) status, host->hostname);
+               break;
+           }
        } else {
-           if (msg_verbose)
-               msg_info("dict_pgsql: successful query from host %s", host->hostname);
-           event_request_timer(dict_pgsql_event, (char *) host, IDLE_CONN_INTV);
-           break;
+           /* 
+            * This driver treats null pointers like fatal, non-null
+            * result pointer errors, as suggested by the PostgreSQL 
+            * 8.1.4 documentation.
+            */
+           msg_warn("pgsql query failed: fatal error from host %s: %s",
+                    host->hostname, PQerrorMessage(host->db));
        }
+
+       /* 
+        * XXX An error occurred. Clean up memory and skip this connection.
+        */
+       if (res != 0)
+           PQclear(res);
+       plpgsql_down_host(host);
     }
 
-    return res;
+    return (0);
 }
 
 /*
@@ -489,22 +614,33 @@ static PGSQL_RES *plpgsql_query(PLPGSQL *PLDB,
 static void plpgsql_connect_single(HOST *host, char *dbname, char *username, char *password)
 {
     if ((host->db = PQsetdbLogin(host->name, host->port, NULL, NULL,
-                                dbname, username, password)) != NULL) {
-       if (PQstatus(host->db) == CONNECTION_OK) {
-           if (msg_verbose)
-               msg_info("dict_pgsql: successful connection to host %s",
-                        host->hostname);
-           host->stat = STATACTIVE;
-       } else {
-           msg_warn("connect to pgsql server %s: %s",
-                    host->hostname, PQerrorMessage(host->db));
-           plpgsql_down_host(host);
-       }
-    } else {
+                                dbname, username, password)) == NULL
+       || PQstatus(host->db) != CONNECTION_OK) {
        msg_warn("connect to pgsql server %s: %s",
                 host->hostname, PQerrorMessage(host->db));
        plpgsql_down_host(host);
+       return;
     }
+
+    if (msg_verbose)
+       msg_info("dict_pgsql: successful connection to host %s",
+                host->hostname);
+
+    /*
+     * XXX Postfix does not send multi-byte characters. The following
+     * piece of code is an explicit statement of this fact, and the 
+     * database server should not accept multi-byte information after
+     * this point.
+     */
+    if (PQsetClientEncoding(host->db, "LATIN1") != 0) {
+       msg_warn("dict_pgsql: cannot set the encoding to LATIN1, skipping %s",
+                host->hostname);
+       plpgsql_down_host(host);
+       return;
+    }
+
+    /* Success. */
+    host->stat = STATACTIVE;
 }
 
 /* plpgsql_close_host - close an established PostgreSQL connection */
@@ -537,7 +673,6 @@ static void pgsql_parse_config(DICT_PGSQL *dict_pgsql, const char *pgsqlcf)
 {
     const char *myname = "pgsql_parse_config";
     CFG_PARSER *p;
-    int     i;
     char   *hosts;
     VSTRING *query;
     char   *select_function;
@@ -621,6 +756,7 @@ DICT   *dict_pgsql_open(const char *name, int open_flags, int dict_flags)
     dict_pgsql->dict.lookup = dict_pgsql_lookup;
     dict_pgsql->dict.close = dict_pgsql_close;
     pgsql_parse_config(dict_pgsql, name);
+    dict_pgsql->active_host = 0;
     dict_pgsql->pldb = plpgsql_init(dict_pgsql->hosts);
     dict_pgsql->dict.flags = dict_flags | DICT_FLAG_FIXED;
     if (dict_pgsql->pldb == NULL)
@@ -684,7 +820,6 @@ static HOST *host_init(const char *hostname)
 
 static void dict_pgsql_close(DICT *dict)
 {
-    int     i;
     DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict;
 
     plpgsql_dealloc(dict_pgsql->pldb);
index 9a79a1b95e2fafb3d73ed7ada66df8c5e19602b0..feec47729962223b21e887faab255f0c217857a3 100644 (file)
@@ -89,7 +89,8 @@ int     hold_message(VSTRING *path_buf, const char *queue_name,
     (void) mail_queue_path(old_path, queue_name, queue_id);
     (void) mail_queue_path(path_buf, MAIL_QUEUE_HOLD, queue_id);
     if ((err = sane_rename(STR(old_path), STR(path_buf))) == 0
-       || ((err = mail_queue_mkdirs(STR(path_buf)) == 0)
+       /* 20060604: BEAM found mis-placed parenthesis */
+       || ((err = mail_queue_mkdirs(STR(path_buf))) == 0
            && (err = sane_rename(STR(old_path), STR(path_buf))) == 0)) {
        if (msg_verbose)
            msg_info("%s: placed on hold", queue_id);
index 2bd40025e7dc97552394271fe657af764e76a806..f23987c9fe04176fec872098fb7035da72e33c1e 100644 (file)
@@ -122,6 +122,7 @@ extern char *mail_pathname(const char *, const char *);
 #define MAIL_ATTR_SASL_METHOD  "sasl_method"
 #define MAIL_ATTR_SASL_USERNAME        "sasl_username"
 #define MAIL_ATTR_SASL_SENDER  "sasl_sender"
+#define MAIL_ATTR_ETRN_DOMAIN  "etrn_domain"
 #define MAIL_ATTR_DUMMY                "dummy"
 #define MAIL_ATTR_RWR_CONTEXT  "rewrite_context"
 
index faad697b670e26252c32b5fb45f2929833eda293..0185724f76050807dc806839f740992bbbf0fcf8 100644 (file)
@@ -20,8 +20,8 @@
   * Patches change the patchlevel and the release date. Snapshots change the
   * release date only.
   */
-#define MAIL_RELEASE_DATE      "20060405"
-#define MAIL_VERSION_NUMBER    "2.2.10"
+#define MAIL_RELEASE_DATE      "20060722"
+#define MAIL_VERSION_NUMBER    "2.2.11-RC1"
 
 #define VAR_MAIL_VERSION       "mail_version"
 #ifdef SNAPSHOT
index 4866ac5ad3db2dd5a5d763dc7e05436ca5c8e5fa..4678c9a2851f4cc021066f9304c1ec2e31405bdd 100644 (file)
@@ -250,6 +250,7 @@ static void strip_address(VSTRING *vp, int start, TOK822 *addr)
     /*
      * Emit plain <address>. Discard any comments or phrases.
      */
+    VSTRING_TERMINATE(vp);
     msg_warn("stripping too many comments from address: %.100s...",
             printable(vstring_str(vp) + start, '?'));
     vstring_truncate(vp, start);
index 2041dcec749cc1960a1eb4119c4a0d3beb0499f7..7bb244af39d8a0cfc2847d70e6585678ee409bfb 100644 (file)
@@ -338,81 +338,88 @@ int     smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
                                   "host %s refused to talk to me: %s",
                                   session->namaddr,
                                   translit(resp->str, "\n", " ")));
-       return (0);
     }
 
     /*
-     * Determine what server EHLO keywords to ignore, typically to avoid
-     * inter-operability problems.
+     * No early returns allowed, to ensure consistent handling of TLS and
+     * SASL policies.
      */
-    if (smtp_ehlo_dis_maps == 0
-       || (ehlo_words = maps_find(smtp_ehlo_dis_maps, state->session->addr, 0)) == 0)
-       ehlo_words = var_smtp_ehlo_dis_words;
-    discard_mask = ehlo_mask(ehlo_words);
-    if (discard_mask && !(discard_mask & EHLO_MASK_SILENT))
-       msg_info("discarding EHLO keywords: %s", str_ehlo_mask(discard_mask));
+    if (session->features & SMTP_FEATURE_ESMTP) {
 
-    /*
-     * Pick up some useful features offered by the SMTP server. XXX Until we
-     * have a portable routine to convert from string to off_t with proper
-     * overflow detection, ignore the message size limit advertised by the
-     * SMTP server. Otherwise, we might do the wrong thing when the server
-     * advertises a really huge message size limit.
-     * 
-     * XXX Allow for "code (SP|-) ehlo-keyword (SP|=) ehlo-param...", because
-     * MicroSoft implemented AUTH based on an old draft.
-     */
-    lines = resp->str;
-    for (n = 0; (words = mystrtok(&lines, "\n")) != 0; /* see below */ ) {
-       if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t=")) != 0) {
-           if (n == 0) {
-               if (session->helo != 0)
-                   myfree(session->helo);
-               session->helo = lowercase(mystrdup(word));
-               if (strcasecmp(word, var_myhostname) == 0
-                   && (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) != 0) {
-                   msg_warn("host %s replied to HELO/EHLO with my own hostname %s",
-                            session->namaddr, var_myhostname);
-                   return (smtp_site_fail(state,
-                    (session->features & SMTP_FEATURE_BEST_MX) ? 550 : 450,
+       /*
+        * Determine what server EHLO keywords to ignore, typically to avoid
+        * inter-operability problems.
+        */
+       if (smtp_ehlo_dis_maps == 0
+           || (ehlo_words = maps_find(smtp_ehlo_dis_maps,
+                                      state->session->addr, 0)) == 0)
+           ehlo_words = var_smtp_ehlo_dis_words;
+       discard_mask = ehlo_mask(ehlo_words);
+       if (discard_mask && !(discard_mask & EHLO_MASK_SILENT))
+           msg_info("discarding EHLO keywords: %s",
+                    str_ehlo_mask(discard_mask));
+
+       /*
+        * Pick up some useful features offered by the SMTP server.
+        * 
+        * XXX Allow for "code (SP|-) ehlo-keyword (SP|=) ehlo-param...",
+        * because MicroSoft implemented AUTH based on an old draft.
+        */
+       lines = resp->str;
+       for (n = 0; (words = mystrtok(&lines, "\n")) != 0; /* see below */ ) {
+           if (mystrtok(&words, "- ")
+               && (word = mystrtok(&words, " \t=")) != 0) {
+               if (n == 0) {
+                   if (session->helo != 0)
+                       myfree(session->helo);
+                   session->helo = lowercase(mystrdup(word));
+                   if (strcasecmp(word, var_myhostname) == 0
+                       && (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) != 0) {
+                       msg_warn("host %s replied to HELO/EHLO"
+                                " with my own hostname %s",
+                                session->namaddr, var_myhostname);
+                       return (smtp_site_fail(state,
+                                              (session->features & SMTP_FEATURE_BEST_MX) ? 550 : 450,
                                         "mail for %s loops back to myself",
-                                          request->nexthop));
-               }
-           } else if (strcasecmp(word, "8BITMIME") == 0) {
-               if ((discard_mask & EHLO_MASK_8BITMIME) == 0)
-                   session->features |= SMTP_FEATURE_8BITMIME;
-           } else if (strcasecmp(word, "PIPELINING") == 0) {
-               if ((discard_mask & EHLO_MASK_PIPELINING) == 0)
-                   session->features |= SMTP_FEATURE_PIPELINING;
-           } else if (strcasecmp(word, "XFORWARD") == 0) {
-               if ((discard_mask & EHLO_MASK_XFORWARD) == 0)
-                   while ((word = mystrtok(&words, " \t")) != 0)
-                       session->features |= name_code(xforward_features,
+                                              request->nexthop));
+                   }
+               } else if (strcasecmp(word, "8BITMIME") == 0) {
+                   if ((discard_mask & EHLO_MASK_8BITMIME) == 0)
+                       session->features |= SMTP_FEATURE_8BITMIME;
+               } else if (strcasecmp(word, "PIPELINING") == 0) {
+                   if ((discard_mask & EHLO_MASK_PIPELINING) == 0)
+                       session->features |= SMTP_FEATURE_PIPELINING;
+               } else if (strcasecmp(word, "XFORWARD") == 0) {
+                   if ((discard_mask & EHLO_MASK_XFORWARD) == 0)
+                       while ((word = mystrtok(&words, " \t")) != 0)
+                           session->features |= name_code(xforward_features,
                                                 NAME_CODE_FLAG_NONE, word);
-           } else if (strcasecmp(word, "SIZE") == 0) {
-               if ((discard_mask & EHLO_MASK_SIZE) == 0) {
-                   session->features |= SMTP_FEATURE_SIZE;
-                   if ((word = mystrtok(&words, " \t")) != 0) {
-                       if (!alldig(word))
-                           msg_warn("bad EHLO SIZE limit \"%s\" from %s",
-                                    word, session->namaddr);
-                       else
-                           session->size_limit = off_cvt_string(word);
+               } else if (strcasecmp(word, "SIZE") == 0) {
+                   if ((discard_mask & EHLO_MASK_SIZE) == 0) {
+                       session->features |= SMTP_FEATURE_SIZE;
+                       if ((word = mystrtok(&words, " \t")) != 0) {
+                           if (!alldig(word))
+                               msg_warn("bad EHLO SIZE limit \"%s\" from %s",
+                                        word, session->namaddr);
+                           else
+                               session->size_limit = off_cvt_string(word);
+                       }
                    }
-               }
 #ifdef USE_TLS
-           } else if (strcasecmp(word, "STARTTLS") == 0) {
-               /* Ignored later if we already sent STARTTLS. */
-               if ((discard_mask & EHLO_MASK_STARTTLS) == 0)
-                   session->features |= SMTP_FEATURE_STARTTLS;
+               } else if (strcasecmp(word, "STARTTLS") == 0) {
+                   /* Ignored later if we already sent STARTTLS. */
+                   if ((discard_mask & EHLO_MASK_STARTTLS) == 0)
+                       session->features |= SMTP_FEATURE_STARTTLS;
 #endif
 #ifdef USE_SASL_AUTH
-           } else if (var_smtp_sasl_enable && strcasecmp(word, "AUTH") == 0) {
-               if ((discard_mask & EHLO_MASK_AUTH) == 0)
-                   smtp_sasl_helo_auth(session, words);
+               } else if (var_smtp_sasl_enable
+                          && strcasecmp(word, "AUTH") == 0) {
+                   if ((discard_mask & EHLO_MASK_AUTH) == 0)
+                       smtp_sasl_helo_auth(session, words);
 #endif
+               }
+               n++;
            }
-           n++;
        }
     }
     if (msg_verbose)
index f9fa6186b313a8cb7ae25e28b7cc823e334525b6..2a3a5ee0646855342f0912cfabf821eeb42c4626 100644 (file)
@@ -35,6 +35,7 @@
 /*
 /*     smtp_reuse_addr() looks up a cached session by its server
 /*     address, and verifies that the session is still alive.
+/*     Lookup is disabled when the tls_per_site policy table is enabled.
 /*     The result is null in case of failure.
 /*
 /*     Arguments:
@@ -176,6 +177,22 @@ static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
     }
     state->session = session;
 
+#ifdef USE_TLS
+
+    /*
+     * Cached sessions are never TLS encrypted, so they must not be reused
+     * when TLS encryption is required.
+     */
+    if (session->tls_enforce_tls) {
+       if (msg_verbose)
+           msg_info("%s: skipping plain-text cached session to %s",
+                    myname, label);
+       smtp_quit(state);                       /* Close politely */
+       smtp_session_free(session);             /* And avoid leaks */
+       return (state->session = 0);
+    }
+#endif
+
     /*
      * Send an RSET probe to verify that the session is still good.
      */
@@ -229,6 +246,16 @@ SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, DNS_RR *addr, unsigned port)
     SMTP_SESSION *session;
     int     fd;
 
+    /*
+     * XXX Disable connection cache lookup by server IP address when the
+     * tls_per_site policy or smtp_sasl_password_maps features are enabled.
+     * Different server names may resolve to the same IP address. We don't
+     * want to use the wrong SASL credentials or the wrong TLS policy.
+     */
+    if ((var_smtp_tls_per_site && *var_smtp_tls_per_site)
+       || (var_smtp_sasl_passwd && *var_smtp_sasl_passwd))
+       return (0);
+
     /*
      * Look up the session by its IP address. This means that we have no
      * destination-to-address binding properties.
index 8d02bd3cf4099b6773d9b864f05483b416e65657..7db46df005e6db7a3cc18081244e9aad26731f85 100644 (file)
@@ -2974,8 +2974,16 @@ static void smtpd_proto(SMTPD_STATE *state, const char *service)
         * to the client.  This is unfortunate.
         */
 #ifdef USE_TLS
-       if (SMTPD_STAND_ALONE(state) == 0 && var_smtpd_tls_wrappermode)
+       if (SMTPD_STAND_ALONE(state) == 0 && var_smtpd_tls_wrappermode) {
+           if (smtpd_tls_ctx == 0) {
+               msg_warn("Wrapper-mode request dropped from %s for service %s."
+                        "TLS context initialization failed. For details see"
+                        " earlier warnings in your logs.",
+                        state->namaddr, state->service);
+               break;
+           }
            smtpd_start_tls(state);
+       }
 #endif
 
        /*
index dfd1d9952776cb9567ce0e511e9de25264bfd56c..96930395c569570ea2a97dfb8e5dfef9212940a8 100644 (file)
 #include <is_header.h>
 #include <rewrite_clnt.h>
 #include <valid_mailhost_addr.h>
+#include <xtext.h>
 
 /* Application-specific. */
 
@@ -3042,6 +3043,15 @@ static int check_policy_service(SMTPD_STATE *state, const char *server,
     static VSTRING *action = 0;
     ATTR_CLNT *policy_clnt;
 
+#ifdef USE_TLS
+    VSTRING *subject_buf;
+    VSTRING *issuer_buf;
+    const char *subject;
+    const char *issuer;
+
+#endif
+    int     ret;
+
     /*
      * Sanity check.
      */
@@ -3056,6 +3066,22 @@ static int check_policy_service(SMTPD_STATE *state, const char *server,
     if (action == 0)
        action = vstring_alloc(10);
 
+#ifdef USE_TLS
+#define ENCODE_CN(coded_CN, coded_CN_buf, CN) do { \
+       if (state->tls_info.peer_verified == 0) { \
+           coded_CN_buf = 0; \
+           coded_CN = ""; \
+       } else { \
+           coded_CN_buf = vstring_alloc(strlen(CN)); \
+           xtext_quote(coded_CN_buf, CN, ""); \
+           coded_CN = STR(coded_CN_buf); \
+       } \
+    } while (0);
+
+    ENCODE_CN(subject, subject_buf, state->tls_info.peer_CN);
+    ENCODE_CN(issuer, issuer_buf, state->tls_info.issuer_CN);
+#endif
+
     if (attr_clnt_request(policy_clnt,
                          ATTR_FLAG_NONE,       /* Query attributes. */
                        ATTR_TYPE_STR, MAIL_ATTR_REQ, "smtpd_access_policy",
@@ -3076,6 +3102,8 @@ static int check_policy_service(SMTPD_STATE *state, const char *server,
                          ATTR_TYPE_LONG, MAIL_ATTR_SIZE,
                          (unsigned long) (state->act_size > 0 ?
                                         state->act_size : state->msg_size),
+                         ATTR_TYPE_STR, MAIL_ATTR_ETRN_DOMAIN,
+                         state->etrn_name ? state->etrn_name : "",
 #ifdef USE_SASL_AUTH
                          ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD,
                          var_smtpd_sasl_enable && state->sasl_method ?
@@ -3088,12 +3116,8 @@ static int check_policy_service(SMTPD_STATE *state, const char *server,
                          state->sasl_sender : "",
 #endif
 #ifdef USE_TLS
-                         ATTR_TYPE_STR, MAIL_ATTR_CCERT_SUBJECT,
-                         state->tls_info.peer_verified ?
-                         state->tls_info.peer_CN : "",
-                         ATTR_TYPE_STR, MAIL_ATTR_CCERT_ISSSUER,
-                         state->tls_info.peer_verified ?
-                         state->tls_info.issuer_CN : "",
+                         ATTR_TYPE_STR, MAIL_ATTR_CCERT_SUBJECT, subject,
+                         ATTR_TYPE_STR, MAIL_ATTR_CCERT_ISSSUER, issuer,
                          ATTR_TYPE_STR, MAIL_ATTR_CCERT_FINGERPRINT,
                          state->tls_info.peer_verified ?
                          state->tls_info.peer_fingerprint : "",
@@ -3102,18 +3126,25 @@ static int check_policy_service(SMTPD_STATE *state, const char *server,
                          ATTR_FLAG_MISSING,    /* Reply attributes. */
                          ATTR_TYPE_STR, MAIL_ATTR_ACTION, action,
                          ATTR_TYPE_END) != 1) {
-       return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
-                                  "450 Server configuration problem"));
+       ret = smtpd_check_reject(state, MAIL_ERROR_POLICY,
+                                "450 Server configuration problem");
     } else {
 
        /*
         * XXX This produces bogus error messages when the reply is
         * malformed.
         */
-       return (check_table_result(state, server, STR(action),
-                                  "policy query", reply_name,
-                                  reply_class, def_acl));
+       ret = check_table_result(state, server, STR(action),
+                                "policy query", reply_name,
+                                reply_class, def_acl);
     }
+#ifdef USE_TLS
+    if (subject_buf)
+       vstring_free(subject_buf);
+    if (issuer_buf)
+       vstring_free(issuer_buf);
+#endif
+    return (ret);
 }
 
 /* is_map_command - restriction has form: check_xxx_access type:name */
index c6181717372f797ee8c7e2b0cfd672fd9e83e714..9bfe1db194844518aaa65d0e7f4bc9ca1d103386 100644 (file)
@@ -133,10 +133,10 @@ void    tls_prng_exch_close(TLS_PRNG_SRC *eh)
 
     if (close(eh->fd) < 0)
        msg_fatal("close PRNG exchange file %s: %m", eh->name);
-    myfree(eh->name);
-    myfree((char *) eh);
     if (msg_verbose)
        msg_info("%s: closed PRNG exchange file %s", myname, eh->name);
+    myfree(eh->name);
+    myfree((char *) eh);
 }
 
 #endif
index c047a5bac546c96981301b82677b4adb188d04df..81ec379bddb2409b11ba993be12f4c3a43bae6af 100644 (file)
@@ -175,7 +175,7 @@ static VSTREAM *safe_open_exist(const char *path, int flags,
 /* safe_open_create - create new file */
 
 static VSTREAM *safe_open_create(const char *path, int flags, int mode,
-                   struct stat * st, uid_t user, uid_t group, VSTRING *why)
+                   struct stat * st, uid_t user, gid_t group, VSTRING *why)
 {
     VSTREAM *fp;
 
@@ -188,6 +188,12 @@ static VSTREAM *safe_open_create(const char *path, int flags, int mode,
        return (0);
     }
 
+    /*
+     * Optionally look up the file attributes.
+     */
+    if (st != 0 && fstat(vstream_fileno(fp), st) < 0)
+       msg_fatal("%s: bad open file status: %m", path);
+
     /*
      * Optionally change ownership after creating a new file. If there is a
      * problem we should not attempt to delete the file. Something else may
@@ -200,12 +206,6 @@ static VSTREAM *safe_open_create(const char *path, int flags, int mode,
        msg_warn("%s: cannot change file ownership: %m", path);
     }
 
-    /*
-     * Optionally look up the file attributes.
-     */
-    if (st != 0 && fstat(vstream_fileno(fp), st) < 0)
-       msg_fatal("%s: bad open file status: %m", path);
-
     /*
      * We are almost there...
      */
index b67b273010eb578c9c085e6d619d5f111d8a2991..ef1af01f54d628696e2df98a19f0b6b0a897d6d2 100644 (file)
@@ -1221,7 +1221,9 @@ typedef int pid_t;
   * doubles.
   */
 #ifndef ALIGN_TYPE
-# ifdef __ia64__
+# if defined(__hpux) && defined(__ia64)
+# define ALIGN_TYPE    __float80
+# elif defined(__ia64__)
 # define ALIGN_TYPE    long double
 # else
 # define ALIGN_TYPE    double