2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-22 09:57:34 +00:00

postfix-2.11-20130326

This commit is contained in:
Wietse Venema 2013-03-26 00:00:00 -05:00 committed by Viktor Dukhovni
parent 6896772b76
commit c771f8cafb
24 changed files with 710 additions and 824 deletions

View File

@ -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.

View File

@ -689,7 +689,9 @@ SMTPD(8) SMTPD(8)
<b><a href="postconf.5.html#smtpd_log_access_permit_actions">smtpd_log_access_permit_actions</a> (empty)</b>
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).
<b>KNOWN VERSUS UNKNOWN RECIPIENT CONTROLS</b>
As of Postfix version 2.0, the SMTP server rejects mail

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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"),

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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()");

View File

@ -0,0 +1,250 @@
/*++
/* NAME
/* tls_fprint 3
/* SUMMARY
/* Digests fingerprints and all that.
/* SYNOPSIS
/* #include <tls.h>
/*
/* 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 <sys_defs.h>
#include <ctype.h>
#ifdef USE_TLS
#include <string.h>
/* Utility library. */
#include <msg.h>
#include <mymalloc.h>
#include <stringops.h>
/* Global library. */
#include <mail_params.h>
/* TLS library. */
#define TLS_INTERNAL
#include <tls.h>
/* 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

View File

@ -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,

View File

@ -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
/*

View File

@ -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("");
}
/*

View File

@ -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 <mymalloc.h>
#include <stringops.h>
/* Global library. */
#include <mail_params.h>
/* TLS library. */
#define TLS_INTERNAL
#include <tls.h>
/* 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

View File

@ -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");

View File

@ -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

View File

@ -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

268
postfix/src/util/poll_fd.c Normal file
View File

@ -0,0 +1,268 @@
/*++
/* NAME
/* poll_fd 3
/* SUMMARY
/* wait until file descriptor becomes readable or writable
/* SYNOPSIS
/* #include <iostuff.h>
/*
/* 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 <sys_defs.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#ifdef USE_SYSV_POLL
#include <poll.h>
#endif
#ifdef USE_SYS_SELECT_H
#include <sys/select.h>
#endif
/* Utility library. */
#include <msg.h>
#include <iostuff.h>
/*
* 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

View File

@ -1,146 +0,0 @@
/*++
/* NAME
/* read_wait 3
/* SUMMARY
/* wait until descriptor becomes readable
/* SYNOPSIS
/* #include <iostuff.h>
/*
/* 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 <sys_defs.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#ifdef USE_SYSV_POLL
#include <poll.h>
#endif
#ifdef USE_SYS_SELECT_H
#include <sys/select.h>
#endif
/* Utility library. */
#include <msg.h>
#include <iostuff.h>
/* 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
}

View File

@ -1,130 +0,0 @@
/*++
/* NAME
/* readable 3
/* SUMMARY
/* test if descriptor is readable
/* SYNOPSIS
/* #include <iostuff.h>
/*
/* 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 <sys_defs.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#ifdef USE_SYSV_POLL
#include <poll.h>
#endif
#ifdef USE_SYS_SELECT_H
#include <sys/select.h>
#endif
/* Utility library. */
#include <msg.h>
#include <iostuff.h>
/* 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
}

View File

@ -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.

View File

@ -1,130 +0,0 @@
/*++
/* NAME
/* writable 3
/* SUMMARY
/* test if descriptor is writable
/* SYNOPSIS
/* #include <iostuff.h>
/*
/* 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 <sys_defs.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#ifdef USE_SYSV_POLL
#include <poll.h>
#endif
#ifdef USE_SYS_SELECT_H
#include <sys/select.h>
#endif
/* Utility library. */
#include <msg.h>
#include <iostuff.h>
/* 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
}

View File

@ -1,146 +0,0 @@
/*++
/* NAME
/* write_wait 3
/* SUMMARY
/* wait until descriptor becomes writable
/* SYNOPSIS
/* #include <iostuff.h>
/*
/* 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 <sys_defs.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#ifdef USE_SYSV_POLL
#include <poll.h>
#endif
#ifdef USE_SYS_SELECT_H
#include <sys/select.h>
#endif
/* Utility library. */
#include <msg.h>
#include <iostuff.h>
/* 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
}