From: Wietse Venema Date: Sat, 22 Jul 2006 05:00:00 +0000 (-0500) Subject: postfix-2.2.11-RC1 X-Git-Tag: v2.2.11-RC1^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d1ce14a8b5333bdb1abd88ef85c828962293c2ab;p=thirdparty%2Fpostfix.git postfix-2.2.11-RC1 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 0c85ec7f4..982eb16fe 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -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. + diff --git a/postfix/README_FILES/PGSQL_README b/postfix/README_FILES/PGSQL_README index c7d8c279f..d6ecf4c7e 100644 --- a/postfix/README_FILES/PGSQL_README +++ b/postfix/README_FILES/PGSQL_README @@ -102,4 +102,7 @@ CCrreeddiittss * 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 --git a/postfix/README_FILES/SMTPD_POLICY_README b/postfix/README_FILES/SMTPD_POLICY_README index 3f0a2cafc..94c05e0c4 100644 --- a/postfix/README_FILES/SMTPD_POLICY_README +++ b/postfix/README_FILES/SMTPD_POLICY_README @@ -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: diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index e7e2cd84c..6d01a3884 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -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 ------------------------------------- diff --git a/postfix/html/PGSQL_README.html b/postfix/html/PGSQL_README.html index 254f580dd..5f46d58d0 100644 --- a/postfix/html/PGSQL_README.html +++ b/postfix/html/PGSQL_README.html @@ -139,6 +139,10 @@ configuration feature. 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 --git a/postfix/html/SMTPD_POLICY_README.html b/postfix/html/SMTPD_POLICY_README.html index 8cb3d4f4b..13198555d 100644 --- a/postfix/html/SMTPD_POLICY_README.html +++ b/postfix/html/SMTPD_POLICY_README.html @@ -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] @@ -133,8 +134,14 @@ size=12345

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

    + 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 --git a/postfix/proto/PGSQL_README.html b/postfix/proto/PGSQL_README.html index 167c45b95..954c8a0d1 100644 --- a/postfix/proto/PGSQL_README.html +++ b/postfix/proto/PGSQL_README.html @@ -139,6 +139,10 @@ configuration feature.
  • 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 --git a/postfix/proto/SMTPD_POLICY_README.html b/postfix/proto/SMTPD_POLICY_README.html index d745a08fb..40fab3aef 100644 --- a/postfix/proto/SMTPD_POLICY_README.html +++ b/postfix/proto/SMTPD_POLICY_README.html @@ -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] @@ -133,8 +134,14 @@ size=12345

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

    + 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 --git a/postfix/src/global/dict_pgsql.c b/postfix/src/global/dict_pgsql.c index 9fe6768bf..11ed847c2 100644 --- a/postfix/src/global/dict_pgsql.c +++ b/postfix/src/global/dict_pgsql.c @@ -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); diff --git a/postfix/src/global/hold_message.c b/postfix/src/global/hold_message.c index 9a79a1b95..feec47729 100644 --- a/postfix/src/global/hold_message.c +++ b/postfix/src/global/hold_message.c @@ -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); diff --git a/postfix/src/global/mail_proto.h b/postfix/src/global/mail_proto.h index 2bd40025e..f23987c9f 100644 --- a/postfix/src/global/mail_proto.h +++ b/postfix/src/global/mail_proto.h @@ -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" diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index faad697b6..0185724f7 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -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 diff --git a/postfix/src/global/tok822_parse.c b/postfix/src/global/tok822_parse.c index 4866ac5ad..4678c9a28 100644 --- a/postfix/src/global/tok822_parse.c +++ b/postfix/src/global/tok822_parse.c @@ -250,6 +250,7 @@ static void strip_address(VSTRING *vp, int start, TOK822 *addr) /* * 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 --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index 2041dcec7..7bb244af3 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -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) diff --git a/postfix/src/smtp/smtp_reuse.c b/postfix/src/smtp/smtp_reuse.c index f9fa6186b..2a3a5ee06 100644 --- a/postfix/src/smtp/smtp_reuse.c +++ b/postfix/src/smtp/smtp_reuse.c @@ -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. diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 8d02bd3cf..7db46df00 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -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 /* diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index dfd1d9952..96930395c 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -226,6 +226,7 @@ #include #include #include +#include /* 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 */ diff --git a/postfix/src/tls/tls_prng_exch.c b/postfix/src/tls/tls_prng_exch.c index c61817173..9bfe1db19 100644 --- a/postfix/src/tls/tls_prng_exch.c +++ b/postfix/src/tls/tls_prng_exch.c @@ -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 diff --git a/postfix/src/util/safe_open.c b/postfix/src/util/safe_open.c index c047a5bac..81ec379bd 100644 --- a/postfix/src/util/safe_open.c +++ b/postfix/src/util/safe_open.c @@ -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... */ diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h index b67b27301..ef1af01f5 100644 --- a/postfix/src/util/sys_defs.h +++ b/postfix/src/util/sys_defs.h @@ -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