2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-31 06:05:37 +00:00

postfix-2.11-20130403

This commit is contained in:
Wietse Venema
2013-04-03 00:00:00 -05:00
committed by Viktor Dukhovni
parent fc52b35219
commit 46a1d9a44f
10 changed files with 308 additions and 106 deletions

View File

@@ -18400,3 +18400,13 @@ Apologies for any names omitted.
Cleanup: "zero time limit" corner case in read_wait() and Cleanup: "zero time limit" corner case in read_wait() and
write_wait() emulation. Files: util/poll_fd.c, util/iostuff.h. write_wait() emulation. Files: util/poll_fd.c, util/iostuff.h.
20130401
Refactoring: allow smtp_session_alloc() to fail gracefully
and report an error.
20130403
Documentation: in smtpd.c, the comment that justifies the
454 reply for "TLS unavailable" cited the wrong RFC.

View File

@@ -8,6 +8,49 @@ Wish list:
Spellcheck and double-word check. Spellcheck and double-word check.
Begin code revision, after DANE support stabilizes. This
should be one pass that changes only names and no code.
Generally, macro and function names should make a program
more clear, not merely reduce the number of programmer
keystrokes; similar considerations hold for variable names
and constants. Avoid 1-letter names except "for i=1 to
some_bound". Instead of bare numbers use named constants
in function argument lists.
Code clarity: replace "valid" with or "dnssec_valid".
Code clarity: replace obscure macro/function names: for
example SMTP_X(XXX) -> VAR_SMTP(XXX), as the purpose is to
choose between VAR_SMTP_XXX or VAR_LMTP_XXX; replace
digestpl() etc. with names that make clear what operation
is being performed (in this case, update a digest with the
contents of a buffer with the specified length). Replace r
with res_opt, ditto for other 1-letter names.
Code consistency: replace the VSTRING-based digest output
loop with a tls_digest_encode() call.
Code clarity: rename tls_fingerprint() to tls_cert_fprint()
(compute certifate fingerprint). Keep tls_pkey_fprint()
(compute public-key fingerprint). Rename tls_fprint() to
tls_data_fprint() (compute fingerprint for arbitrary data).
Collect SMTP client connection-management state in one
iterator object, that provides the same information for
SMTP reuse policy, TLS policy, and SASL password lookups.
Unnecessary complexity: the SMTP_SESSION "tls" field is
mandatory (always allocated) therefore the content can be
a permanent part of the SMTP_SESSION structure, just like
SASL-related information. This avoids silly indirections
all over the code, as well as awkward smtp_tls_sess_alloc()
error semantics.
Provide an iterator object API that provides consistent
search key generation for SMTP reuse policy, TLS policy,
and SASL password lookups.
We have smtp_host_lookup, smtp_dns_resolver_options, and We have smtp_host_lookup, smtp_dns_resolver_options, and
now smtp_dns_support_level. Of these, smtp_dns_resolver_options now smtp_dns_support_level. Of these, smtp_dns_resolver_options
is orthogonal but the rest has overlap. is orthogonal but the rest has overlap.
@@ -18,6 +61,8 @@ Wish list:
for several releases before the deprecated feature can be for several releases before the deprecated feature can be
removed. removed.
End code revision, after DANE support stabilizes.
It would be nice if the result from one table lookup could It would be nice if the result from one table lookup could
serve as input for another (e.g. virtual aliases before the serve as input for another (e.g. virtual aliases before the
list of valid recipients). For this to work the magical list of valid recipients). For this to work the magical

View File

@@ -462,12 +462,10 @@ ReliantUNIX-?.5.43) SYSTYPE=ReliantUnix543
: ${CC=cc} : ${CC=cc}
CCARGS="$CCARGS \$(WARN)" CCARGS="$CCARGS \$(WARN)"
# Darwin > 1.3 uses awk and flat_namespace # Darwin > 1.3 uses awk and flat_namespace
# MacOS X 10.8.x (Darwin 12.x) needs libresolv, likely much
# older earlier also, but I don't have access to test systems.
case $RELEASE in case $RELEASE in
1.[0-3]) AWK=gawk;; 1.[0-3]) AWK=gawk;;
?.*|1[0-1].*) AWK=awk; SYSLIBS="-flat_namespace";; *) AWK=awk
*) AWK=awk; SYSLIBS="-lresolv -flat_namespace";; SYSLIBS="$SYSLIBS -flat_namespace";;
esac esac
# Darwin 7 adds IPv6 support, BIND_8_COMPAT, NO_NETINFO # Darwin 7 adds IPv6 support, BIND_8_COMPAT, NO_NETINFO
case $RELEASE in case $RELEASE in
@@ -483,6 +481,11 @@ ReliantUNIX-?.5.43) SYSTYPE=ReliantUnix543
?.*) CCARGS="$CCARGS -DRESOLVE_H_NEEDS_NAMESER8_COMPAT_H";; ?.*) CCARGS="$CCARGS -DRESOLVE_H_NEEDS_NAMESER8_COMPAT_H";;
*) CCARGS="$CCARGS -DRESOLVE_H_NEEDS_ARPA_NAMESER_COMPAT_H";; *) CCARGS="$CCARGS -DRESOLVE_H_NEEDS_ARPA_NAMESER_COMPAT_H";;
esac esac
# Darwin 12.x (MacOS X 10.8.x), maybe earlier, needs libresolv.
case $RELEASE in
?.*|1[0-1].*) ;;
*) SYSLIBS="$SYSLIBS -lresolv";;
esac
# kqueue and/or poll are broken in MacOS X 10.5 (Darwin 9). # kqueue and/or poll are broken in MacOS X 10.5 (Darwin 9).
# kqueue works in Mac OS X 10.8 (Darwin 12). # kqueue works in Mac OS X 10.8 (Darwin 12).
case $RELEASE in case $RELEASE in

View File

@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no * Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only. * patchlevel; they change the release date only.
*/ */
#define MAIL_RELEASE_DATE "20130331" #define MAIL_RELEASE_DATE "20130403"
#define MAIL_VERSION_NUMBER "2.11" #define MAIL_VERSION_NUMBER "2.11"
#ifdef SNAPSHOT #ifdef SNAPSHOT

View File

@@ -149,6 +149,7 @@ typedef struct SMTP_STATE {
#define SMTP_MISC_FLAG_COMPLETE_SESSION (1<<8) #define SMTP_MISC_FLAG_COMPLETE_SESSION (1<<8)
#define SMTP_MISC_FLAG_PREF_IPV6 (1<<9) #define SMTP_MISC_FLAG_PREF_IPV6 (1<<9)
#define SMTP_MISC_FLAG_PREF_IPV4 (1<<10) #define SMTP_MISC_FLAG_PREF_IPV4 (1<<10)
#define SMTP_MISC_FLAG_NO_TLS (1<<11)
#define SMTP_MISC_FLAG_CONN_CACHE_MASK \ #define SMTP_MISC_FLAG_CONN_CACHE_MASK \
(SMTP_MISC_FLAG_CONN_LOAD | SMTP_MISC_FLAG_CONN_STORE) (SMTP_MISC_FLAG_CONN_LOAD | SMTP_MISC_FLAG_CONN_STORE)
@@ -259,19 +260,21 @@ typedef struct SMTP_SESSION {
SMTP_STATE *state; /* back link */ SMTP_STATE *state; /* back link */
} SMTP_SESSION; } SMTP_SESSION;
extern SMTP_SESSION *smtp_session_alloc(VSTREAM *, const char *, const char *, extern SMTP_SESSION *smtp_session_alloc(DSN_BUF *, const char *, const char *,
const char *, unsigned, time_t, int); const char *, unsigned, int);
extern void smtp_session_new_stream(SMTP_SESSION *, VSTREAM *, time_t, int);
extern int smtp_sess_tls_check(const char *, const char *, unsigned, int);
extern void smtp_session_free(SMTP_SESSION *); extern void smtp_session_free(SMTP_SESSION *);
extern int smtp_session_passivate(SMTP_SESSION *, VSTRING *, VSTRING *); extern int smtp_session_passivate(SMTP_SESSION *, VSTRING *, VSTRING *);
extern SMTP_SESSION *smtp_session_activate(int, VSTRING *, VSTRING *); extern SMTP_SESSION *smtp_session_activate(int, VSTRING *, VSTRING *);
#ifdef USE_TLS #ifdef USE_TLS
extern void smtp_tls_list_init(void);
/* /*
* smtp_tls_sess.c * smtp_tls_sess.c
*/ */
extern SMTP_TLS_SESS *smtp_tls_sess_alloc(const char *, const char *, extern void smtp_tls_list_init(void);
extern SMTP_TLS_SESS *smtp_tls_sess_alloc(DSN_BUF *, const char *, const char *,
unsigned, int); unsigned, int);
extern SMTP_TLS_SESS *smtp_tls_sess_free(SMTP_TLS_SESS *); extern SMTP_TLS_SESS *smtp_tls_sess_free(SMTP_TLS_SESS *);

View File

@@ -293,6 +293,17 @@ static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr * sa,
int saved_errno; int saved_errno;
VSTREAM *stream; VSTREAM *stream;
time_t start_time; time_t start_time;
SMTP_SESSION *session;
/*
* Session construction is cheap, and can now tempfail when TLSA lookups
* don't work at the DANE security level. This also handles table lookup
* errors more gracefully. So construct the session, and then connect. If
* the connection fails, tear down the session.
*/
if ((session = smtp_session_alloc(why, destination, name, addr,
port, sess_flags)) == 0)
return (0);
start_time = time((time_t *) 0); start_time = time((time_t *) 0);
if (var_smtp_conn_tmout > 0) { if (var_smtp_conn_tmout > 0) {
@@ -311,6 +322,7 @@ static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr * sa,
else else
dsb_simple(why, "4.4.1", "connect to %s[%s]: %m", name, addr); dsb_simple(why, "4.4.1", "connect to %s[%s]: %m", name, addr);
close(sock); close(sock);
smtp_session_free(session);
return (0); return (0);
} }
stream = vstream_fdopen(sock, O_RDWR); stream = vstream_fdopen(sock, O_RDWR);
@@ -326,10 +338,13 @@ static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr * sa,
vstream_tweak_tcp(stream); vstream_tweak_tcp(stream);
/* /*
* Bundle up what we have into a nice SMTP_SESSION object. * Update the SMTP_SESSION state with this newly-created stream, and make
* it subject to the new-stream connection caching policy (as opposed to
* the reused-stream caching policy).
*/ */
return (smtp_session_alloc(stream, destination, name, addr, smtp_session_new_stream(session, stream, start_time, sess_flags);
port, start_time, sess_flags));
return (session);
} }
/* smtp_parse_destination - parse host/port destination */ /* smtp_parse_destination - parse host/port destination */

View File

@@ -274,6 +274,7 @@ SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, const char *addr,
* credentials or the wrong TLS policy. * credentials or the wrong TLS policy.
*/ */
if ((var_smtp_tls_per_site && *var_smtp_tls_per_site) if ((var_smtp_tls_per_site && *var_smtp_tls_per_site)
|| (var_smtp_tls_policy && *var_smtp_tls_policy)
|| (var_smtp_sasl_passwd && *var_smtp_sasl_passwd)) || (var_smtp_sasl_passwd && *var_smtp_sasl_passwd))
return (0); return (0);

View File

@@ -6,16 +6,27 @@
/* SYNOPSIS /* SYNOPSIS
/* #include "smtp.h" /* #include "smtp.h"
/* /*
/* SMTP_SESSION *smtp_session_alloc(stream, dest, host, addr, /* SMTP_SESSION *smtp_session_alloc(why, dest, host, addr,
/* port, start, flags) /* port, flags)
/* VSTREAM *stream; /* DSN_BUF *why;
/* char *dest; /* char *dest;
/* char *host; /* char *host;
/* char *addr; /* char *addr;
/* unsigned port; /* unsigned port;
/* int flags;
/*
/* void smtp_session_new_stream(session, stream, start, flags)
/* SMTP_SESSION *session;
/* VSTREAM *stream;
/* time_t start; /* time_t start;
/* int flags; /* int flags;
/* /*
/* int smtp_sess_tls_check(dest, host, port, valid)
/* char *dest;
/* char *host;
/* unsigned port;
/* int valid;
/*
/* void smtp_session_free(session) /* void smtp_session_free(session)
/* SMTP_SESSION *session; /* SMTP_SESSION *session;
/* /*
@@ -30,13 +41,24 @@
/* VSTRING *endp_prop; /* VSTRING *endp_prop;
/* DESCRIPTION /* DESCRIPTION
/* smtp_session_alloc() allocates memory for an SMTP_SESSION structure /* smtp_session_alloc() allocates memory for an SMTP_SESSION structure
/* and initializes it with the given stream and destination, host name /* and initializes it with the given destination, host name and address
/* and address information. The host name and address strings are /* information. The host name and address strings are copied. The port
/* copied. The port is in network byte order. /* is in network byte order. When TLS is enabled, smtp_session_alloc()
/* When TLS is enabled, smtp_session_alloc() looks up the /* looks up the per-site TLS policies for TLS enforcement and certificate
/* per-site TLS policies for TLS enforcement and certificate /* verification. The resulting policy is stored into the SMTP_SESSION
/* verification. The resulting policy is stored into the /* object. Table and DNS lookups can fail during TLS policy creation,
/* SMTP_SESSION object. /* when this happens, "why" is updated with the error reason and a null
/* session pointer is returned.
/*
/* smtp_session_new_stream() updates an SMTP_SESSION structure
/* with a newly-created stream that was created at the specified
/* start time, and makes it subject to the specified connection
/* caching policy.
/*
/* smtp_sess_tls_check() returns true if TLS use is mandatory, invalid
/* or indeterminate. The return value is false only if TLS is optional.
/* This is not yet used anywhere, it can be used to safely enable TLS
/* policy with smtp_reuse_addr().
/* /*
/* smtp_session_free() destroys an SMTP_SESSION structure and its /* smtp_session_free() destroys an SMTP_SESSION structure and its
/* members, making memory available for reuse. It will handle the /* members, making memory available for reuse. It will handle the
@@ -61,6 +83,8 @@
/* The address of the host that we are connected to. /* The address of the host that we are connected to.
/* .IP port /* .IP port
/* The remote port, network byte order. /* The remote port, network byte order.
/* .IP valid
/* The DNSSEC validation status of the host name.
/* .IP start /* .IP start
/* The time when this connection was opened. /* The time when this connection was opened.
/* .IP flags /* .IP flags
@@ -72,6 +96,8 @@
/* Enable re-use of cached SMTP or LMTP connections. /* Enable re-use of cached SMTP or LMTP connections.
/* .IP SMTP_MISC_FLAG_CONN_STORE /* .IP SMTP_MISC_FLAG_CONN_STORE
/* Enable saving of cached SMTP or LMTP connections. /* Enable saving of cached SMTP or LMTP connections.
/* .IP SMTP_MISC_FLAG_NO_TLS
/* Used only internally in smtp_session.c
/* .RE /* .RE
/* SMTP_MISC_FLAG_CONN_MASK corresponds with both _LOAD and _STORE. /* SMTP_MISC_FLAG_CONN_MASK corresponds with both _LOAD and _STORE.
/* .IP dest_prop /* .IP dest_prop
@@ -117,7 +143,6 @@
#include <vstream.h> #include <vstream.h>
#include <stringops.h> #include <stringops.h>
#include <valid_hostname.h> #include <valid_hostname.h>
#include <name_code.h>
/* Global library. */ /* Global library. */
@@ -134,15 +159,16 @@
/* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */ /* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */
SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, const char *dest, SMTP_SESSION *smtp_session_alloc(DSN_BUF *why, const char *dest,
const char *host, const char *addr, const char *host, const char *addr,
unsigned port, time_t start, unsigned port, int flags)
int flags)
{ {
const char *myname = "smtp_session_alloc";
SMTP_SESSION *session; SMTP_SESSION *session;
int valid = (flags & SMTP_MISC_FLAG_TLSA_HOST);
session = (SMTP_SESSION *) mymalloc(sizeof(*session)); session = (SMTP_SESSION *) mymalloc(sizeof(*session));
session->stream = stream; session->stream = 0;
session->dest = mystrdup(dest); session->dest = mystrdup(dest);
session->host = mystrdup(host); session->host = mystrdup(host);
session->addr = mystrdup(addr); session->addr = mystrdup(addr);
@@ -168,29 +194,75 @@ SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, const char *dest,
session->send_proto_helo = 0; session->send_proto_helo = 0;
if (flags & SMTP_MISC_FLAG_CONN_STORE)
CACHE_THIS_SESSION_UNTIL(start + var_smtp_reuse_time);
else
DONT_CACHE_THIS_SESSION;
session->reuse_count = 0;
USE_NEWBORN_SESSION; /* He's not dead Jim! */ USE_NEWBORN_SESSION; /* He's not dead Jim! */
#ifdef USE_SASL_AUTH #ifdef USE_SASL_AUTH
smtp_sasl_connect(session); smtp_sasl_connect(session);
#endif #endif
/*
* When the destination argument of smtp_tls_sess_alloc() is null, a
* trivial TLS policy (level = "none") is returned unconditionally and
* the other arguments are not used. Soon the DSN_BUF "why" argument
* will be optional when the caller is not interested in the error
* status.
*/
#define NO_DSN_BUF (DSN_BUF *) 0
#define NO_DEST (char *) 0
#define NO_HOST (char *) 0
#define NO_PORT 0
#define NO_FLAGS 0
#ifdef USE_TLS #ifdef USE_TLS
session->tls_context = 0; session->tls_context = 0;
session->tls_retry_plain = 0; session->tls_retry_plain = 0;
session->tls_nexthop = 0; session->tls_nexthop = 0;
session->tls = if (flags & SMTP_MISC_FLAG_NO_TLS)
smtp_tls_sess_alloc(dest, host, port, flags & SMTP_MISC_FLAG_TLSA_HOST); session->tls = smtp_tls_sess_alloc(NO_DSN_BUF, NO_DEST, NO_HOST,
NO_PORT, NO_FLAGS);
else {
if (why == 0)
msg_panic("%s: null error buffer", myname);
session->tls = smtp_tls_sess_alloc(why, dest, host, port, valid);
}
if (!session->tls) {
smtp_session_free(session);
return (0);
}
#endif #endif
session->state = 0; session->state = 0;
debug_peer_check(host, addr); debug_peer_check(host, addr);
return (session); return (session);
} }
/* smtp_session_new_stream - finalize session with newly-created connection */
void smtp_session_new_stream(SMTP_SESSION *session, VSTREAM *stream,
time_t start, int flags)
{
const char *myname = "smtp_session_new_stream";
/*
* Sanity check.
*/
if (session->stream != 0)
msg_panic("%s: session exists", myname);
session->stream = stream;
/*
* Make the session subject to the new-stream connection caching policy,
* as opposed to the reused-stream connection caching policy at the
* bottom of this module. Both policies are enforced in this file, not
* one policy here and the other at some random place in smtp_connect.c.
*/
if (flags & SMTP_MISC_FLAG_CONN_STORE)
CACHE_THIS_SESSION_UNTIL(start + var_smtp_reuse_time);
else
DONT_CACHE_THIS_SESSION;
session->reuse_count = 0;
}
/* smtp_session_free - destroy SMTP_SESSION structure and contents */ /* smtp_session_free - destroy SMTP_SESSION structure and contents */
void smtp_session_free(SMTP_SESSION *session) void smtp_session_free(SMTP_SESSION *session)
@@ -232,6 +304,32 @@ void smtp_session_free(SMTP_SESSION *session)
myfree((char *) session); myfree((char *) session);
} }
/* smtp_sess_tls_check - does session require tls */
int smtp_sess_tls_check(const char *dest, const char *host, unsigned port,
int valid)
{
#ifdef USE_TLS
static DSN_BUF *why;
SMTP_TLS_SESS *tls;
if (!why)
why = dsb_create();
tls = smtp_tls_sess_alloc(why, dest, host, ntohs(port), valid);
dsb_reset(why);
if (tls && tls->level >= TLS_LEV_NONE && tls->level <= TLS_LEV_MAY)
return (0);
if (tls)
smtp_tls_sess_free(tls);
return (1);
#else
return (0);
#endif
}
/* smtp_session_passivate - passivate an SMTP_SESSION object */ /* smtp_session_passivate - passivate an SMTP_SESSION object */
int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop, int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
@@ -372,11 +470,15 @@ SMTP_SESSION *smtp_session_activate(int fd, VSTRING *dest_prop,
/* /*
* Allright, bundle up what we have sofar. * Allright, bundle up what we have sofar.
*
* Caller is responsible for not reusing plain-text connections when TLS is
* required. With TLS disabled, a trivial TLS policy (level "none") is
* returned without fail, with no other ways for session setup to fail,
* we assume it must succeed.
*/ */
#define NO_FLAGS 0 session = smtp_session_alloc(NO_DSN_BUF, dest, host, addr, port,
SMTP_MISC_FLAG_NO_TLS);
session = smtp_session_alloc(vstream_fdopen(fd, O_RDWR), dest, host, session->stream = vstream_fdopen(fd, O_RDWR);
addr, port, (time_t) 0, NO_FLAGS);
session->features = (features | SMTP_FEATURE_FROM_CACHE); session->features = (features | SMTP_FEATURE_FROM_CACHE);
CACHE_THIS_SESSION_UNTIL(expire_time); CACHE_THIS_SESSION_UNTIL(expire_time);
session->reuse_count = ++reuse_count; session->reuse_count = ++reuse_count;

View File

@@ -8,7 +8,8 @@
/* /*
/* void smtp_tls_list_init() /* void smtp_tls_list_init()
/* /*
/* SMTP_TLS_SESS *smtp_tls_sess_alloc(dest, host, port, valid) /* SMTP_TLS_SESS *smtp_tls_sess_alloc(why, dest, host, port, valid)
/* DSN_BUF *why;
/* char *dest; /* char *dest;
/* char *host; /* char *host;
/* unsigned port; /* unsigned port;
@@ -21,8 +22,12 @@
/* policy engine. /* policy engine.
/* /*
/* smtp_tls_sess_alloc() allocates memory for an SMTP_TLS_SESS structure /* smtp_tls_sess_alloc() allocates memory for an SMTP_TLS_SESS structure
/* and initializes it based on the given information. NOTE: the /* and initializes it based on the given information. Any required
/* port is in network byte order. /* table and DNS lookups can fail. When this happens, "why" is updated
/* with the error reason and a null pointer is returned. NOTE: the
/* port is in network byte order. If "dest" is null, no policy checks are
/* made, rather a trivial policy with tls disabled is returned (the
/* remaining arguments are unused in this case and may be null).
/* /*
/* smtp_tls_sess_free() destroys an SMTP_TLS_SESS structure and its /* smtp_tls_sess_free() destroys an SMTP_TLS_SESS structure and its
/* members. A null pointer is returned for convenience. /* members. A null pointer is returned for convenience.
@@ -126,8 +131,9 @@ static const char *policy_name(int tls_level)
/* tls_site_lookup - look up per-site TLS security level */ /* tls_site_lookup - look up per-site TLS security level */
static void tls_site_lookup(int *site_level, const char *site_name, static void tls_site_lookup(SMTP_TLS_SESS *tls, int *site_level,
const char *site_class) const char *site_name, const char *site_class,
DSN_BUF *why)
{ {
const char *lookup; const char *lookup;
@@ -154,19 +160,27 @@ static void tls_site_lookup(int *site_level, const char *site_name,
if (*site_level < TLS_LEV_VERIFY) if (*site_level < TLS_LEV_VERIFY)
*site_level = TLS_LEV_VERIFY; *site_level = TLS_LEV_VERIFY;
} else { } else {
msg_warn("Table %s: ignoring unknown TLS policy '%s' for %s %s", msg_warn("%s: unknown TLS policy '%s' for %s %s",
var_smtp_tls_per_site, lookup, site_class, site_name); tls_per_site->title, lookup, site_class, site_name);
dsb_simple(why, "4.7.5", "client TLS configuration problem");
*site_level = TLS_LEV_INVALID;
return;
} }
} else if (tls_per_site->error) { } else if (tls_per_site->error) {
msg_fatal("%s lookup error for %s", tls_per_site->title, site_name); msg_warn("%s: %s \"%s\": per-site table lookup error",
tls_per_site->title, site_class, site_name);
dsb_simple(why, "4.3.0", "Temporary lookup error");
*site_level = TLS_LEV_INVALID;
return;
} }
return;
} }
/* tls_policy_lookup_one - look up destination TLS policy */ /* tls_policy_lookup_one - look up destination TLS policy */
static int tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level, static void tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
const char *site_name, const char *site_name,
const char *site_class) const char *site_class, DSN_BUF *why)
{ {
const char *lookup; const char *lookup;
char *policy; char *policy;
@@ -178,35 +192,37 @@ static int tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
static VSTRING *cbuf; static VSTRING *cbuf;
#undef FREE_RETURN #undef FREE_RETURN
#define FREE_RETURN(x) do { myfree(saved_policy); return (x); } while (0) #define FREE_RETURN do { myfree(saved_policy); return; } while (0)
#define WHERE \
vstring_str(vstring_sprintf(cbuf, "%s, %s \"%s\"", \
tls_policy->title, site_class, site_name))
if ((lookup = maps_find(tls_policy, site_name, 0)) == 0) {
if (tls_policy->error) {
msg_fatal("%s lookup error for %s",
tls_policy->title, site_name);
/* XXX session->stream has no longjmp context yet. */
}
return (0);
}
if (cbuf == 0) if (cbuf == 0)
cbuf = vstring_alloc(10); cbuf = vstring_alloc(10);
#define WHERE \ if ((lookup = maps_find(tls_policy, site_name, 0)) == 0) {
vstring_str(vstring_sprintf(cbuf, "TLS policy table, %s \"%s\"", \ if (tls_policy->error) {
site_class, site_name)) msg_warn("%s: policy table lookup error", WHERE);
dsb_simple(why, "4.3.0", "Temporary lookup error");
*site_level = TLS_LEV_INVALID;
}
return;
}
saved_policy = policy = mystrdup(lookup); saved_policy = policy = mystrdup(lookup);
if ((tok = mystrtok(&policy, "\t\n\r ,")) == 0) { if ((tok = mystrtok(&policy, "\t\n\r ,")) == 0) {
msg_warn("%s: invalid empty policy", WHERE); msg_warn("%s: invalid empty policy", WHERE);
dsb_simple(why, "4.7.5", "client TLS configuration problem");
*site_level = TLS_LEV_INVALID; *site_level = TLS_LEV_INVALID;
FREE_RETURN(1); /* No further lookups */ FREE_RETURN;
} }
*site_level = tls_level_lookup(tok); *site_level = tls_level_lookup(tok);
if (*site_level == TLS_LEV_INVALID) { if (*site_level == TLS_LEV_INVALID) {
/* tls_level_lookup() logs no warning. */ /* tls_level_lookup() logs no warning. */
msg_warn("%s: invalid security level \"%s\"", WHERE, tok); msg_warn("%s: invalid security level \"%s\"", WHERE, tok);
FREE_RETURN(1); /* No further lookups */ dsb_simple(why, "4.7.5", "client TLS configuration problem");
FREE_RETURN;
} }
/* /*
@@ -216,7 +232,7 @@ static int tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0) while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0)
msg_warn("%s: ignoring attribute \"%s\" with TLS disabled", msg_warn("%s: ignoring attribute \"%s\" with TLS disabled",
WHERE, tok); WHERE, tok);
FREE_RETURN(1); FREE_RETURN;
} }
/* /*
@@ -225,23 +241,26 @@ static int tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
*/ */
while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0) { while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0) {
if ((err = split_nameval(tok, &name, &val)) != 0) { if ((err = split_nameval(tok, &name, &val)) != 0) {
*site_level = TLS_LEV_INVALID;
msg_warn("%s: malformed attribute/value pair \"%s\": %s", msg_warn("%s: malformed attribute/value pair \"%s\": %s",
WHERE, tok, err); WHERE, tok, err);
break; dsb_simple(why, "4.7.5", "client TLS configuration problem");
*site_level = TLS_LEV_INVALID;
FREE_RETURN;
} }
/* Only one instance per policy. */ /* Only one instance per policy. */
if (!strcasecmp(name, "ciphers")) { if (!strcasecmp(name, "ciphers")) {
if (*val == 0) { if (*val == 0) {
msg_warn("%s: attribute \"%s\" has empty value", WHERE, name); msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
dsb_simple(why, "4.7.5", "client TLS configuration problem");
*site_level = TLS_LEV_INVALID; *site_level = TLS_LEV_INVALID;
break; FREE_RETURN;
} }
if (tls->grade) { if (tls->grade) {
msg_warn("%s: attribute \"%s\" is specified multiple times", msg_warn("%s: attribute \"%s\" is specified multiple times",
WHERE, name); WHERE, name);
dsb_simple(why, "4.7.5", "client TLS configuration problem");
*site_level = TLS_LEV_INVALID; *site_level = TLS_LEV_INVALID;
break; FREE_RETURN;
} }
tls->grade = mystrdup(val); tls->grade = mystrdup(val);
continue; continue;
@@ -251,8 +270,9 @@ static int tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
if (tls->protocols) { if (tls->protocols) {
msg_warn("%s: attribute \"%s\" is specified multiple times", msg_warn("%s: attribute \"%s\" is specified multiple times",
WHERE, name); WHERE, name);
dsb_simple(why, "4.7.5", "client TLS configuration problem");
*site_level = TLS_LEV_INVALID; *site_level = TLS_LEV_INVALID;
break; FREE_RETURN;
} }
tls->protocols = mystrdup(val); tls->protocols = mystrdup(val);
continue; continue;
@@ -264,13 +284,15 @@ static int tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
if (*site_level <= TLS_LEV_ENCRYPT) { if (*site_level <= TLS_LEV_ENCRYPT) {
msg_warn("%s: attribute \"%s\" invalid at security level " msg_warn("%s: attribute \"%s\" invalid at security level "
"\"%s\"", WHERE, name, policy_name(*site_level)); "\"%s\"", WHERE, name, policy_name(*site_level));
dsb_simple(why, "4.7.5", "client TLS configuration problem");
*site_level = TLS_LEV_INVALID; *site_level = TLS_LEV_INVALID;
break; FREE_RETURN;
} }
if (*val == 0) { if (*val == 0) {
msg_warn("%s: attribute \"%s\" has empty value", WHERE, name); msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
dsb_simple(why, "4.7.5", "client TLS configuration problem");
*site_level = TLS_LEV_INVALID; *site_level = TLS_LEV_INVALID;
break; FREE_RETURN;
} }
if (tls->matchargv == 0) if (tls->matchargv == 0)
tls->matchargv = argv_split(val, delim); tls->matchargv = argv_split(val, delim);
@@ -283,25 +305,27 @@ static int tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
if (tls->exclusions) { if (tls->exclusions) {
msg_warn("%s: attribute \"%s\" is specified multiple times", msg_warn("%s: attribute \"%s\" is specified multiple times",
WHERE, name); WHERE, name);
dsb_simple(why, "4.7.5", "client TLS configuration problem");
*site_level = TLS_LEV_INVALID; *site_level = TLS_LEV_INVALID;
break; FREE_RETURN;
} }
tls->exclusions = vstring_strcpy(vstring_alloc(10), val); tls->exclusions = vstring_strcpy(vstring_alloc(10), val);
continue; continue;
} else {
msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name);
*site_level = TLS_LEV_INVALID;
break;
} }
msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name);
dsb_simple(why, "4.7.5", "client TLS configuration problem");
*site_level = TLS_LEV_INVALID;
FREE_RETURN;
} }
FREE_RETURN(1);
FREE_RETURN;
} }
/* tls_policy_lookup - look up destination TLS policy */ /* tls_policy_lookup - look up destination TLS policy */
static void tls_policy_lookup(SMTP_TLS_SESS *tls, int *site_level, static void tls_policy_lookup(SMTP_TLS_SESS *tls, int *site_level,
const char *site_name, const char *site_name,
const char *site_class) const char *site_class, DSN_BUF *why)
{ {
/* /*
@@ -312,22 +336,13 @@ static void tls_policy_lookup(SMTP_TLS_SESS *tls, int *site_level,
* sub-domains of the recipient domain. * sub-domains of the recipient domain.
*/ */
if (!valid_hostname(site_name, DONT_GRIPE)) { if (!valid_hostname(site_name, DONT_GRIPE)) {
tls_policy_lookup_one(tls, site_level, site_name, site_class); tls_policy_lookup_one(tls, site_level, site_name, site_class, why);
return; return;
} }
do {
/* tls_policy_lookup_one(tls, site_level, site_name, site_class, why);
* XXX For clarity consider using ``do { .. } while'', instead of using } while (*site_level == TLS_LEV_NOTFOUND
* ``while { .. }'' with loop control at the bottom. && (site_name = strchr(site_name + 1, '.')) != 0);
*/
while (1) {
/* Try the given domain */
if (tls_policy_lookup_one(tls, site_level, site_name, site_class))
return;
/* Re-try with parent domain */
if ((site_name = strchr(site_name + 1, '.')) == 0)
return;
}
} }
/* set_cipher_grade - Set cipher grade and exclusions */ /* set_cipher_grade - Set cipher grade and exclusions */
@@ -390,10 +405,10 @@ static void set_cipher_grade(SMTP_TLS_SESS *tls)
/* smtp_tls_sess_alloc - session TLS policy parameters */ /* smtp_tls_sess_alloc - session TLS policy parameters */
SMTP_TLS_SESS *smtp_tls_sess_alloc(const char *dest, const char *host, SMTP_TLS_SESS *smtp_tls_sess_alloc(DSN_BUF *why, const char *dest,
unsigned port, int valid) const char *host, unsigned port, int valid)
{ {
const char *myname = "session_tls_init"; const char *myname = "smtp_tls_sess_alloc";
int global_level; int global_level;
int site_level; int site_level;
SMTP_TLS_SESS *tls = (SMTP_TLS_SESS *) mymalloc(sizeof(*tls)); SMTP_TLS_SESS *tls = (SMTP_TLS_SESS *) mymalloc(sizeof(*tls));
@@ -404,6 +419,9 @@ SMTP_TLS_SESS *smtp_tls_sess_alloc(const char *dest, const char *host,
tls->exclusions = 0; tls->exclusions = 0;
tls->matchargv = 0; tls->matchargv = 0;
if (!dest)
return (tls);
/* /*
* Compute the global TLS policy. This is the default policy level when * Compute the global TLS policy. This is the default policy level when
* no per-site policy exists. It also is used to override a wild-card * no per-site policy exists. It also is used to override a wild-card
@@ -433,13 +451,12 @@ SMTP_TLS_SESS *smtp_tls_sess_alloc(const char *dest, const char *host,
site_level = TLS_LEV_NOTFOUND; site_level = TLS_LEV_NOTFOUND;
if (tls_policy) { if (tls_policy) {
tls_policy_lookup(tls, &site_level, dest, "next-hop destination"); tls_policy_lookup(tls, &site_level, dest, "next-hop destination", why);
} else if (tls_per_site) { } else if (tls_per_site) {
tls_site_lookup(&site_level, dest, "next-hop destination"); tls_site_lookup(tls, &site_level, dest, "next-hop destination", why);
if (strcasecmp(dest, host) != 0) if (site_level != TLS_LEV_INVALID
tls_site_lookup(&site_level, host, "server hostname"); && strcasecmp(dest, host) != 0)
if (msg_verbose) tls_site_lookup(tls, &site_level, host, "server hostname", why);
msg_info("%s TLS level: %s", "site", policy_name(site_level));
/* /*
* Override a wild-card per-site policy with a more specific global * Override a wild-card per-site policy with a more specific global
@@ -460,10 +477,16 @@ SMTP_TLS_SESS *smtp_tls_sess_alloc(const char *dest, const char *host,
if (site_level == TLS_LEV_MAY && global_level > TLS_LEV_MAY) if (site_level == TLS_LEV_MAY && global_level > TLS_LEV_MAY)
site_level = global_level; site_level = global_level;
} }
if (site_level == TLS_LEV_NOTFOUND) switch (site_level) {
case TLS_LEV_INVALID:
return (smtp_tls_sess_free(tls));
case TLS_LEV_NOTFOUND:
tls->level = global_level; tls->level = global_level;
else break;
default:
tls->level = site_level; tls->level = site_level;
break;
}
/* /*
* Use main.cf protocols setting if not set in per-destination table. * Use main.cf protocols setting if not set in per-destination table.

View File

@@ -4347,14 +4347,14 @@ static int starttls_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
state->port, var_smtpd_tmout); state->port, var_smtpd_tmout);
if (state->tlsproxy == 0) { if (state->tlsproxy == 0) {
state->error_mask |= MAIL_ERROR_SOFTWARE; state->error_mask |= MAIL_ERROR_SOFTWARE;
/* RFC 4954 Section 6. */ /* RFC 3207 Section 4. */
smtpd_chat_reply(state, "454 4.7.0 TLS not available due to local problem"); smtpd_chat_reply(state, "454 4.7.0 TLS not available due to local problem");
return (-1); return (-1);
} }
#else /* USE_TLSPROXY */ #else /* USE_TLSPROXY */
if (smtpd_tls_ctx == 0) { if (smtpd_tls_ctx == 0) {
state->error_mask |= MAIL_ERROR_SOFTWARE; state->error_mask |= MAIL_ERROR_SOFTWARE;
/* RFC 4954 Section 6. */ /* RFC 3207 Section 4. */
smtpd_chat_reply(state, "454 4.7.0 TLS not available due to local problem"); smtpd_chat_reply(state, "454 4.7.0 TLS not available due to local problem");
return (-1); return (-1);
} }