summaryrefslogtreecommitdiffstats
path: root/src/smtp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/smtp/lmtp_params.c2
-rw-r--r--src/smtp/smtp.c136
-rw-r--r--src/smtp/smtp.h2
-rw-r--r--src/smtp/smtp_addr.c2
-rw-r--r--src/smtp/smtp_params.c2
-rw-r--r--src/smtp/smtp_proto.c2
-rw-r--r--src/smtp/smtp_sasl_glue.c4
-rw-r--r--src/smtp/smtp_tls_policy.c56
-rw-r--r--src/smtpd/Makefile.in11
-rw-r--r--src/smtpd/smtpd.c117
-rw-r--r--src/smtpd/smtpd.h4
-rw-r--r--src/smtpd/smtpd_check.c177
-rw-r--r--src/smtpd/smtpd_check_backup.ref1
-rw-r--r--src/smtpd/smtpd_deprecated.in20
-rw-r--r--src/smtpd/smtpd_deprecated.ref35
-rw-r--r--src/smtpd/smtpd_exp.ref10
-rw-r--r--src/smtpd/smtpd_sasl_glue.c4
-rw-r--r--src/smtpd/smtpd_state.c1
-rw-r--r--src/smtpstone/smtp-source.c94
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);
}
/*