This patch is a back-port of fixes from Postfix 2.3. The main changes are: - 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 older releases requires major code changes that will have to wait until Postfix 2.4. - The Postfix SMTP client enforced Mandatory TLS only when talking to an ESMTP server; enforcement did not happen if Postfix could somehow be forced to send HELO instead of EHLO. This is minor compared to the DNS spoofing issues that were fixed with Postfix 2.2.10. Prereq: "2.2.10" diff -cr /var/tmp/postfix-2.2.10/src/global/mail_version.h ./src/global/mail_version.h *** /var/tmp/postfix-2.2.10/src/global/mail_version.h Wed Apr 5 16:40:28 2006 --- ./src/global/mail_version.h Mon Jul 24 16:17:03 2006 *************** *** 20,27 **** * 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 VAR_MAIL_VERSION "mail_version" #ifdef SNAPSHOT --- 20,27 ---- * Patches change the patchlevel and the release date. Snapshots change the * release date only. */ ! #define MAIL_RELEASE_DATE "20060724" ! #define MAIL_VERSION_NUMBER "2.2.11" #define VAR_MAIL_VERSION "mail_version" #ifdef SNAPSHOT diff -cr /var/tmp/postfix-2.2.10/HISTORY ./HISTORY *** /var/tmp/postfix-2.2.10/HISTORY Wed Apr 5 15:15:03 2006 --- ./HISTORY Mon Jul 24 16:20:23 2006 *************** *** 10920,10922 **** --- 10920,11014 ---- 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; enforcement did not happen + if Postfix could somehow be forced to send HELO instead of + EHLO. Victor Duchovni. File: src/smtp/smtp_proto.c. + diff -cr /var/tmp/postfix-2.2.10/README_FILES/PGSQL_README ./README_FILES/PGSQL_README *** /var/tmp/postfix-2.2.10/README_FILES/PGSQL_README Tue Mar 8 14:01:13 2005 --- ./README_FILES/PGSQL_README Sun Jun 11 12:12:02 2006 *************** *** 102,105 **** --- 102,108 ---- * 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. diff -cr /var/tmp/postfix-2.2.10/README_FILES/SMTPD_POLICY_README ./README_FILES/SMTPD_POLICY_README *** /var/tmp/postfix-2.2.10/README_FILES/SMTPD_POLICY_README Tue Jul 5 20:08:40 2005 --- ./README_FILES/SMTPD_POLICY_README Sat Jul 22 20:15:24 2006 *************** *** 53,61 **** sasl_username=you sasl_sender= ccert_subject=solaris9.porcupine.org ! ccert_issuer=Wietse Venema ccert_fingerprint=C2:9D:F4:87:71:73:73:D9:18:E7:C2:F3:C1:DA:6E:04 size=12345 [empty line] Notes: --- 53,62 ---- sasl_username=you sasl_sender= ccert_subject=solaris9.porcupine.org ! 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,96 **** 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. The following is specific to SMTPD delegated policy requests: --- 91,103 ---- 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. 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: diff -cr /var/tmp/postfix-2.2.10/RELEASE_NOTES ./RELEASE_NOTES *** /var/tmp/postfix-2.2.10/RELEASE_NOTES Wed Mar 9 14:00:39 2005 --- ./RELEASE_NOTES Fri Jul 7 15:38:07 2006 *************** *** 11,16 **** --- 11,30 ---- 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 ------------------------------------- diff -cr /var/tmp/postfix-2.2.10/html/PGSQL_README.html ./html/PGSQL_README.html *** /var/tmp/postfix-2.2.10/html/PGSQL_README.html Tue Mar 8 14:01:12 2005 --- ./html/PGSQL_README.html Sun Jun 11 12:12:02 2006 *************** *** 139,144 **** --- 139,148 ---- 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.
  • + diff -cr /var/tmp/postfix-2.2.10/html/SMTPD_POLICY_README.html ./html/SMTPD_POLICY_README.html *** /var/tmp/postfix-2.2.10/html/SMTPD_POLICY_README.html Tue Jul 5 20:08:40 2005 --- ./html/SMTPD_POLICY_README.html Sat Jul 22 20:15:24 2006 *************** *** 85,93 **** sasl_username=you sasl_sender= ccert_subject=solaris9.porcupine.org ! ccert_issuer=Wietse Venema ccert_fingerprint=C2:9D:F4:87:71:73:73:D9:18:E7:C2:F3:C1:DA:6E:04 size=12345 [empty line] --- 85,94 ---- sasl_username=you sasl_sender= ccert_subject=solaris9.porcupine.org ! 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] *************** *** 133,140 ****

  • The "ccert_*" attributes (Postfix 2.2 and later) specify ! information about how the client was authenticated via TLS. !

    --- 134,147 ----

  • The "ccert_*" attributes (Postfix 2.2 and later) specify ! 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.

    ! !
  • The "etrn_domain" attribute is defined only in the ! context of the ETRN command, and specifies the ETRN command ! parameter.

    diff -cr /var/tmp/postfix-2.2.10/proto/PGSQL_README.html ./proto/PGSQL_README.html *** /var/tmp/postfix-2.2.10/proto/PGSQL_README.html Tue Mar 8 13:58:44 2005 --- ./proto/PGSQL_README.html Sun Jun 11 12:11:53 2006 *************** *** 139,144 **** --- 139,148 ---- 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.
  • + diff -cr /var/tmp/postfix-2.2.10/proto/SMTPD_POLICY_README.html ./proto/SMTPD_POLICY_README.html *** /var/tmp/postfix-2.2.10/proto/SMTPD_POLICY_README.html Tue Jul 5 20:08:07 2005 --- ./proto/SMTPD_POLICY_README.html Sat Jul 22 19:41:27 2006 *************** *** 85,93 **** sasl_username=you sasl_sender= ccert_subject=solaris9.porcupine.org ! ccert_issuer=Wietse Venema ccert_fingerprint=C2:9D:F4:87:71:73:73:D9:18:E7:C2:F3:C1:DA:6E:04 size=12345 [empty line] --- 85,94 ---- sasl_username=you sasl_sender= ccert_subject=solaris9.porcupine.org ! 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] *************** *** 133,140 ****

  • The "ccert_*" attributes (Postfix 2.2 and later) specify ! information about how the client was authenticated via TLS. !

    --- 134,147 ----

  • The "ccert_*" attributes (Postfix 2.2 and later) specify ! 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.

    ! !
  • The "etrn_domain" attribute is defined only in the ! context of the ETRN command, and specifies the ETRN command ! parameter.

    diff -cr /var/tmp/postfix-2.2.10/src/global/dict_pgsql.c ./src/global/dict_pgsql.c *** /var/tmp/postfix-2.2.10/src/global/dict_pgsql.c Thu Sep 22 11:45:11 2005 --- ./src/global/dict_pgsql.c Sun Jun 11 12:08:43 2006 *************** *** 217,222 **** --- 217,223 ---- char *table; ARGV *hosts; PLPGSQL *pldb; + HOST *active_host; } DICT_PGSQL; *************** *** 225,231 **** /* internal function declarations */ static PLPGSQL *plpgsql_init(ARGV *); ! static PGSQL_RES *plpgsql_query(PLPGSQL *, const char *, char *, char *, char *); static void plpgsql_dealloc(PLPGSQL *); static void plpgsql_close_host(HOST *); static void plpgsql_down_host(HOST *); --- 226,233 ---- /* internal function declarations */ static PLPGSQL *plpgsql_init(ARGV *); ! 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,275 **** 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) { ! const char *sub; /* ! * 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 */ ! 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; ! } } - VSTRING_TERMINATE(result); } /* dict_pgsql_lookup - find database entry */ --- 237,319 ---- 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 *dict, const char *name, VSTRING *result) { ! 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); /* ! * We won't get arithmetic overflows in 2*len + 1, because Postfix ! * input keys have reasonable size limits, better safe than sorry. */ ! 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); } } /* dict_pgsql_lookup - find database entry */ *************** *** 315,328 **** } /* ! * Suppress the actual lookup if the expansion is empty */ if (!db_common_expand(dict_pgsql->ctx, dict_pgsql->query, ! name, 0, query, dict_pgsql_quote)) return (0); /* do the query - set dict_errno & cleanup if there's an error */ ! if ((query_res = plpgsql_query(pldb, vstring_str(query), dict_pgsql->dbname, dict_pgsql->username, dict_pgsql->password)) == 0) { --- 359,377 ---- } /* ! * 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, 0)) return (0); /* do the query - set dict_errno & cleanup if there's an error */ ! if ((query_res = plpgsql_query(dict_pgsql, name, query, dict_pgsql->dbname, dict_pgsql->username, dict_pgsql->password)) == 0) { *************** *** 457,484 **** * close unnecessary active connections */ ! static PGSQL_RES *plpgsql_query(PLPGSQL *PLDB, ! const char *query, char *dbname, char *username, char *password) { HOST *host; PGSQL_RES *res = 0; 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)); plpgsql_down_host(host); } 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; } } ! return res; } /* --- 506,609 ---- * close unnecessary active connections */ ! 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) { ! /* ! * 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 { ! /* ! * 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 (0); } /* *************** *** 489,510 **** 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 { msg_warn("connect to pgsql server %s: %s", host->hostname, PQerrorMessage(host->db)); plpgsql_down_host(host); } } /* plpgsql_close_host - close an established PostgreSQL connection */ --- 614,646 ---- 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 ! || 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,543 **** { const char *myname = "pgsql_parse_config"; CFG_PARSER *p; - int i; char *hosts; VSTRING *query; char *select_function; --- 673,678 ---- *************** *** 621,626 **** --- 756,762 ---- 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,690 **** static void dict_pgsql_close(DICT *dict) { - int i; DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict; plpgsql_dealloc(dict_pgsql->pldb); --- 820,825 ---- diff -cr /var/tmp/postfix-2.2.10/src/global/hold_message.c ./src/global/hold_message.c *** /var/tmp/postfix-2.2.10/src/global/hold_message.c Sat Nov 30 16:29:27 2002 --- ./src/global/hold_message.c Sun Jun 4 16:51:39 2006 *************** *** 89,95 **** (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) && (err = sane_rename(STR(old_path), STR(path_buf))) == 0)) { if (msg_verbose) msg_info("%s: placed on hold", queue_id); --- 89,96 ---- (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 ! /* 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); diff -cr /var/tmp/postfix-2.2.10/src/global/mail_proto.h ./src/global/mail_proto.h *** /var/tmp/postfix-2.2.10/src/global/mail_proto.h Tue Feb 8 18:02:54 2005 --- ./src/global/mail_proto.h Sat Jul 22 19:42:23 2006 *************** *** 122,127 **** --- 122,128 ---- #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" diff -cr /var/tmp/postfix-2.2.10/src/global/tok822_parse.c ./src/global/tok822_parse.c *** /var/tmp/postfix-2.2.10/src/global/tok822_parse.c Sat Mar 8 11:34:24 2003 --- ./src/global/tok822_parse.c Sun Apr 30 09:50:25 2006 *************** *** 250,255 **** --- 250,256 ---- /* * Emit plain
    . 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); diff -cr /var/tmp/postfix-2.2.10/src/smtp/smtp_proto.c ./src/smtp/smtp_proto.c *** /var/tmp/postfix-2.2.10/src/smtp/smtp_proto.c Tue Jan 3 19:20:21 2006 --- ./src/smtp/smtp_proto.c Tue Jul 18 20:24:18 2006 *************** *** 338,418 **** "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. */ ! 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 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, "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, 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); } - } #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; #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); #endif } - n++; } } if (msg_verbose) --- 338,425 ---- "host %s refused to talk to me: %s", session->namaddr, translit(resp->str, "\n", " "))); } /* ! * No early returns allowed, to ensure consistent handling of TLS and ! * SASL policies. */ ! if (session->features & SMTP_FEATURE_ESMTP) { ! /* ! * 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, 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); ! } } #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; #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); #endif + } + n++; } } } if (msg_verbose) diff -cr /var/tmp/postfix-2.2.10/src/smtp/smtp_reuse.c ./src/smtp/smtp_reuse.c *** /var/tmp/postfix-2.2.10/src/smtp/smtp_reuse.c Tue Jan 18 20:22:05 2005 --- ./src/smtp/smtp_reuse.c Thu Jul 6 14:59:32 2006 *************** *** 35,40 **** --- 35,41 ---- /* /* 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,181 **** --- 177,198 ---- } 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. */ *************** *** 228,233 **** --- 245,260 ---- MAI_HOSTADDR_STR hostaddr; 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 diff -cr /var/tmp/postfix-2.2.10/src/smtpd/smtpd.c ./src/smtpd/smtpd.c *** /var/tmp/postfix-2.2.10/src/smtpd/smtpd.c Mon Nov 28 17:32:41 2005 --- ./src/smtpd/smtpd.c Tue Apr 18 09:39:58 2006 *************** *** 2974,2981 **** * to the client. This is unfortunate. */ #ifdef USE_TLS ! if (SMTPD_STAND_ALONE(state) == 0 && var_smtpd_tls_wrappermode) smtpd_start_tls(state); #endif /* --- 2974,2989 ---- * to the client. This is unfortunate. */ #ifdef USE_TLS ! 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 /* diff -cr /var/tmp/postfix-2.2.10/src/smtpd/smtpd_check.c ./src/smtpd/smtpd_check.c *** /var/tmp/postfix-2.2.10/src/smtpd/smtpd_check.c Fri Feb 3 15:06:51 2006 --- ./src/smtpd/smtpd_check.c Sat Jul 22 19:40:23 2006 *************** *** 226,231 **** --- 226,232 ---- #include #include #include + #include /* Application-specific. */ *************** *** 3042,3047 **** --- 3043,3057 ---- 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,3061 **** --- 3066,3087 ---- 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,3081 **** --- 3102,3109 ---- 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,3099 **** 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_FINGERPRINT, state->tls_info.peer_verified ? state->tls_info.peer_fingerprint : "", --- 3116,3123 ---- state->sasl_sender : "", #endif #ifdef USE_TLS ! 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,3119 **** 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")); } 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)); } } /* is_map_command - restriction has form: check_xxx_access type:name */ --- 3126,3150 ---- ATTR_FLAG_MISSING, /* Reply attributes. */ ATTR_TYPE_STR, MAIL_ATTR_ACTION, action, ATTR_TYPE_END) != 1) { ! ret = smtpd_check_reject(state, MAIL_ERROR_POLICY, ! "450 Server configuration problem"); } else { /* * XXX This produces bogus error messages when the reply is * malformed. */ ! 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 */ diff -cr /var/tmp/postfix-2.2.10/src/tls/tls_prng_exch.c ./src/tls/tls_prng_exch.c *** /var/tmp/postfix-2.2.10/src/tls/tls_prng_exch.c Fri Dec 10 11:00:44 2004 --- ./src/tls/tls_prng_exch.c Thu May 11 10:23:33 2006 *************** *** 133,142 **** 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); } #endif --- 133,142 ---- if (close(eh->fd) < 0) msg_fatal("close PRNG exchange file %s: %m", eh->name); if (msg_verbose) msg_info("%s: closed PRNG exchange file %s", myname, eh->name); + myfree(eh->name); + myfree((char *) eh); } #endif diff -cr /var/tmp/postfix-2.2.10/src/util/safe_open.c ./src/util/safe_open.c *** /var/tmp/postfix-2.2.10/src/util/safe_open.c Sat Jun 21 10:08:55 2003 --- ./src/util/safe_open.c Sat Jul 22 19:00:52 2006 *************** *** 175,181 **** /* 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) { VSTREAM *fp; --- 175,181 ---- /* safe_open_create - create new file */ static VSTREAM *safe_open_create(const char *path, int flags, int mode, ! struct stat * st, uid_t user, gid_t group, VSTRING *why) { VSTREAM *fp; *************** *** 189,194 **** --- 189,200 ---- } /* + * 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 * have opened the file in the mean time. *************** *** 199,210 **** && fchown(vstream_fileno(fp), user, group) < 0) { 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... --- 205,210 ---- diff -cr /var/tmp/postfix-2.2.10/src/util/sys_defs.h ./src/util/sys_defs.h *** /var/tmp/postfix-2.2.10/src/util/sys_defs.h Tue Jan 3 16:52:17 2006 --- ./src/util/sys_defs.h Tue Jul 11 12:24:55 2006 *************** *** 1221,1227 **** * doubles. */ #ifndef ALIGN_TYPE ! # ifdef __ia64__ # define ALIGN_TYPE long double # else # define ALIGN_TYPE double --- 1221,1229 ---- * doubles. */ #ifndef ALIGN_TYPE ! # if defined(__hpux) && defined(__ia64) ! # define ALIGN_TYPE __float80 ! # elif defined(__ia64__) # define ALIGN_TYPE long double # else # define ALIGN_TYPE double