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.
+
* 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.
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:
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:
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
-------------------------------------
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>
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>
</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>
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>
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>
</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>
char *table;
ARGV *hosts;
PLPGSQL *pldb;
+ HOST *active_host;
} DICT_PGSQL;
/* 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 *);
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 */
}
/*
- * 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) {
* 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);
}
/*
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 */
{
const char *myname = "pgsql_parse_config";
CFG_PARSER *p;
- int i;
char *hosts;
VSTRING *query;
char *select_function;
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)
static void dict_pgsql_close(DICT *dict)
{
- int i;
DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict;
plpgsql_dealloc(dict_pgsql->pldb);
(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);
#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"
* 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
/*
* 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);
"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)
/*
/* 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:
}
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.
*/
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.
* 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
/*
#include <is_header.h>
#include <rewrite_clnt.h>
#include <valid_mailhost_addr.h>
+#include <xtext.h>
/* Application-specific. */
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.
*/
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",
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 ?
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 : "",
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 */
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
/* 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;
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
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...
*/
* 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