diff options
Diffstat (limited to '')
-rw-r--r-- | src/smtp/lmtp_params.c | 2 | ||||
-rw-r--r-- | src/smtp/smtp.c | 136 | ||||
-rw-r--r-- | src/smtp/smtp.h | 2 | ||||
-rw-r--r-- | src/smtp/smtp_addr.c | 2 | ||||
-rw-r--r-- | src/smtp/smtp_params.c | 2 | ||||
-rw-r--r-- | src/smtp/smtp_proto.c | 2 | ||||
-rw-r--r-- | src/smtp/smtp_sasl_glue.c | 4 | ||||
-rw-r--r-- | src/smtp/smtp_tls_policy.c | 56 | ||||
-rw-r--r-- | src/smtpd/Makefile.in | 11 | ||||
-rw-r--r-- | src/smtpd/smtpd.c | 117 | ||||
-rw-r--r-- | src/smtpd/smtpd.h | 4 | ||||
-rw-r--r-- | src/smtpd/smtpd_check.c | 177 | ||||
-rw-r--r-- | src/smtpd/smtpd_check_backup.ref | 1 | ||||
-rw-r--r-- | src/smtpd/smtpd_deprecated.in | 20 | ||||
-rw-r--r-- | src/smtpd/smtpd_deprecated.ref | 35 | ||||
-rw-r--r-- | src/smtpd/smtpd_exp.ref | 10 | ||||
-rw-r--r-- | src/smtpd/smtpd_sasl_glue.c | 4 | ||||
-rw-r--r-- | src/smtpd/smtpd_state.c | 1 | ||||
-rw-r--r-- | src/smtpstone/smtp-source.c | 94 |
19 files changed, 476 insertions, 204 deletions
diff --git a/src/smtp/lmtp_params.c b/src/smtp/lmtp_params.c index bca7cd4..385c81f 100644 --- a/src/smtp/lmtp_params.c +++ b/src/smtp/lmtp_params.c @@ -4,6 +4,7 @@ VAR_BESTMX_TRANSP, DEF_BESTMX_TRANSP, &var_bestmx_transp, 0, 0, VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0, VAR_LMTP_SASL_PASSWD, DEF_LMTP_SASL_PASSWD, &var_smtp_sasl_passwd, 0, 0, + VAR_LMTP_SASL_PASSWD_RES_DELIM, DEF_LMTP_SASL_PASSWD_RES_DELIM, &var_smtp_sasl_passwd_res_delim, 1, 1, VAR_LMTP_SASL_OPTS, DEF_LMTP_SASL_OPTS, &var_smtp_sasl_opts, 0, 0, VAR_LMTP_SASL_PATH, DEF_LMTP_SASL_PATH, &var_smtp_sasl_path, 0, 0, #ifdef USE_TLS @@ -120,6 +121,7 @@ VAR_LMTP_TLS_NOTEOFFER, DEF_LMTP_TLS_NOTEOFFER, &var_smtp_tls_note_starttls_offer, VAR_LMTP_TLS_BLK_EARLY_MAIL_REPLY, DEF_LMTP_TLS_BLK_EARLY_MAIL_REPLY, &var_smtp_tls_blk_early_mail_reply, VAR_LMTP_TLS_FORCE_TLSA, DEF_LMTP_TLS_FORCE_TLSA, &var_smtp_tls_force_tlsa, + VAR_LMTP_TLS_ENABLE_RPK, DEF_LMTP_TLS_ENABLE_RPK, &var_smtp_tls_enable_rpk, #endif VAR_LMTP_TLS_WRAPPER, DEF_LMTP_TLS_WRAPPER, &var_smtp_tls_wrappermode, VAR_LMTP_SENDER_AUTH, DEF_LMTP_SENDER_AUTH, &var_smtp_sender_auth, diff --git a/src/smtp/smtp.c b/src/smtp/smtp.c index f7f2fc1..51b2e6d 100644 --- a/src/smtp/smtp.c +++ b/src/smtp/smtp.c @@ -1,17 +1,21 @@ /*++ /* NAME -/* smtp 8 +/* smtp, lmtp 8 /* SUMMARY /* Postfix SMTP+LMTP client /* SYNOPSIS /* \fBsmtp\fR [generic Postfix daemon options] [flags=DORX] +/* +/* \fBlmtp\fR [generic Postfix daemon options] [flags=DORX] /* DESCRIPTION /* The Postfix SMTP+LMTP client implements the SMTP and LMTP mail /* delivery protocols. It processes message delivery requests from /* the queue manager. Each request specifies a queue file, a sender /* address, a domain or host to deliver to, and recipient information. /* This program expects to be run from the \fBmaster\fR(8) process -/* manager. +/* manager. The process name, \fBsmtp\fR or \fBlmtp\fR, controls +/* the protocol, and the names of the configuration parameters +/* that will be used. /* /* The SMTP+LMTP client updates the queue file and marks recipients /* as finished, or it informs the queue manager that delivery should @@ -19,13 +23,9 @@ /* to the \fBbounce\fR(8), \fBdefer\fR(8) or \fBtrace\fR(8) daemon as /* appropriate. /* -/* The SMTP+LMTP client looks up a list of mail exchanger addresses for -/* the destination host, sorts the list by preference, and connects -/* to each listed address until it finds a server that responds. -/* -/* When a server is not reachable, or when mail delivery fails due -/* to a recoverable error condition, the SMTP+LMTP client will try to -/* deliver the mail to an alternate host. +/* The server lookup strategy is different for SMTP and LMTP, +/* as described in the sections "SMTP SERVER LOOKUP" and "LMTP +/* SERVER LOOKUP". /* /* After a successful mail transaction, a connection may be saved /* to the \fBscache\fR(8) connection cache server, so that it @@ -35,44 +35,58 @@ /* destinations that have a high volume of mail in the active /* queue. Connection caching can be enabled permanently for /* specific destinations. -/* SMTP DESTINATION SYNTAX +/* SMTP SERVER LOOKUP /* .ad /* .fi -/* The Postfix SMTP+LMTP client supports multiple destinations +/* The Postfix SMTP client supports multiple destinations /* separated by comma or whitespace (Postfix 3.5 and later). +/* Each destination is tried in the specified order. +/* /* SMTP destinations have the following form: /* .IP \fIdomainname\fR -/* .IP \fIdomainname\fR:\fIport\fR +/* .IP \fIdomainname\fR:\fIservice\fR /* Look up the mail exchangers for the specified domain, and -/* connect to the specified port (default: \fBsmtp\fR). +/* connect to the specified service (default: \fBsmtp\fR). +/* Optionally, mail exchangers may be looked up with SRV queries +/* instead of MX; this requires that \fIservice\fR is given +/* in symbolic form. /* .IP [\fIhostname\fR] -/* .IP [\fIhostname\fR]:\fIport\fR -/* Look up the address(es) of the specified host, and connect to -/* the specified port (default: \fBsmtp\fR). +/* .IP [\fIhostname\fR]:\fIservice\fR +/* Look up the address(es) for the specified host, and connect to +/* the specified service (default: \fBsmtp\fR). /* .IP [\fIaddress\fR] -/* .IP [\fIaddress\fR]:\fIport\fR +/* .IP [\fIaddress\fR]:\fIservice\fR /* Connect to the host at the specified address, and connect -/* to the specified port (default: \fBsmtp\fR). An IPv6 address +/* to the specified service (default: \fBsmtp\fR). An IPv6 address /* must be formatted as [\fBipv6\fR:\fIaddress\fR]. -/* LMTP DESTINATION SYNTAX +/* LMTP SERVER LOOKUP /* .ad /* .fi -/* The Postfix SMTP+LMTP client supports multiple destinations +/* The Postfix LMTP client supports multiple destinations /* separated by comma or whitespace (Postfix 3.5 and later). +/* Each destination is tried in the specified order. +/* /* LMTP destinations have the following form: /* .IP \fBunix\fR:\fIpathname\fR /* Connect to the local UNIX-domain server that is bound to the specified /* \fIpathname\fR. If the process runs chrooted, an absolute pathname /* is interpreted relative to the Postfix queue directory. +/* .IP \fBinet\fR:\fIdomainname\fR +/* .IP \fBinet\fR:\fIdomainname\fR:\fIservice\fR +/* Look up the LMTP servers for the specified domain and service +/* (default: \fBlmtp\fR). +/* This form is supported when SRV lookups are enabled, and +/* requires that \fIservice\fR is in symbolic form. /* .IP \fBinet\fR:\fIhostname\fR -/* .IP \fBinet\fR:\fIhostname\fR:\fIport\fR +/* .IP \fBinet\fR:\fIhostname\fR:\fIservice\fR +/* Look up the address(es) for the specified host, and connect to +/* the specified service (default: \fBlmtp\fR). When SRV lookups +/* are enabled, use the form \fB[\fIhostname\fB]\fR to force +/* address lookups. /* .IP \fBinet\fR:[\fIaddress\fR] -/* .IP \fBinet\fR:[\fIaddress\fR]:\fIport\fR -/* Connect to the specified TCP port on the specified local or -/* remote host. If no port is specified, connect to the port defined as -/* \fBlmtp\fR in \fBservices\fR(4). -/* If no such service is found, the \fBlmtp_tcp_port\fR configuration -/* parameter (default value of 24) will be used. +/* .IP \fBinet\fR:[\fIaddress\fR]:\fIservice\fR +/* Connect to the specified local or remote host and service +/* (default: \fBlmtp\fR). /* An IPv6 address must be formatted as [\fBipv6\fR:\fIaddress\fR]. /* SINGLE-RECIPIENT DELIVERY /* .ad @@ -130,6 +144,8 @@ /* This feature is available as of Postfix 3.5. /* .RE /* SECURITY +/* .ad +/* .fi /* The SMTP+LMTP client is moderately security-sensitive. It /* talks to SMTP or LMTP servers and to DNS servers on the /* network. The SMTP+LMTP client can be run chrooted at fixed @@ -175,11 +191,10 @@ /* CONFIGURATION PARAMETERS /* .ad /* .fi -/* Before Postfix version 2.3, the LMTP client is a separate -/* program that implements only a subset of the functionality -/* available with SMTP: there is no support for TLS, and -/* connections are cached in-process, making it ineffective -/* when the client is used for multiple domains. +/* Postfix versions 2.3 and later implement the SMTP and LMTP +/* client with the same program, and choose the protocol and +/* configuration parameters based on the process name, \fBsmtp\fR +/* or \fBlmtp\fR. /* /* Most smtp_\fIxxx\fR configuration parameters have an /* lmtp_\fIxxx\fR "mirror" parameter for the equivalent LMTP @@ -432,6 +447,11 @@ /* .IP "\fBsmtp_send_dummy_mail_auth (no)\fR" /* Whether or not to append the "AUTH=<>" option to the MAIL /* FROM command in SASL-authenticated SMTP sessions. +/* .PP +/* Available in Postfix version 3.9 and later: +/* .IP "\fBsmtp_sasl_password_result_delimiter (:)\fR" +/* The delimiter between username and password in sasl_passwd_maps lookup +/* results. /* STARTTLS SUPPORT CONTROLS /* .ad /* .fi @@ -532,7 +552,7 @@ /* certificate fingerprints. /* .PP /* Available in Postfix version 2.6 and later: -/* .IP "\fBsmtp_tls_protocols (see postconf -d output)\fR" +/* .IP "\fBsmtp_tls_protocols (see 'postconf -d' output)\fR" /* TLS protocols that the Postfix SMTP client will use with /* opportunistic TLS encryption. /* .IP "\fBsmtp_tls_ciphers (medium)\fR" @@ -613,6 +633,11 @@ /* .IP "\fBtls_config_name (empty)\fR" /* The application name passed by Postfix to OpenSSL library /* initialization functions. +/* .PP +/* Available in Postfix version 3.9 and later: +/* .IP "\fBsmtp_tls_enable_rpk (no)\fR" +/* Request that remote SMTP servers send an RFC7250 raw public key +/* instead of an X.509 certificate. /* OBSOLETE STARTTLS CONTROLS /* .ad /* .fi @@ -799,9 +824,9 @@ /* .IP "\fBdisable_dns_lookups (no)\fR" /* Disable DNS lookups in the Postfix SMTP and LMTP clients. /* .IP "\fBinet_interfaces (all)\fR" -/* The local network interface addresses that this mail system receives -/* mail on. -/* .IP "\fBinet_protocols (see 'postconf -d output')\fR" +/* The local network interface addresses that this mail system +/* receives mail on. +/* .IP "\fBinet_protocols (see 'postconf -d' output)\fR" /* The Internet protocols Postfix will attempt to use when making /* or accepting connections. /* .IP "\fBipc_timeout (3600s)\fR" @@ -1020,6 +1045,7 @@ int var_smtp_never_ehlo; char *var_smtp_sasl_opts; char *var_smtp_sasl_path; char *var_smtp_sasl_passwd; +char *var_smtp_sasl_passwd_res_delim; bool var_smtp_sasl_enable; char *var_smtp_sasl_mechs; char *var_smtp_sasl_type; @@ -1090,6 +1116,7 @@ char *var_smtp_tls_sni; bool var_smtp_tls_blk_early_mail_reply; bool var_smtp_tls_force_tlsa; char *var_smtp_tls_insecure_mx_policy; +bool var_smtp_tls_enable_rpk; #endif @@ -1117,8 +1144,8 @@ bool var_smtp_balance_inet_proto; bool var_smtp_req_deadline; int var_smtp_min_data_rate; char *var_use_srv_lookup; -bool var_ign_srv_lookup_err; -bool var_allow_srv_fallback; +bool var_ign_srv_lookup_err; +bool var_allow_srv_fallback; /* Special handling of 535 AUTH errors. */ char *var_smtp_sasl_auth_cache_name; @@ -1126,7 +1153,7 @@ int var_smtp_sasl_auth_cache_time; bool var_smtp_sasl_auth_soft_bounce; char *var_hfrom_format; -bool var_smtp_bind_addr_enforce; +bool var_smtp_bind_addr_enforce; /* * Global variables. @@ -1459,6 +1486,19 @@ static void pre_init(char *unused_name, char **unused_argv) }; /* + * The process name, "smtp" or "lmtp", determines the configuration + * parameters to use, protocol, DSN server reply type, SASL service + * information lookup, and more. We peeked at the name in the main() + * function before logging was initialized. Here, we detect and report an + * invalid process name. + */ + if (strcmp(var_procname, MAIL_PROC_NAME_SMTP) != 0 + && strcmp(var_procname, MAIL_PROC_NAME_LMTP) != 0) + msg_fatal("unexpected process name \"%s\" - " + "specify \"%s\" or \"%s\"", var_procname, + MAIL_PROC_NAME_SMTP, MAIL_PROC_NAME_LMTP); + + /* * Turn on per-peer debugging. */ debug_peer_init(); @@ -1649,21 +1689,15 @@ int main(int argc, char **argv) MAIL_VERSION_STAMP_ALLOCATE; /* - * XXX At this point, var_procname etc. are not initialized. - * - * The process name, "smtp" or "lmtp", determines the protocol, the DSN - * server reply type, SASL service information lookup, and more. Prepare - * for the possibility there may be another personality. + * XXX The process name, "smtp" or "lmtp", determines what configuration + * parameter settings to use, and more. However, at this point, logging + * and var_procname are not initialized. Here, we peek at the process + * name to determine what configuration parameter settings to use. Later, + * we detect and report an invalid process name. */ sane_procname = sane_basename((VSTRING *) 0, argv[0]); - if (strcmp(sane_procname, "smtp") == 0) + if (strcmp(sane_procname, MAIL_PROC_NAME_SMTP) == 0) smtp_mode = 1; - else if (strcmp(sane_procname, "lmtp") == 0) - smtp_mode = 0; - else - /* TODO: logging is not initialized. */ - msg_fatal("unexpected process name \"%s\" - " - "specify \"smtp\" or \"lmtp\"", var_procname); /* * Initialize with the LMTP or SMTP parameter name space. diff --git a/src/smtp/smtp.h b/src/smtp/smtp.h index f8c8f58..60c68f8 100644 --- a/src/smtp/smtp.h +++ b/src/smtp/smtp.h @@ -107,6 +107,7 @@ typedef struct SMTP_TLS_POLICY { TLS_DANE *dane; /* DANE TLSA digests */ char *sni; /* Optional SNI name when not DANE */ int conn_reuse; /* enable connection reuse */ + int enable_rpk; /* Enable server->client RPK */ } SMTP_TLS_POLICY; /* @@ -142,6 +143,7 @@ extern void smtp_tls_policy_cache_flush(void); _tls_policy_init_tmp->dane = 0; \ _tls_policy_init_tmp->sni = 0; \ _tls_policy_init_tmp->conn_reuse = 0; \ + _tls_policy_init_tmp->enable_rpk = 0; \ } while (0) #endif diff --git a/src/smtp/smtp_addr.c b/src/smtp/smtp_addr.c index f30ce73..8c384fc 100644 --- a/src/smtp/smtp_addr.c +++ b/src/smtp/smtp_addr.c @@ -262,7 +262,7 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt, msg_fatal("host %s: conversion error for address family " "%d: %m", host, res0->ai_addr->sa_family); addr_list = dns_rr_append(addr_list, addr); - if (DNS_RR_IS_TRUNCATED(addr_list)) + if (DNS_RR_IS_TRUNCATED(addr_list)) break; if (msg_verbose) { MAI_HOSTADDR_STR hostaddr_str; diff --git a/src/smtp/smtp_params.c b/src/smtp/smtp_params.c index 22f4709..cebff93 100644 --- a/src/smtp/smtp_params.c +++ b/src/smtp/smtp_params.c @@ -4,6 +4,7 @@ VAR_BESTMX_TRANSP, DEF_BESTMX_TRANSP, &var_bestmx_transp, 0, 0, VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0, VAR_SMTP_SASL_PASSWD, DEF_SMTP_SASL_PASSWD, &var_smtp_sasl_passwd, 0, 0, + VAR_SMTP_SASL_PASSWD_RES_DELIM, DEF_SMTP_SASL_PASSWD_RES_DELIM, &var_smtp_sasl_passwd_res_delim, 1, 1, VAR_SMTP_SASL_OPTS, DEF_SMTP_SASL_OPTS, &var_smtp_sasl_opts, 0, 0, VAR_SMTP_SASL_PATH, DEF_SMTP_SASL_PATH, &var_smtp_sasl_path, 0, 0, #ifdef USE_TLS @@ -124,6 +125,7 @@ VAR_SMTP_TLS_NOTEOFFER, DEF_SMTP_TLS_NOTEOFFER, &var_smtp_tls_note_starttls_offer, VAR_SMTP_TLS_BLK_EARLY_MAIL_REPLY, DEF_SMTP_TLS_BLK_EARLY_MAIL_REPLY, &var_smtp_tls_blk_early_mail_reply, VAR_SMTP_TLS_FORCE_TLSA, DEF_SMTP_TLS_FORCE_TLSA, &var_smtp_tls_force_tlsa, + VAR_SMTP_TLS_ENABLE_RPK, DEF_SMTP_TLS_ENABLE_RPK, &var_smtp_tls_enable_rpk, #endif VAR_SMTP_TLS_WRAPPER, DEF_SMTP_TLS_WRAPPER, &var_smtp_tls_wrappermode, VAR_SMTP_SENDER_AUTH, DEF_SMTP_SENDER_AUTH, &var_smtp_sender_auth, diff --git a/src/smtp/smtp_proto.c b/src/smtp/smtp_proto.c index 097d518..e022bc2 100644 --- a/src/smtp/smtp_proto.c +++ b/src/smtp/smtp_proto.c @@ -929,6 +929,7 @@ static int smtp_start_tls(SMTP_STATE *state) TLS_PROXY_CLIENT_START_PROPS(&start_props, timeout = var_smtp_starttls_tmout, tls_level = state->tls->level, + enable_rpk = state->tls->enable_rpk, nexthop = session->tls_nexthop, host = STR(iter->host), namaddr = session->namaddrport, @@ -1051,6 +1052,7 @@ static int smtp_start_tls(SMTP_STATE *state) fd = -1, timeout = var_smtp_starttls_tmout, tls_level = state->tls->level, + enable_rpk = state->tls->enable_rpk, nexthop = session->tls_nexthop, host = STR(iter->host), namaddr = session->namaddrport, diff --git a/src/smtp/smtp_sasl_glue.c b/src/smtp/smtp_sasl_glue.c index ef8e8c4..cce5ef7 100644 --- a/src/smtp/smtp_sasl_glue.c +++ b/src/smtp/smtp_sasl_glue.c @@ -200,7 +200,9 @@ int smtp_sasl_passwd_lookup(SMTP_SESSION *session) if (session->sasl_username) myfree(session->sasl_username); session->sasl_username = mystrdup(value); - passwd = split_at(session->sasl_username, ':'); + /* Historically, the delimiter may appear in the password. */ + passwd = split_at(session->sasl_username, + *var_smtp_sasl_passwd_res_delim); if (session->sasl_passwd) myfree(session->sasl_passwd); session->sasl_passwd = mystrdup(passwd ? passwd : ""); diff --git a/src/smtp/smtp_tls_policy.c b/src/smtp/smtp_tls_policy.c index 92a231d..f407d65 100644 --- a/src/smtp/smtp_tls_policy.c +++ b/src/smtp/smtp_tls_policy.c @@ -334,9 +334,10 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level, INVALID_RETURN(tls->why, site_level); break; case TLS_LEV_FPRINT: - if (!tls->dane) - tls->dane = tls_dane_alloc(); - tls_dane_add_fpt_digests(tls->dane, val, "|", smtp_mode); + if (tls->matchargv == 0) + tls->matchargv = argv_split(val, "|"); + else + argv_split_append(tls->matchargv, val, "|"); break; case TLS_LEV_VERIFY: case TLS_LEV_SECURE: @@ -390,6 +391,19 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level, } continue; } + if (!strcasecmp(name, "enable_rpk")) { + /* Ultimately ignored at some security levels */ + if (strcasecmp(val, "yes") == 0) { + tls->enable_rpk = 1; + } else if (strcasecmp(val, "no") == 0) { + tls->enable_rpk = 0; + } else { + msg_warn("%s: attribute \"%s\" has bad value: \"%s\"", + WHERE, name, val); + INVALID_RETURN(tls->why, site_level); + } + continue; + } msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name); INVALID_RETURN(tls->why, site_level); } @@ -518,6 +532,7 @@ static void *policy_create(const char *unused_key, void *context) smtp_tls_policy_init(tls, dsb_create()); tls->conn_reuse = var_smtp_tls_conn_reuse; + tls->enable_rpk = var_smtp_tls_enable_rpk; /* * Compute the per-site TLS enforcement level. For compatibility with the @@ -602,6 +617,13 @@ static void *policy_create(const char *unused_key, void *context) */ set_cipher_grade(tls); +/* + * Even when soliciting raw public keys, synthesize TLSA RRs that also match + * certificates. Though this is fragile, it maintains compatibility with + * servers that never return RPKs. + */ +#define DONT_SUPPRESS_CERT_MATCH 0 + /* * Use main.cf cert_match setting if not set in per-destination table. */ @@ -617,16 +639,26 @@ static void *policy_create(const char *unused_key, void *context) case TLS_LEV_FPRINT: if (tls->dane == 0) tls->dane = tls_dane_alloc(); - if (tls->dane->tlsa == 0) { - tls_dane_add_fpt_digests(tls->dane, var_smtp_tls_fpt_cmatch, - CHARS_COMMA_SP, smtp_mode); - if (tls->dane->tlsa == 0) { - msg_warn("nexthop domain %s: configured at fingerprint " - "security level, but with no fingerprints to match.", - dest); - MARK_INVALID(tls->why, &tls->level); - return ((void *) tls); + /* Process the specified fingerprint match patterns */ + if (tls->matchargv) { + int i; + + for (i = 0; i < tls->matchargv->argc; ++i) { + tls_dane_add_fpt_digests(tls->dane, DONT_SUPPRESS_CERT_MATCH, + tls->matchargv->argv[i], "", + smtp_mode); } + } else { + tls_dane_add_fpt_digests(tls->dane, DONT_SUPPRESS_CERT_MATCH, + var_smtp_tls_fpt_cmatch, CHARS_COMMA_SP, + smtp_mode); + } + if (tls->dane->tlsa == 0) { + msg_warn("nexthop domain %s: configured at fingerprint " + "security level, but with no fingerprints to match.", + dest); + MARK_INVALID(tls->why, &tls->level); + return ((void *) tls); } break; case TLS_LEV_VERIFY: diff --git a/src/smtpd/Makefile.in b/src/smtpd/Makefile.in index 7fdfe12..c8837fe 100644 --- a/src/smtpd/Makefile.in +++ b/src/smtpd/Makefile.in @@ -75,7 +75,8 @@ broken-tests: smtpd_check_test smtpd_check_test2 tests: smtpd_acl_test smtpd_addr_valid_test smtpd_exp_test \ smtpd_token_test smtpd_check_test4 smtpd_check_dsn_test \ smtpd_check_backup_test smtpd_dnswl_test smtpd_error_test \ - smtpd_server_test smtpd_nullmx_test smtpd_dns_filter_test + smtpd_server_test smtpd_nullmx_test smtpd_dns_filter_test \ + smtpd_deprecated_test root_tests: @@ -114,7 +115,8 @@ smtpd_addr_valid_test: smtpd_check smtpd_addr_valid.in smtpd_addr_valid.ref # This requires that the DNS server can query porcupine.org. -ADDRINFO_FIX = sed 's/No address associated with hostname/hostname nor servname provided, or not known/' +ADDRINFO_FIX = sed -e 's/No address associated with hostname/hostname nor servname provided, or not known/' \ + -e 's/Name or service not known/hostname nor servname provided, or not known/' smtpd_exp_test: smtpd_check smtpd_exp.in smtpd_exp.ref $(SHLIB_ENV) $(VALGRIND) ../postmap/postmap hash:smtpd_check_access @@ -170,6 +172,11 @@ smtpd_error_test: smtpd_check smtpd_error.in smtpd_error.ref diff smtpd_error.ref smtpd_check.tmp rm -f smtpd_check.tmp +smtpd_deprecated_test: smtpd_check smtpd_deprecated.in smtpd_deprecated.ref + $(SHLIB_ENV) $(VALGRIND) ./smtpd_check <smtpd_deprecated.in >smtpd_check.tmp 2>&1 + diff smtpd_deprecated.ref smtpd_check.tmp + rm -f smtpd_check.tmp + depend: $(MAKES) (sed '1,/^# do not edit/!d' Makefile.in; \ set -e; for i in [a-z][a-z0-9]*.c; do \ diff --git a/src/smtpd/smtpd.c b/src/smtpd/smtpd.c index 6a2cf01..bce0d43 100644 --- a/src/smtpd/smtpd.c +++ b/src/smtpd/smtpd.c @@ -468,7 +468,7 @@ /* \fBcheck_ccert_access\fR and \fBpermit_tls_clientcerts\fR. /* .PP /* Available in Postfix version 2.6 and later: -/* .IP "\fBsmtpd_tls_protocols (see postconf -d output)\fR" +/* .IP "\fBsmtpd_tls_protocols (see 'postconf -d' output)\fR" /* TLS protocols accepted by the Postfix SMTP server with opportunistic /* TLS encryption. /* .IP "\fBsmtpd_tls_ciphers (medium)\fR" @@ -537,6 +537,12 @@ /* .IP "\fBtls_config_name (empty)\fR" /* The application name passed by Postfix to OpenSSL library /* initialization functions. +/* .PP +/* Available in Postfix version 3.9 and later: +/* .IP "\fBsmtpd_tls_enable_rpk (no)\fR" +/* Request that remote SMTP clients send an RFC7250 raw public key +/* instead of an X.509 certificate, when asking for or requiring client +/* authentication. /* OBSOLETE STARTTLS CONTROLS /* .ad /* .fi @@ -662,12 +668,12 @@ /* The list of domains that are delivered via the $local_transport /* mail delivery transport. /* .IP "\fBinet_interfaces (all)\fR" -/* The local network interface addresses that this mail system receives -/* mail on. +/* The local network interface addresses that this mail system +/* receives mail on. /* .IP "\fBproxy_interfaces (empty)\fR" /* The remote network interface addresses that this mail system receives mail /* on by way of a proxy or network address translation unit. -/* .IP "\fBinet_protocols (see 'postconf -d output')\fR" +/* .IP "\fBinet_protocols (see 'postconf -d' output)\fR" /* The Internet protocols Postfix will attempt to use when making /* or accepting connections. /* .IP "\fBlocal_recipient_maps (proxy:unix:passwd.byname $alias_maps)\fR" @@ -698,8 +704,9 @@ /* alias domains, that is, domains for which all addresses are aliased /* to addresses in other local or remote domains. /* .IP "\fBvirtual_alias_maps ($virtual_maps)\fR" -/* Optional lookup tables that alias specific mail addresses or domains -/* to other local or remote addresses. +/* Optional lookup tables with aliases that apply to all recipients: +/* \fBlocal\fR(8), virtual, and remote; this is unlike alias_maps that apply +/* only to \fBlocal\fR(8) recipients. /* .IP "\fBunknown_virtual_alias_reject_code (550)\fR" /* The Postfix SMTP server reply code when a recipient address matches /* $virtual_alias_domains, and $virtual_alias_maps specifies a list @@ -817,7 +824,7 @@ /* command pipelining constraints. /* .PP /* Available in Postfix 3.9, 3.8.4, 3.7.9, 3.6.13, 3.5.23 and later: -/* .IP "\fBsmtpd_forbid_bare_newline (Postfix < 3.9: no)\fR" +/* .IP "\fBsmtpd_forbid_bare_newline (Postfix >= 3.9: normalize)\fR" /* Reject or restrict input lines from an SMTP client that end in /* <LF> instead of the standard <CR><LF>. /* .IP "\fBsmtpd_forbid_bare_newline_exclusions ($mynetworks)\fR" @@ -1492,6 +1499,7 @@ char *var_smtpd_tls_eecdh; char *var_smtpd_tls_eccert_file; char *var_smtpd_tls_eckey_file; char *var_smtpd_tls_chain_files; +int var_smtpd_tls_enable_rpk; #endif @@ -1664,13 +1672,16 @@ int smtpd_hfrom_format; */ #define BARE_LF_FLAG_WANT_STD_EOD (1<<0) /* Require CRLF.CRLF */ #define BARE_LF_FLAG_REPLY_REJECT (1<<1) /* Reject bare newline */ +#define BARE_LF_FLAG_NOTE_LOG (1<<2) /* Note bare newline */ #define IS_BARE_LF_WANT_STD_EOD(m) ((m) & BARE_LF_FLAG_WANT_STD_EOD) #define IS_BARE_LF_REPLY_REJECT(m) ((m) & BARE_LF_FLAG_REPLY_REJECT) +#define IS_BARE_LF_NOTE_LOG(m) ((m) & BARE_LF_FLAG_NOTE_LOG) static const NAME_CODE bare_lf_mask_table[] = { "normalize", BARE_LF_FLAG_WANT_STD_EOD, /* Default */ "yes", BARE_LF_FLAG_WANT_STD_EOD, /* Migration aid */ + "note", BARE_LF_FLAG_WANT_STD_EOD | BARE_LF_FLAG_NOTE_LOG, "reject", BARE_LF_FLAG_WANT_STD_EOD | BARE_LF_FLAG_REPLY_REJECT, "no", 0, 0, -1, /* error */ @@ -3504,11 +3515,15 @@ static void common_pre_message_handling(SMTPD_STATE *state, } if (state->tls_context->srvr_sig_curve && *state->tls_context->srvr_sig_curve) - vstring_sprintf_append(state->buffer, " (%s)", - state->tls_context->srvr_sig_curve); + vstring_sprintf_append(state->buffer, " (%s%s)", + state->tls_context->srvr_sig_curve, + state->tls_context->stoc_rpk ? + " raw public key" : ""); else if (state->tls_context->srvr_sig_bits > 0) - vstring_sprintf_append(state->buffer, " (%d bits)", - state->tls_context->srvr_sig_bits); + vstring_sprintf_append(state->buffer, " (%d bit%s)", + state->tls_context->srvr_sig_bits, + state->tls_context->stoc_rpk ? + " raw public key" : "s"); if (state->tls_context->srvr_sig_dgst && *state->tls_context->srvr_sig_dgst) vstring_sprintf_append(state->buffer, " server-digest %s", @@ -3522,11 +3537,15 @@ static void common_pre_message_handling(SMTPD_STATE *state, state->tls_context->clnt_sig_name); if (state->tls_context->clnt_sig_curve && *state->tls_context->clnt_sig_curve) - vstring_sprintf_append(state->buffer, " (%s)", - state->tls_context->clnt_sig_curve); + vstring_sprintf_append(state->buffer, " (%s%s)", + state->tls_context->clnt_sig_curve, + state->tls_context->ctos_rpk ? + " raw public key" : ""); else if (state->tls_context->clnt_sig_bits > 0) - vstring_sprintf_append(state->buffer, " (%d bits)", - state->tls_context->clnt_sig_bits); + vstring_sprintf_append(state->buffer, " (%d bit%s)", + state->tls_context->clnt_sig_bits, + state->tls_context->ctos_rpk ? + " raw public key" : "s"); if (state->tls_context->clnt_sig_dgst && *state->tls_context->clnt_sig_dgst) vstring_sprintf_append(state->buffer, " client-digest %s", @@ -3546,6 +3565,11 @@ static void common_pre_message_handling(SMTPD_STATE *state, "verified OK" : "not verified"); vstring_free(issuer_CN); vstring_free(peer_CN); + } else if (TLS_RPK_IS_PRESENT(state->tls_context)) { + out_fprintf(out_stream, REC_TYPE_NORM, + "\t(Client RPK %s digest %s)", + var_smtpd_tls_fpt_dgst, + state->tls_context->peer_pkey_fprint); } else if (var_smtpd_tls_ask_ccert) out_fprintf(out_stream, REC_TYPE_NORM, "\t(Client did not present a certificate)"); @@ -3648,6 +3672,8 @@ static void receive_data_message(SMTPD_STATE *state, curr_rec_type = REC_TYPE_CONT; if (IS_BARE_LF_REPLY_REJECT(smtp_got_bare_lf)) state->err |= CLEANUP_STAT_BARE_LF; + else if (IS_BARE_LF_NOTE_LOG(smtp_got_bare_lf)) + state->notes |= SMTPD_NOTE_BARE_LF; start = vstring_str(state->buffer); len = VSTRING_LEN(state->buffer); if (first) { @@ -4168,6 +4194,8 @@ static int bdat_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) } if (IS_BARE_LF_REPLY_REJECT(smtp_got_bare_lf)) state->err |= CLEANUP_STAT_BARE_LF; + else if (IS_BARE_LF_NOTE_LOG(smtp_got_bare_lf)) + state->notes |= SMTPD_NOTE_BARE_LF; start = vstring_str(state->bdat_get_buffer); len = VSTRING_LEN(state->bdat_get_buffer); if (state->err == CLEANUP_STAT_OK) { @@ -5231,6 +5259,7 @@ static void smtpd_start_tls(SMTPD_STATE *state) stream = state->client, fd = -1, timeout = var_smtpd_starttls_tmout, + enable_rpk = var_smtpd_tls_enable_rpk, requirecert = requirecert, serverid = state->service, namaddr = state->namaddr, @@ -5469,8 +5498,6 @@ static void tls_reset(SMTPD_STATE *state) #endif -#if !defined(USE_TLS) || !defined(USE_SASL_AUTH) - /* unimpl_cmd - dummy for functionality that is not compiled in */ static int unimpl_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) @@ -5487,8 +5514,6 @@ static int unimpl_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) return (-1); } -#endif - /* * The table of all SMTP commands that we know. Set the junk limit flag on * any command that can be repeated an arbitrary number of times without @@ -5513,6 +5538,8 @@ typedef struct SMTPD_CMD { #define SMTPD_CMD_FLAG_PRE_TLS (1<<1) /* allow before STARTTLS */ #define SMTPD_CMD_FLAG_LAST (1<<2) /* last in PIPELINING command group */ +static int help_cmd(SMTPD_STATE *, int, SMTPD_TOKEN *); + static SMTPD_CMD smtpd_cmd_table[] = { {SMTPD_CMD_HELO, helo_cmd, SMTPD_CMD_FLAG_LIMIT | SMTPD_CMD_FLAG_PRE_TLS | SMTPD_CMD_FLAG_LAST,}, {SMTPD_CMD_EHLO, ehlo_cmd, SMTPD_CMD_FLAG_LIMIT | SMTPD_CMD_FLAG_PRE_TLS | SMTPD_CMD_FLAG_LAST,}, @@ -5537,12 +5564,44 @@ static SMTPD_CMD smtpd_cmd_table[] = { {SMTPD_CMD_VRFY, vrfy_cmd, SMTPD_CMD_FLAG_LIMIT | SMTPD_CMD_FLAG_LAST,}, {SMTPD_CMD_ETRN, etrn_cmd, SMTPD_CMD_FLAG_LIMIT,}, {SMTPD_CMD_QUIT, quit_cmd, SMTPD_CMD_FLAG_PRE_TLS,}, + {SMTPD_CMD_HELP, help_cmd, SMTPD_CMD_FLAG_PRE_TLS,}, {0,}, }; static STRING_LIST *smtpd_noop_cmds; static STRING_LIST *smtpd_forbid_cmds; +/* help_cmd - process HELP command */ + +static int help_cmd(SMTPD_STATE *state, int unused_argc, SMTPD_TOKEN *unused_argv) +{ + ARGV *argv = argv_alloc(sizeof(smtpd_cmd_table) + / sizeof(*smtpd_cmd_table)); + VSTRING *buf = vstring_alloc(100); + SMTPD_CMD *cmdp; + + /* + * Return a list of implemented commands. + * + * The HELP command does not suppress commands that can be dynamically + * disabled in the EHLO response or through access control. That would + * require refactoring the EHLO feature-suppression and per-feature + * access control, so that they can be reused (not duplicated) here. + * + * The HELP command does not provide information that makes Postfix easier + * to fingerprint, such as software name, version, or build information. + */ + for (cmdp = smtpd_cmd_table; cmdp->name != 0; cmdp++) + if (cmdp->action != unimpl_cmd) + argv_add(argv, cmdp->name, ARGV_END); + argv_sort(argv); + smtpd_chat_reply(state, "214 2.0.0 Commands: %s", + argv_join(buf, argv, ' ')); + vstring_free(buf); + argv_free(argv); + return (0); +} + /* smtpd_flag_ill_pipelining - flag pipelining protocol violation */ static int smtpd_flag_ill_pipelining(SMTPD_STATE *state) @@ -5869,9 +5928,11 @@ static void smtpd_proto(SMTPD_STATE *state) var_smtpd_forbid_bare_lf_code, var_myhostname); break; } + if (IS_BARE_LF_NOTE_LOG(smtp_got_bare_lf)) + state->notes |= SMTPD_NOTE_BARE_LF; /* Safety: protect internal interfaces against malformed UTF-8. */ - if (var_smtputf8_enable && valid_utf8_string(STR(state->buffer), - LEN(state->buffer)) == 0) { + if (var_smtputf8_enable + && valid_utf8_stringz(STR(state->buffer)) == 0) { state->error_mask |= MAIL_ERROR_PROTOCOL; smtpd_chat_reply(state, "500 5.5.2 Error: bad UTF-8 syntax"); state->error_count++; @@ -6055,11 +6116,12 @@ static void smtpd_proto(SMTPD_STATE *state) /* smtpd_format_cmd_stats - format per-command statistics */ -static char *smtpd_format_cmd_stats(VSTRING *buf) +static char *smtpd_format_cmd_stats(SMTPD_STATE *state) { SMTPD_CMD *cmdp; int all_success = 0; int all_total = 0; + VSTRING *buf = state->buffer; /* * Log the statistics. Note that this loop produces no output when no @@ -6103,6 +6165,13 @@ static char *smtpd_format_cmd_stats(VSTRING *buf) vstring_sprintf_append(buf, " commands=%d", all_success); if (all_success != all_total || all_total == 0) vstring_sprintf_append(buf, "/%d", all_total); + + /* + * Log aggregated warnings. + */ + if (state->notes & SMTPD_NOTE_BARE_LF) + vstring_sprintf_append(buf, " notes=bare_lf"); + return (lowercase(STR(buf))); } @@ -6244,7 +6313,7 @@ static void smtpd_service(VSTREAM *stream, char *service, char **argv) * connection time. */ msg_info("disconnect from %s%s", state.namaddr, - smtpd_format_cmd_stats(state.buffer)); + smtpd_format_cmd_stats(&state)); teardown_milters(&state); /* duplicates xclient_cmd */ smtpd_state_reset(&state); debug_peer_restore(); @@ -6403,7 +6472,6 @@ static void pre_jail_init(char *unused_name, char **unused_argv) no_server_cert_ok = 0; cert_file = var_smtpd_tls_cert_file; } - have_server_cert = *cert_file != 0; have_server_cert |= *var_smtpd_tls_eccert_file != 0; have_server_cert |= *var_smtpd_tls_dcert_file != 0; @@ -6653,6 +6721,7 @@ int main(int argc, char **argv) #ifdef USE_TLS VAR_SMTPD_TLS_ACERT, DEF_SMTPD_TLS_ACERT, &var_smtpd_tls_ask_ccert, VAR_SMTPD_TLS_RCERT, DEF_SMTPD_TLS_RCERT, &var_smtpd_tls_req_ccert, + VAR_SMTPD_TLS_ENABLE_RPK, DEF_SMTPD_TLS_ENABLE_RPK, &var_smtpd_tls_enable_rpk, VAR_SMTPD_TLS_RECHEAD, DEF_SMTPD_TLS_RECHEAD, &var_smtpd_tls_received_header, VAR_SMTPD_TLS_SET_SESSID, DEF_SMTPD_TLS_SET_SESSID, &var_smtpd_tls_set_sessid, #endif diff --git a/src/smtpd/smtpd.h b/src/smtpd/smtpd.h index 56ebc07..c049194 100644 --- a/src/smtpd/smtpd.h +++ b/src/smtpd/smtpd.h @@ -114,6 +114,7 @@ typedef struct { int junk_cmds; /* counter */ int rcpt_overshoot; /* counter */ char *rewrite_context; /* address rewriting context */ + int notes; /* notes aggregator */ /* * SASL specific. @@ -209,6 +210,8 @@ typedef struct { #define SMTPD_FLAG_SMTPUTF8 (1<<3) /* RFC 6531/2 transaction */ #define SMTPD_FLAG_NEED_MILTER_ABORT (1<<4) /* undo milter_mail_event() */ +#define SMTPD_NOTE_BARE_LF (1<<0) /* saw at least one bare LF */ + /* Security: don't reset SMTPD_FLAG_AUTH_USED. */ #define SMTPD_MASK_MAIL_KEEP \ ~(SMTPD_FLAG_SMTPUTF8) /* Fix 20140706 */ @@ -260,6 +263,7 @@ extern void smtpd_state_reset(SMTPD_STATE *); #define SMTPD_CMD_XCLIENT "XCLIENT" #define SMTPD_CMD_XFORWARD "XFORWARD" #define SMTPD_CMD_UNKNOWN "UNKNOWN" +#define SMTPD_CMD_HELP "HELP" /* * Representation of unknown and non-existent client information. Throughout diff --git a/src/smtpd/smtpd_check.c b/src/smtpd/smtpd_check.c index 093aa06..6aeda74 100644 --- a/src/smtpd/smtpd_check.c +++ b/src/smtpd/smtpd_check.c @@ -1598,6 +1598,7 @@ static int permit_auth_destination(SMTPD_STATE *state, char *recipient); static int permit_tls_clientcerts(SMTPD_STATE *state, int permit_all_certs) { #ifdef USE_TLS + const char *myname = "permit_tls_clientcerts"; const char *found = 0; if (!state->tls_context) @@ -1612,9 +1613,9 @@ static int permit_tls_clientcerts(SMTPD_STATE *state, int permit_all_certs) /* * When directly checking the fingerprint, it is OK if the issuing CA is - * not trusted. + * not trusted. Raw public keys are also acceptable. */ - if (TLS_CERT_IS_PRESENT(state->tls_context)) { + if (TLS_CRED_IS_PRESENT(state->tls_context)) { int i; char *prints[2]; @@ -1623,12 +1624,24 @@ static int permit_tls_clientcerts(SMTPD_STATE *state, int permit_all_certs) VAR_SMTPD_TLS_FPT_DGST "=md5 to compute certificate " "fingerprints"); - prints[0] = state->tls_context->peer_cert_fprint; - prints[1] = state->tls_context->peer_pkey_fprint; + prints[0] = state->tls_context->peer_pkey_fprint; + prints[1] = state->tls_context->peer_cert_fprint; /* After lookup error, leave relay_ccerts->error at non-zero value. */ for (i = 0; i < 2; ++i) { + /* With RFC7250 RPK, no certificate may be available */ + if (!*prints[i]) + continue; found = maps_find(relay_ccerts, prints[i], DICT_FLAG_NONE); + if (var_smtpd_tls_enable_rpk && i > 0 && found) { + msg_warn("%s: %s: %s: Fragile access policy: %s=yes, but" + " public key fingerprint \"%s\" not matched, while" + " certificate fingerprint \"%s\" matched", + myname, state->namaddr, relay_ccerts->title, + VAR_SMTPD_TLS_ENABLE_RPK, + state->tls_context->peer_cert_fprint, + state->tls_context->peer_pkey_fprint); + } if (found != 0) { if (msg_verbose) msg_info("Relaying allowed for certified client: %s", found); @@ -1659,44 +1672,16 @@ static int check_relay_domains(SMTPD_STATE *state, char *recipient, { const char *myname = "check_relay_domains"; -#if 1 - static int once; - - if (once == 0) { - once = 1; - msg_warn("support for restriction \"%s\" will be removed from %s; " - "use \"%s\" instead", - CHECK_RELAY_DOMAINS, var_mail_name, REJECT_UNAUTH_DEST); - } -#endif - - if (msg_verbose) - msg_info("%s: %s", myname, recipient); - /* - * Permit if the client matches the relay_domains list. + * Restriction check_relay_domains is deprecated as of Postfix 2.2. */ - if (domain_list_match(relay_domains, state->name)) { - if (warn_compat_break_relay_domains) - msg_info("using backwards-compatible default setting " - VAR_RELAY_DOMAINS "=$mydestination to permit " - "request from client \"%s\"", state->name); - return (SMTPD_CHECK_OK); - } - - /* - * Permit authorized destinations. - */ - if (permit_auth_destination(state, recipient) == SMTPD_CHECK_OK) - return (SMTPD_CHECK_OK); + if (msg_verbose) + msg_info("%s: %s", myname, recipient); - /* - * Deny relaying between sites that both are not in relay_domains. - */ - return (smtpd_check_reject(state, MAIL_ERROR_POLICY, - var_relay_code, "5.7.1", - "<%s>: %s rejected: Relay access denied", - reply_name, reply_class)); + msg_warn("support for restriction \"%s\" has been removed in %s 3.9; " + "instead, specify \"%s\"", + CHECK_RELAY_DOMAINS, var_mail_name, REJECT_UNAUTH_DEST); + reject_server_error(state); } /* permit_auth_destination - OK for message relaying */ @@ -2002,11 +1987,22 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient, DNS_RR *middle; DNS_RR *rest; int dns_status; + static int once; if (msg_verbose) msg_info("%s: %s", myname, recipient); /* + * Restriction permit_mx_backup is deprecated as of Postfix 3.9. + */ + if (once == 0) { + once = 1; + msg_warn("support for restriction \"%s\" will be removed from %s; " + "instead, specify \"%s\"", + PERMIT_MX_BACKUP, var_mail_name, VAR_RELAY_DOMAINS); + } + + /* * Resolve the address. */ reply = smtpd_resolve_addr(state->sender, recipient); @@ -3185,6 +3181,9 @@ static int check_ccert_access(SMTPD_STATE *state, const char *acl_spec, #ifdef USE_TLS const char *myname = "check_ccert_access"; + int cert_result = SMTPD_CHECK_DUNNO; + int pkey_result = SMTPD_CHECK_DUNNO; + int *respt; int found; const MAP_SEARCH *acl; const char default_search[] = { @@ -3211,9 +3210,9 @@ static int check_ccert_access(SMTPD_STATE *state, const char *acl_spec, /* * When directly checking the fingerprint, it is OK if the issuing CA is - * not trusted. + * not trusted. Raw public keys are also acceptable. */ - if (TLS_CERT_IS_PRESENT(state->tls_context)) { + if (TLS_CRED_IS_PRESENT(state->tls_context)) { const char *action; const char *match_this; const char *known_action; @@ -3222,17 +3221,19 @@ static int check_ccert_access(SMTPD_STATE *state, const char *acl_spec, switch (*action) { case SMTPD_ACL_SEARCH_CODE_CERT_FPRINT: match_this = state->tls_context->peer_cert_fprint; - if (warn_compat_break_smtpd_tls_fpt_dgst) + if (*match_this && warn_compat_break_smtpd_tls_fpt_dgst) msg_info("using backwards-compatible default setting " VAR_SMTPD_TLS_FPT_DGST "=md5 to compute " "certificate fingerprints"); + respt = &cert_result; break; case SMTPD_ACL_SEARCH_CODE_PKEY_FPRINT: match_this = state->tls_context->peer_pkey_fprint; - if (warn_compat_break_smtpd_tls_fpt_dgst) + if (*match_this && warn_compat_break_smtpd_tls_fpt_dgst) msg_info("using backwards-compatible default setting " VAR_SMTPD_TLS_FPT_DGST "=md5 to compute " - "certificate fingerprints"); + "public key fingerprints"); + respt = &pkey_result; break; default: known_action = str_name_code(search_actions, *action); @@ -3245,6 +3246,9 @@ static int check_ccert_access(SMTPD_STATE *state, const char *acl_spec, 451, "4.3.5", "Server configuration error")); } + /* With RFC7250 RPK, no certificate may be available */ + if (!*match_this) + continue; if (msg_verbose) msg_info("%s: look up %s %s", myname, str_name_code(search_actions, *action), @@ -3257,11 +3261,16 @@ static int check_ccert_access(SMTPD_STATE *state, const char *acl_spec, * "reject" event. XXX Should log the thing that is rejected * (fingerprint etc.) or would that give away too much? */ - result = check_access(state, acl->map_type_name, match_this, + *respt = check_access(state, acl->map_type_name, match_this, DICT_FLAG_NONE, &found, state->tls_context->peer_CN, SMTPD_NAME_CCERT, def_acl); - if (result != SMTPD_CHECK_DUNNO) + if (*respt == SMTPD_CHECK_DUNNO) + continue; + if (result == SMTPD_CHECK_DUNNO) + result = *respt; + if (!var_smtpd_tls_enable_rpk + || *action == SMTPD_ACL_SEARCH_CODE_PKEY_FPRINT) break; } } else if (!var_smtpd_tls_ask_ccert) { @@ -3271,6 +3280,17 @@ static int check_ccert_access(SMTPD_STATE *state, const char *acl_spec, if (msg_verbose) msg_info("%s: no client certificate", myname); } + if (var_smtpd_tls_enable_rpk + && cert_result != SMTPD_CHECK_DUNNO + && cert_result != pkey_result) { + msg_warn("%s: %s: %s: Fragile access policy: %s=yes, but" + " the action for certificate fingerprint \"%s\" !=" + " the action for public key fingerprint \"%s\"", + myname, state->namaddr, acl->map_type_name, + VAR_SMTPD_TLS_ENABLE_RPK, + state->tls_context->peer_cert_fprint, + state->tls_context->peer_pkey_fprint); + } #endif return (result); } @@ -3877,34 +3897,18 @@ static int permit_dnswl_domain(SMTPD_STATE *state, const char *dnswl_domain, static int reject_maps_rbl(SMTPD_STATE *state) { const char *myname = "reject_maps_rbl"; - char *saved_domains = mystrdup(var_maps_rbl_domains); - char *bp = saved_domains; - char *rbl_domain; - int result = SMTPD_CHECK_DUNNO; - static int warned; if (msg_verbose) msg_info("%s: %s", myname, state->addr); - if (warned == 0) { - warned++; - msg_warn("support for restriction \"%s\" will be removed from %s; " - "use \"%s domain-name\" instead", - REJECT_MAPS_RBL, var_mail_name, REJECT_RBL_CLIENT); - } - while ((rbl_domain = mystrtok(&bp, CHARS_COMMA_SP)) != 0) { - result = reject_rbl_addr(state, rbl_domain, state->addr, - SMTPD_NAME_CLIENT); - if (result != SMTPD_CHECK_DUNNO) - break; - } - /* - * Clean up. + * Restriction reject_maps_rbl is deprecated as of Postfix 2.1. */ - myfree(saved_domains); + msg_warn("support for restriction \"%s\" has been removed in %s 3.9; " + "instead, specify \"%s domain-name\"", + REJECT_MAPS_RBL, var_mail_name, REJECT_RBL_CLIENT); - return (result); + reject_server_error(state); } #ifdef USE_SASL_AUTH @@ -3980,7 +3984,7 @@ static int valid_utf8_action(const char *server, const char *action) { int retval; - if ((retval = valid_utf8_string(action, strlen(action))) == 0) + if ((retval = valid_utf8_stringz(action)) == 0) msg_warn("malformed UTF-8 in policy server %s response: \"%s\"", server, action); return (retval); @@ -4035,6 +4039,8 @@ static int check_policy_service(SMTPD_STATE *state, const char *server, ENCODE_CN(subject, subject_buf, state->tls_context->peer_CN); ENCODE_CN(issuer, issuer_buf, state->tls_context->issuer_CN); +#define NONEMPTY(x) ((x) != 0 && (*x) != 0) + /* * XXX: Too noisy to warn for each policy lookup, especially because we * don't even know whether the policy server will use the fingerprint. So @@ -4044,12 +4050,12 @@ static int check_policy_service(SMTPD_STATE *state, const char *server, if (!warned && warn_compat_break_smtpd_tls_fpt_dgst && state->tls_context - && state->tls_context->peer_cert_fprint - && *state->tls_context->peer_cert_fprint) { + && (NONEMPTY(state->tls_context->peer_cert_fprint) + || NONEMPTY(state->tls_context->peer_pkey_fprint))) { warned = 1; msg_info("using backwards-compatible default setting " VAR_SMTPD_TLS_FPT_DGST "=md5 to compute certificate " - "fingerprints"); + "and public key fingerprints"); } #endif @@ -4480,15 +4486,12 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, state->helo_name, SMTPD_NAME_HELO); } } else if (strcasecmp(name, PERMIT_NAKED_IP_ADDR) == 0) { - msg_warn("restriction %s is deprecated. Use %s or %s instead", - PERMIT_NAKED_IP_ADDR, PERMIT_MYNETWORKS, PERMIT_SASL_AUTH); - if (state->helo_name) { - if (state->helo_name[strspn(state->helo_name, "0123456789.:")] == 0 - && (status = reject_invalid_hostaddr(state, state->helo_name, - state->helo_name, SMTPD_NAME_HELO)) == 0) - status = smtpd_acl_permit(state, name, SMTPD_NAME_HELO, - state->helo_name, NO_PRINT_ARGS); - } + /* permit_naked_ip_addr is deprecated as of Postfix 2.0. */ + msg_warn("support for restriction \"%s\" has been removed in %s" + " 3.9; instead, specify \"%s\" or \"%s\"", + PERMIT_NAKED_IP_ADDR, var_mail_name, + PERMIT_MYNETWORKS, PERMIT_SASL_AUTH); + reject_server_error(state); } else if (is_map_command(state, name, CHECK_HELO_NS_ACL, &cpp)) { if (state->helo_name) { status = check_server_access(state, *cpp, state->helo_name, @@ -5255,8 +5258,9 @@ static int check_recipient_rcpt_maps(SMTPD_STATE *state, const char *recipient) { /* - * Duplicate suppression. There's an implicit check_recipient_maps - * restriction at the end of all recipient restrictions. + * Duplicate suppression. With "smtpd_reject_unlisted_recipient = yes", + * there's an implicit reject_unlisted_recipient restriction at the end + * of all recipient restrictions. */ if (smtpd_input_transp_mask & INPUT_TRANSP_UNKNOWN_RCPT) return (0); @@ -5275,8 +5279,9 @@ static int check_sender_rcpt_maps(SMTPD_STATE *state, const char *sender) { /* - * Duplicate suppression. There's an implicit check_sender_maps - * restriction at the end of all sender restrictions. + * Duplicate suppression. With "smtpd_reject_unlisted_sender = yes", + * there's an implicit reject_unlisted_sender restriction at the end of + * all sender restrictions. */ if (smtpd_input_transp_mask & INPUT_TRANSP_UNKNOWN_RCPT) return (0); @@ -5832,6 +5837,7 @@ char *var_smtpd_dns_re_filter; bool var_smtpd_tls_ask_ccert; int var_smtpd_cipv4_prefix; int var_smtpd_cipv6_prefix; +bool var_smtpd_tls_enable_rpk; #define int_table test_int_table @@ -5869,6 +5875,7 @@ static const INT_TABLE int_table[] = { VAR_SMTPD_TLS_ACERT, DEF_SMTPD_TLS_ACERT, &var_smtpd_tls_ask_ccert, VAR_SMTPD_CIPV4_PREFIX, DEF_SMTPD_CIPV4_PREFIX, &var_smtpd_cipv4_prefix, VAR_SMTPD_CIPV6_PREFIX, DEF_SMTPD_CIPV6_PREFIX, &var_smtpd_cipv6_prefix, + VAR_SMTPD_TLS_ENABLE_RPK, DEF_SMTPD_TLS_ENABLE_RPK, &var_smtpd_tls_enable_rpk, 0, }; @@ -6406,7 +6413,7 @@ int main(int argc, char **argv) state.tls_context->peer_cert_fprint = state.tls_context->peer_pkey_fprint = 0; } - state.tls_context->peer_status |= TLS_CERT_FLAG_PRESENT; + state.tls_context->peer_status |= TLS_CRED_FLAG_CERT; UPDATE_STRING(state.tls_context->peer_cert_fprint, args->argv[1]); state.tls_context->peer_pkey_fprint = diff --git a/src/smtpd/smtpd_check_backup.ref b/src/smtpd/smtpd_check_backup.ref index 8f4a0f2..c15be35 100644 --- a/src/smtpd/smtpd_check_backup.ref +++ b/src/smtpd/smtpd_check_backup.ref @@ -17,6 +17,7 @@ OK >>> recipient_restrictions permit_mx_backup,reject OK >>> rcpt wietse@wzv.porcupine.org +./smtpd_check: warning: support for restriction "permit_mx_backup" will be removed from Postfix; instead, use "relay_domains" OK >>> rcpt wietse@backup.porcupine.org OK diff --git a/src/smtpd/smtpd_deprecated.in b/src/smtpd/smtpd_deprecated.in new file mode 100644 index 0000000..345ee71 --- /dev/null +++ b/src/smtpd/smtpd_deprecated.in @@ -0,0 +1,20 @@ +# +# permit_naked_ip_address +# +client foo 127.0.0.2 +recipient_restrictions permit_naked_ip_address +helo 127.0.0.2 +mail sname@sdomain.example +rcpt rname@rdomain.example +# +# check_relay_domains +# +client foo 127.0.0.2 +recipient_restrictions check_relay_domains +relay_domains foo +helo 127.0.0.2 +mail sname@sdomain.example +rcpt rname@rdomain.example +# +# reject_maps_rbl is already covered elsewhere. +# diff --git a/src/smtpd/smtpd_deprecated.ref b/src/smtpd/smtpd_deprecated.ref new file mode 100644 index 0000000..d64f1b3 --- /dev/null +++ b/src/smtpd/smtpd_deprecated.ref @@ -0,0 +1,35 @@ +>>> # +>>> # permit_naked_ip_address +>>> # +>>> client foo 127.0.0.2 +OK +>>> recipient_restrictions permit_naked_ip_address +OK +>>> helo 127.0.0.2 +OK +>>> mail sname@sdomain.example +OK +>>> rcpt rname@rdomain.example +./smtpd_check: warning: restriction permit_naked_ip_address has been removed in Postfix 3.9; use permit_mynetworks or permit_sasl_authenticated instead +./smtpd_check: <queue id>: reject: RCPT from foo[127.0.0.2]: 451 4.3.5 Server configuration error; from=<sname@sdomain.example> to=<rname@rdomain.example> proto=SMTP helo=<127.0.0.2> +451 4.3.5 Server configuration error +>>> # +>>> # check_relay_domains +>>> # +>>> client foo 127.0.0.2 +OK +>>> recipient_restrictions check_relay_domains +OK +>>> relay_domains foo +OK +>>> helo 127.0.0.2 +OK +>>> mail sname@sdomain.example +OK +>>> rcpt rname@rdomain.example +./smtpd_check: warning: support for restriction "check_relay_domains" has been removed in Postfix 3.9; use "reject_unauth_destination" instead +./smtpd_check: <queue id>: reject: RCPT from foo[127.0.0.2]: 451 4.3.5 Server configuration error; from=<sname@sdomain.example> to=<rname@rdomain.example> proto=SMTP helo=<127.0.0.2> +451 4.3.5 Server configuration error +>>> # +>>> # reject_maps_rbl is already covered elsewhere. +>>> # diff --git a/src/smtpd/smtpd_exp.ref b/src/smtpd/smtpd_exp.ref index 22c027e..00848a5 100644 --- a/src/smtpd/smtpd_exp.ref +++ b/src/smtpd/smtpd_exp.ref @@ -25,13 +25,15 @@ OK >>> client spike.porcupine.org 168.100.3.2 OK >>> rcpt rname@rdomain -./smtpd_check: warning: support for restriction "reject_maps_rbl" will be removed from Postfix; use "reject_rbl_client domain-name" instead -OK +./smtpd_check: warning: support for restriction "reject_maps_rbl" has been removed in Postfix 3.9; use "reject_rbl_client domain-name" instead +./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.3.2]: 451 4.3.5 Server configuration error; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<foobar> +451 4.3.5 Server configuration error >>> client foo 127.0.0.2 OK >>> rcpt rname@rdomain -./smtpd_check: <queue id>: reject: RCPT from foo[127.0.0.2]: 554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org; DNS blocklist test; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<foobar> -554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org; DNS blocklist test +./smtpd_check: warning: support for restriction "reject_maps_rbl" has been removed in Postfix 3.9; use "reject_rbl_client domain-name" instead +./smtpd_check: <queue id>: reject: RCPT from foo[127.0.0.2]: 451 4.3.5 Server configuration error; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<foobar> +451 4.3.5 Server configuration error >>> # >>> recipient_restrictions reject_rbl_client,dnsbltest.porcupine.org OK diff --git a/src/smtpd/smtpd_sasl_glue.c b/src/smtpd/smtpd_sasl_glue.c index d9db7b0..1163366 100644 --- a/src/smtpd/smtpd_sasl_glue.c +++ b/src/smtpd/smtpd_sasl_glue.c @@ -120,6 +120,10 @@ /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org +/* Amawalk, NY 10501, USA /*--*/ /* System library. */ diff --git a/src/smtpd/smtpd_state.c b/src/smtpd/smtpd_state.c index f2f5f89..fefc543 100644 --- a/src/smtpd/smtpd_state.c +++ b/src/smtpd/smtpd_state.c @@ -135,6 +135,7 @@ void smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream, state->instance = vstring_alloc(10); state->seqno = 0; state->rewrite_context = 0; + state->notes = 0; #if 0 state->ehlo_discard_mask = ~0; #else diff --git a/src/smtpstone/smtp-source.c b/src/smtpstone/smtp-source.c index be388d6..f9fa64f 100644 --- a/src/smtpstone/smtp-source.c +++ b/src/smtpstone/smtp-source.c @@ -42,7 +42,7 @@ /* Don't disconnect after sending a message; send the next /* message over the same connection. /* .IP "\fB-f \fIfrom\fR" -/* Use the specified sender address (default: <foo@myhostname>). +/* Use the specified sender address (default: <foo@my-hostname>). /* .IP "\fB-F \fIfile\fR" /* Send the pre-formatted message header and body in the /* specified \fIfile\fR, while prepending '.' before lines that @@ -54,31 +54,49 @@ /* Speak LMTP rather than SMTP. /* .IP "\fB-m \fImessage_count\fR" /* Send the specified number of messages (default: 1). -/* .IP "\fB-M \fImyhostname\fR" +/* .IP "\fB-M \fImy-hostname\fR" /* Use the specified hostname or [address] in the HELO command /* and in the default sender and recipient addresses, instead /* of the machine hostname. /* .IP "\fB-N\fR" -/* Prepend a non-repeating sequence number to each recipient -/* address. This avoids the artificial 100% hit rate in the -/* resolve and rewrite client caches and exercises the -/* trivial-rewrite daemon, better approximating Postfix -/* performance under real-life work-loads. +/* Generate each recipient address by appending a number (a +/* per-process recipient counter) to the recipient address +/* localpart specified with the \fB-t\fR option. +/* +/* Note: to use the number as an address extension, specify +/* an explicit address delimiter at the end of the recipient +/* localpart, as in "\fB-t localpart+@domain\fR" or "\fB-t +/* localpart+\fR", where "\fB+\fR" is a Postfix recipient +/* address delimiter. +/* +/* Benefits: +/* .RS +/* .IP \(bu +/* A non-constant recipient address avoids an unrealistic 100% +/* cache hit rate in clients of the Postfix trivial-rewrite +/* service, better approximating performance under real-life +/* work-loads. +/* .IP \(bu +/* A fixed recipient address local-part with a non-constant +/* address extension avoids the need to configure a large +/* number of valid recipient addresses in the receiving Postfix +/* server. +/* .RE /* .IP \fB-o\fR /* Old mode: don't send HELO, and don't send message headers. /* .IP "\fB-r \fIrecipient_count\fR" -/* Send the specified number of recipients per transaction (default: 1). -/* Recipient names are generated by prepending a number to the -/* recipient address. +/* Send the specified number of recipients per transaction +/* (default: 1), and generate recipient addresses as described +/* under the \fB-N\fR option. /* .IP "\fB-R \fIinterval\fR" -/* Wait for a random period of time 0 <= n <= interval between messages. +/* Wait a random time (0 <= n <= \fIinterval\fR) between messages. /* Suspending one thread does not affect other delivery threads. /* .IP "\fB-s \fIsession_count\fR" /* Run the specified number of SMTP sessions in parallel (default: 1). /* .IP "\fB-S \fIsubject\fR" /* Send mail with the named subject line (default: none). /* .IP "\fB-t \fIto\fR" -/* Use the specified recipient address (default: <foo@myhostname>). +/* Use the specified recipient address (default: <foo@my-hostname>). /* .IP "\fB-T \fIwindowsize\fR" /* Override the default TCP window size. To work around /* broken TCP window scaling implementations, specify a @@ -172,6 +190,7 @@ typedef struct SESSION { int rcpt_done; /* # of recipients done */ int rcpt_count; /* # of recipients to go */ int rcpt_accepted; /* # of recipients accepted */ + int rcpt_sample; /* Sample recipient # for To: header */ VSTREAM *stream; /* open connection */ int connect_count; /* # of connect()s to retry */ struct SESSION *next; /* connect() queue linkage */ @@ -202,7 +221,11 @@ static struct sockaddr *sa; static int sa_length; static int recipients = 1; static char *defaddr; -static char *recipient; +typedef struct { + char *local; + char *at_domain; +} RECIPIENT; +static RECIPIENT *recipient; static char *sender; static char *message_data; static int message_length; @@ -216,7 +239,8 @@ static int random_delay = 0; static int fixed_delay = 0; static int talk_lmtp = 0; static char *subject = 0; -static int number_rcpts = 0; +static int global_rcpt_suffix = 0; +static int global_rcpt_done = 0; static int allow_reject = 0; static void enqueue_connect(SESSION *); @@ -238,6 +262,20 @@ static void send_quit(SESSION *); static void quit_done(int, void *); static void close_session(SESSION *); +/* make_recipient - parse recipient into localpart and at_domain */ + +static RECIPIENT *make_recipient(const char *address) +{ + RECIPIENT *rp = (RECIPIENT *) mymalloc(sizeof(*rp)); + const char *at; + + if ((at = strrchr(address, '@')) == 0) + at = address + strlen(address); + rp->local = mystrndup(address, at - address); + rp->at_domain = mystrdup(at); + return (rp); +} + /* random_interval - generate a random value in 0 .. (small) interval */ static int random_interval(int interval) @@ -655,12 +693,13 @@ static void send_rcpt(int unused_event, void *context) if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while sending recipient", exception_text(except)); - if (session->rcpt_count > 1 || number_rcpts > 0) - command(session->stream, "RCPT TO:<%d%s>", - number_rcpts ? number_rcpts++ : session->rcpt_count, - recipient); + if (global_rcpt_suffix) + command(session->stream, "RCPT TO:<%s%d%s>", + recipient->local, session->rcpt_sample = global_rcpt_done++, + recipient->at_domain); else - command(session->stream, "RCPT TO:<%s>", recipient); + command(session->stream, "RCPT TO:<%s%s>", + recipient->local, recipient->at_domain); session->rcpt_count--; session->rcpt_done++; @@ -765,10 +804,16 @@ static void data_done(int unused, void *context) mypid = getpid(); } smtp_printf(session->stream, "From: <%s>", sender); - smtp_printf(session->stream, "To: <%s>", recipient); + if (global_rcpt_suffix) + smtp_printf(session->stream, "To: <%s%d%s>", recipient->local, + session->rcpt_sample, recipient->at_domain); + else + smtp_printf(session->stream, "To: <%s%s>", + recipient->local, recipient->at_domain); smtp_printf(session->stream, "Date: %s", mydate); smtp_printf(session->stream, "Message-Id: <%04x.%04x.%04x@%s>", - mypid, vstream_fileno(session->stream), message_count, var_myhostname); + mypid, vstream_fileno(session->stream), message_count, + var_myhostname); if (subject) smtp_printf(session->stream, "Subject: %s", subject); smtp_fputs("", 0, session->stream); @@ -1021,7 +1066,7 @@ int main(int argc, char **argv) var_myhostname = optarg; break; case 'N': - number_rcpts = 1; + global_rcpt_suffix = 1; break; case 'o': send_helo_first = 0; @@ -1030,6 +1075,7 @@ int main(int argc, char **argv) case 'r': if ((recipients = atoi(optarg)) <= 0) msg_fatal("bad recipient count: %s", optarg); + global_rcpt_suffix = 1; break; case 'R': if (fixed_delay > 0) @@ -1045,7 +1091,7 @@ int main(int argc, char **argv) subject = optarg; break; case 't': - recipient = optarg; + recipient = make_recipient(optarg); break; case 'T': if ((inet_windowsize = atoi(optarg)) <= 0) @@ -1160,7 +1206,7 @@ int main(int argc, char **argv) if (sender == 0) sender = defaddr; if (recipient == 0) - recipient = defaddr; + recipient = make_recipient(defaddr); } /* |