From 4a25a6b519d57a762089f505f48e07a53bf2eaed Mon Sep 17 00:00:00 2001
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 */