diff --git a/postfix/HISTORY b/postfix/HISTORY index cd2d89f9f..245645be3 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -26921,3 +26921,31 @@ Apologies for any names omitted. example is to capture output from "postmulti -p status" to figure out which instances are or are not running. Files: postfix/postfix.c, postlog/postlog.c. + +20230209 + + Cleanup: in smtp_service_addr() refined the loop detection + code for SRV lookup. File: smtp/smtp_addr.c. + + Cleanup: renamed macros with invisible side effects and + implicit inputs to upper case. Verified that the compiled + code did not change. File: tls_fprint.c. + +20230310 + + Cleanup: the milter header/body checks logged less text (up + to 60 bytes) than the 'original' header/body checks (up to + 200 bytes). Problem reported by Aleksandr Stankevic. Fixed + the same inconsistency in the Postfix SMTP client. Files: + cleanup/cleanup_milter.c, smtp/smtp_proto.c. + +20230311 + + Hardening: the Postfix SMTP server can now aggregate + smtpd_client_*_rate and smtpd_client_*_count statistics by + network block, as specified with smtpd_client_ipv4_prefix_length + (default 32, no aggregation) and smtpd_client_ipv6_prefix_length + (default 72, aggregation by /72 network blocks). The latter + raises the bar for a memory exhaustion attack. Files: + util/net_mask_top.[hc], smtpd/smtpd.c, smtpd/smtpd_peer.c, + mantools/postlink, proto/postconf.proto. diff --git a/postfix/WISHLIST b/postfix/WISHLIST index 71b1685ca..7c891253c 100644 --- a/postfix/WISHLIST +++ b/postfix/WISHLIST @@ -14,6 +14,13 @@ Wish list: Multi-recipient support in sender/recipient_bcc_maps and always_bcc. + mail_conf_xxx supprt for non-negative numbers (i.e. + numbers with a lower bound of zero). + + Log anvil transgressions with their address range (in + addition to the offending IP address. We should not disclose + to random clients how we aggregate anvil event counters. + Should "postconf -f" pretty-print text inside {}? Is there any code that calls attr_scan*() and that works diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index a83d227f4..10816cb9a 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -14639,6 +14639,33 @@ This feature is available in Postfix 2.2 and later.

+ + +
smtpd_client_ipv4_prefix_length +(default: 32)
+ +

Aggregate smtpd_client_*_count and smtpd_client_*_rate statistics +by IPv4 network blocks with the specified network prefix. Aggregation +reduces the anvil(8) resources needed to maintain counters. By +default, aggregation is disabled for IPv4.

+ +

This feature is available in Postfix 3.8 and later.

+ + +
+ +
smtpd_client_ipv6_prefix_length +(default: 72)
+ +

Aggregate smtpd_client_*_count and smtpd_client_*_rate statistics +by IPv6 network blocks with the specified network prefix. Aggregation +reduces the anvil(8) resources needed to maintain counters. By +default, aggregation is enabled for IPv6. +

+ +

This feature is available in Postfix 3.8 and later.

+ +
smtpd_client_message_rate_limit diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html index dca57a0ac..ecf775cf8 100644 --- a/postfix/html/smtpd.8.html +++ b/postfix/html/smtpd.8.html @@ -962,6 +962,16 @@ SMTPD(8) SMTPD(8) header_from_format (standard) The format of the Postfix-generated From: header. + Available in Postfix version 3.8 and later: + + smtpd_client_ipv4_prefix_length (32) + Aggregate smtpd_client_*_count and smtpd_client_*_rate statis- + tics by IPv4 network blocks with the specified network prefix. + + smtpd_client_ipv6_prefix_length (72) + Aggregate smtpd_client_*_count and smtpd_client_*_rate statis- + tics by IPv6 network blocks with the specified network prefix. + TARPIT CONTROLS When a remote SMTP client makes errors, the Postfix SMTP server can insert delays before responding. This can help to slow down run-away diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index 38fabbb23..2d84788ba 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -9956,6 +9956,20 @@ parent_domain_matches_subdomains parameter value (Postfix 3.0 and later). .PP This feature is available in Postfix 2.2 and later. +.SH smtpd_client_ipv4_prefix_length (default: 32) +Aggregate smtpd_client_*_count and smtpd_client_*_rate statistics +by IPv4 network blocks with the specified network prefix. Aggregation +reduces the \fBanvil\fR(8) resources needed to maintain counters. By +default, aggregation is disabled for IPv4. +.PP +This feature is available in Postfix 3.8 and later. +.SH smtpd_client_ipv6_prefix_length (default: 72) +Aggregate smtpd_client_*_count and smtpd_client_*_rate statistics +by IPv6 network blocks with the specified network prefix. Aggregation +reduces the \fBanvil\fR(8) resources needed to maintain counters. By +default, aggregation is enabled for IPv6. +.PP +This feature is available in Postfix 3.8 and later. .SH smtpd_client_message_rate_limit (default: 0) The maximal number of message delivery requests that any client is allowed to make to this service per time unit, regardless of whether diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8 index 3bfe323c6..3062953e3 100644 --- a/postfix/man/man8/smtpd.8 +++ b/postfix/man/man8/smtpd.8 @@ -841,6 +841,14 @@ DATA and BDAT requests, when deadlines are enabled with smtpd_per_request_deadline. .IP "\fBheader_from_format (standard)\fR" The format of the Postfix\-generated \fBFrom:\fR header. +.PP +Available in Postfix version 3.8 and later: +.IP "\fBsmtpd_client_ipv4_prefix_length (32)\fR" +Aggregate smtpd_client_*_count and smtpd_client_*_rate statistics +by IPv4 network blocks with the specified network prefix. +.IP "\fBsmtpd_client_ipv6_prefix_length (72)\fR" +Aggregate smtpd_client_*_count and smtpd_client_*_rate statistics +by IPv6 network blocks with the specified network prefix. .SH "TARPIT CONTROLS" .na .nf diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index 39057abe5..7185c757c 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -543,6 +543,8 @@ while (<>) { s;\bsmtpd_client_port_logging\b;$&;g; s;\bsmtpd_client_recipient_rate_limit\b;$&;g; s;\bsmtpd_client_new_tls_session_rate_limit\b;$&;g; + s;\bsmtpd_client_ipv4_prefix_length\b;$&;g; + s;\bsmtpd_client_ipv6_prefix_length\b;$&;g; s;\bsmtpd_client_restrictions\b;$&;g; s;\bsmtpd_command_filter\b;$&;g; s;\bsmtpd_data_restrictions\b;$&;g; diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index 20a22cfaf..d661dda65 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -18598,3 +18598,22 @@ lookup as if SRV record lookup was not enabled.

to MX or IP address lookup as if SRV record lookup was not enabled.

This feature is available in Postfix 3.8 and later.

+ +%PARAM smtpd_client_ipv4_prefix_length 32 + +

Aggregate smtpd_client_*_count and smtpd_client_*_rate statistics +by IPv4 network blocks with the specified network prefix. Aggregation +reduces the anvil(8) resources needed to maintain counters. By +default, aggregation is disabled for IPv4.

+ +

This feature is available in Postfix 3.8 and later.

+ +%PARAM smtpd_client_ipv6_prefix_length 72 + +

Aggregate smtpd_client_*_count and smtpd_client_*_rate statistics +by IPv6 network blocks with the specified network prefix. Aggregation +reduces the anvil(8) resources needed to maintain counters. By +default, aggregation is enabled for IPv6. +

+ +

This feature is available in Postfix 3.8 and later.

diff --git a/postfix/proto/stop.double-cc b/postfix/proto/stop.double-cc index 333b55738..09a32d874 100644 --- a/postfix/proto/stop.double-cc +++ b/postfix/proto/stop.double-cc @@ -331,3 +331,4 @@ USE_FNV_32BIT USE_FNV_32BIT void void cleanup_milter_receive state count struct DICT open const char int int dict_xx_open Available in in Postfix version 2 3 3 7 +length length of 0 31 0 127 diff --git a/postfix/proto/stop.double-history b/postfix/proto/stop.double-history index ff18887cd..ff1eceaf4 100644 --- a/postfix/proto/stop.double-history +++ b/postfix/proto/stop.double-history @@ -35,3 +35,4 @@ proto proto SASL_README html proto SQLITE_README html tls tls_proxy h tlsproxy tlsproxy c postfix postfix c postlog postlog c postfix postfix c postlog postlog c + util net_mask_top hc smtpd smtpd c smtpd smtpd_peer c diff --git a/postfix/proto/stop.spell-cc b/postfix/proto/stop.spell-cc index b1fce0d3b..ec3df3008 100644 --- a/postfix/proto/stop.spell-cc +++ b/postfix/proto/stop.spell-cc @@ -1793,3 +1793,6 @@ Korbar ign noport nopref +ADDRP +iffalse +iftrue diff --git a/postfix/proto/stop.spell-history b/postfix/proto/stop.spell-history index e46264975..9cca6240f 100644 --- a/postfix/proto/stop.spell-history +++ b/postfix/proto/stop.spell-history @@ -47,3 +47,5 @@ modernisms Bordo css makemanidx +soho +soho diff --git a/postfix/src/cleanup/cleanup_milter.c b/postfix/src/cleanup/cleanup_milter.c index 11510b559..b71a6dd3a 100644 --- a/postfix/src/cleanup/cleanup_milter.c +++ b/postfix/src/cleanup/cleanup_milter.c @@ -244,7 +244,7 @@ static void cleanup_milter_hbc_log(void *context, const char *action, const CLEANUP_STATE *state = (CLEANUP_STATE *) context; const char *attr; - vstring_sprintf(state->temp1, "%s: milter-%s-%s: %s %.60s from %s[%s];", + vstring_sprintf(state->temp1, "%s: milter-%s-%s: %s %.200s from %s[%s];", state->queue_id, where, action, where, line, state->client_name, state->client_addr); if (state->sender) diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index ad227e240..d83c9eb50 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -3198,6 +3198,16 @@ extern int var_smtpd_cntls_limit; #define DEF_SMTPD_CAUTH_LIMIT 0 extern int var_smtpd_cauth_limit; +#define VAR_SMTPD_CIPV4_PREFIX "smtpd_client_ipv4_prefix_length" +#define DEF_SMTPD_CIPV4_PREFIX 32 +#define MAX_SMTPD_CIPV4_PREFIX 32 +extern int var_smtpd_cipv4_prefix; + +#define VAR_SMTPD_CIPV6_PREFIX "smtpd_client_ipv6_prefix_length" +#define DEF_SMTPD_CIPV6_PREFIX 72 +#define MAX_SMTPD_CIPV6_PREFIX 128 +extern int var_smtpd_cipv6_prefix; + #define VAR_SMTPD_HOGGERS "smtpd_client_event_limit_exceptions" #define DEF_SMTPD_HOGGERS "${smtpd_client_connection_limit_exceptions:$" VAR_MYNETWORKS "}" extern char *var_smtpd_hoggers; diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 52bc0bdf7..e07da4233 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 "20230308" +#define MAIL_RELEASE_DATE "20230312" #define MAIL_VERSION_NUMBER "3.8" #ifdef SNAPSHOT diff --git a/postfix/src/smtp/smtp_addr.c b/postfix/src/smtp/smtp_addr.c index a31cb38c1..5e23388d6 100644 --- a/postfix/src/smtp/smtp_addr.c +++ b/postfix/src/smtp/smtp_addr.c @@ -840,7 +840,6 @@ DNS_RR *smtp_service_addr(const char *name, const char *service, DNS_RR **mxrr, dsb_status(why, "5.4.4"); break; } - *found_myself |= (self != 0); /* * If permitted, fall back to non-SRV record lookups. @@ -854,5 +853,12 @@ DNS_RR *smtp_service_addr(const char *name, const char *service, DNS_RR **mxrr, else addr_list = smtp_host_addr(name, misc_flags, why); } + + /* + * Only if we're not falling back. + */ + else { + *found_myself |= (self != 0); + } return (addr_list); } diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index 2ceb0f35c..097d51842 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -1146,10 +1146,10 @@ static void smtp_hbc_logger(void *context, const char *action, const SMTP_STATE *state = (SMTP_STATE *) context; if (*text) { - msg_info("%s: %s: %s %.60s: %s", + msg_info("%s: %s: %s %.200s: %s", state->request->queue_id, action, where, content, text); } else { - msg_info("%s: %s: %s %.60s", + msg_info("%s: %s: %s %.200s", state->request->queue_id, action, where, content); } } diff --git a/postfix/src/smtpd/Makefile.in b/postfix/src/smtpd/Makefile.in index f48d38f02..422cbac5a 100644 --- a/postfix/src/smtpd/Makefile.in +++ b/postfix/src/smtpd/Makefile.in @@ -492,6 +492,7 @@ smtpd_peer.o: ../../include/myaddrinfo.h smtpd_peer.o: ../../include/mymalloc.h smtpd_peer.o: ../../include/name_code.h smtpd_peer.o: ../../include/name_mask.h +smtpd_peer.o: ../../include/net_mask_top.h smtpd_peer.o: ../../include/nvtable.h smtpd_peer.o: ../../include/sock_addr.h smtpd_peer.o: ../../include/split_at.h diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 0807387ff..7580629a0 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -795,6 +795,14 @@ /* smtpd_per_request_deadline. /* .IP "\fBheader_from_format (standard)\fR" /* The format of the Postfix-generated \fBFrom:\fR header. +/* .PP +/* Available in Postfix version 3.8 and later: +/* .IP "\fBsmtpd_client_ipv4_prefix_length (32)\fR" +/* Aggregate smtpd_client_*_count and smtpd_client_*_rate statistics +/* by IPv4 network blocks with the specified network prefix. +/* .IP "\fBsmtpd_client_ipv6_prefix_length (72)\fR" +/* Aggregate smtpd_client_*_count and smtpd_client_*_rate statistics +/* by IPv6 network blocks with the specified network prefix. /* TARPIT CONTROLS /* .ad /* .fi @@ -1411,6 +1419,8 @@ int var_smtpd_cmail_limit; int var_smtpd_crcpt_limit; int var_smtpd_cntls_limit; int var_smtpd_cauth_limit; +int var_smtpd_cipv4_prefix; +int var_smtpd_cipv6_prefix; char *var_smtpd_hoggers; char *var_local_rwr_clients; char *var_smtpd_ehlo_dis_words; @@ -2054,7 +2064,7 @@ static int smtpd_sasl_auth_cmd_wrapper(SMTPD_STATE *state, int argc, && anvil_clnt && var_smtpd_cauth_limit > 0 && !namadr_list_match(hogger_list, state->name, state->addr) - && anvil_clnt_auth(anvil_clnt, state->service, state->addr, + && anvil_clnt_auth(anvil_clnt, state->service, state->anvil_range, &rate) == ANVIL_STAT_OK && rate > var_smtpd_cauth_limit) { state->error_mask |= MAIL_ERROR_POLICY; @@ -2512,7 +2522,7 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) && anvil_clnt && var_smtpd_cmail_limit > 0 && !namadr_list_match(hogger_list, state->name, state->addr) - && anvil_clnt_mail(anvil_clnt, state->service, state->addr, + && anvil_clnt_mail(anvil_clnt, state->service, state->anvil_range, &rate) == ANVIL_STAT_OK && rate > var_smtpd_cmail_limit) { state->error_mask |= MAIL_ERROR_POLICY; @@ -2906,7 +2916,7 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) && anvil_clnt && var_smtpd_crcpt_limit > 0 && !namadr_list_match(hogger_list, state->name, state->addr) - && anvil_clnt_rcpt(anvil_clnt, state->service, state->addr, + && anvil_clnt_rcpt(anvil_clnt, state->service, state->anvil_range, &rate) == ANVIL_STAT_OK && rate > var_smtpd_crcpt_limit) { state->error_mask |= MAIL_ERROR_POLICY; @@ -4230,7 +4240,7 @@ static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) && anvil_clnt && var_smtpd_crcpt_limit > 0 && !namadr_list_match(hogger_list, state->name, state->addr) - && anvil_clnt_rcpt(anvil_clnt, state->service, state->addr, + && anvil_clnt_rcpt(anvil_clnt, state->service, state->anvil_range, &rate) == ANVIL_STAT_OK && rate > var_smtpd_crcpt_limit) { state->error_mask |= MAIL_ERROR_POLICY; @@ -5146,7 +5156,7 @@ static void smtpd_start_tls(SMTPD_STATE *state) && !xclient_allowed && anvil_clnt && !namadr_list_match(hogger_list, state->name, state->addr) - && anvil_clnt_newtls(anvil_clnt, state->service, state->addr, + && anvil_clnt_newtls(anvil_clnt, state->service, state->anvil_range, &rate) == ANVIL_STAT_OK && rate > var_smtpd_cntls_limit) { state->error_mask |= MAIL_ERROR_POLICY; @@ -5292,8 +5302,8 @@ static int starttls_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) && !xclient_allowed && anvil_clnt && !namadr_list_match(hogger_list, state->name, state->addr) - && anvil_clnt_newtls_stat(anvil_clnt, state->service, state->addr, - &rate) == ANVIL_STAT_OK + && anvil_clnt_newtls_stat(anvil_clnt, state->service, + state->anvil_range, &rate) == ANVIL_STAT_OK && rate > var_smtpd_cntls_limit) { state->error_mask |= MAIL_ERROR_POLICY; msg_warn("Refusing STARTTLS request from %s for service %s", @@ -5560,7 +5570,7 @@ static void smtpd_proto(SMTPD_STATE *state) && anvil_clnt && !namadr_list_match(hogger_list, state->name, state->addr) && anvil_clnt_newtls_stat(anvil_clnt, state->service, - state->addr, &tls_rate) == ANVIL_STAT_OK + state->anvil_range, &tls_rate) == ANVIL_STAT_OK && tls_rate > var_smtpd_cntls_limit) { state->error_mask |= MAIL_ERROR_POLICY; msg_warn("Refusing TLS service request from %s for service %s", @@ -5586,8 +5596,9 @@ static void smtpd_proto(SMTPD_STATE *state) && !xclient_allowed && anvil_clnt && !namadr_list_match(hogger_list, state->name, state->addr) - && anvil_clnt_connect(anvil_clnt, state->service, state->addr, - &state->conn_count, &state->conn_rate) + && anvil_clnt_connect(anvil_clnt, state->service, + state->anvil_range, &state->conn_count, + &state->conn_rate) == ANVIL_STAT_OK) { if (var_smtpd_cconn_limit > 0 && state->conn_count > var_smtpd_cconn_limit) { @@ -5845,7 +5856,7 @@ static void smtpd_proto(SMTPD_STATE *state) && !xclient_allowed && anvil_clnt && !namadr_list_match(hogger_list, state->name, state->addr)) - anvil_clnt_disconnect(anvil_clnt, state->service, state->addr); + anvil_clnt_disconnect(anvil_clnt, state->service, state->anvil_range); /* * Log abnormal session termination, in case postmaster notification has @@ -6427,6 +6438,8 @@ int main(int argc, char **argv) VAR_SMTPD_CRCPT_LIMIT, DEF_SMTPD_CRCPT_LIMIT, &var_smtpd_crcpt_limit, 0, 0, VAR_SMTPD_CNTLS_LIMIT, DEF_SMTPD_CNTLS_LIMIT, &var_smtpd_cntls_limit, 0, 0, VAR_SMTPD_CAUTH_LIMIT, DEF_SMTPD_CAUTH_LIMIT, &var_smtpd_cauth_limit, 0, 0, + VAR_SMTPD_CIPV4_PREFIX, DEF_SMTPD_CIPV4_PREFIX, &var_smtpd_cipv4_prefix, 0, MAX_SMTPD_CIPV4_PREFIX, + VAR_SMTPD_CIPV6_PREFIX, DEF_SMTPD_CIPV6_PREFIX, &var_smtpd_cipv6_prefix, 0, MAX_SMTPD_CIPV6_PREFIX, #ifdef USE_TLS VAR_SMTPD_TLS_CCERT_VD, DEF_SMTPD_TLS_CCERT_VD, &var_smtpd_tls_ccert_vd, 0, 0, #endif diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h index 0bb5bbbce..56ebc078d 100644 --- a/postfix/src/smtpd/smtpd.h +++ b/postfix/src/smtpd/smtpd.h @@ -77,6 +77,7 @@ typedef struct { char *addr; /* client host address string */ char *port; /* port for logging */ char *namaddr; /* name[address]:port */ + char *anvil_range; /* client address or network/length */ char *rfc_addr; /* address for RFC 2821 */ int addr_family; /* address family */ char *dest_addr; /* Dovecot AUTH, Milter {daemon_addr} */ diff --git a/postfix/src/smtpd/smtpd_peer.c b/postfix/src/smtpd/smtpd_peer.c index 3a5c1d46b..f21203544 100644 --- a/postfix/src/smtpd/smtpd_peer.c +++ b/postfix/src/smtpd/smtpd_peer.c @@ -139,6 +139,7 @@ #include #include #include +#include /* Global library. */ @@ -250,10 +251,12 @@ static int smtpd_peer_sockaddr_to_hostaddr(SMTPD_STATE *state) state->addr = mystrdup(colonp + 1); state->rfc_addr = mystrdup(colonp + 1); state->addr_family = AF_INET; - aierr = hostaddr_to_sockaddr(state->addr, (char *) 0, 0, &res0); + aierr = + hostaddr_to_sockaddr(state->addr, state->port, 0, &res0); if (aierr) - msg_fatal("%s: cannot convert %s from string to binary: %s", - myname, state->addr, MAI_STRERROR(aierr)); + msg_fatal("%s: cannot convert [%s]:%s to binary: %s", + myname, state->addr, state->port, + MAI_STRERROR(aierr)); sa_length = res0->ai_addrlen; if (sa_length > sizeof(state->sockaddr)) sa_length = sizeof(state->sockaddr); @@ -264,10 +267,10 @@ static int smtpd_peer_sockaddr_to_hostaddr(SMTPD_STATE *state) /* * Following RFC 2821 section 4.1.3, an IPv6 address literal gets * a prefix of 'IPv6:'. We do this consistently for all IPv6 - * addresses that appear in headers or envelopes. The fact - * that valid_mailhost_addr() enforces the form helps of course. - * We use the form without IPV6: prefix when doing access - * control, or when accessing the connection cache. + * addresses that appear in headers or envelopes. The fact that + * valid_mailhost_addr() enforces the form helps of course. We + * use the form without IPV6: prefix when doing access control, + * or when accessing the connection cache. */ else { state->addr = mystrdup(client_addr.buf); @@ -581,6 +584,7 @@ static void smtpd_peer_from_proxy(SMTPD_STATE *state) void smtpd_peer_init(SMTPD_STATE *state) { + int af; /* * Initialize. @@ -599,6 +603,7 @@ void smtpd_peer_init(SMTPD_STATE *state) state->namaddr = 0; state->rfc_addr = 0; state->port = 0; + state->anvil_range = 0; state->dest_addr = 0; state->dest_port = 0; @@ -633,6 +638,16 @@ void smtpd_peer_init(SMTPD_STATE *state) */ state->namaddr = SMTPD_BUILD_NAMADDRPORT(state->name, state->addr, state->port); + + /* + * Generate 'address' or 'net/mask' index for anvil event aggregation. + */ + af = SOCK_ADDR_FAMILY(&(state->sockaddr)); + state->anvil_range = net_mask_top(af, + SOCK_ADDR_ADDRP(&(state->sockaddr)), + af == AF_INET ? + var_smtpd_cipv4_prefix : + var_smtpd_cipv6_prefix); } /* smtpd_peer_reset - destroy peer information */ @@ -655,4 +670,6 @@ void smtpd_peer_reset(SMTPD_STATE *state) myfree(state->dest_addr); if (state->dest_port) myfree(state->dest_port); + if (state->anvil_range) + myfree(state->anvil_range); } diff --git a/postfix/src/tls/tls_fprint.c b/postfix/src/tls/tls_fprint.c index 802157045..39b5a5219 100644 --- a/postfix/src/tls/tls_fprint.c +++ b/postfix/src/tls/tls_fprint.c @@ -130,11 +130,11 @@ static const char hexcodes[] = "0123456789ABCDEF"; -#define checkok(stillok) (ok = ok && (stillok)) -#define digest_object(p) digest_data((unsigned char *)(p), sizeof(*(p))) -#define digest_data(p, l) checkok(digest_bytes(mdctx, (p), (l))) -#define digest_string(s) checkok(digest_chars(mdctx, (s))) -#define digest_dane(tlsa) checkok(tls_digest_tlsa(mdctx, tlsa)) +#define CHECK_OK_AND(stillok) (ok = ok && (stillok)) +#define CHECK_OK_AND_DIGEST_OBJECT(m, p) \ + CHECK_OK_AND_DIGEST_DATA((m), (unsigned char *)(p), sizeof(*(p))) +#define CHECK_OK_AND_DIGEST_DATA(m, p, l) CHECK_OK_AND(digest_bytes((m), (p), (l))) +#define CHECK_OK_AND_DIGEST_CHARS(m, s) CHECK_OK_AND(digest_chars((m), (s))) /* digest_bytes - hash octet string of given length */ @@ -186,13 +186,13 @@ static int tls_digest_tlsa(EVP_MD_CTX *mdctx, TLS_TLSA *tlsa) arr[i++] = (void *) p; qsort(arr, n, sizeof(arr[0]), tlsa_cmp); - digest_object(&n); + CHECK_OK_AND_DIGEST_OBJECT(mdctx, &n); for (i = 0; i < n; ++i) { - digest_object(&arr[i]->usage); - digest_object(&arr[i]->selector); - digest_object(&arr[i]->mtype); - digest_object(&arr[i]->length); - digest_data(arr[i]->data, arr[i]->length); + CHECK_OK_AND_DIGEST_OBJECT(mdctx, &arr[i]->usage); + CHECK_OK_AND_DIGEST_OBJECT(mdctx, &arr[i]->selector); + CHECK_OK_AND_DIGEST_OBJECT(mdctx, &arr[i]->mtype); + CHECK_OK_AND_DIGEST_OBJECT(mdctx, &arr[i]->length); + CHECK_OK_AND_DIGEST_DATA(mdctx, arr[i]->data, arr[i]->length); } myfree((void *) arr); return (ok); @@ -218,15 +218,15 @@ const EVP_MD *tls_digest_byname(const char *mdalg, EVP_MD_CTX **mdctxPtr) * Note that EVP_MD_CTX_{create,destroy} were renamed to, respectively, * EVP_MD_CTX_{new,free} in OpenSSL 1.1.0. */ - checkok(md = EVP_get_digestbyname(mdalg)); + CHECK_OK_AND(md = EVP_get_digestbyname(mdalg)); /* * Sanity check: Newer shared libraries could (hypothetical ABI break) * allow larger digests, we avoid such poison algorithms. */ - checkok(EVP_MD_size(md) <= EVP_MAX_MD_SIZE); - checkok(mdctx = EVP_MD_CTX_new()); - checkok(EVP_DigestInit_ex(mdctx, md, NULL)); + CHECK_OK_AND(EVP_MD_size(md) <= EVP_MAX_MD_SIZE); + CHECK_OK_AND(mdctx = EVP_MD_CTX_new()); + CHECK_OK_AND(EVP_DigestInit_ex(mdctx, md, NULL)); if (ok && mdctxPtr != 0) @@ -269,10 +269,10 @@ char *tls_serverid_digest(TLS_SESS_STATE *TLScontext, /* Salt the session lookup key with the OpenSSL runtime version. */ sslversion = OpenSSL_version_num(); - digest_string(props->helo ? props->helo : ""); - digest_object(&sslversion); - digest_string(props->protocols); - digest_string(ciphers); + CHECK_OK_AND_DIGEST_CHARS(mdctx, props->helo ? props->helo : ""); + CHECK_OK_AND_DIGEST_OBJECT(mdctx, &sslversion); + CHECK_OK_AND_DIGEST_CHARS(mdctx, props->protocols); + CHECK_OK_AND_DIGEST_CHARS(mdctx, ciphers); /* * Ensure separation of caches for sessions where DANE trust @@ -280,7 +280,7 @@ char *tls_serverid_digest(TLS_SESS_STATE *TLScontext, * should always see a certificate validation failure, both on initial * handshake and on resumption. */ - digest_object(&TLScontext->must_fail); + CHECK_OK_AND_DIGEST_OBJECT(mdctx, &TLScontext->must_fail); /* * DNS-based or synthetic DANE trust settings are potentially used at all @@ -288,11 +288,11 @@ char *tls_serverid_digest(TLS_SESS_STATE *TLScontext, */ if (TLScontext->level > TLS_LEV_ENCRYPT && props->dane && props->dane->tlsa) { - digest_dane(props->dane->tlsa); + CHECK_OK_AND(tls_digest_tlsa(mdctx, props->dane->tlsa)); } else { int none = 0; /* Record a TLSA RR count of zero */ - digest_object(&none); + CHECK_OK_AND_DIGEST_OBJECT(mdctx, &none); } /* @@ -300,11 +300,11 @@ char *tls_serverid_digest(TLS_SESS_STATE *TLScontext, * selection. */ if (TLScontext->level > TLS_LEV_ENCRYPT && TLScontext->peer_sni) - digest_string(TLScontext->peer_sni); + CHECK_OK_AND_DIGEST_CHARS(mdctx, TLScontext->peer_sni); else - digest_string(""); + CHECK_OK_AND_DIGEST_CHARS(mdctx, ""); - checkok(EVP_DigestFinal_ex(mdctx, md_buf, &md_len)); + CHECK_OK_AND(EVP_DigestFinal_ex(mdctx, md_buf, &md_len)); EVP_MD_CTX_destroy(mdctx); if (!ok) msg_fatal("error computing %s message digest", mdalg); @@ -368,8 +368,8 @@ static char *tls_data_fprint(const unsigned char *buf, int len, const char *mdal if (tls_digest_byname(mdalg, &mdctx) == 0) msg_panic("digest algorithm \"%s\" not found", mdalg); - digest_data(buf, len); - checkok(EVP_DigestFinal_ex(mdctx, md_buf, &md_len)); + CHECK_OK_AND_DIGEST_DATA(mdctx, buf, len); + CHECK_OK_AND(EVP_DigestFinal_ex(mdctx, md_buf, &md_len)); EVP_MD_CTX_destroy(mdctx); if (!ok) msg_fatal("error computing %s message digest", mdalg); diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index cdc671cff..4592f6a50 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -44,7 +44,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \ msg_logger.c logwriter.c unix_dgram_connect.c unix_dgram_listen.c \ byte_mask.c known_tcp_ports.c argv_split_at.c dict_stream.c \ sane_strtol.c hash_fnv.c ldseed.c mkmap_cdb.c mkmap_db.c mkmap_dbm.c \ - mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c + mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c net_mask_top.c OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \ attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \ @@ -90,7 +90,7 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ msg_logger.o logwriter.o unix_dgram_connect.o unix_dgram_listen.o \ byte_mask.o known_tcp_ports.o argv_split_at.o dict_stream.o \ sane_strtol.o hash_fnv.o ldseed.o mkmap_db.o mkmap_dbm.o \ - mkmap_fail.o mkmap_open.o + mkmap_fail.o mkmap_open.o net_mask_top.o # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf. # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ), # otherwise it sets the PLUGIN_* macros. @@ -121,7 +121,8 @@ HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \ slmdb.h compat_va_copy.h dict_pipe.h dict_random.h \ valid_utf8_hostname.h midna_domain.h dict_union.h dict_inline.h \ check_arg.h argv_attr.h msg_logger.h logwriter.h byte_mask.h \ - known_tcp_ports.h sane_strtol.h hash_fnv.h ldseed.h mkmap.h + known_tcp_ports.h sane_strtol.h hash_fnv.h ldseed.h mkmap.h \ + net_mask_top.h TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \ stream_test.c dup2_pass_on_exec.c DEFS = -I. -D$(SYSTYPE) @@ -2415,6 +2416,15 @@ nbbio.o: mymalloc.h nbbio.o: nbbio.c nbbio.o: nbbio.h nbbio.o: sys_defs.h +net_mask_top.o: check_arg.h +net_mask_top.o: mask_addr.h +net_mask_top.o: msg.h +net_mask_top.o: myaddrinfo.h +net_mask_top.o: net_mask_top.c +net_mask_top.o: net_mask_top.h +net_mask_top.o: sys_defs.h +net_mask_top.o: vbuf.h +net_mask_top.o: vstring.h netstring.o: check_arg.h netstring.o: compat_va_copy.h netstring.o: msg.h diff --git a/postfix/src/util/net_mask_top.c b/postfix/src/util/net_mask_top.c new file mode 100644 index 000000000..5483fcaa9 --- /dev/null +++ b/postfix/src/util/net_mask_top.c @@ -0,0 +1,120 @@ +/*++ +/* NAME +/* net_mask_top 3 +/* SUMMARY +/* convert net/mask to printable string +/* SYNOPSIS +/* #include +/* +/* char *net_mask_top( +/* int family, +/* const void *src, +/* int prefix_len) +/* DESCRIPTION +/* net_mask_top() prints the network portion of the specified +/* IPv4 or IPv6 address, null bits for the host portion, and +/* the prefix length if it is shorter than the address. +/* The result should be passed to myfree(). The code can +/* handle addresses of any length, and bytes of any width. +/* +/* Arguments: +/* .IP af +/* The address family, as with inet_ntop(). +/* .IP src +/* Pointer to storage for an IPv4 or IPv6 address, as with +/* inet_ntop(). +/* .IP prefix_len +/* The number of most-significant bits in \fBsrc\fR that should +/* not be cleared. +/* DIAGNOSTICS +/* Panic: unexpected protocol family, bad prefix length. Fatal +/* errors: address conversion error. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/*--*/ + + /* + * System library. + */ +#include +#include +#include +#include +#include + + /* + * Utility library. + */ +#include +#include +#include +#include +#include + +/* + * XXX Factor out if we also need this in other places. + */ +struct addr_size { + int af; /* address family (binary) */ + char ipproto_str[5]; /* IP protocol version (string) */ + int addr_bitcount; /* bits per address */ + int addr_bytecount; /* bytes per address */ + int addr_strlen; /* string representation length */ + int slashdigs_strlen; /* length of /0-31, /0-127 */ +}; +static struct addr_size addr_sizes[] = { + AF_INET, "IPv4", MAI_V4ADDR_BITS, MAI_V4ADDR_BYTES, INET_ADDRSTRLEN, 3, +#ifdef HAS_IPV6 + AF_INET6, "IPv6", MAI_V6ADDR_BITS, MAI_V6ADDR_BYTES, INET6_ADDRSTRLEN, 4, +#endif +}; + +/* get_addr_size - get bit-banging numbers for address family */ + +static struct addr_size *get_addr_size(int af) +{ + struct addr_size *ap; + + for (ap = addr_sizes; /* see below */ ; ap++) { + if (ap >= addr_sizes + sizeof(addr_sizes) / sizeof(struct addr_size)) + return (0); + if (ap->af == af) + return (ap); + } +} + +/* net_mask_top - printable net/mask pattern */ + +char *net_mask_top(int af, const void *src, int prefix_len) +{ + const char myname[] = "net_mask_top"; + union { + struct in_addr in_addr; + struct in6_addr in6_addr; + } u; + VSTRING *buf; + struct addr_size *ap; + + if ((ap = get_addr_size(af)) == 0) + msg_panic("%s: unexpected address family: %d", myname, af); + if (prefix_len > ap->addr_bitcount || prefix_len < 0) + msg_fatal("%s: bad %s address prefix length: %d", + myname, ap->ipproto_str, prefix_len); + memcpy((void *) &u, src, ap->addr_bytecount); + if (prefix_len < ap->addr_bitcount) { + mask_addr((unsigned char *) &u, ap->addr_bytecount, prefix_len); + buf = vstring_alloc(ap->addr_strlen + ap->slashdigs_strlen); + } else { + buf = vstring_alloc(ap->addr_strlen); + } + if (inet_ntop(af, &u, vstring_str(buf), vstring_avail(buf)) == 0) + msg_fatal("%s: inet_ntop: %m", myname); + vstring_set_payload_size(buf, strlen(vstring_str(buf))); + if (prefix_len < ap->addr_bitcount) + vstring_sprintf_append(buf, "/%d", prefix_len); + return (vstring_export(buf)); +} diff --git a/postfix/src/util/net_mask_top.h b/postfix/src/util/net_mask_top.h new file mode 100644 index 000000000..8f043f5b7 --- /dev/null +++ b/postfix/src/util/net_mask_top.h @@ -0,0 +1,27 @@ +#ifndef _NET_MASK_TOP_H_INCLUDED_ +#define _NET_MASK_TOP_H_INCLUDED_ + +/*++ +/* NAME +/* net_mask_top 3h +/* SUMMARY +/* convert net/mask to printable string +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern char *net_mask_top(int, const void *, int); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/*--*/ + +#endif diff --git a/postfix/src/util/sock_addr.c b/postfix/src/util/sock_addr.c index dc5c4b146..38ccc9142 100644 --- a/postfix/src/util/sock_addr.c +++ b/postfix/src/util/sock_addr.c @@ -28,6 +28,7 @@ /* struct sockaddr *SOCK_ADDR_PTR(ptr) /* unsigned char SOCK_ADDR_FAMILY(ptr) /* unsigned char SOCK_ADDR_LEN(ptr) +/* void *SOCK_ADDR_ADDRP(ptr) /* unsigned short SOCK_ADDR_PORT(ptr) /* unsigned short *SOCK_ADDR_PORTP(ptr) /* @@ -68,7 +69,8 @@ /* address family and length of the real structure that hides /* inside a generic sockaddr structure. On systems where struct /* sockaddr has no sa_len member, SOCK_ADDR_LEN() cannot be -/* used as lvalue. SOCK_ADDR_PORT() returns the IPv4 or IPv6 +/* used as lvalue. SOCKADDR_ADDRP() returns a pointer to the +/* IPv4 or IPv6 address. SOCK_ADDR_PORT() returns the IPv4 or IPv6 /* port number, in network byte order; it must not be used as /* lvalue. SOCK_ADDR_PORTP() returns a pointer to the same. /* diff --git a/postfix/src/util/sock_addr.h b/postfix/src/util/sock_addr.h index 1f5407a4b..92b6835cc 100644 --- a/postfix/src/util/sock_addr.h +++ b/postfix/src/util/sock_addr.h @@ -45,6 +45,9 @@ extern int sock_addr_in_loopback(const struct sockaddr *); sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) #endif +#define SOCK_ADDR_ADDRP(sa) \ + (SOCK_ADDR_FAMILY(sa) == AF_INET ? \ + (void *) &SOCK_ADDR_IN_ADDR(sa) : (void *) &SOCK_ADDR_IN6_ADDR(sa)) #define SOCK_ADDR_PORT(sa) \ (SOCK_ADDR_PTR(sa)->sa_family == AF_INET6 ? \ SOCK_ADDR_IN6_PORT(sa) : SOCK_ADDR_IN_PORT(sa)) @@ -78,8 +81,9 @@ extern int sock_addr_in_loopback(const struct sockaddr *); #define SOCK_ADDR_LEN(sa) sizeof(struct sockaddr_in) #endif -#define SOCK_ADDR_PORT(sa) SOCK_ADDR_IN_PORT(sa)) -#define SOCK_ADDR_PORTP(sa) &SOCK_ADDR_IN_PORT(sa)) +#define SOCK_ADDR_ADDRP(sa) (&SOCK_ADDR_IN_ADDR(sa)) +#define SOCK_ADDR_PORT(sa) SOCK_ADDR_IN_PORT(sa) +#define SOCK_ADDR_PORTP(sa) (&SOCK_ADDR_IN_PORT(sa)) #define SOCK_ADDR_EQ_ADDR(sa, sb) \ (SOCK_ADDR_FAMILY(sa) == AF_INET && SOCK_ADDR_FAMILY(sb) == AF_INET \