]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.11-20130403
authorWietse Venema <wietse@porcupine.org>
Wed, 3 Apr 2013 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Sat, 6 Apr 2013 03:31:58 +0000 (23:31 -0400)
postfix/HISTORY
postfix/WISHLIST
postfix/makedefs
postfix/src/global/mail_version.h
postfix/src/smtp/smtp.h
postfix/src/smtp/smtp_connect.c
postfix/src/smtp/smtp_reuse.c
postfix/src/smtp/smtp_session.c
postfix/src/smtp/smtp_tls_sess.c
postfix/src/smtpd/smtpd.c

index ec8a2b775433bb22c23a3414f776f82206747a47..b2e474cd6c25d20b55dd0d77b90570d8a274b878 100644 (file)
@@ -18400,3 +18400,13 @@ Apologies for any names omitted.
 
        Cleanup: "zero time limit" corner case in read_wait() and
        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.
index 59a933eac8244a0e90550f1d8531fcd6b390a1e9..b61e913e76cc60f9eed5dc43136552a87359400c 100644 (file)
@@ -8,6 +8,49 @@ Wish list:
 
        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
        now smtp_dns_support_level.  Of these, smtp_dns_resolver_options
        is orthogonal but the rest has overlap.
@@ -18,6 +61,8 @@ Wish list:
        for several releases before the deprecated feature can be
        removed.
 
+       End code revision, after DANE support stabilizes.
+
        It would be nice if the result from one table lookup could
        serve as input for another (e.g. virtual aliases before the
        list of valid recipients). For this to work the magical
index 15edabcd1daea71d798806a75f564e71a531fb91..2ceff2e4c488a3f5da1b853e62bf8554de501928 100644 (file)
@@ -462,12 +462,10 @@ ReliantUNIX-?.5.43) SYSTYPE=ReliantUnix543
                : ${CC=cc}
                CCARGS="$CCARGS \$(WARN)"
                # 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
                 1.[0-3]) AWK=gawk;;
-                ?.*|1[0-1].*) AWK=awk; SYSLIBS="-flat_namespace";;
-                *) AWK=awk; SYSLIBS="-lresolv -flat_namespace";;
+                      *) AWK=awk
+                         SYSLIBS="$SYSLIBS -flat_namespace";;
                esac
                # Darwin 7 adds IPv6 support, BIND_8_COMPAT, NO_NETINFO
                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_ARPA_NAMESER_COMPAT_H";;
                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 works in Mac OS X 10.8 (Darwin 12).
                case $RELEASE in
index 85a1359a41b4e5f77ded1b9610babdbffb52556b..a869f257d30069be25d76c11a4e14b09338fe77e 100644 (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      "20130331"
+#define MAIL_RELEASE_DATE      "20130403"
 #define MAIL_VERSION_NUMBER    "2.11"
 
 #ifdef SNAPSHOT
index 03dbf926bf403f34d2c5a8aa4ffcbcb8767197d6..7ccd29bfb8f9618044c36204bff141f51cbc43ff 100644 (file)
@@ -149,6 +149,7 @@ typedef struct SMTP_STATE {
 #define SMTP_MISC_FLAG_COMPLETE_SESSION        (1<<8)
 #define SMTP_MISC_FLAG_PREF_IPV6       (1<<9)
 #define SMTP_MISC_FLAG_PREF_IPV4       (1<<10)
+#define SMTP_MISC_FLAG_NO_TLS          (1<<11)
 
 #define SMTP_MISC_FLAG_CONN_CACHE_MASK \
        (SMTP_MISC_FLAG_CONN_LOAD | SMTP_MISC_FLAG_CONN_STORE)
@@ -259,19 +260,21 @@ typedef struct SMTP_SESSION {
     SMTP_STATE *state;                 /* back link */
 } SMTP_SESSION;
 
-extern SMTP_SESSION *smtp_session_alloc(VSTREAM *, const char *, const char *,
-                                      const char *, unsigned, time_t, int);
+extern SMTP_SESSION *smtp_session_alloc(DSN_BUF *, const char *, const char *,
+                                               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 int smtp_session_passivate(SMTP_SESSION *, VSTRING *, VSTRING *);
 extern SMTP_SESSION *smtp_session_activate(int, VSTRING *, VSTRING *);
 
 #ifdef USE_TLS
-extern void smtp_tls_list_init(void);
 
  /*
   * 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);
 extern SMTP_TLS_SESS *smtp_tls_sess_free(SMTP_TLS_SESS *);
 
index 21b5e42529cfd5121d74fb042230283a88434f43..aba88009ab9fc4149db9e24ec99fca60f998f845 100644 (file)
@@ -293,6 +293,17 @@ static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr * sa,
     int     saved_errno;
     VSTREAM *stream;
     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);
     if (var_smtp_conn_tmout > 0) {
@@ -311,6 +322,7 @@ static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr * sa,
        else
            dsb_simple(why, "4.4.1", "connect to %s[%s]: %m", name, addr);
        close(sock);
+       smtp_session_free(session);
        return (0);
     }
     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);
 
     /*
-     * 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,
-                              port, start_time, sess_flags));
+    smtp_session_new_stream(session, stream, start_time, sess_flags);
+
+    return (session);
 }
 
 /* smtp_parse_destination - parse host/port destination */
index 108b51ddc80f36f30027b34b517c277ae4e10349..fad09e1da2f1d23721abb5caee8d826d1dc7c47f 100644 (file)
@@ -274,6 +274,7 @@ SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, const char *addr,
      * credentials or the wrong TLS policy.
      */
     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))
        return (0);
 
index 8390cccac7a9a607a43cf7aa603f8794b613edfb..62937926ac62485346ee30594c8e3e66a1a07ba1 100644 (file)
@@ -6,16 +6,27 @@
 /* SYNOPSIS
 /*     #include "smtp.h"
 /*
-/*     SMTP_SESSION *smtp_session_alloc(stream, dest, host, addr,
-/*                                     port, start, flags)
-/*     VSTREAM *stream;
+/*     SMTP_SESSION *smtp_session_alloc(why, dest, host, addr,
+/*                                     port, flags)
+/*     DSN_BUF *why;
 /*     char    *dest;
 /*     char    *host;
 /*     char    *addr;
 /*     unsigned port;
+/*     int     flags;
+/*
+/*     void    smtp_session_new_stream(session, stream, start, flags)
+/*     SMTP_SESSION *session;
+/*     VSTREAM *stream;
 /*     time_t  start;
 /*     int     flags;
 /*
+/*     int     smtp_sess_tls_check(dest, host, port, valid)
+/*     char    *dest;
+/*     char    *host;
+/*     unsigned port;
+/*     int     valid;
+/*
 /*     void    smtp_session_free(session)
 /*     SMTP_SESSION *session;
 /*
 /*     VSTRING *endp_prop;
 /* DESCRIPTION
 /*     smtp_session_alloc() allocates memory for an SMTP_SESSION structure
-/*     and initializes it with the given stream and destination, host name
-/*     and address information.  The host name and address strings are
-/*     copied. The port is in network byte order.
-/*     When TLS is enabled, smtp_session_alloc() looks up the
-/*     per-site TLS policies for TLS enforcement and certificate
-/*     verification.  The resulting policy is stored into the
-/*     SMTP_SESSION object.
+/*     and initializes it with the given destination, host name and address
+/*     information.  The host name and address strings are copied. The port
+/*     is in network byte order.  When TLS is enabled, smtp_session_alloc()
+/*     looks up the per-site TLS policies for TLS enforcement and certificate
+/*     verification.  The resulting policy is stored into the SMTP_SESSION
+/*     object.  Table and DNS lookups can fail during TLS policy creation,
+/*     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
 /*     members, making memory available for reuse. It will handle the
@@ -61,6 +83,8 @@
 /*     The address of the host that we are connected to.
 /* .IP port
 /*     The remote port, network byte order.
+/* .IP valid
+/*     The DNSSEC validation status of the host name.
 /* .IP start
 /*     The time when this connection was opened.
 /* .IP flags
@@ -72,6 +96,8 @@
 /*     Enable re-use of cached SMTP or LMTP connections.
 /* .IP SMTP_MISC_FLAG_CONN_STORE
 /*     Enable saving of cached SMTP or LMTP connections.
+/* .IP SMTP_MISC_FLAG_NO_TLS
+/*     Used only internally in smtp_session.c
 /* .RE
 /*     SMTP_MISC_FLAG_CONN_MASK corresponds with both _LOAD and _STORE.
 /* .IP dest_prop
 #include <vstream.h>
 #include <stringops.h>
 #include <valid_hostname.h>
-#include <name_code.h>
 
 /* Global library. */
 
 
 /* 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,
-                                        unsigned port, time_t start,
-                                        int flags)
+                                        unsigned port, int flags)
 {
+    const char *myname = "smtp_session_alloc";
     SMTP_SESSION *session;
+    int     valid = (flags & SMTP_MISC_FLAG_TLSA_HOST);
 
     session = (SMTP_SESSION *) mymalloc(sizeof(*session));
-    session->stream = stream;
+    session->stream = 0;
     session->dest = mystrdup(dest);
     session->host = mystrdup(host);
     session->addr = mystrdup(addr);
@@ -168,29 +194,75 @@ SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, const char *dest,
 
     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! */
 
 #ifdef USE_SASL_AUTH
     smtp_sasl_connect(session);
 #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
     session->tls_context = 0;
     session->tls_retry_plain = 0;
     session->tls_nexthop = 0;
-    session->tls =
-       smtp_tls_sess_alloc(dest, host, port, flags & SMTP_MISC_FLAG_TLSA_HOST);
+    if (flags & SMTP_MISC_FLAG_NO_TLS)
+       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
     session->state = 0;
     debug_peer_check(host, addr);
     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 */
 
 void    smtp_session_free(SMTP_SESSION *session)
@@ -232,6 +304,32 @@ void    smtp_session_free(SMTP_SESSION *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 */
 
 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.
+     * 
+     * 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(vstream_fdopen(fd, O_RDWR), dest, host,
-                                addr, port, (time_t) 0, NO_FLAGS);
+    session = smtp_session_alloc(NO_DSN_BUF, dest, host, addr, port,
+                                SMTP_MISC_FLAG_NO_TLS);
+    session->stream = vstream_fdopen(fd, O_RDWR);
     session->features = (features | SMTP_FEATURE_FROM_CACHE);
     CACHE_THIS_SESSION_UNTIL(expire_time);
     session->reuse_count = ++reuse_count;
index f127a803a4fa7a7685dee32a5e019e3c7d44e7ce..74467da1dcd56cb6334625de634c3427260761c3 100644 (file)
@@ -8,7 +8,8 @@
 /*
 /*     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    *host;
 /*     unsigned port;
 /*     policy engine.
 /*
 /*     smtp_tls_sess_alloc() allocates memory for an SMTP_TLS_SESS structure
-/*     and initializes it based on the given information.  NOTE: the
-/*     port is in network byte order.
+/*     and initializes it based on the given information.  Any required
+/*     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
 /*     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 */
 
-static void tls_site_lookup(int *site_level, const char *site_name,
-                                   const char *site_class)
+static void tls_site_lookup(SMTP_TLS_SESS *tls, int *site_level,
+                             const char *site_name, const char *site_class,
+                                   DSN_BUF *why)
 {
     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)
                *site_level = TLS_LEV_VERIFY;
        } else {
-           msg_warn("Table %s: ignoring unknown TLS policy '%s' for %s %s",
-                    var_smtp_tls_per_site, lookup, site_class, site_name);
+           msg_warn("%s: unknown TLS policy '%s' for %s %s",
+                    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) {
-       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 */
 
-static int tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
-                                        const char *site_name,
-                                        const char *site_class)
+static void tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
+                                         const char *site_name,
+                                      const char *site_class, DSN_BUF *why)
 {
     const char *lookup;
     char   *policy;
@@ -178,35 +192,37 @@ static int tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
     static VSTRING *cbuf;
 
 #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 (cbuf == 0)
+       cbuf = vstring_alloc(10);
 
     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. */
+           msg_warn("%s: policy table lookup error", WHERE);
+           dsb_simple(why, "4.3.0", "Temporary lookup error");
+           *site_level = TLS_LEV_INVALID;
        }
-       return (0);
+       return;
     }
-    if (cbuf == 0)
-       cbuf = vstring_alloc(10);
-
-#define WHERE \
-    vstring_str(vstring_sprintf(cbuf, "TLS policy table, %s \"%s\"", \
-               site_class, site_name))
-
     saved_policy = policy = mystrdup(lookup);
 
     if ((tok = mystrtok(&policy, "\t\n\r ,")) == 0) {
        msg_warn("%s: invalid empty policy", WHERE);
+       dsb_simple(why, "4.7.5", "client TLS configuration problem");
        *site_level = TLS_LEV_INVALID;
-       FREE_RETURN(1);                         /* No further lookups */
+       FREE_RETURN;
     }
     *site_level = tls_level_lookup(tok);
     if (*site_level == TLS_LEV_INVALID) {
        /* tls_level_lookup() logs no warning. */
        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)
            msg_warn("%s: ignoring attribute \"%s\" with TLS disabled",
                     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) {
        if ((err = split_nameval(tok, &name, &val)) != 0) {
-           *site_level = TLS_LEV_INVALID;
            msg_warn("%s: malformed attribute/value pair \"%s\": %s",
                     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. */
        if (!strcasecmp(name, "ciphers")) {
            if (*val == 0) {
                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;
-               break;
+               FREE_RETURN;
            }
            if (tls->grade) {
                msg_warn("%s: attribute \"%s\" is specified multiple times",
                         WHERE, name);
+               dsb_simple(why, "4.7.5", "client TLS configuration problem");
                *site_level = TLS_LEV_INVALID;
-               break;
+               FREE_RETURN;
            }
            tls->grade = mystrdup(val);
            continue;
@@ -251,8 +270,9 @@ static int tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
            if (tls->protocols) {
                msg_warn("%s: attribute \"%s\" is specified multiple times",
                         WHERE, name);
+               dsb_simple(why, "4.7.5", "client TLS configuration problem");
                *site_level = TLS_LEV_INVALID;
-               break;
+               FREE_RETURN;
            }
            tls->protocols = mystrdup(val);
            continue;
@@ -264,13 +284,15 @@ static int tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
            if (*site_level <= TLS_LEV_ENCRYPT) {
                msg_warn("%s: attribute \"%s\" invalid at security level "
                         "\"%s\"", WHERE, name, policy_name(*site_level));
+               dsb_simple(why, "4.7.5", "client TLS configuration problem");
                *site_level = TLS_LEV_INVALID;
-               break;
+               FREE_RETURN;
            }
            if (*val == 0) {
                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;
-               break;
+               FREE_RETURN;
            }
            if (tls->matchargv == 0)
                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) {
                msg_warn("%s: attribute \"%s\" is specified multiple times",
                         WHERE, name);
+               dsb_simple(why, "4.7.5", "client TLS configuration problem");
                *site_level = TLS_LEV_INVALID;
-               break;
+               FREE_RETURN;
            }
            tls->exclusions = vstring_strcpy(vstring_alloc(10), val);
            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 */
 
 static void tls_policy_lookup(SMTP_TLS_SESS *tls, int *site_level,
                                      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.
      */
     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;
     }
-
-    /*
-     * XXX For clarity consider using ``do { .. } while'', instead of using
-     * ``while { .. }'' with loop control at the bottom.
-     */
-    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;
-    }
+    do {
+       tls_policy_lookup_one(tls, site_level, site_name, site_class, why);
+    } while (*site_level == TLS_LEV_NOTFOUND
+            && (site_name = strchr(site_name + 1, '.')) != 0);
 }
 
 /* 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 *smtp_tls_sess_alloc(const char *dest, const char *host,
-                                          unsigned port, int valid)
+SMTP_TLS_SESS *smtp_tls_sess_alloc(DSN_BUF *why, const char *dest,
+                                const char *host, unsigned port, int valid)
 {
-    const char *myname = "session_tls_init";
+    const char *myname = "smtp_tls_sess_alloc";
     int     global_level;
     int     site_level;
     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->matchargv = 0;
 
+    if (!dest)
+       return (tls);
+
     /*
      * 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
@@ -433,13 +451,12 @@ SMTP_TLS_SESS *smtp_tls_sess_alloc(const char *dest, const char *host,
     site_level = TLS_LEV_NOTFOUND;
 
     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) {
-       tls_site_lookup(&site_level, dest, "next-hop destination");
-       if (strcasecmp(dest, host) != 0)
-           tls_site_lookup(&site_level, host, "server hostname");
-       if (msg_verbose)
-           msg_info("%s TLS level: %s", "site", policy_name(site_level));
+       tls_site_lookup(tls, &site_level, dest, "next-hop destination", why);
+       if (site_level != TLS_LEV_INVALID
+           && strcasecmp(dest, host) != 0)
+           tls_site_lookup(tls, &site_level, host, "server hostname", why);
 
        /*
         * 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)
            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;
-    else
+       break;
+    default:
        tls->level = site_level;
+       break;
+    }
 
     /*
      * Use main.cf protocols setting if not set in per-destination table.
index b1bc22097cf480529a2b81623560de0e76e8c631..1f219e187c513e5640f3a33fb04349b96b6b4207 100644 (file)
@@ -4347,14 +4347,14 @@ static int starttls_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
                                     state->port, var_smtpd_tmout);
     if (state->tlsproxy == 0) {
        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");
        return (-1);
     }
 #else                                          /* USE_TLSPROXY */
     if (smtpd_tls_ctx == 0) {
        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");
        return (-1);
     }