diff --git a/postfix/HISTORY b/postfix/HISTORY
index 9194cb2b6..91b190cf0 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -18332,7 +18332,7 @@ Apologies for any names omitted.
src/dns/dns_strtype.c, src/dns/test_dns_lookup.c,
Cleanup: the personality switch between "smtp" and "lmtp".
- This streamlies the swicth in the SMTP/LMTP protocol, DNS
+ This streamlines the switch in the SMTP/LMTP protocol, DNS
MX lookups, and configuration parameter names in error
messages. Viktor Dukhovni. Files: src/smtp/smtp.c,
src/smtp/smtp.h, src/smtp/smtp_chat.c, src/smtp/smtp_connect.c,
@@ -18357,3 +18357,20 @@ Apologies for any names omitted.
Portability: support for NetBSD 5.x, NetBSD 6.x and DragonFly
BSD. Viktor Dukhovni. Files: makedefs, src/util/sys_defs.h.
+
+20130326
+
+ Cleanup: new module that consolidates all system-dependent
+ code to enforce read/write timeouts. This includes a final
+ workaround for MacOS X that uses poll() first, and select()
+ if that fails. This makes their /dev/urandom workaround
+ unnecessary. Files: util/poll_fd.c, util/iostuff.h. Removed:
+ util/readable.c, util/writable.c, util/read_wait.c,
+ util/write_wait.c.
+
+ Cleanup: refactor TLS digest functions, improved signature
+ for TLS session cache. Viktor Dukhovni. Files: src/smtp/smtp.c,
+ src/smtp/smtp_proto.c, src/smtpd/smtpd.c, src/tls/Makefile.in,
+ src/tls/tls.h, src/tls/tls_client.c, src/tls/tls_fprint.c,
+ src/tls/tls_level.c, src/tls/tls_misc.c, src/tls/tls_server.c,
+ src/tls/tls_verify.c, src/tlsproxy/tlsproxy.c.
diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html
index 1bde6223c..dc934bb90 100644
--- a/postfix/html/smtpd.8.html
+++ b/postfix/html/smtpd.8.html
@@ -689,7 +689,9 @@ SMTPD(8) SMTPD(8)
smtpd_log_access_permit_actions (empty)
Enable logging of the named "permit" actions in
- SMTP server access lists.
+ SMTP server access lists (by default, the SMTP
+ server logs "reject" actions but not "permit"
+ actions).
KNOWN VERSUS UNKNOWN RECIPIENT CONTROLS
As of Postfix version 2.0, the SMTP server rejects mail
diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8
index fc0f81655..d6c8bce00 100644
--- a/postfix/man/man8/smtpd.8
+++ b/postfix/man/man8/smtpd.8
@@ -569,7 +569,8 @@ What remote SMTP clients are allowed to use the XCLIENT feature.
Available in Postfix version 2.10 and later:
.IP "\fBsmtpd_log_access_permit_actions (empty)\fR"
Enable logging of the named "permit" actions in SMTP server
-access lists.
+access lists (by default, the SMTP server logs "reject" actions but
+not "permit" actions).
.SH "KNOWN VERSUS UNKNOWN RECIPIENT CONTROLS"
.na
.nf
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index f519f386d..fe4f14218 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20130325"
+#define MAIL_RELEASE_DATE "20130326"
#define MAIL_VERSION_NUMBER "2.11"
#ifdef SNAPSHOT
diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c
index 26e581c0d..841910b0b 100644
--- a/postfix/src/smtp/smtp.c
+++ b/postfix/src/smtp/smtp.c
@@ -1110,7 +1110,7 @@ static void pre_init(char *unused_name, char **unused_argv)
eckey_file = var_smtp_tls_eckey_file,
CAfile = var_smtp_tls_CAfile,
CApath = var_smtp_tls_CApath,
- fpt_dgst = var_smtp_tls_fpt_dgst);
+ mdalg = var_smtp_tls_fpt_dgst);
smtp_tls_list_init();
#else
msg_warn("TLS has been selected, but TLS support is not compiled in");
diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c
index cd5299e65..aa3fe4080 100644
--- a/postfix/src/smtp/smtp_proto.c
+++ b/postfix/src/smtp/smtp_proto.c
@@ -751,15 +751,6 @@ static int smtp_start_tls(SMTP_STATE *state)
DONT_CACHE_THIS_SESSION;
/*
- * As of Postfix 2.5, tls_client_start() tries hard to always complete
- * the TLS handshake. It records the verification and match status in the
- * resulting TLScontext. It is now up to the application to abort the TLS
- * connection if it chooses.
- *
- * XXX When tls_client_start() fails then we don't know what state the SMTP
- * connection is in, so we give up on this connection even if we are not
- * required to use TLS.
- *
* The following assumes sites that use TLS in a perverse configuration:
* multiple hosts per hostname, or even multiple hosts per IP address.
* All this without a shared TLS session cache, and they still want to
@@ -779,15 +770,28 @@ static int smtp_start_tls(SMTP_STATE *state)
* ehlo response name to build a lookup key that works for split caches
* (that announce distinct names) behind a load balancer.
*
- * XXX: The TLS library may salt the serverid with further details of the
- * protocol and cipher requirements.
+ * XXX: The TLS library will salt the serverid with further details of the
+ * protocol and cipher requirements including the server ehlo response.
+ * Deferring the helo to the digested suffix results in more predictable
+ * SSL session lookup key lengths.
+ */
+ serverid = vstring_alloc(10);
+ vstring_sprintf(serverid, "%s:%s:%u",
+ state->service, session->addr, ntohs(session->port));
+
+ /*
+ * As of Postfix 2.5, tls_client_start() tries hard to always complete
+ * the TLS handshake. It records the verification and match status in the
+ * resulting TLScontext. It is now up to the application to abort the TLS
+ * connection if it chooses.
+ *
+ * XXX When tls_client_start() fails then we don't know what state the SMTP
+ * connection is in, so we give up on this connection even if we are not
+ * required to use TLS.
*
* Large parameter lists are error-prone, so we emulate a language feature
* that C does not have natively: named parameter lists.
*/
- serverid = vstring_alloc(10);
- vstring_sprintf(serverid, "%s:%s:%u:%s", state->service, session->addr,
- ntohs(session->port), session->helo ? session->helo : "");
session->tls_context =
TLS_CLIENT_START(&tls_props,
ctx = smtp_tls_ctx,
@@ -798,12 +802,13 @@ static int smtp_start_tls(SMTP_STATE *state)
host = session->host,
namaddr = session->namaddrport,
serverid = vstring_str(serverid),
+ helo = session->helo,
protocols = session->tls_protocols,
cipher_grade = session->tls_grade,
cipher_exclusions
= vstring_str(session->tls_exclusions),
matchargv = session->tls_matchargv,
- fpt_dgst = var_smtp_tls_fpt_dgst);
+ mdalg = var_smtp_tls_fpt_dgst);
vstring_free(serverid);
if (session->tls_context == 0) {
@@ -851,7 +856,7 @@ static int smtp_start_tls(SMTP_STATE *state)
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "4.7.5"),
"Server certificate not trusted"));
- if (session->tls_level > TLS_LEV_ENCRYPT)
+ if (session->tls_level >= TLS_LEV_DANE)
if (!TLS_CERT_IS_MATCHED(session->tls_context))
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "4.7.5"),
diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c
index 8cf169131..b1bc22097 100644
--- a/postfix/src/smtpd/smtpd.c
+++ b/postfix/src/smtpd/smtpd.c
@@ -529,7 +529,8 @@
/* Available in Postfix version 2.10 and later:
/* .IP "\fBsmtpd_log_access_permit_actions (empty)\fR"
/* Enable logging of the named "permit" actions in SMTP server
-/* access lists.
+/* access lists (by default, the SMTP server logs "reject" actions but
+/* not "permit" actions).
/* KNOWN VERSUS UNKNOWN RECIPIENT CONTROLS
/* .ad
/* .fi
@@ -4206,7 +4207,7 @@ static void smtpd_start_tls(SMTPD_STATE *state)
namaddr = state->namaddr,
cipher_grade = cipher_grade,
cipher_exclusions = STR(cipher_exclusions),
- fpt_dgst = var_smtpd_tls_fpt_dgst);
+ mdalg = var_smtpd_tls_fpt_dgst);
#endif /* USE_TLSPROXY */
@@ -5148,7 +5149,7 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
var_smtpd_tls_mand_proto :
var_smtpd_tls_proto,
ask_ccert = ask_client_cert,
- fpt_dgst = var_smtpd_tls_fpt_dgst);
+ mdalg = var_smtpd_tls_fpt_dgst);
else
msg_warn("No server certs available. TLS won't be enabled");
#endif /* USE_TLSPROXY */
diff --git a/postfix/src/tls/Makefile.in b/postfix/src/tls/Makefile.in
index 57096e124..f1565f3bb 100644
--- a/postfix/src/tls/Makefile.in
+++ b/postfix/src/tls/Makefile.in
@@ -1,11 +1,11 @@
SHELL = /bin/sh
-SRCS = tls_prng_dev.c tls_prng_egd.c tls_prng_file.c \
+SRCS = tls_prng_dev.c tls_prng_egd.c tls_prng_file.c tls_fprint.c \
tls_prng_exch.c tls_stream.c tls_bio_ops.c tls_misc.c tls_dh.c \
tls_rsa.c tls_verify.c tls_certkey.c tls_session.c \
tls_client.c tls_server.c tls_scache.c tls_mgr.c tls_seed.c \
tls_level.c \
tls_proxy_clnt.c tls_proxy_print.c tls_proxy_scan.c
-OBJS = tls_prng_dev.o tls_prng_egd.o tls_prng_file.o \
+OBJS = tls_prng_dev.o tls_prng_egd.o tls_prng_file.o tls_fprint.o \
tls_prng_exch.o tls_stream.o tls_bio_ops.o tls_misc.o tls_dh.o \
tls_rsa.o tls_verify.o tls_certkey.o tls_session.o \
tls_client.o tls_server.o tls_scache.o tls_mgr.o tls_seed.o \
@@ -139,6 +139,19 @@ tls_dh.o: ../../include/vstream.h
tls_dh.o: ../../include/vstring.h
tls_dh.o: tls.h
tls_dh.o: tls_dh.c
+tls_fprint.o: ../../include/argv.h
+tls_fprint.o: ../../include/mail_params.h
+tls_fprint.o: ../../include/msg.h
+tls_fprint.o: ../../include/mymalloc.h
+tls_fprint.o: ../../include/name_code.h
+tls_fprint.o: ../../include/name_mask.h
+tls_fprint.o: ../../include/stringops.h
+tls_fprint.o: ../../include/sys_defs.h
+tls_fprint.o: ../../include/vbuf.h
+tls_fprint.o: ../../include/vstream.h
+tls_fprint.o: ../../include/vstring.h
+tls_fprint.o: tls.h
+tls_fprint.o: tls_fprint.c
tls_level.o: ../../include/argv.h
tls_level.o: ../../include/name_code.h
tls_level.o: ../../include/name_mask.h
@@ -320,7 +333,6 @@ tls_stream.o: ../../include/vstring.h
tls_stream.o: tls.h
tls_stream.o: tls_stream.c
tls_verify.o: ../../include/argv.h
-tls_verify.o: ../../include/mail_params.h
tls_verify.o: ../../include/msg.h
tls_verify.o: ../../include/mymalloc.h
tls_verify.o: ../../include/name_code.h
diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h
index 66972c509..3592f97bd 100644
--- a/postfix/src/tls/tls.h
+++ b/postfix/src/tls/tls.h
@@ -29,9 +29,10 @@
#define TLS_LEV_NONE 0 /* plain-text only */
#define TLS_LEV_MAY 1 /* wildcard */
#define TLS_LEV_ENCRYPT 2 /* encrypted connection */
-#define TLS_LEV_FPRINT 3 /* "peer" CA-less verification */
-#define TLS_LEV_VERIFY 4 /* certificate verified */
-#define TLS_LEV_SECURE 5 /* "secure" verification */
+#define TLS_LEV_DANE 3 /* "peer" CA-less verification */
+#define TLS_LEV_FPRINT 4 /* "peer" CA-less verification */
+#define TLS_LEV_VERIFY 5 /* certificate verified */
+#define TLS_LEV_SECURE 6 /* "secure" verification */
extern const NAME_CODE tls_level_table[];
@@ -97,8 +98,8 @@ typedef struct {
int log_mask; /* What to log */
int session_reused; /* this session was reused */
int am_server; /* Are we an SSL server or client? */
+ const char *mdalg; /* default message digest algorithm */
/* Built-in vs external SSL_accept/read/write/shutdown support. */
- char *fpt_dgst; /* Certificate fingerprint digest */
VSTREAM *stream; /* Blocking-mode SMTP session */
} TLS_SESS_STATE;
@@ -234,7 +235,7 @@ typedef struct {
const char *eckey_file;
const char *CAfile;
const char *CApath;
- const char *fpt_dgst; /* Fingerprint digest algorithm */
+ const char *mdalg; /* default message digest algorithm */
} TLS_CLIENT_INIT_PROPS;
typedef struct {
@@ -246,11 +247,12 @@ typedef struct {
const char *host; /* MX hostname */
const char *namaddr; /* nam[addr] for logging */
const char *serverid; /* Session cache key */
+ const char *helo; /* Server name from EHLO response */
const char *protocols; /* Enabled protocols */
const char *cipher_grade; /* Minimum cipher grade */
const char *cipher_exclusions; /* Ciphers to exclude */
const ARGV *matchargv; /* Cert match patterns */
- const char *fpt_dgst; /* Fingerprint digest algorithm */
+ const char *mdalg; /* default message digest algorithm */
} TLS_CLIENT_START_PROPS;
extern TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *);
@@ -267,11 +269,11 @@ extern TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *);
((props)->a12), ((props)->a13), (props)))
#define TLS_CLIENT_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \
- a10, a11, a12, a13) \
+ a10, a11, a12, a13, a14) \
tls_client_start((((props)->a1), ((props)->a2), ((props)->a3), \
((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
- ((props)->a12), ((props)->a13), (props)))
+ ((props)->a12), ((props)->a13), ((props)->a14), (props)))
/*
* tls_server.c
@@ -296,7 +298,7 @@ typedef struct {
const char *dh1024_param_file;
const char *dh512_param_file;
int ask_ccert;
- const char *fpt_dgst; /* Fingerprint digest algorithm */
+ const char *mdalg; /* default message digest algorithm */
} TLS_SERVER_INIT_PROPS;
typedef struct {
@@ -309,7 +311,7 @@ typedef struct {
const char *namaddr; /* Client nam[addr] for logging */
const char *cipher_grade;
const char *cipher_exclusions;
- const char *fpt_dgst; /* Fingerprint digest algorithm */
+ const char *mdalg; /* default message digest algorithm */
} TLS_SERVER_START_PROPS;
extern TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *);
@@ -397,9 +399,15 @@ extern RSA *tls_tmp_rsa_cb(SSL *, int, int);
extern char *tls_peer_CN(X509 *, const TLS_SESS_STATE *);
extern char *tls_issuer_CN(X509 *, const TLS_SESS_STATE *);
extern const char *tls_dns_name(const GENERAL_NAME *, const TLS_SESS_STATE *);
+extern int tls_verify_certificate_callback(int, X509_STORE_CTX *);
+
+ /*
+ * tls_fprint.c
+ */
extern char *tls_fingerprint(X509 *, const char *);
extern char *tls_pkey_fprint(X509 *, const char *);
-extern int tls_verify_certificate_callback(int, X509_STORE_CTX *);
+extern char *tls_serverid_digest(const TLS_CLIENT_START_PROPS *, long,
+ const char *);
/*
* tls_certkey.c
@@ -423,6 +431,7 @@ extern long tls_bug_bits(void);
extern void tls_print_errors(void);
extern void tls_info_callback(const SSL *, int, int);
extern long tls_bio_dump_cb(BIO *, int, const char *, int, long, long);
+extern int tls_validate_digest(const char *);
/*
* tls_seed.c
diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c
index 2252b00fa..19582ee6e 100644
--- a/postfix/src/tls/tls_client.c
+++ b/postfix/src/tls/tls_client.c
@@ -168,7 +168,7 @@ static SSL_SESSION *load_clnt_session(TLS_SESS_STATE *TLScontext)
* Prepare the query.
*/
if (TLScontext->log_mask & TLS_LOG_CACHE)
- /* serverid already contains namaddrport information */
+ /* serverid contains transport:addr:port information */
msg_info("looking for session %s in %s cache",
TLScontext->serverid, TLScontext->cache_type);
@@ -190,7 +190,7 @@ static SSL_SESSION *load_clnt_session(TLS_SESS_STATE *TLScontext)
session = tls_session_activate(STR(session_data), LEN(session_data));
if (session) {
if (TLScontext->log_mask & TLS_LOG_CACHE)
- /* serverid already contains namaddrport information */
+ /* serverid contains transport:addr:port information */
msg_info("reloaded session %s from %s cache",
TLScontext->serverid, TLScontext->cache_type);
}
@@ -230,7 +230,7 @@ static int new_client_session_cb(SSL *ssl, SSL_SESSION *session)
myname);
if (TLScontext->log_mask & TLS_LOG_CACHE)
- /* serverid already contains namaddrport information */
+ /* serverid contains transport:addr:port information */
msg_info("save session %s to %s cache",
TLScontext->serverid, TLScontext->cache_type);
@@ -278,7 +278,7 @@ static void uncache_session(SSL_CTX *ctx, TLS_SESS_STATE *TLScontext)
return;
if (TLScontext->log_mask & TLS_LOG_CACHE)
- /* serverid already contains namaddrport information */
+ /* serverid contains transport:addr:port information */
msg_info("remove session %s from client cache", TLScontext->serverid);
tls_mgr_delete(TLScontext->cache_type, TLScontext->serverid);
@@ -292,8 +292,6 @@ TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props)
int cachable;
SSL_CTX *client_ctx;
TLS_APPL_STATE *app_ctx;
- const EVP_MD *md_alg;
- unsigned int md_len;
int log_mask;
/*
@@ -339,18 +337,8 @@ TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props)
* If the administrator specifies an unsupported digest algorithm, fail
* now, rather than in the middle of a TLS handshake.
*/
- if ((md_alg = EVP_get_digestbyname(props->fpt_dgst)) == 0) {
- msg_warn("Digest algorithm \"%s\" not found: disabling TLS support",
- props->fpt_dgst);
- return (0);
- }
-
- /*
- * Sanity check: Newer shared libraries may use larger digests.
- */
- if ((md_len = EVP_MD_size(md_alg)) > EVP_MAX_MD_SIZE) {
- msg_warn("Digest algorithm \"%s\" output size %u too large:"
- " disabling TLS support", props->fpt_dgst, md_len);
+ if (!tls_validate_digest(props->mdalg)) {
+ msg_warn("disabling TLS support");
return (0);
}
@@ -732,8 +720,8 @@ static void verify_extract_print(TLS_SESS_STATE *TLScontext, X509 *peercert,
char **cpp;
/* Non-null by contract */
- TLScontext->peer_fingerprint = tls_fingerprint(peercert, props->fpt_dgst);
- TLScontext->peer_pkey_fprint = tls_pkey_fprint(peercert, props->fpt_dgst);
+ TLScontext->peer_fingerprint = tls_fingerprint(peercert, props->mdalg);
+ TLScontext->peer_pkey_fprint = tls_pkey_fprint(peercert, props->mdalg);
/*
* Compare the fingerprint against each acceptable value, ignoring
@@ -765,7 +753,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
X509 *peercert;
TLS_SESS_STATE *TLScontext;
TLS_APPL_STATE *app_ctx = props->ctx;
- VSTRING *myserverid;
+ char *myserverid;
int log_mask = app_ctx->log_mask;
/*
@@ -781,19 +769,8 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
/*
* First make sure we have valid protocol and cipher parameters
*
- * The cipherlist will be applied to the global SSL context, where it can be
- * repeatedly reset if necessary, but the protocol restrictions will be
- * is applied to the SSL connection, because protocol restrictions in the
- * global context cannot be cleared.
- */
-
- /*
- * OpenSSL will ignore cached sessions that use the wrong protocol. So we
- * do not need to filter out cached sessions with the "wrong" protocol,
- * rather OpenSSL will simply negotiate a new session.
- *
- * Still, we salt the session lookup key with the protocol list, so that
- * sessions found in the cache are always acceptable.
+ * Per-session protocol restrictions must be applied to the SSL connection,
+ * as restrictions in the global context cannot be cleared.
*/
protomask = tls_protocol_mask(props->protocols);
if (protomask == TLS_PROTOCOL_INVALID) {
@@ -802,35 +779,39 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
props->namaddr, props->protocols);
return (0);
}
- myserverid = vstring_alloc(100);
- vstring_sprintf_append(myserverid, "%s&p=%d", props->serverid, protomask);
/*
* Per session cipher selection for sessions with mandatory encryption
*
+ * The cipherlist is applied to the global SSL context, since it is likely
+ * to stay the same between connections, so we make use of a 1-element
+ * cache to return the same result for identical inputs.
+ */
+ cipher_list = tls_set_ciphers(app_ctx, "TLS", props->cipher_grade,
+ props->cipher_exclusions);
+ if (cipher_list == 0) {
+ msg_warn("%s: %s: aborting TLS session",
+ props->namaddr, vstring_str(app_ctx->why));
+ return (0);
+ }
+ if (log_mask & TLS_LOG_VERBOSE)
+ msg_info("%s: TLS cipher list \"%s\"", props->namaddr, cipher_list);
+
+ /*
+ * OpenSSL will ignore cached sessions that use the wrong protocol. So we
+ * do not need to filter out cached sessions with the "wrong" protocol,
+ * rather OpenSSL will simply negotiate a new session.
+ *
+ * We salt the session lookup key with the protocol list, so that sessions
+ * found in the cache are plausibly acceptable.
+ *
* By the time a TLS client is negotiating ciphers it has already offered to
* re-use a session, it is too late to renege on the offer. So we must
* not attempt to re-use sessions whose ciphers are too weak. We salt the
* session lookup key with the cipher list, so that sessions found in the
* cache are always acceptable.
*/
- cipher_list = tls_set_ciphers(app_ctx, "TLS", props->cipher_grade,
- props->cipher_exclusions);
- if (cipher_list == 0) {
- msg_warn("%s: %s: aborting TLS session",
- props->namaddr, vstring_str(app_ctx->why));
- vstring_free(myserverid);
- return (0);
- }
- if (log_mask & TLS_LOG_VERBOSE)
- msg_info("%s: TLS cipher list \"%s\"", props->namaddr, cipher_list);
- vstring_sprintf_append(myserverid, "&c=%s", cipher_list);
-
- /*
- * Finally, salt the session key with the OpenSSL library version,
- * (run-time, rather than compile-time, just in case that matters).
- */
- vstring_sprintf_append(myserverid, "&l=%ld", (long) SSLeay());
+ myserverid = tls_serverid_digest(props, protomask, cipher_list);
/*
* Allocate a new TLScontext for the new connection and get an SSL
@@ -843,8 +824,9 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
TLScontext = tls_alloc_sess_context(log_mask, props->namaddr);
TLScontext->cache_type = app_ctx->cache_type;
- TLScontext->serverid = vstring_export(myserverid);
+ TLScontext->serverid = myserverid;
TLScontext->stream = props->stream;
+ TLScontext->mdalg = props->mdalg;
if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) {
msg_warn("Could not allocate 'TLScontext->con' with SSL_new()");
diff --git a/postfix/src/tls/tls_fprint.c b/postfix/src/tls/tls_fprint.c
new file mode 100644
index 000000000..f7c10eaf9
--- /dev/null
+++ b/postfix/src/tls/tls_fprint.c
@@ -0,0 +1,250 @@
+/*++
+/* NAME
+/* tls_fprint 3
+/* SUMMARY
+/* Digests fingerprints and all that.
+/* SYNOPSIS
+/* #include
+/*
+/* char *tls_serverid_digest(props, protomask, ciphers);
+/* const TLS_CLIENT_START_PROPS *props;
+/* long protomask;
+/* const char *ciphers;
+/*
+/* char *tls_fingerprint(peercert, mdalg)
+/* X509 *peercert;
+/* const char *mdalg;
+/*
+/* char *tls_pkey_fprint(peercert, mdalg)
+/* X509 *peercert;
+/* const char *mdalg;
+/* DESCRIPTION
+/* tls_fingerprint() returns a fingerprint of the the given
+/* certificate using the requested message digest. Panics if the
+/* (previously verified) digest algorithm is not found. The return
+/* value is dynamically allocated with mymalloc(), and the caller
+/* must eventually free it with myfree().
+/*
+/* tls_pkey_fprint() returns a public-key fingerprint; in all
+/* other respects the function behaves as tls_fingerprint().
+/* The var_tls_bc_pkey_fprint variable enables an incorrect
+/* algorithm that was used in Postfix versions 2.9.[0-5].
+/*
+/* tls_serverid_digest() suffixes props->serverid computed by the SMTP
+/* client with a digest of additional parameters needed to ensure
+/* that re-used sessions are more likely to be reused and will satisfy
+/* all protocol and security requirements. The caller should pass
+/* the result to myfree().
+/*
+/* Arguments:
+/* .IP peercert
+/* Server or client X.509 certificate.
+/* .IP mdalg
+/* Name of a message digest algorithm suitable for computing secure
+/* (1st pre-image resistant) message digests of certificates. For now,
+/* md5, sha1, or member of SHA-2 family if supported by OpenSSL.
+/* .IP props
+/* The client start properties for the session, which include the
+/* initial serverid from the SMTP client.
+/* .IP protomask
+/* The mask of protocol exclusions.
+/* .IP ciphers
+/* The SSL client cipherlist.
+/* LICENSE
+/* .ad
+/* .fi
+/* This software is free. You can do with it whatever you want.
+/* The original author kindly requests that you acknowledge
+/* the use of his software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Viktor Dukhovni
+/*--*/
+
+/* System library. */
+
+#include
+#include
+
+#ifdef USE_TLS
+#include
+
+/* Utility library. */
+
+#include
+#include
+#include
+
+/* Global library. */
+
+#include
+
+/* TLS library. */
+
+#define TLS_INTERNAL
+#include
+
+/* Application-specific. */
+
+static const char hexcodes[] = "0123456789ABCDEF";
+
+#define chknonzero(ret) (ok &= ((ret) ? 1 : 0))
+#define digestpl(p, l) chknonzero(EVP_DigestUpdate(mdctx, (char *)(p), (l)))
+#define digestptr(p) digestpl((p), sizeof(*(p)))
+#define digeststr(s) digestpl((s), strlen(s)+1)
+
+/* tls_serverid_digest - suffix props->serverid with parameter digest */
+
+char *tls_serverid_digest(const TLS_CLIENT_START_PROPS *props, long protomask,
+ const char *ciphers)
+{
+ EVP_MD_CTX *mdctx;
+ const EVP_MD *md;
+ const char *mdalg;
+ unsigned char md_buf[EVP_MAX_MD_SIZE];
+ unsigned int md_len;
+ int ok = 1;
+ int i;
+ long sslversion;
+ VSTRING *result;
+
+ /*
+ * Try to use sha256: our serverid choice should be strong enough to
+ * resist 2nd-preimage attacks with a difficulty comparable to that of
+ * DANE TLSA digests. Failing that, we compute serverid digests with the
+ * default digest, but DANE requires sha256 and sha512, so if we must
+ * fall back to our default digest, DANE support won't be available. We
+ * panic if the fallback algorithm is not available, as it was verified
+ * available in tls_client_init() and must not simply vanish.
+ */
+ if ((md = EVP_get_digestbyname(mdalg = "sha256")) == 0
+ && (md = EVP_get_digestbyname(mdalg = props->mdalg)) == 0)
+ msg_panic("digest algorithm \"%s\" not found", mdalg);
+
+ /* Salt the session lookup key with the OpenSSL runtime version. */
+ sslversion = SSLeay();
+
+ mdctx = EVP_MD_CTX_create();
+ chknonzero(EVP_DigestInit_ex(mdctx, md, NULL));
+ digeststr(props->helo ? props->helo : "");
+ digestptr(&sslversion);
+ digestptr(&protomask);
+ digeststr(ciphers);
+ chknonzero(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
+ EVP_MD_CTX_destroy(mdctx);
+ if (!ok)
+ msg_fatal("error computing %s message digest", mdalg);
+
+ /* Check for OpenSSL contract violation */
+ if (md_len > EVP_MAX_MD_SIZE)
+ msg_panic("unexpectedly large %s digest size: %u", mdalg, md_len);
+
+ /* Append the digest to the serverid */
+ result = vstring_alloc(strlen(props->serverid) + 1 + 2 * md_len);
+ vstring_strcpy(result, props->serverid);
+ VSTRING_ADDCH(result, ':');
+ for (i = 0; i < md_len; i++) {
+ VSTRING_ADDCH(result, hexcodes[(md_buf[i] & 0xf0) >> 4U]);
+ VSTRING_ADDCH(result, hexcodes[(md_buf[i] & 0x0f)]);
+ }
+ VSTRING_TERMINATE(result);
+ return (vstring_export(result));
+}
+
+/* tls_fprint - compute and encode digest of DER-encoded object */
+
+static char *tls_fprint(const char *buf, int len, const char *mdalg)
+{
+ EVP_MD_CTX *mdctx;
+ const EVP_MD *md;
+ unsigned char md_buf[EVP_MAX_MD_SIZE];
+ unsigned int md_len;
+ int i;
+ int ok = 1;
+ char *result = 0;
+
+ /* Previously available in "init" routine. */
+ if ((md = EVP_get_digestbyname(mdalg)) == 0)
+ msg_panic("digest algorithm \"%s\" not found", mdalg);
+
+ mdctx = EVP_MD_CTX_create();
+ chknonzero(EVP_DigestInit_ex(mdctx, md, NULL));
+ digestpl(buf, len);
+ chknonzero(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
+ EVP_MD_CTX_destroy(mdctx);
+ if (!ok)
+ msg_fatal("error computing %s message digest", mdalg);
+
+ /* Check for OpenSSL contract violation */
+ if (md_len > EVP_MAX_MD_SIZE || md_len >= INT_MAX / 3)
+ msg_panic("unexpectedly large %s digest size: %u", mdalg, md_len);
+
+ result = mymalloc(md_len * 3);
+ for (i = 0; i < md_len; i++) {
+ result[i * 3] = hexcodes[(md_buf[i] & 0xf0) >> 4U];
+ result[(i * 3) + 1] = hexcodes[(md_buf[i] & 0x0f)];
+ result[(i * 3) + 2] = (i + 1 != md_len) ? ':' : '\0';
+ }
+ return (result);
+}
+
+/* tls_fingerprint - extract certificate fingerprint */
+
+char *tls_fingerprint(X509 *peercert, const char *mdalg)
+{
+ int len;
+ char *buf;
+ char *buf2;
+ char *result;
+
+ len = i2d_X509(peercert, NULL);
+ buf2 = buf = mymalloc(len);
+ i2d_X509(peercert, (unsigned char **) &buf2);
+ if (buf2 - buf != len)
+ msg_panic("i2d_X509 invalid result length");
+
+ result = tls_fprint(buf, len, mdalg);
+ myfree(buf);
+
+ return (result);
+}
+
+/* tls_pkey_fprint - extract public key fingerprint from certificate */
+
+char *tls_pkey_fprint(X509 *peercert, const char *mdalg)
+{
+ if (var_tls_bc_pkey_fprint) {
+ const char *myname = "tls_pkey_fprint";
+ ASN1_BIT_STRING *key;
+ char *result;
+
+ key = X509_get0_pubkey_bitstr(peercert);
+ if (key == 0)
+ msg_fatal("%s: error extracting legacy public-key fingerprint: %m",
+ myname);
+
+ result = tls_fprint((char *) key->data, key->length, mdalg);
+ return (result);
+ } else {
+ int len;
+ char *buf;
+ char *buf2;
+ char *result;
+
+ len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(peercert), NULL);
+ buf2 = buf = mymalloc(len);
+ i2d_X509_PUBKEY(X509_get_X509_PUBKEY(peercert), (unsigned char **) &buf2);
+ if (buf2 - buf != len)
+ msg_panic("i2d_X509_PUBKEY invalid result length");
+
+ result = tls_fprint(buf, len, mdalg);
+ myfree(buf);
+ return (result);
+ }
+}
+
+#endif
diff --git a/postfix/src/tls/tls_level.c b/postfix/src/tls/tls_level.c
index 32063200e..d87ec0cd9 100644
--- a/postfix/src/tls/tls_level.c
+++ b/postfix/src/tls/tls_level.c
@@ -66,6 +66,9 @@ const NAME_CODE tls_level_table[] = {
"none", TLS_LEV_NONE,
"may", TLS_LEV_MAY,
"encrypt", TLS_LEV_ENCRYPT,
+#if 0 /* Not yet */
+ "dane", TLS_LEV_DANE,
+#endif
"fingerprint", TLS_LEV_FPRINT,
"verify", TLS_LEV_VERIFY,
"secure", TLS_LEV_SECURE,
diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c
index d09bb3225..c09e7f189 100644
--- a/postfix/src/tls/tls_misc.c
+++ b/postfix/src/tls/tls_misc.c
@@ -72,6 +72,9 @@
/* int tls_log_mask(log_param, log_level)
/* const char *log_param;
/* const char *log_level;
+/*
+/* int tls_validate_digest(dgst)
+/* const char *dgst;
/* DESCRIPTION
/* This module implements routines that support the TLS client
/* and server internals.
@@ -136,6 +139,9 @@
/* tls_log_mask() converts a TLS log_level value from string
/* to mask. The main.cf parameter name is passed along for
/* diagnostics.
+/*
+/* tls_validate_digest() returns non-zero if the named digest
+/* is usable and zero otherwise.
/* LICENSE
/* .ad
/* .fi
@@ -740,7 +746,7 @@ TLS_SESS_STATE *tls_alloc_sess_context(int log_mask, const char *namaddr)
TLScontext->cipher_name = 0;
TLScontext->log_mask = log_mask;
TLScontext->namaddr = lowercase(mystrdup(namaddr));
- TLScontext->fpt_dgst = 0;
+ TLScontext->mdalg = 0; /* Alias for props->mdalg */
return (TLScontext);
}
@@ -771,8 +777,6 @@ void tls_free_context(TLS_SESS_STATE *TLScontext)
myfree(TLScontext->peer_fingerprint);
if (TLScontext->peer_pkey_fprint)
myfree(TLScontext->peer_pkey_fprint);
- if (TLScontext->fpt_dgst)
- myfree(TLScontext->fpt_dgst);
myfree((char *) TLScontext);
}
@@ -1054,6 +1058,31 @@ long tls_bio_dump_cb(BIO *bio, int cmd, const char *argp, int argi,
return (ret);
}
+int tls_validate_digest(const char *dgst)
+{
+ const EVP_MD *md_alg;
+ unsigned int md_len;
+
+ /*
+ * If the administrator specifies an unsupported digest algorithm, fail
+ * now, rather than in the middle of a TLS handshake.
+ */
+ if ((md_alg = EVP_get_digestbyname(dgst)) == 0) {
+ msg_warn("Digest algorithm \"%s\" not found", dgst);
+ return (0);
+ }
+
+ /*
+ * Sanity check: Newer shared libraries may use larger digests.
+ */
+ if ((md_len = EVP_MD_size(md_alg)) > EVP_MAX_MD_SIZE) {
+ msg_warn("Digest algorithm \"%s\" output size %u too large",
+ dgst, md_len);
+ return (0);
+ }
+ return (1);
+}
+
#else
/*
diff --git a/postfix/src/tls/tls_server.c b/postfix/src/tls/tls_server.c
index f0ebf669c..c1bf81740 100644
--- a/postfix/src/tls/tls_server.c
+++ b/postfix/src/tls/tls_server.c
@@ -286,8 +286,6 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props)
int cachable;
int protomask;
TLS_APPL_STATE *app_ctx;
- const EVP_MD *md_alg;
- unsigned int md_len;
int log_mask;
/*
@@ -344,18 +342,8 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props)
* If the administrator specifies an unsupported digest algorithm, fail
* now, rather than in the middle of a TLS handshake.
*/
- if ((md_alg = EVP_get_digestbyname(props->fpt_dgst)) == 0) {
- msg_warn("Digest algorithm \"%s\" not found: disabling TLS support",
- props->fpt_dgst);
- return (0);
- }
-
- /*
- * Sanity check: Newer shared libraries may use larger digests.
- */
- if ((md_len = EVP_MD_size(md_alg)) > EVP_MAX_MD_SIZE) {
- msg_warn("Digest algorithm \"%s\" output size %u too large:"
- " disabling TLS support", props->fpt_dgst, md_len);
+ if (!tls_validate_digest(props->mdalg)) {
+ msg_warn("disabling TLS support");
return (0);
}
@@ -643,9 +631,8 @@ TLS_SESS_STATE *tls_server_start(const TLS_SERVER_START_PROPS *props)
TLScontext->serverid = mystrdup(props->serverid);
TLScontext->am_server = 1;
-
- TLScontext->fpt_dgst = mystrdup(props->fpt_dgst);
TLScontext->stream = props->stream;
+ TLScontext->mdalg = props->mdalg;
ERR_clear_error();
if ((TLScontext->con = (SSL *) SSL_new(app_ctx->ssl_ctx)) == 0) {
@@ -777,10 +764,8 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext)
}
TLScontext->peer_CN = tls_peer_CN(peer, TLScontext);
TLScontext->issuer_CN = tls_issuer_CN(peer, TLScontext);
- TLScontext->peer_fingerprint =
- tls_fingerprint(peer, TLScontext->fpt_dgst);
- TLScontext->peer_pkey_fprint =
- tls_pkey_fprint(peer, TLScontext->fpt_dgst);
+ TLScontext->peer_fingerprint = tls_fingerprint(peer, TLScontext->mdalg);
+ TLScontext->peer_pkey_fprint = tls_pkey_fprint(peer, TLScontext->mdalg);
if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_PEERCERT)) {
msg_info("%s: subject_CN=%s, issuer=%s, fingerprint=%s"
@@ -795,6 +780,7 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext)
TLScontext->peer_CN = mystrdup("");
TLScontext->issuer_CN = mystrdup("");
TLScontext->peer_fingerprint = mystrdup("");
+ TLScontext->peer_pkey_fprint = mystrdup("");
}
/*
diff --git a/postfix/src/tls/tls_verify.c b/postfix/src/tls/tls_verify.c
index 9d909fe63..50dddc8cf 100644
--- a/postfix/src/tls/tls_verify.c
+++ b/postfix/src/tls/tls_verify.c
@@ -19,13 +19,6 @@
/* const GENERAL_NAME *gn;
/* TLS_SESS_STATE *TLScontext;
/*
-/* char *tls_fingerprint(peercert, dgst)
-/* X509 *peercert;
-/* const char *dgst;
-/*
-/* char *tls_pkey_fprint(peercert, dgst)
-/* X509 *peercert;
-/* const char *dgst;
/*
/* int tls_verify_certificate_callback(ok, ctx)
/* int ok;
@@ -48,17 +41,6 @@
/* are found, a null string is returned instead. Further sanity
/* checks may be added if the need arises.
/*
-/* tls_fingerprint() returns a fingerprint of the the given
-/* certificate using the requested message digest. Panics if the
-/* (previously verified) digest algorithm is not found. The return
-/* value is dynamically allocated with mymalloc(), and the caller
-/* must eventually free it with myfree().
-/*
-/* tls_pkey_fprint() returns a public-key fingerprint; in all
-/* other respects the function behaves as tls_fingerprint().
-/* The var_tls_bc_pkey_fprint variable enables an incorrect
-/* algorithm that was used in Postfix versions 2.9.[0-5].
-/*
/* tls_verify_callback() is called several times (directly or
/* indirectly) from crypto/x509/x509_vfy.c. It is called as
/* a final check, and if it returns "0", the handshake is
@@ -99,10 +81,6 @@
/* to be decoded and checked for validity.
/* .IP peercert
/* Server or client X.509 certificate.
-/* .IP dgst
-/* Name of a message digest algorithm suitable for computing secure
-/* (1st pre-image resistant) message digests of certificates. For now,
-/* md5, sha1, or member of SHA-2 family if supported by OpenSSL.
/* .IP TLScontext
/* Server or client context for warning messages.
/* DIAGNOSTICS
@@ -149,19 +127,11 @@
#include
#include
-/* Global library. */
-
-#include
-
/* TLS library. */
#define TLS_INTERNAL
#include
-/* Application-specific. */
-
-static const char hexcodes[] = "0123456789ABCDEF";
-
/* tls_verify_certificate_callback - verify peer certificate info */
int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx)
@@ -503,96 +473,4 @@ char *tls_issuer_CN(X509 *peer, const TLS_SESS_STATE *TLScontext)
return (cn ? cn : mystrdup(""));
}
-/* tls_fprint - compute and encode digest of DER-encoded object */
-
-static char *tls_fprint(const char *buf, int len, const char *dgst)
-{
- const char *myname = "tls_fprint";
- EVP_MD_CTX *mdctx;
- const EVP_MD *md_alg;
- unsigned char md_buf[EVP_MAX_MD_SIZE];
- unsigned int md_len;
- int i;
- char *result = 0;
-
- /* Previously available in "init" routine. */
- if ((md_alg = EVP_get_digestbyname(dgst)) == 0)
- msg_panic("%s: digest algorithm \"%s\" not found", myname, dgst);
-
- mdctx = EVP_MD_CTX_create();
- if (EVP_DigestInit_ex(mdctx, md_alg, NULL) == 0
- || EVP_DigestUpdate(mdctx, buf, len) == 0
- || EVP_DigestFinal_ex(mdctx, md_buf, &md_len) == 0)
- msg_fatal("%s: error computing %s message digest", myname, dgst);
- EVP_MD_CTX_destroy(mdctx);
-
- /* Check for OpenSSL contract violation */
- if (md_len > EVP_MAX_MD_SIZE || md_len >= INT_MAX / 3)
- msg_panic("%s: unexpectedly large %s digest size: %u",
- myname, dgst, md_len);
-
- result = mymalloc(md_len * 3);
- for (i = 0; i < md_len; i++) {
- result[i * 3] = hexcodes[(md_buf[i] & 0xf0) >> 4U];
- result[(i * 3) + 1] = hexcodes[(md_buf[i] & 0x0f)];
- result[(i * 3) + 2] = (i + 1 != md_len) ? ':' : '\0';
- }
- return (result);
-}
-
-/* tls_fingerprint - extract certificate fingerprint */
-
-char *tls_fingerprint(X509 *peercert, const char *dgst)
-{
- int len;
- char *buf;
- char *buf2;
- char *result;
-
- len = i2d_X509(peercert, NULL);
- buf2 = buf = mymalloc(len);
- i2d_X509(peercert, (unsigned char **) &buf2);
- if (buf2 - buf != len)
- msg_panic("i2d_X509 invalid result length");
-
- result = tls_fprint(buf, len, dgst);
- myfree(buf);
-
- return (result);
-}
-
-/* tls_pkey_fprint - extract public key fingerprint from certificate */
-
-char *tls_pkey_fprint(X509 *peercert, const char *dgst)
-{
- if (var_tls_bc_pkey_fprint) {
- const char *myname = "tls_pkey_fprint";
- ASN1_BIT_STRING *key;
- char *result;
-
- key = X509_get0_pubkey_bitstr(peercert);
- if (key == 0)
- msg_fatal("%s: error extracting legacy public-key fingerprint: %m",
- myname);
-
- result = tls_fprint((char *) key->data, key->length, dgst);
- return (result);
- } else {
- int len;
- char *buf;
- char *buf2;
- char *result;
-
- len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(peercert), NULL);
- buf2 = buf = mymalloc(len);
- i2d_X509_PUBKEY(X509_get_X509_PUBKEY(peercert), (unsigned char **) &buf2);
- if (buf2 - buf != len)
- msg_panic("i2d_X509_PUBKEY invalid result length");
-
- result = tls_fprint(buf, len, dgst);
- myfree(buf);
- return (result);
- }
-}
-
#endif
diff --git a/postfix/src/tlsproxy/tlsproxy.c b/postfix/src/tlsproxy/tlsproxy.c
index 78d15a8f0..e2e1d348f 100644
--- a/postfix/src/tlsproxy/tlsproxy.c
+++ b/postfix/src/tlsproxy/tlsproxy.c
@@ -698,7 +698,7 @@ static void tlsp_start_tls(TLSP_STATE *state)
namaddr = state->remote_endpt,
cipher_grade = cipher_grade,
cipher_exclusions = STR(cipher_exclusions),
- fpt_dgst = var_tlsp_tls_fpt_dgst);
+ mdalg = var_tlsp_tls_fpt_dgst);
if (state->tls_context == 0) {
tlsp_state_free(state);
@@ -993,7 +993,7 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
var_tlsp_tls_mand_proto :
var_tlsp_tls_proto,
ask_ccert = ask_client_cert,
- fpt_dgst = var_tlsp_tls_fpt_dgst);
+ mdalg = var_tlsp_tls_fpt_dgst);
else
msg_warn("No server certs available. TLS can't be enabled");
diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in
index 14216c62b..7be09a311 100644
--- a/postfix/src/util/Makefile.in
+++ b/postfix/src/util/Makefile.in
@@ -18,7 +18,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
mymalloc.c myrand.c mystrtok.c name_code.c name_mask.c netstring.c \
neuter.c non_blocking.c nvtable.c open_as.c open_limit.c open_lock.c \
peekfd.c percentm.c posix_signals.c printable.c rand_sleep.c \
- read_wait.c readable.c readlline.c ring.c safe_getenv.c safe_open.c \
+ readlline.c ring.c safe_getenv.c safe_open.c \
sane_accept.c sane_connect.c sane_link.c sane_rename.c \
sane_socketpair.c sane_time.c scan_dir.c set_eugid.c set_ugid.c \
sigdelay.c skipblanks.c sock_addr.c spawn_command.c split_at.c \
@@ -28,14 +28,15 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
translit.c trimblanks.c unescape.c unix_connect.c unix_listen.c \
unix_recv_fd.c unix_send_fd.c unix_trigger.c unsafe.c uppercase.c \
username.c valid_hostname.c vbuf.c vbuf_print.c vstream.c \
- vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \
- write_buf.c write_wait.c sane_basename.c format_tv.c allspace.c \
+ vstream_popen.c vstring.c vstring_vstream.c watchdog.c \
+ write_buf.c sane_basename.c format_tv.c allspace.c \
allascii.c load_file.c killme_after.c vstream_tweak.c \
pass_trigger.c edit_file.c inet_windowsize.c \
unix_pass_fd_fix.c dict_cache.c valid_utf_8.c dict_thash.c \
ip_match.c nbbio.c base32_code.c dict_test.c \
dict_fail.c msg_rate_delay.c dict_surrogate.c warn_stat.c \
- dict_sockmap.c line_number.c recv_pass_attr.c pass_accept.c
+ dict_sockmap.c line_number.c recv_pass_attr.c pass_accept.c \
+ poll_fd.c
OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@@ -55,7 +56,7 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
mymalloc.o myrand.o mystrtok.o name_code.o name_mask.o netstring.o \
neuter.o non_blocking.o nvtable.o open_as.o open_limit.o open_lock.o \
peekfd.o percentm.o posix_signals.o printable.o rand_sleep.o \
- read_wait.o readable.o readlline.o ring.o safe_getenv.o safe_open.o \
+ readlline.o ring.o safe_getenv.o safe_open.o \
sane_accept.o sane_connect.o sane_link.o sane_rename.o \
sane_socketpair.o sane_time.o scan_dir.o set_eugid.o set_ugid.o \
sigdelay.o skipblanks.o sock_addr.o spawn_command.o split_at.o \
@@ -65,14 +66,15 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
translit.o trimblanks.o unescape.o unix_connect.o unix_listen.o \
unix_recv_fd.o unix_send_fd.o unix_trigger.o unsafe.o uppercase.o \
username.o valid_hostname.o vbuf.o vbuf_print.o vstream.o \
- vstream_popen.o vstring.o vstring_vstream.o watchdog.o writable.o \
- write_buf.o write_wait.o sane_basename.o format_tv.o allspace.o \
+ vstream_popen.o vstring.o vstring_vstream.o watchdog.o \
+ write_buf.o sane_basename.o format_tv.o allspace.o \
allascii.o load_file.o killme_after.o vstream_tweak.o \
pass_trigger.o edit_file.o inet_windowsize.o \
unix_pass_fd_fix.o dict_cache.o valid_utf_8.o dict_thash.o \
ip_match.o nbbio.o base32_code.o dict_test.o \
dict_fail.o msg_rate_delay.o dict_surrogate.o warn_stat.o \
- dict_sockmap.o line_number.o recv_pass_attr.o pass_accept.o
+ dict_sockmap.o line_number.o recv_pass_attr.o pass_accept.o \
+ poll_fd.o
HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \
dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \
@@ -1644,6 +1646,10 @@ percentm.o: percentm.h
percentm.o: sys_defs.h
percentm.o: vbuf.h
percentm.o: vstring.h
+poll_fd.o: iostuff.h
+poll_fd.o: msg.h
+poll_fd.o: poll_fd.c
+poll_fd.o: sys_defs.h
posix_signals.o: posix_signals.c
posix_signals.o: posix_signals.h
posix_signals.o: sys_defs.h
@@ -1657,14 +1663,6 @@ rand_sleep.o: msg.h
rand_sleep.o: myrand.h
rand_sleep.o: rand_sleep.c
rand_sleep.o: sys_defs.h
-read_wait.o: iostuff.h
-read_wait.o: msg.h
-read_wait.o: read_wait.c
-read_wait.o: sys_defs.h
-readable.o: iostuff.h
-readable.o: msg.h
-readable.o: readable.c
-readable.o: sys_defs.h
readlline.o: msg.h
readlline.o: readlline.c
readlline.o: readlline.h
@@ -1981,15 +1979,7 @@ watchdog.o: posix_signals.h
watchdog.o: sys_defs.h
watchdog.o: watchdog.c
watchdog.o: watchdog.h
-writable.o: iostuff.h
-writable.o: msg.h
-writable.o: sys_defs.h
-writable.o: writable.c
write_buf.o: iostuff.h
write_buf.o: msg.h
write_buf.o: sys_defs.h
write_buf.o: write_buf.c
-write_wait.o: iostuff.h
-write_wait.o: msg.h
-write_wait.o: sys_defs.h
-write_wait.o: write_wait.c
diff --git a/postfix/src/util/iostuff.h b/postfix/src/util/iostuff.h
index 8a2704a96..da3fa3a38 100644
--- a/postfix/src/util/iostuff.h
+++ b/postfix/src/util/iostuff.h
@@ -16,13 +16,10 @@
extern int non_blocking(int, int);
extern int close_on_exec(int, int);
extern int open_limit(int);
-extern int readable(int);
-extern int writable(int);
+extern int poll_fd(int, int, int, int);
extern off_t get_file_limit(void);
extern void set_file_limit(off_t);
extern ssize_t peekfd(int);
-extern int read_wait(int, int);
-extern int write_wait(int, int);
extern ssize_t write_buf(int, const char *, ssize_t, int);
extern ssize_t timed_read(int, void *, size_t, int, void *);
extern ssize_t timed_write(int, void *, size_t, int, void *);
@@ -36,9 +33,18 @@ extern int unix_send_fd(int, int);
extern ssize_t dummy_read(int, void *, size_t, int, void *);
extern ssize_t dummy_write(int, void *, size_t, int, void *);
+#define readable(fd) poll_fd((fd), POLL_FD_READ, 0, 1)
+#define writable(fd) poll_fd((fd), POLL_FD_WRITE, 0, 1)
+
+#define read_wait(fd, time_limit) poll_fd((fd), POLL_FD_READ, (time_limit), 0)
+#define write_wait(fd, time_limit) poll_fd((fd), POLL_FD_WRITE, (time_limit), 0)
+
extern int inet_windowsize;
extern void set_inet_windowsize(int, int);
+#define POLL_FD_READ 0
+#define POLL_FD_WRITE 1
+
#define BLOCKING 0
#define NON_BLOCKING 1
diff --git a/postfix/src/util/poll_fd.c b/postfix/src/util/poll_fd.c
new file mode 100644
index 000000000..d7f84a222
--- /dev/null
+++ b/postfix/src/util/poll_fd.c
@@ -0,0 +1,268 @@
+/*++
+/* NAME
+/* poll_fd 3
+/* SUMMARY
+/* wait until file descriptor becomes readable or writable
+/* SYNOPSIS
+/* #include
+/*
+/* int readable(fd)
+/* int fd;
+/*
+/* int writable(fd)
+/* int fd;
+/*
+/* int read_wait(fd, timeout)
+/* int fd;
+/* int timeout
+/*
+/* int write_wait(fd, timeout)
+/* int fd;
+/* int timeout
+/*
+/* int poll_fd(fd, request, time_limit, success_val)
+/* int fd;
+/* int request;
+/* int time_limit;
+/* int success_val;
+/* DESCRIPTION
+/* The functions in this module are macros that provide a
+/* convenient interface to poll_fd().
+/*
+/* readable() asks the kernel if the specified file descriptor
+/* is readable, i.e. a read operation would not block.
+/*
+/* writable() asks the kernel if the specified file descriptor
+/* is writable, i.e. a write operation would not block.
+/*
+/* read_wait() waits until the specified file descriptor becomes
+/* readable, or until the time limit is reached.
+/*
+/* write_wait() waits until the specified file descriptor
+/* becomes writable, or until the time limit is reached.
+/*
+/* poll_fd() waits until the specified file descriptor becomes
+/* readable or writable, or until the time limit is reached.
+/*
+/* Arguments:
+/* .IP fd
+/* File descriptor. With implementations based on select(), a
+/* best effort is made to handle descriptors >=FD_SETSIZE.
+/* .IP request
+/* POLL_FD_READ (wait until readable) or POLL_FD_WRITE (wait
+/* until writable).
+/* .IP time_limit
+/* A positive value specifies a time limit in seconds. A zero
+/* value effects a poll (return immediately). A negative value
+/* means wait until the requested POLL_FD_READ or POLL_FD_WRITE
+/* condition becomes true.
+/* .IP success_val
+/* Result value when the requested POLL_FD_READ or POLL_FD_WRITE
+/* condition is true.
+/* DIAGNOSTICS
+/* Panic: interface violation. All system call errors are fatal
+/* unless specified otherwise.
+/*
+/* readable() and writable() return 1 when the requested
+/* condition is true, zero when it is false. They never return
+/* an error indication.
+/*
+/* read_wait() and write_wait() return zero when successful,
+/* -1 with errno set to ETIMEDOUT when the time limit was
+/* reached.
+/*
+/* poll_fd() returns -1 with errno set to ETIMEDOUT when the
+/* time limit was reached, success_val if the requested
+/* POLL_FD_READ or POLL_FD_WRITE condition is true, and returns
+/* zero otherwise.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef USE_SYSV_POLL
+#include
+#endif
+
+#ifdef USE_SYS_SELECT_H
+#include
+#endif
+
+/* Utility library. */
+
+#include
+#include
+
+ /*
+ * Use select() only.
+ */
+#ifdef USE_BSD_SELECT
+#define poll_fd_bsd poll_fd
+#undef USE_SYSV_POLL
+#undef USE_SYSV_POLL_WITH_SELECT
+#endif
+
+ /*
+ * Use poll() only.
+ */
+#ifdef USE_SYSV_POLL
+#define poll_fd_sysv poll_fd
+#undef USE_SYSV_POLL_WITH_SELECT
+#endif
+
+ /*
+ * Use poll() with fall-back to select(). MacOSX needs this for devices.
+ */
+#ifdef USE_SYSV_POLL_WITH_SELECT
+#define poll_fd_sysv poll_fd
+#define USE_SYSV_POLL
+#define USE_BSD_SELECT
+int poll_fd_bsd(int, int, int, int);
+
+#endif
+
+ /*
+ * Sanity check.
+ */
+#if !defined(USE_BSD_SELECT) && !defined(USE_SYSV_POLL)
+#error "specify USE_BSD_SELECT, USE_SYSV_POLL or USE_SYSV_POLL_WITH_SELECT"
+#endif
+
+#ifdef USE_BSD_SELECT
+
+/* poll_fd_bsd - block with time_limit until file descriptor is ready */
+
+int poll_fd_bsd(int fd, int request, int time_limit, int success_val)
+{
+ fd_set req_fds;
+ fd_set *read_fds;
+ fd_set *write_fds;
+ fd_set except_fds;
+ struct timeval tv;
+ struct timeval *tp;
+ int temp_fd = -1;
+
+ /*
+ * Sanity checks.
+ */
+ if (FD_SETSIZE <= fd) {
+ if ((temp_fd = dup(fd)) < 0 || temp_fd >= FD_SETSIZE)
+ msg_fatal("descriptor %d does not fit FD_SETSIZE %d", fd, FD_SETSIZE);
+ fd = temp_fd;
+ }
+
+ /*
+ * Use select() so we do not depend on alarm() and on signal() handlers.
+ * Restart the select when interrupted by some signal. Some select()
+ * implementations reduce the time to wait when interrupted, which is
+ * exactly what we want.
+ */
+ FD_ZERO(&req_fds);
+ FD_SET(fd, &req_fds);
+ except_fds = req_fds;
+ if (request == POLL_FD_READ) {
+ read_fds = &req_fds;
+ write_fds = 0;
+ } else if (request == POLL_FD_WRITE) {
+ read_fds = 0;
+ write_fds = &req_fds;
+ } else {
+ msg_panic("poll_fd: bad request %d", request);
+ }
+
+ if (time_limit >= 0) {
+ tv.tv_usec = 0;
+ tv.tv_sec = time_limit;
+ tp = &tv;
+ } else {
+ tp = 0;
+ }
+
+ for (;;) {
+ switch (select(fd + 1, read_fds, write_fds, &except_fds, tp)) {
+ case -1:
+ if (errno != EINTR)
+ msg_fatal("select: %m");
+ continue;
+ case 0:
+ if (temp_fd != -1)
+ (void) close(temp_fd);
+ if (time_limit == 0) {
+ return (0);
+ } else {
+ errno = ETIMEDOUT;
+ return (-1);
+ }
+ default:
+ if (temp_fd != -1)
+ (void) close(temp_fd);
+ return (success_val);
+ }
+ }
+}
+
+#endif
+
+#ifdef USE_SYSV_POLL
+
+/* poll_fd_sysv - block with time_limit until file descriptor is ready */
+
+int poll_fd_sysv(int fd, int request, int time_limit, int success_val)
+{
+ struct pollfd pollfd;
+
+ /*
+ * System-V poll() is optimal for polling a few descriptors.
+ */
+#define WAIT_FOR_EVENT (-1)
+
+ pollfd.fd = fd;
+ if (request == POLL_FD_READ)
+ pollfd.events = POLLIN;
+ else if (request == POLL_FD_WRITE)
+ pollfd.events = POLLOUT;
+ else
+ msg_panic("poll_fd: bad request %d", request);
+
+ for (;;) {
+ switch (poll(&pollfd, 1, time_limit < 0 ?
+ WAIT_FOR_EVENT : time_limit * 1000)) {
+ case -1:
+ if (errno != EINTR)
+#ifdef USE_SYSV_POLL_WITH_SELECT
+ return (poll_fd_bsd(fd, request, time_limit, success_val));
+#else
+ msg_fatal("poll: %m");
+#endif
+ continue;
+ case 0:
+ if (time_limit == 0) {
+ return (0);
+ } else {
+ errno = ETIMEDOUT;
+ return (-1);
+ }
+ default:
+ if (pollfd.revents & POLLNVAL)
+ msg_fatal("poll: %m");
+ return (success_val);
+ }
+ }
+}
+
+#endif
diff --git a/postfix/src/util/read_wait.c b/postfix/src/util/read_wait.c
deleted file mode 100644
index 096b38a51..000000000
--- a/postfix/src/util/read_wait.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/*++
-/* NAME
-/* read_wait 3
-/* SUMMARY
-/* wait until descriptor becomes readable
-/* SYNOPSIS
-/* #include
-/*
-/* int read_wait(fd, timeout)
-/* int fd;
-/* int timeout;
-/* DESCRIPTION
-/* read_wait() blocks the current process until the specified file
-/* descriptor becomes readable, or until the deadline is exceeded.
-/*
-/* Arguments:
-/* .IP fd
-/* File descriptor. With implementations based on select(),
-/* a best effort is made to handle descriptors >=FD_SETSIZE.
-/* .IP timeout
-/* If positive, deadline in seconds. A zero value effects a poll.
-/* A negative value means wait until something happens.
-/* DIAGNOSTICS
-/* Panic: interface violation. All system call errors are fatal.
-/*
-/* A zero result means success. When the specified deadline is
-/* exceeded, read_wait() returns -1 and sets errno to ETIMEDOUT.
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Wietse Venema
-/* IBM T.J. Watson Research
-/* P.O. Box 704
-/* Yorktown Heights, NY 10598, USA
-/*--*/
-
-/* System library. */
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef USE_SYSV_POLL
-#include
-#endif
-
-#ifdef USE_SYS_SELECT_H
-#include
-#endif
-
-/* Utility library. */
-
-#include
-#include
-
-/* read_wait - block with timeout until file descriptor is readable */
-
-int read_wait(int fd, int timeout)
-{
-#if defined(NO_SYSV_POLL)
- fd_set read_fds;
- fd_set except_fds;
- struct timeval tv;
- struct timeval *tp;
- int temp_fd = -1;
-
- /*
- * Sanity checks.
- */
- if (FD_SETSIZE <= fd) {
- if ((temp_fd = dup(fd)) < 0 || temp_fd >= FD_SETSIZE)
- msg_fatal("descriptor %d does not fit FD_SETSIZE %d", fd, FD_SETSIZE);
- fd = temp_fd;
- }
-
- /*
- * Use select() so we do not depend on alarm() and on signal() handlers.
- * Restart the select when interrupted by some signal. Some select()
- * implementations reduce the time to wait when interrupted, which is
- * exactly what we want.
- */
- FD_ZERO(&read_fds);
- FD_SET(fd, &read_fds);
- FD_ZERO(&except_fds);
- FD_SET(fd, &except_fds);
- if (timeout >= 0) {
- tv.tv_usec = 0;
- tv.tv_sec = timeout;
- tp = &tv;
- } else {
- tp = 0;
- }
-
- for (;;) {
- switch (select(fd + 1, &read_fds, (fd_set *) 0, &except_fds, tp)) {
- case -1:
- if (errno != EINTR)
- msg_fatal("select: %m");
- continue;
- case 0:
- if (temp_fd != -1)
- (void) close(temp_fd);
- errno = ETIMEDOUT;
- return (-1);
- default:
- if (temp_fd != -1)
- (void) close(temp_fd);
- return (0);
- }
- }
-#elif defined(USE_SYSV_POLL)
-
- /*
- * System-V poll() is optimal for polling a few descriptors.
- */
- struct pollfd pollfd;
-
-#define WAIT_FOR_EVENT (-1)
-
- pollfd.fd = fd;
- pollfd.events = POLLIN;
- for (;;) {
- switch (poll(&pollfd, 1, timeout < 0 ?
- WAIT_FOR_EVENT : timeout * 1000)) {
- case -1:
- if (errno != EINTR)
- msg_fatal("poll: %m");
- continue;
- case 0:
- errno = ETIMEDOUT;
- return (-1);
- default:
- if (pollfd.revents & POLLNVAL)
- msg_fatal("poll: %m");
- return (0);
- }
- }
-#else
-#error "define USE_SYSV_POLL or NO_SYSV_POLL"
-#endif
-}
diff --git a/postfix/src/util/readable.c b/postfix/src/util/readable.c
deleted file mode 100644
index 00756cc78..000000000
--- a/postfix/src/util/readable.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*++
-/* NAME
-/* readable 3
-/* SUMMARY
-/* test if descriptor is readable
-/* SYNOPSIS
-/* #include
-/*
-/* int readable(fd)
-/* int fd;
-/* DESCRIPTION
-/* readable() asks the kernel if the specified file descriptor
-/* is readable, i.e. a read operation would not block.
-/*
-/* Arguments:
-/* .IP fd
-/* File descriptor. With implementations based on select(),
-/* a best effort is made to handle descriptors >=FD_SETSIZE.
-/* DIAGNOSTICS
-/* All system call errors are fatal.
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Wietse Venema
-/* IBM T.J. Watson Research
-/* P.O. Box 704
-/* Yorktown Heights, NY 10598, USA
-/*--*/
-
-/* System library. */
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef USE_SYSV_POLL
-#include
-#endif
-
-#ifdef USE_SYS_SELECT_H
-#include
-#endif
-
-/* Utility library. */
-
-#include
-#include
-
-/* readable - see if file descriptor is readable */
-
-int readable(int fd)
-{
-#if defined(NO_SYSV_POLL)
- struct timeval tv;
- fd_set read_fds;
- fd_set except_fds;
- int temp_fd = -1;
-
- /*
- * Sanity checks.
- */
- if (fd >= FD_SETSIZE) {
- if ((temp_fd = dup(fd)) < 0 || temp_fd >= FD_SETSIZE)
- msg_fatal("fd %d does not fit in FD_SETSIZE", fd);
- fd = temp_fd;
- }
-
- /*
- * Initialize.
- */
- FD_ZERO(&read_fds);
- FD_SET(fd, &read_fds);
- FD_ZERO(&except_fds);
- FD_SET(fd, &except_fds);
- tv.tv_sec = 0;
- tv.tv_usec = 0;
-
- /*
- * Loop until we have an authoritative answer.
- */
- for (;;) {
- switch (select(fd + 1, &read_fds, (fd_set *) 0, &except_fds, &tv)) {
- case -1:
- if (errno != EINTR)
- msg_fatal("select: %m");
- continue;
- default:
- if (temp_fd != -1)
- (void) close(temp_fd);
- return (FD_ISSET(fd, &read_fds));
- case 0:
- if (temp_fd != -1)
- (void) close(temp_fd);
- return (0);
- }
- }
-#elif defined(USE_SYSV_POLL)
-
- /*
- * System-V poll() is optimal for polling a few descriptors.
- */
- struct pollfd pollfd;
-
-#define DONT_WAIT_FOR_EVENT 0
-
- pollfd.fd = fd;
- pollfd.events = POLLIN;
- for (;;) {
- switch (poll(&pollfd, 1, DONT_WAIT_FOR_EVENT)) {
- case -1:
- if (errno != EINTR)
- msg_fatal("poll: %m");
- continue;
- case 0:
- return (0);
- default:
- if (pollfd.revents & POLLNVAL)
- msg_fatal("poll: %m");
- return (1);
- }
- }
-#else
-#error "define USE_SYSV_POLL or NO_SYSV_POLL"
-#endif
-}
diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h
index a32c351be..60d6b7f60 100644
--- a/postfix/src/util/sys_defs.h
+++ b/postfix/src/util/sys_defs.h
@@ -254,7 +254,7 @@
#define SOCKOPT_SIZE socklen_t
#ifndef NO_KQUEUE
# define EVENTS_STYLE EVENTS_STYLE_KQUEUE
-# define NO_SYSV_POLL
+# define USE_SYSV_POLL_WITH_SELECT
#endif
#ifndef NO_POSIX_GETPW_R
# define HAVE_POSIX_GETPW_R
@@ -1374,16 +1374,15 @@ extern int inet_pton(int, const char *, void *);
#if !defined(EVENTS_STYLE)
#define EVENTS_STYLE EVENTS_STYLE_SELECT
#endif
+#if !defined(USE_SYSV_POLL) && !defined(USE_SYSV_POLL_WITH_SELECT)
+#define USE_BSD_SELECT
+#endif
#define EVENTS_STYLE_SELECT 1 /* Traditional BSD select */
#define EVENTS_STYLE_KQUEUE 2 /* FreeBSD kqueue */
#define EVENTS_STYLE_DEVPOLL 3 /* Solaris /dev/poll */
#define EVENTS_STYLE_EPOLL 4 /* Linux epoll */
-#if !defined(USE_SYSV_POLL) && !defined(NO_SYSV_POLL) && (EVENTS_STYLE != EVENTS_STYLE_SELECT)
-#error "need USE_SYSV_POLL or NO_SYSV_POLL with EVENTS_STYLE != EVENTS_STYLE_SELECT"
-#endif
-
/*
* The Postfix 2.9 post-install workaround assumes that the inet_protocols
* default value is "ipv4" when Postfix is compiled without IPv6 support.
diff --git a/postfix/src/util/writable.c b/postfix/src/util/writable.c
deleted file mode 100644
index a388dcbda..000000000
--- a/postfix/src/util/writable.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*++
-/* NAME
-/* writable 3
-/* SUMMARY
-/* test if descriptor is writable
-/* SYNOPSIS
-/* #include
-/*
-/* int writable(fd)
-/* int fd;
-/* DESCRIPTION
-/* writable() asks the kernel if the specified file descriptor
-/* is writable, i.e. a write operation would not block.
-/*
-/* Arguments:
-/* .IP fd
-/* File descriptor. With implementations based on select(),
-/* a best effort is made to handle descriptors >=FD_SETSIZE.
-/* DIAGNOSTICS
-/* All system call errors are fatal.
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Wietse Venema
-/* IBM T.J. Watson Research
-/* P.O. Box 704
-/* Yorktown Heights, NY 10598, USA
-/*--*/
-
-/* System library. */
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef USE_SYSV_POLL
-#include
-#endif
-
-#ifdef USE_SYS_SELECT_H
-#include
-#endif
-
-/* Utility library. */
-
-#include
-#include
-
-/* writable - see if file descriptor is writable */
-
-int writable(int fd)
-{
-#if defined(NO_SYSV_POLL)
- struct timeval tv;
- fd_set write_fds;
- fd_set except_fds;
- int temp_fd = -1;
-
- /*
- * Sanity checks.
- */
- if (fd >= FD_SETSIZE) {
- if ((temp_fd = dup(fd)) < 0 || temp_fd >= FD_SETSIZE)
- msg_fatal("fd %d does not fit in FD_SETSIZE", fd);
- fd = temp_fd;
- }
-
- /*
- * Initialize.
- */
- FD_ZERO(&write_fds);
- FD_SET(fd, &write_fds);
- FD_ZERO(&except_fds);
- FD_SET(fd, &except_fds);
- tv.tv_sec = 0;
- tv.tv_usec = 0;
-
- /*
- * Loop until we have an authoritative answer.
- */
- for (;;) {
- switch (select(fd + 1, (fd_set *) 0, &write_fds, &except_fds, &tv)) {
- case -1:
- if (errno != EINTR)
- msg_fatal("select: %m");
- continue;
- default:
- if (temp_fd != -1)
- (void) close(temp_fd);
- return (FD_ISSET(fd, &write_fds));
- case 0:
- if (temp_fd != -1)
- (void) close(temp_fd);
- return (0);
- }
- }
-#elif defined(USE_SYSV_POLL)
-
- /*
- * System-V poll() is optimal for polling a few descriptors.
- */
- struct pollfd pollfd;
-
-#define DONT_WAIT_FOR_EVENT 0
-
- pollfd.fd = fd;
- pollfd.events = POLLOUT;
- for (;;) {
- switch (poll(&pollfd, 1, DONT_WAIT_FOR_EVENT)) {
- case -1:
- if (errno != EINTR)
- msg_fatal("poll: %m");
- continue;
- case 0:
- return (0);
- default:
- if (pollfd.revents & POLLNVAL)
- msg_fatal("poll: %m");
- return (1);
- }
- }
-#else
-#error "define USE_SYSV_POLL or NO_SYSV_POLL"
-#endif
-}
diff --git a/postfix/src/util/write_wait.c b/postfix/src/util/write_wait.c
deleted file mode 100644
index cf6dde15b..000000000
--- a/postfix/src/util/write_wait.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/*++
-/* NAME
-/* write_wait 3
-/* SUMMARY
-/* wait until descriptor becomes writable
-/* SYNOPSIS
-/* #include
-/*
-/* int write_wait(fd, timeout)
-/* int fd;
-/* int timeout;
-/* DESCRIPTION
-/* write_wait() blocks the current process until the specified file
-/* descriptor becomes writable, or until the deadline is exceeded.
-/*
-/* Arguments:
-/* .IP fd
-/* File descriptor. With implementations based on select(),
-/* a best effort is made to handle descriptors >=FD_SETSIZE.
-/* .IP timeout
-/* If positive, deadline in seconds. A zero value effects a poll.
-/* A negative value means wait until something happens.
-/* DIAGNOSTICS
-/* Panic: interface violation. All system call errors are fatal.
-/*
-/* A zero result means success. When the specified deadline is
-/* exceeded, write_wait() returns -1 and sets errno to ETIMEDOUT.
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Wietse Venema
-/* IBM T.J. Watson Research
-/* P.O. Box 704
-/* Yorktown Heights, NY 10598, USA
-/*--*/
-
-/* System library. */
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef USE_SYSV_POLL
-#include
-#endif
-
-#ifdef USE_SYS_SELECT_H
-#include
-#endif
-
-/* Utility library. */
-
-#include
-#include
-
-/* write_wait - block with timeout until file descriptor is writable */
-
-int write_wait(int fd, int timeout)
-{
-#if defined(NO_SYSV_POLL)
- fd_set write_fds;
- fd_set except_fds;
- struct timeval tv;
- struct timeval *tp;
- int temp_fd = -1;
-
- /*
- * Sanity checks.
- */
- if (FD_SETSIZE <= fd) {
- if ((temp_fd = dup(fd)) < 0 || temp_fd >= FD_SETSIZE)
- msg_fatal("descriptor %d does not fit FD_SETSIZE %d", fd, FD_SETSIZE);
- fd = temp_fd;
- }
-
- /*
- * Guard the write() with select() so we do not depend on alarm() and on
- * signal() handlers. Restart the select when interrupted by some signal.
- * Some select() implementations may reduce the time to wait when
- * interrupted, which is exactly what we want.
- */
- FD_ZERO(&write_fds);
- FD_SET(fd, &write_fds);
- FD_ZERO(&except_fds);
- FD_SET(fd, &except_fds);
- if (timeout >= 0) {
- tv.tv_usec = 0;
- tv.tv_sec = timeout;
- tp = &tv;
- } else {
- tp = 0;
- }
-
- for (;;) {
- switch (select(fd + 1, (fd_set *) 0, &write_fds, &except_fds, tp)) {
- case -1:
- if (errno != EINTR)
- msg_fatal("select: %m");
- continue;
- case 0:
- if (temp_fd != -1)
- (void) close(temp_fd);
- errno = ETIMEDOUT;
- return (-1);
- default:
- if (temp_fd != -1)
- (void) close(temp_fd);
- return (0);
- }
- }
-#elif defined(USE_SYSV_POLL)
-
- /*
- * System-V poll() is optimal for polling a few descriptors.
- */
- struct pollfd pollfd;
-
-#define WAIT_FOR_EVENT (-1)
-
- pollfd.fd = fd;
- pollfd.events = POLLOUT;
- for (;;) {
- switch (poll(&pollfd, 1, timeout < 0 ?
- WAIT_FOR_EVENT : timeout * 1000)) {
- case -1:
- if (errno != EINTR)
- msg_fatal("poll: %m");
- continue;
- case 0:
- errno = ETIMEDOUT;
- return (-1);
- default:
- if (pollfd.revents & POLLNVAL)
- msg_fatal("poll: %m");
- return (0);
- }
- }
-#else
-#error "define USE_SYSV_POLL or NO_SYSV_POLL"
-#endif
-}