From c771f8cafb72402fdee85ddea1cc6320ff7a198d Mon Sep 17 00:00:00 2001 From: Wietse Venema Date: Tue, 26 Mar 2013 00:00:00 -0500 Subject: [PATCH] postfix-2.11-20130326 --- postfix/HISTORY | 19 ++- postfix/html/smtpd.8.html | 4 +- postfix/man/man8/smtpd.8 | 3 +- postfix/src/global/mail_version.h | 2 +- postfix/src/smtp/smtp.c | 2 +- postfix/src/smtp/smtp_proto.c | 37 +++-- postfix/src/smtpd/smtpd.c | 7 +- postfix/src/tls/Makefile.in | 18 +- postfix/src/tls/tls.h | 31 ++-- postfix/src/tls/tls_client.c | 90 ++++------ postfix/src/tls/tls_fprint.c | 250 ++++++++++++++++++++++++++++ postfix/src/tls/tls_level.c | 3 + postfix/src/tls/tls_misc.c | 35 +++- postfix/src/tls/tls_server.c | 26 +-- postfix/src/tls/tls_verify.c | 122 -------------- postfix/src/tlsproxy/tlsproxy.c | 4 +- postfix/src/util/Makefile.in | 38 ++--- postfix/src/util/iostuff.h | 14 +- postfix/src/util/poll_fd.c | 268 ++++++++++++++++++++++++++++++ postfix/src/util/read_wait.c | 146 ---------------- postfix/src/util/readable.c | 130 --------------- postfix/src/util/sys_defs.h | 9 +- postfix/src/util/writable.c | 130 --------------- postfix/src/util/write_wait.c | 146 ---------------- 24 files changed, 710 insertions(+), 824 deletions(-) create mode 100644 postfix/src/tls/tls_fprint.c create mode 100644 postfix/src/util/poll_fd.c delete mode 100644 postfix/src/util/read_wait.c delete mode 100644 postfix/src/util/readable.c delete mode 100644 postfix/src/util/writable.c delete mode 100644 postfix/src/util/write_wait.c 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 -}