From: Wietse Venema
Prime-field groups (EDH): The server needs to be configured with a suitably-large prime and a corresponding "generator". -The acronym for forward secrecy over prime fields is EDH or Ephemeral -Diffie-Hellman (sometimes also abbreviated as DHE).
+The acronym for forward secrecy over prime fields is EDH for Ephemeral +Diffie-Hellman (also abbreviated as DHE for Diffie-Hellman Exchange). +Elliptic-curve groups (EECDH): The server needs to be configured with a "named curve". These offer better security at lower computational cost than prime field groups, but are not as widely implemented. The acronym for the elliptic curve version -is EECDH which is short for Ephemeral Elliptic Curve Diffie-Hellman. -
+is EECDH which is short for Ephemeral Elliptic Curve Diffie-Hellman +(also abbreviated as ECDHE for Elliptic Curve Diffie-Hellman +Exchange).It is not essential to know what these are, but one does need -to know that OpenSSL only supports EECDH as of version 1.0.0. Thus -the configuration parameters related to Elliptic Curve forward secrecy -are only available when Postfix is linked with OpenSSL 1.0.0 or -later (provided EC support has not been disabled by the vendor, as -in some versions of RedHat Linux).
+to know that OpenSSL supports EECDH with version 1.0.0 or later. +Thus the configuration parameters related to Elliptic-Curve forward +secrecy are available when Postfix is linked with OpenSSL ≥ 1.0.0 +(provided EC support has not been disabled by the vendor, as in +some versions of RedHat Linux).Elliptic curves used in cryptography are typically identified by a "name" that stands for a set of well-known parameter values, @@ -200,7 +202,7 @@ parameter file and the prime need not actually be 1024 bits long
It turns out that (inadvisably-patched in some Debian releases) -Exim SMTP clients enforce a minimum 2048-bit length for the non-export +Exim SMTP clients require a ≥ 2048-bit length for the non-export prime. See the quick-start section for the recommended configuration to work around this issue.
@@ -269,10 +271,12 @@ href="TLS_README.html#client_tls_policy">TLS policy table.Postfix 2.6 and 2.7: Enable elliptic-curve support. This -is the default with Postfix ≥ 2.8. +
With Postfix 2.6 and 2.7, enable elliptic-curve support in the +Postfix SMTP client and server. This is the default with Postfix +≥ 2.8.
-@@ -282,12 +286,16 @@ is the default with Postfix ≥ 2.8.
Optionally generate non-default EDH parameters for improved -security against pre-computation attacks and for compatibility with -Debian-patched EXIM SMTP clients (these require a minimum 2048-bit -length for the non-export prime). The parameter files are not -secret, after all these parameters are sent to all SMTP clients in -the clear. Mode 0644 is fine.
+This space intentionally left blank.
+ +Optionally generate non-default Postfix SMTP server EDH parameters +for improved security against pre-computation attacks and for +compatibility with Debian-patched Exim SMTP clients that require a +≥ 2048-bit length for the non-export prime.
Execute as root (prime group generation can take a few seconds to a few minutes):
@@ -295,6 +303,7 @@ few seconds to a few minutes):+# cd /etc/postfix +# umask 022 # openssl dhparam -out dh512.tmp 512 && mv dh512.tmp dh512.pem # openssl dhparam -out dh1024.tmp 1024 && mv dh1024.tmp dh1024.pem # openssl dhparam -out dh2048.tmp 2048 && mv dh2048.tmp dh2048.pem @@ -302,9 +311,14 @@ few seconds to a few minutes):
The Postfix SMTP server EDH parameter files are not secret, +after all these parameters are sent to all remote SMTP clients in +the clear. Mode 0644 is fine.
+You can improve security against pre-computation attacks further -by regenerating the EDH parameters periodically (an hourly or daily -cron job running as root can automate this task).
+by regenerating the Postfix SMTP server EDH parameters periodically +(an hourly or daily cron job running the above commands as root can +automate this task).Once the parameters are in place, update main.cf as follows:
@@ -332,8 +346,6 @@ need to adjust the submission entry in master.cf acc -- -diff --git a/postfix/html/SASL_README.html b/postfix/html/SASL_README.html index e3a7eca41..37b374740 100644 --- a/postfix/html/SASL_README.html +++ b/postfix/html/SASL_README.html @@ -1,4 +1,4 @@ -X diff --git a/postfix/proto/FORWARD_SECRECY_README.html b/postfix/proto/FORWARD_SECRECY_README.html index 6b33f61ad..4aa9012fe 100644 --- a/postfix/proto/FORWARD_SECRECY_README.html +++ b/postfix/proto/FORWARD_SECRECY_README.html @@ -125,24 +125,26 @@ Presently, there are two flavors of "groups" that work with PFS:Prime-field groups (EDH): The server needs to be configured with a suitably-large prime and a corresponding "generator". -The acronym for forward secrecy over prime fields is EDH or Ephemeral -Diffie-Hellman (sometimes also abbreviated as DHE).
+The acronym for forward secrecy over prime fields is EDH for Ephemeral +Diffie-Hellman (also abbreviated as DHE for Diffie-Hellman Exchange). +Elliptic-curve groups (EECDH): The server needs to be configured with a "named curve". These offer better security at lower computational cost than prime field groups, but are not as widely implemented. The acronym for the elliptic curve version -is EECDH which is short for Ephemeral Elliptic Curve Diffie-Hellman. -
+is EECDH which is short for Ephemeral Elliptic Curve Diffie-Hellman +(also abbreviated as ECDHE for Elliptic Curve Diffie-Hellman +Exchange).It is not essential to know what these are, but one does need -to know that OpenSSL only supports EECDH as of version 1.0.0. Thus -the configuration parameters related to Elliptic Curve forward secrecy -are only available when Postfix is linked with OpenSSL 1.0.0 or -later (provided EC support has not been disabled by the vendor, as -in some versions of RedHat Linux).
+to know that OpenSSL supports EECDH with version 1.0.0 or later. +Thus the configuration parameters related to Elliptic-Curve forward +secrecy are available when Postfix is linked with OpenSSL ≥ 1.0.0 +(provided EC support has not been disabled by the vendor, as in +some versions of RedHat Linux).Elliptic curves used in cryptography are typically identified by a "name" that stands for a set of well-known parameter values, @@ -200,7 +202,7 @@ parameter file and the prime need not actually be 1024 bits long
It turns out that (inadvisably-patched in some Debian releases) -Exim SMTP clients enforce a minimum 2048-bit length for the non-export +Exim SMTP clients require a ≥ 2048-bit length for the non-export prime. See the quick-start section for the recommended configuration to work around this issue.
@@ -269,10 +271,12 @@ href="TLS_README.html#client_tls_policy">TLS policy table.Getting started, quick and dirty
-+
EECDH Client and server support (Postfix ≥ 2.6 with OpenSSL +≥ 1.0.0)
-Postfix 2.6 and 2.7: Enable elliptic-curve support. This -is the default with Postfix ≥ 2.8. +
With Postfix 2.6 and 2.7, enable elliptic-curve support in the +Postfix SMTP client and server. This is the default with Postfix +≥ 2.8.
-@@ -282,12 +286,16 @@ is the default with Postfix ≥ 2.8.Optionally generate non-default EDH parameters for improved -security against pre-computation attacks and for compatibility with -Debian-patched EXIM SMTP clients (these require a minimum 2048-bit -length for the non-export prime). The parameter files are not -secret, after all these parameters are sent to all SMTP clients in -the clear. Mode 0644 is fine.
+EDH Client support (Postfix ≥ 2.2)
+ +This space intentionally left blank.
+ +EDH Server support (Postfix ≥ 2.2)
+ +Optionally generate non-default Postfix SMTP server EDH parameters +for improved security against pre-computation attacks and for +compatibility with Debian-patched Exim SMTP clients that require a +≥ 2048-bit length for the non-export prime.
Execute as root (prime group generation can take a few seconds to a few minutes):
@@ -295,6 +303,7 @@ few seconds to a few minutes):+# cd /etc/postfix +# umask 022 # openssl dhparam -out dh512.tmp 512 && mv dh512.tmp dh512.pem # openssl dhparam -out dh1024.tmp 1024 && mv dh1024.tmp dh1024.pem # openssl dhparam -out dh2048.tmp 2048 && mv dh2048.tmp dh2048.pem @@ -302,9 +311,14 @@ few seconds to a few minutes):The Postfix SMTP server EDH parameter files are not secret, +after all these parameters are sent to all remote SMTP clients in +the clear. Mode 0644 is fine.
+You can improve security against pre-computation attacks further -by regenerating the EDH parameters periodically (an hourly or daily -cron job running as root can automate this task).
+by regenerating the Postfix SMTP server EDH parameters periodically +(an hourly or daily cron job running the above commands as root can +automate this task).Once the parameters are in place, update main.cf as follows:
@@ -332,8 +346,6 @@ need to adjust the submission entry in master.cf accordingly:
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index c4e730f26..8254d0187 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20131228"
+#define MAIL_RELEASE_DATE "20140104"
#define MAIL_VERSION_NUMBER "2.11"
#ifdef SNAPSHOT
diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h
index c69470830..336a4f47f 100644
--- a/postfix/src/smtp/smtp.h
+++ b/postfix/src/smtp/smtp.h
@@ -301,9 +301,7 @@ extern HBC_CHECKS *smtp_body_checks; /* limited body checks */
typedef struct SMTP_SESSION {
VSTREAM *stream; /* network connection */
- char *dest; /* nexthop or fallback */
- char *host; /* mail exchanger */
- char *addr; /* mail exchanger */
+ SMTP_ITERATOR *iterator; /* dest, host, addr, port */
char *namaddr; /* mail exchanger */
char *helo; /* helo response */
unsigned port; /* network byte order */
diff --git a/postfix/src/smtp/smtp_chat.c b/postfix/src/smtp/smtp_chat.c
index 11dc90998..5e2f82c7d 100644
--- a/postfix/src/smtp/smtp_chat.c
+++ b/postfix/src/smtp/smtp_chat.c
@@ -363,7 +363,8 @@ SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session)
session->namaddrport, STR(session->buffer));
if (var_helpful_warnings)
msg_warn("to prevent loss of mail, turn off command pipelining "
- "for %s with the %s parameter", session->addr,
+ "for %s with the %s parameter",
+ STR(session->iterator->addr),
SMTP_X(EHLO_DIS_MAPS));
}
}
diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c
index 70e8dc56d..ff278c1ff 100644
--- a/postfix/src/smtp/smtp_connect.c
+++ b/postfix/src/smtp/smtp_connect.c
@@ -159,7 +159,7 @@ static SMTP_SESSION *smtp_connect_unix(SMTP_ITERATOR *iter, DSN_BUF *why,
if (msg_verbose)
msg_info("%s: trying: %s...", myname, addr);
- return (smtp_connect_sock(sock, (struct sockaddr *) & sock_un,
+ return (smtp_connect_sock(sock, (struct sockaddr *) &sock_un,
sizeof(sock_un), iter, why, sess_flags));
}
@@ -170,7 +170,7 @@ static SMTP_SESSION *smtp_connect_addr(SMTP_ITERATOR *iter, DSN_BUF *why,
{
const char *myname = "smtp_connect_addr";
struct sockaddr_storage ss; /* remote */
- struct sockaddr *sa = (struct sockaddr *) & ss;
+ struct sockaddr *sa = (struct sockaddr *) &ss;
SOCKADDR_SIZE salen = sizeof(ss);
MAI_HOSTADDR_STR hostaddr;
DNS_RR *addr = iter->rr;
@@ -274,7 +274,7 @@ static SMTP_SESSION *smtp_connect_addr(SMTP_ITERATOR *iter, DSN_BUF *why,
/* smtp_connect_sock - connect a socket over some transport */
-static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr * sa,
+static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr *sa,
int salen,
SMTP_ITERATOR *iter,
DSN_BUF *why,
@@ -668,7 +668,7 @@ static int smtp_reuse_session(SMTP_STATE *state, DNS_RR **addr_list,
if (*addr_list && SMTP_RCPT_LEFT(state) > 0
&& (session = smtp_reuse_nexthop(state, SMTP_KEY_MASK_SCACHE_DEST_LABEL)) != 0) {
session_count = 1;
- smtp_update_addr_list(addr_list, session->addr, session_count);
+ smtp_update_addr_list(addr_list, STR(iter->addr), session_count);
if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP)
&& *addr_list == 0)
state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER;
@@ -726,7 +726,7 @@ static int smtp_reuse_session(SMTP_STATE *state, DNS_RR **addr_list,
SMTP_KEY_MASK_SCACHE_ENDP_LABEL)) != 0) {
session->features |= SMTP_FEATURE_BEST_MX;
session_count += 1;
- smtp_update_addr_list(addr_list, session->addr, session_count);
+ smtp_update_addr_list(addr_list, STR(iter->addr), session_count);
if (*addr_list == 0)
next = 0;
if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP)
diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c
index 5d442df41..1f9759d38 100644
--- a/postfix/src/smtp/smtp_proto.c
+++ b/postfix/src/smtp/smtp_proto.c
@@ -261,6 +261,7 @@ int smtp_helo(SMTP_STATE *state)
const char *myname = "smtp_helo";
SMTP_SESSION *session = state->session;
DELIVER_REQUEST *request = state->request;
+ SMTP_ITERATOR *iter = state->iterator;
SMTP_RESP *resp;
SMTP_RESP fake;
int except;
@@ -322,7 +323,7 @@ int smtp_helo(SMTP_STATE *state)
STR(resp->dsn_buf)[0] = '4';
/* FALLTHROUGH */
default:
- return (smtp_site_fail(state, session->host, resp,
+ return (smtp_site_fail(state, STR(iter->host), resp,
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
@@ -350,7 +351,7 @@ int smtp_helo(SMTP_STATE *state)
if (smtp_pix_bug_maps != 0
&& (pix_bug_words =
maps_find(smtp_pix_bug_maps,
- state->session->addr, 0)) != 0) {
+ STR(iter->addr), 0)) != 0) {
pix_bug_source = SMTP_X(PIX_BUG_MAPS);
} else {
pix_bug_words = var_smtp_pix_bug_words;
@@ -415,7 +416,7 @@ int smtp_helo(SMTP_STATE *state)
smtp_chat_cmd(session, "EHLO %s", var_smtp_helo_name);
if ((resp = smtp_chat_resp(session))->code / 100 != 2) {
if (resp->code == 421)
- return (smtp_site_fail(state, session->host, resp,
+ return (smtp_site_fail(state, STR(iter->host), resp,
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
@@ -427,7 +428,7 @@ int smtp_helo(SMTP_STATE *state)
where = "performing the HELO handshake";
smtp_chat_cmd(session, "HELO %s", var_smtp_helo_name);
if ((resp = smtp_chat_resp(session))->code / 100 != 2)
- return (smtp_site_fail(state, session->host, resp,
+ return (smtp_site_fail(state, STR(iter->host), resp,
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
@@ -436,7 +437,7 @@ int smtp_helo(SMTP_STATE *state)
where = "performing the LHLO handshake";
smtp_chat_cmd(session, "LHLO %s", var_smtp_helo_name);
if ((resp = smtp_chat_resp(session))->code / 100 != 2)
- return (smtp_site_fail(state, session->host, resp,
+ return (smtp_site_fail(state, STR(iter->host), resp,
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
@@ -454,12 +455,12 @@ int smtp_helo(SMTP_STATE *state)
*/
if (smtp_ehlo_dis_maps == 0
|| (ehlo_words = maps_find(smtp_ehlo_dis_maps,
- state->session->addr, 0)) == 0)
+ STR(iter->addr), 0)) == 0)
ehlo_words = var_smtp_ehlo_dis_words;
if (smtp_ehlo_dis_maps && smtp_ehlo_dis_maps->error) {
msg_warn("%s: %s map lookup error for %s",
session->state->request->queue_id,
- smtp_ehlo_dis_maps->title, state->session->addr);
+ smtp_ehlo_dis_maps->title, STR(iter->addr));
vstream_longjmp(session->stream, SMTP_ERR_DATA);
}
discard_mask = ehlo_mask(ehlo_words);
@@ -643,7 +644,7 @@ int smtp_helo(SMTP_STATE *state)
if ((session->features & SMTP_FEATURE_STARTTLS) &&
var_smtp_tls_note_starttls_offer &&
session->tls->level <= TLS_LEV_NONE)
- msg_info("Host offered STARTTLS: [%s]", session->host);
+ msg_info("Host offered STARTTLS: [%s]", STR(iter->host));
/*
* Decide whether or not to send STARTTLS.
@@ -690,7 +691,7 @@ int smtp_helo(SMTP_STATE *state)
*/
session->features &= ~SMTP_FEATURE_STARTTLS;
if (TLS_REQUIRED(session->tls->level))
- return (smtp_site_fail(state, session->host, resp,
+ return (smtp_site_fail(state, STR(iter->host), resp,
"TLS is required, but host %s refused to start TLS: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
@@ -739,6 +740,7 @@ int smtp_helo(SMTP_STATE *state)
static int smtp_start_tls(SMTP_STATE *state)
{
SMTP_SESSION *session = state->session;
+ SMTP_ITERATOR *iter = state->iterator;
TLS_CLIENT_START_PROPS tls_props;
VSTRING *serverid;
SMTP_RESP fake;
@@ -805,7 +807,7 @@ static int smtp_start_tls(SMTP_STATE *state)
timeout = var_smtp_starttls_tmout,
tls_level = session->tls->level,
nexthop = session->tls_nexthop,
- host = session->host,
+ host = STR(iter->host),
namaddr = session->namaddrport,
serverid = vstring_str(serverid),
helo = session->helo,
@@ -1142,6 +1144,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
const char *myname = "smtp_loop";
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
+ SMTP_ITERATOR *iter = state->iterator;
SMTP_RESP *resp;
RECIPIENT *rcpt;
VSTRING *next_command = vstring_alloc(100);
@@ -1652,7 +1655,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
*/
case SMTP_STATE_MAIL:
if (resp->code / 100 != 2) {
- smtp_mesg_fail(state, session->host, resp,
+ smtp_mesg_fail(state, STR(iter->host), resp,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
@@ -1724,7 +1727,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
smtp_rcpt_done(state, resp, rcpt);
}
} else {
- smtp_rcpt_fail(state, rcpt, session->host, resp,
+ smtp_rcpt_fail(state, rcpt, STR(iter->host), resp,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
@@ -1746,7 +1749,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
case SMTP_STATE_DATA:
if (resp->code / 100 != 3) {
if (nrcpt > 0)
- smtp_mesg_fail(state, session->host, resp,
+ smtp_mesg_fail(state, STR(iter->host), resp,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
@@ -1770,7 +1773,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
if (smtp_mode) {
if (nrcpt > 0) {
if (resp->code / 100 != 2) {
- smtp_mesg_fail(state, session->host, resp,
+ smtp_mesg_fail(state, STR(iter->host), resp,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
@@ -1797,7 +1800,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
rcpt = request->rcpt_list.info
+ survivors[recv_done++];
if (resp->code / 100 != 2) {
- smtp_rcpt_fail(state, rcpt, session->host, resp,
+ smtp_rcpt_fail(state, rcpt, STR(iter->host), resp,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
diff --git a/postfix/src/smtp/smtp_rcpt.c b/postfix/src/smtp/smtp_rcpt.c
index ec6d2a47c..acb125236 100644
--- a/postfix/src/smtp/smtp_rcpt.c
+++ b/postfix/src/smtp/smtp_rcpt.c
@@ -132,6 +132,7 @@ void smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt)
{
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
+ SMTP_ITERATOR *iter = state->iterator;
DSN_BUF *why = state->why;
const char *dsn_action = "relayed";
int status;
@@ -162,7 +163,7 @@ void smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt)
*
* Note: the DSN action is ignored in case of address probes.
*/
- dsb_update(why, resp->dsn, dsn_action, DSB_MTYPE_DNS, session->host,
+ dsb_update(why, resp->dsn, dsn_action, DSB_MTYPE_DNS, STR(iter->host),
DSB_DTYPE_SMTP, resp->str, "%s", resp->str);
status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
diff --git a/postfix/src/smtp/smtp_reuse.c b/postfix/src/smtp/smtp_reuse.c
index eeadbb20d..3f1270678 100644
--- a/postfix/src/smtp/smtp_reuse.c
+++ b/postfix/src/smtp/smtp_reuse.c
@@ -155,6 +155,7 @@ static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
const char *label)
{
const char *myname = "smtp_reuse_common";
+ SMTP_ITERATOR *iter = state->iterator;
SMTP_SESSION *session;
/*
@@ -200,7 +201,7 @@ static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
/*
* Update the list of used cached addresses.
*/
- htable_enter(state->cache_used, session->addr, (char *) 0);
+ htable_enter(state->cache_used, STR(iter->addr), (char *) 0);
return (session);
}
diff --git a/postfix/src/smtp/smtp_sasl_auth_cache.c b/postfix/src/smtp/smtp_sasl_auth_cache.c
index 562d6f3a5..520f8b6e5 100644
--- a/postfix/src/smtp/smtp_sasl_auth_cache.c
+++ b/postfix/src/smtp/smtp_sasl_auth_cache.c
@@ -230,11 +230,12 @@ static int smtp_sasl_auth_cache_valid_value(SMTP_SASL_AUTH_CACHE *auth_cache,
int smtp_sasl_auth_cache_find(SMTP_SASL_AUTH_CACHE *auth_cache,
const SMTP_SESSION *session)
{
+ SMTP_ITERATOR *iter = session->iterator;
char *key;
const char *entry;
int valid = 0;
- key = smtp_sasl_auth_cache_make_key(session->host, session->sasl_username);
+ key = smtp_sasl_auth_cache_make_key(STR(iter->host), session->sasl_username);
if ((entry = dict_get(auth_cache->dict, key)) != 0)
if ((valid = smtp_sasl_auth_cache_valid_value(auth_cache, entry,
session->sasl_passwd)) == 0)
@@ -255,10 +256,11 @@ void smtp_sasl_auth_cache_store(SMTP_SASL_AUTH_CACHE *auth_cache,
const SMTP_SESSION *session,
const SMTP_RESP *resp)
{
+ SMTP_ITERATOR *iter = session->iterator;
char *key;
char *value;
- key = smtp_sasl_auth_cache_make_key(session->host, session->sasl_username);
+ key = smtp_sasl_auth_cache_make_key(STR(iter->host), session->sasl_username);
value = smtp_sasl_auth_cache_make_value(session->sasl_passwd,
resp->dsn, resp->str);
dict_put(auth_cache->dict, key, value);
diff --git a/postfix/src/smtp/smtp_sasl_glue.c b/postfix/src/smtp/smtp_sasl_glue.c
index 1cf6adac9..5254341d5 100644
--- a/postfix/src/smtp/smtp_sasl_glue.c
+++ b/postfix/src/smtp/smtp_sasl_glue.c
@@ -158,6 +158,7 @@ int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
{
const char *myname = "smtp_sasl_passwd_lookup";
SMTP_STATE *state = session->state;
+ SMTP_ITERATOR *iter = session->iterator;
const char *value;
char *passwd;
@@ -187,10 +188,10 @@ int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
state->request->sender, (char **) 0)) != 0)
|| (smtp_sasl_passwd_map->error == 0
&& (value = maps_find(smtp_sasl_passwd_map,
- session->host, 0)) != 0)
+ STR(iter->host), 0)) != 0)
|| (smtp_sasl_passwd_map->error == 0
&& (value = maps_find(smtp_sasl_passwd_map,
- session->dest, 0)) != 0)) {
+ STR(iter->dest), 0)) != 0)) {
if (session->sasl_username)
myfree(session->sasl_username);
session->sasl_username = mystrdup(value);
@@ -200,7 +201,7 @@ int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
session->sasl_passwd = mystrdup(passwd ? passwd : "");
if (msg_verbose)
msg_info("%s: host `%s' user `%s' pass `%s'",
- myname, session->host,
+ myname, STR(iter->host),
session->sasl_username, session->sasl_passwd);
return (1);
} else if (smtp_sasl_passwd_map->error) {
@@ -210,7 +211,7 @@ int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
} else {
if (msg_verbose)
msg_info("%s: no auth info found (sender=`%s', host=`%s')",
- myname, state->request->sender, session->host);
+ myname, state->request->sender, STR(iter->host));
return (0);
}
}
@@ -284,6 +285,7 @@ void smtp_sasl_start(SMTP_SESSION *session, const char *sasl_opts_name,
const char *sasl_opts_val)
{
XSASL_CLIENT_CREATE_ARGS create_args;
+ SMTP_ITERATOR *iter = session->iterator;
if (msg_verbose)
msg_info("starting new SASL client");
@@ -291,7 +293,7 @@ void smtp_sasl_start(SMTP_SESSION *session, const char *sasl_opts_name,
XSASL_CLIENT_CREATE(smtp_sasl_impl, &create_args,
stream = session->stream,
service = var_procname,
- server_name = session->host,
+ server_name = STR(iter->host),
security_options = sasl_opts_val)) == 0)
msg_fatal("SASL per-connection initialization failed");
session->sasl_reply = vstring_alloc(20);
@@ -302,6 +304,7 @@ void smtp_sasl_start(SMTP_SESSION *session, const char *sasl_opts_name,
int smtp_sasl_authenticate(SMTP_SESSION *session, DSN_BUF *why)
{
const char *myname = "smtp_sasl_authenticate";
+ SMTP_ITERATOR *iter = session->iterator;
SMTP_RESP *resp;
const char *mechanism;
int result;
@@ -330,9 +333,9 @@ int smtp_sasl_authenticate(SMTP_SESSION *session, DSN_BUF *why)
if (var_smtp_sasl_auth_soft_bounce && resp_dsn[0] == '5')
resp_dsn[0] = '4';
dsb_update(why, resp_dsn, DSB_DEF_ACTION, DSB_MTYPE_DNS,
- session->host, var_procname, resp_str,
+ STR(iter->host), var_procname, resp_str,
"SASL [CACHED] authentication failed; server %s said: %s",
- session->host, resp_str);
+ STR(iter->host), resp_str);
return (0);
}
#endif
@@ -416,7 +419,7 @@ int smtp_sasl_authenticate(SMTP_SESSION *session, DSN_BUF *why)
if (var_smtp_sasl_auth_soft_bounce && resp->code / 100 == 5)
STR(resp->dsn_buf)[0] = '4';
dsb_update(why, resp->dsn, DSB_DEF_ACTION,
- DSB_MTYPE_DNS, session->host,
+ DSB_MTYPE_DNS, STR(iter->host),
var_procname, resp->str,
"SASL authentication failed; server %s said: %s",
session->namaddr, resp->str);
diff --git a/postfix/src/smtp/smtp_session.c b/postfix/src/smtp/smtp_session.c
index 2336f0b0a..535e8b628 100644
--- a/postfix/src/smtp/smtp_session.c
+++ b/postfix/src/smtp/smtp_session.c
@@ -123,16 +123,13 @@ SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, SMTP_ITERATOR *iter,
time_t start, int flags)
{
SMTP_SESSION *session;
- const char *dest = STR(iter->dest);
const char *host = STR(iter->host);
const char *addr = STR(iter->addr);
unsigned port = iter->port;
session = (SMTP_SESSION *) mymalloc(sizeof(*session));
session->stream = stream;
- session->dest = mystrdup(dest);
- session->host = mystrdup(host);
- session->addr = mystrdup(addr);
+ session->iterator = iter;
session->namaddr = concatenate(host, "[", addr, "]", (char *) 0);
session->helo = 0;
session->port = port;
@@ -191,9 +188,6 @@ void smtp_session_free(SMTP_SESSION *session)
#endif
if (session->stream)
vstream_fclose(session->stream);
- myfree(session->dest);
- myfree(session->host);
- myfree(session->addr);
myfree(session->namaddr);
myfree(session->namaddrport);
if (session->helo)
@@ -221,6 +215,7 @@ void smtp_session_free(SMTP_SESSION *session)
int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
VSTRING *endp_prop)
{
+ SMTP_ITERATOR *iter = session->iterator;
int fd;
/*
@@ -238,7 +233,7 @@ int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
*
*/
vstring_sprintf(dest_prop, "%s\n%s\n%s\n%u",
- session->dest, session->host, session->addr,
+ STR(iter->dest), STR(iter->host), STR(iter->addr),
session->features & SMTP_FEATURE_DESTINATION_MASK);
/*
diff --git a/postfix/src/tls/Makefile.in b/postfix/src/tls/Makefile.in
index 26e5d5a45..1af941902 100644
--- a/postfix/src/tls/Makefile.in
+++ b/postfix/src/tls/Makefile.in
@@ -19,7 +19,7 @@ INCL =
LIB = libtls.a
TESTPROG= tls_dh tls_mgr tls_rsa tls_dane
-LIBS = ../../lib/libglobal.a ../../lib/libutil.a ../../lib/libdns.a
+LIBS = ../../lib/libdns.a ../../lib/libglobal.a ../../lib/libutil.a
LIB_DIR = ../../lib
INC_DIR = ../../include
MAKES =
diff --git a/postfix/src/util/dict_cache.c b/postfix/src/util/dict_cache.c
index 40cfced39..7a8178c93 100644
--- a/postfix/src/util/dict_cache.c
+++ b/postfix/src/util/dict_cache.c
@@ -705,11 +705,11 @@ const char *dict_cache_name(DICT_CACHE *cp)
"\n\treset (discard pending requests)" \
"\n\trun (execute pending requests in interleaved order)" \
"\n\n\tTo add a pending request:" \
- "\n\tquery (negative to reverse order)" \
- "\n\tupdate (negative to reverse order)" \
- "\n\tdelete (negative to reverse order)" \
- "\n\tpurge " \
- "\n\tcount "
+ "\n\tquery (negative to reverse order)" \
+ "\n\tupdate (negative to reverse order)" \
+ "\n\tdelete (negative to reverse order)" \
+ "\n\tpurge " \
+ "\n\tcount "
/*
* For realism, open the cache with the same flags as postscreen(8) and
@@ -725,7 +725,7 @@ typedef struct DICT_CACHE_SREQ {
int flags; /* per-request: reverse, purge */
char *cmd; /* command for status report */
void (*action) (struct DICT_CACHE_SREQ *, DICT_CACHE *, VSTRING *);
- char *prefix; /* key prefix */
+ char *suffix; /* key suffix */
int done; /* progress indicator */
int todo; /* number of entries to process */
int first_next; /* first/next */
@@ -772,9 +772,9 @@ static void make_tagged_key(VSTRING *bp, DICT_CACHE_SREQ *cp)
msg_panic("make_tagged_key: bad done count: %d", cp->done);
if (cp->todo < 1)
msg_panic("make_tagged_key: bad todo count: %d", cp->todo);
- vstring_sprintf(bp, "%s-%d", cp->prefix,
+ vstring_sprintf(bp, "%d-%s",
(cp->flags & DICT_CACHE_SREQ_FLAG_REVERSE) ?
- cp->todo - cp->done - 1 : cp->done);
+ cp->todo - cp->done - 1 : cp->done, cp->suffix);
}
/* create_requests - create request list */
@@ -793,7 +793,7 @@ static DICT_CACHE_TEST *create_requests(int count)
cp->flags = 0;
cp->cmd = 0;
cp->action = 0;
- cp->prefix = 0;
+ cp->suffix = 0;
cp->todo = 0;
cp->first_next = DICT_SEQ_FUN_FIRST;
}
@@ -815,9 +815,9 @@ static void reset_requests(DICT_CACHE_TEST *tp)
cp->cmd = 0;
}
cp->action = 0;
- if (cp->prefix) {
- myfree(cp->prefix);
- cp->prefix = 0;
+ if (cp->suffix) {
+ myfree(cp->suffix);
+ cp->suffix = 0;
}
cp->todo = 0;
cp->first_next = DICT_SEQ_FUN_FIRST;
@@ -876,8 +876,11 @@ static void show_status(DICT_CACHE_TEST *tp, DICT_CACHE *dp)
#endif
vstream_printf("cache\t%s\n", dp ? dp->name : "(none)");
- vstream_printf("%s\t%s\t%s\t%s\t%s\t%s\n",
- "cmd", "dir", "prefix", "count", "done", "first/next");
+ if (tp->used == 0)
+ vstream_printf("No pending requests\n");
+ else
+ vstream_printf("%s\t%s\t%s\t%s\t%s\t%s\n",
+ "cmd", "dir", "suffix", "count", "done", "first/next");
for (cp = tp->job_list; cp < tp->job_list + tp->used; cp++)
if (cp->todo > 0)
@@ -885,7 +888,7 @@ static void show_status(DICT_CACHE_TEST *tp, DICT_CACHE *dp)
cp->cmd,
(cp->flags & DICT_CACHE_SREQ_FLAG_REVERSE) ?
"reverse" : "forward",
- cp->prefix ? cp->prefix : "(null)", cp->todo,
+ cp->suffix ? cp->suffix : "(null)", cp->todo,
cp->done, cp->first_next);
}
@@ -936,21 +939,21 @@ static void delete_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp)
cp->done += 1;
}
-/* iter_action - iterate over cache and act on entries with given prefix */
+/* iter_action - iterate over cache and act on entries with given suffix */
static void iter_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp)
{
const char *cache_key;
const char *cache_val;
const char *what;
- int len;
+ const char *suffix;
if (dict_cache_sequence(dp, cp->first_next, &cache_key, &cache_val) == 0) {
if (strcmp(cache_key, cache_val) != 0)
msg_warn("value \"%s\" differs from key \"%s\"",
cache_val, cache_key);
- len = strlen(cp->prefix);
- if (strncmp(cache_key, cp->prefix, len) == 0 && cache_key[len] == '-') {
+ suffix = cache_key + strspn(cache_key, "0123456789");
+ if (suffix[0] == '-' && strcmp(suffix + 1, cp->suffix) == 0) {
cp->done += 1;
cp->todo = cp->done + 1; /* XXX */
if ((cp->flags & DICT_CACHE_SREQ_FLAG_PURGE)
@@ -967,7 +970,7 @@ static void iter_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp)
if (dp->error)
msg_warn("%s error after %d: %m", what, cp->done);
else
- vstream_printf("prefix=%s %s=%d\n", cp->prefix, what, cp->done);
+ vstream_printf("suffix=%s %s=%d\n", cp->suffix, what, cp->done);
cp->todo = 0;
}
}
@@ -1001,7 +1004,7 @@ static void add_request(DICT_CACHE_TEST *tp, ARGV *argv)
int req_flags;
int count;
char *cmd = argv->argv[0];
- char *prefix = (argv->argc > 1 ? argv->argv[1] : 0);
+ char *suffix = (argv->argc > 1 ? argv->argv[1] : 0);
char *todo = (argv->argc > 2 ? argv->argv[2] : "1"); /* XXX */
if (tp->used >= tp->size) {
@@ -1034,8 +1037,8 @@ static void add_request(DICT_CACHE_TEST *tp, ARGV *argv)
cp = tp->job_list + tp->used;
cp->cmd = mystrdup(cmd);
cp->action = rp->action;
- if (prefix)
- cp->prefix = mystrdup(prefix);
+ if (suffix)
+ cp->suffix = mystrdup(suffix);
cp->done = 0;
cp->flags = req_flags;
cp->todo = count;
diff --git a/postfix/src/util/slmdb.c b/postfix/src/util/slmdb.c
index 5259d3a76..abb5eeef9 100644
--- a/postfix/src/util/slmdb.c
+++ b/postfix/src/util/slmdb.c
@@ -82,7 +82,8 @@
/*
/* slmdb_cursor_get() is an mdb_cursor_get() wrapper with
/* automatic error recovery. The result value is an LMDB
-/* status code (zero in case of success).
+/* status code (zero in case of success). This wrapper supports
+/* only one cursor per database.
/*
/* slmdb_fd() returns the file descriptor for the specified
/* database. This may be used for file status queries or
@@ -198,6 +199,8 @@
#include
#include
#include
+#include
+#include
/* Application-specific. */
@@ -256,6 +259,80 @@
return (status); \
} while (0)
+ /*
+ * We must close the cursor's read transaction before writing to the
+ * database with MDB_NOLOCK, and before changing the memory map size. Our
+ * database iterator saves the key under the last cursor position, and
+ * restores the cursor if needed. This supports only one cursor per
+ * database.
+ */
+
+/* slmdb_cursor_close - close cursor and its read transaction */
+
+static void slmdb_cursor_close(SLMDB *slmdb)
+{
+ MDB_txn *txn;
+
+ /*
+ * Close the cursor and its read transaction. We can restore it later
+ * from the saved key information.
+ */
+ txn = mdb_cursor_txn(slmdb->cursor);
+ mdb_cursor_close(slmdb->cursor);
+ slmdb->cursor = 0;
+ mdb_txn_abort(txn);
+}
+
+/* slmdb_saved_key_init - initialize saved key info */
+
+static void slmdb_saved_key_init(SLMDB *slmdb)
+{
+ slmdb->saved_key.mv_data = 0;
+ slmdb->saved_key_size = 0;
+}
+
+/* slmdb_saved_key_free - destroy saved key info */
+
+static void slmdb_saved_key_free(SLMDB *slmdb)
+{
+ free(slmdb->saved_key.mv_data);
+ slmdb->saved_key.mv_data = 0;
+ slmdb->saved_key_size = 0;
+}
+
+#define HAVE_SLMDB_SAVED_KEY(s) ((s)->saved_key.mv_data != 0)
+
+/* slmdb_saved_key_assign - copy the saved key */
+
+static int slmdb_saved_key_assign(SLMDB *slmdb, MDB_val *key_val)
+{
+
+ /*
+ * Extend the buffer to fit the key, so that we can avoid malloc()
+ * overhead most of the time.
+ */
+ if (slmdb->saved_key_size < key_val->mv_size) {
+ if (slmdb->saved_key.mv_data == 0)
+ slmdb->saved_key.mv_data = malloc(key_val->mv_size);
+ else
+ slmdb->saved_key.mv_data =
+ realloc(slmdb->saved_key.mv_data, key_val->mv_size);
+ if (slmdb->saved_key.mv_data == 0) {
+ slmdb->saved_key_size = 0;
+ return (ENOMEM);
+ } else {
+ slmdb->saved_key_size = key_val->mv_size;
+ }
+ }
+
+ /*
+ * Copy the key under the cursor.
+ */
+ memcpy(slmdb->saved_key.mv_data, key_val->mv_data, key_val->mv_size);
+ slmdb->saved_key.mv_size = key_val->mv_size;
+ return (0);
+}
+
/* slmdb_prepare - LMDB-specific (re)initialization before actual access */
static int slmdb_prepare(SLMDB *slmdb)
@@ -295,6 +372,13 @@ static int slmdb_recover(SLMDB *slmdb, int status)
{
MDB_envinfo info;
+ /*
+ * Close the cursor and its read transaction before changing the memory
+ * map size. We can restore it later with the saved key information.
+ */
+ if (slmdb->cursor != 0)
+ slmdb_cursor_close(slmdb);
+
/*
* Recover bulk transactions only if they can be restarted. Limit the
* number of recovery attempts per slmdb(3) API request.
@@ -456,6 +540,15 @@ int slmdb_put(SLMDB *slmdb, MDB_val *mdb_key,
else if ((status = slmdb_txn_begin(slmdb, 0, &txn)) != 0)
SLMDB_API_RETURN(slmdb, status);
+ /*
+ * Before doing a non-bulk write transaction in MDB_NOLOCK mode, close a
+ * cursor and its read transaction. We can restore it later with the
+ * saved key information.
+ */
+ if (slmdb->cursor != 0 && slmdb->txn == 0
+ && (slmdb->lmdb_flags & MDB_NOLOCK))
+ slmdb_cursor_close(slmdb);
+
/*
* Do the update.
*/
@@ -493,6 +586,15 @@ int slmdb_del(SLMDB *slmdb, MDB_val *mdb_key)
else if ((status = slmdb_txn_begin(slmdb, 0, &txn)) != 0)
SLMDB_API_RETURN(slmdb, status);
+ /*
+ * Before doing a non-bulk write transaction in MDB_NOLOCK mode, close a
+ * cursor and its read transaction. We can restore it later with the
+ * saved key information.
+ */
+ if (slmdb->cursor != 0 && slmdb->txn == 0
+ && (slmdb->lmdb_flags & MDB_NOLOCK))
+ slmdb_cursor_close(slmdb);
+
/*
* Do the update.
*/
@@ -527,13 +629,27 @@ int slmdb_cursor_get(SLMDB *slmdb, MDB_val *mdb_key,
* Open a read transaction and cursor if needed.
*/
if (slmdb->cursor == 0) {
- slmdb_txn_begin(slmdb, MDB_RDONLY, &txn);
+ if ((status = slmdb_txn_begin(slmdb, MDB_RDONLY, &txn)) != 0)
+ SLMDB_API_RETURN(slmdb, status);
if ((status = mdb_cursor_open(txn, slmdb->dbi, &slmdb->cursor)) != 0) {
mdb_txn_abort(txn);
if ((status = slmdb_recover(slmdb, status)) == 0)
status = slmdb_cursor_get(slmdb, mdb_key, mdb_value, op);
SLMDB_API_RETURN(slmdb, status);
}
+
+ /*
+ * Restore the cursor to the saved key position.
+ */
+ if (HAVE_SLMDB_SAVED_KEY(slmdb) && op != MDB_FIRST) {
+ if ((status = mdb_cursor_get(slmdb->cursor, &slmdb->saved_key,
+ (MDB_val *) 0, MDB_SET)) != 0) {
+ slmdb_cursor_close(slmdb);
+ if ((status = slmdb_recover(slmdb, status)) == 0)
+ status = slmdb_cursor_get(slmdb, mdb_key, mdb_value, op);
+ SLMDB_API_RETURN(slmdb, status);
+ }
+ }
}
/*
@@ -541,15 +657,20 @@ int slmdb_cursor_get(SLMDB *slmdb, MDB_val *mdb_key,
*/
status = mdb_cursor_get(slmdb->cursor, mdb_key, mdb_value, op);
+ /*
+ * Save the cursor position. This can fail only with ENOMEM.
+ */
+ if (status == 0)
+ status = slmdb_saved_key_assign(slmdb, mdb_key);
+
/*
* Handle end-of-database or other error.
*/
- if (status != 0) {
+ else {
if (status == MDB_NOTFOUND) {
- txn = mdb_cursor_txn(slmdb->cursor);
- mdb_cursor_close(slmdb->cursor);
- mdb_txn_abort(txn);
- slmdb->cursor = 0;
+ slmdb_cursor_close(slmdb);
+ if (HAVE_SLMDB_SAVED_KEY(slmdb))
+ slmdb_saved_key_free(slmdb);
} else {
if ((status = slmdb_recover(slmdb, status)) == 0)
status = slmdb_cursor_get(slmdb, mdb_key, mdb_value, op);
@@ -613,14 +734,17 @@ int slmdb_close(SLMDB *slmdb)
/*
* Clean up after an unfinished sequence() operation.
*/
- if (slmdb->cursor) {
- MDB_txn *txn = mdb_cursor_txn(slmdb->cursor);
+ if (slmdb->cursor != 0)
+ slmdb_cursor_close(slmdb);
- mdb_cursor_close(slmdb->cursor);
- mdb_txn_abort(txn);
- }
mdb_env_close(slmdb->env);
+ /*
+ * Clean up the saved key position.
+ */
+ if (HAVE_SLMDB_SAVED_KEY(slmdb))
+ slmdb_saved_key_free(slmdb);
+
SLMDB_API_RETURN(slmdb, status);
}
@@ -703,6 +827,7 @@ int slmdb_open(SLMDB *slmdb, const char *path, int open_flags,
slmdb->dbi = dbi;
slmdb->db_fd = db_fd;
slmdb->cursor = 0;
+ slmdb_saved_key_init(slmdb);
slmdb->api_retry_count = 0;
slmdb->bulk_retry_count = 0;
slmdb->api_retry_limit = SLMDB_DEF_API_RETRY_LIMIT;
@@ -718,4 +843,20 @@ int slmdb_open(SLMDB *slmdb, const char *path, int open_flags,
return (status);
}
+#endif
+
+ /*
+ * Implementation-dependent workaround to debug LMDB assert() failures. The
+ * code below prevents daemons from disappearing without logfile record.
+ */
+#ifdef LMDB_ASSERT_WORKAROUND
+
+#include
+
+void __assert(const char *func, const char *file, int line, const char *text)
+{
+ msg_panic("Assertion failed: %s, function %s, file %s, line %d.",
+ text, func, file, line);
+}
+
#endif
diff --git a/postfix/src/util/slmdb.h b/postfix/src/util/slmdb.h
index 40fb1aeb6..6f5ad5cfa 100644
--- a/postfix/src/util/slmdb.h
+++ b/postfix/src/util/slmdb.h
@@ -31,6 +31,9 @@
#define SLMDB_JMP_BUF sigjmp_buf
#endif
+ /*
+ * All data structure members are private.
+ */
typedef struct {
size_t curr_limit; /* database soft size limit */
int size_incr; /* database expansion factor */
@@ -43,6 +46,8 @@ typedef struct {
MDB_txn *txn; /* bulk transaction */
int db_fd; /* database file handle */
MDB_cursor *cursor; /* iterator */
+ MDB_val saved_key; /* saved cursor key buffer */
+ size_t saved_key_size; /* saved cursor key buffer size */
void (*longjmp_fn) (void *, int);/* exception handling */
void (*notify_fn) (void *, int,...); /* workaround notification */
void *cb_context; /* call-back context */