diff --git a/postfix/HISTORY b/postfix/HISTORY index ec8a2b775..b2e474cd6 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -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. diff --git a/postfix/WISHLIST b/postfix/WISHLIST index 59a933eac..b61e913e7 100644 --- a/postfix/WISHLIST +++ b/postfix/WISHLIST @@ -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 diff --git a/postfix/makedefs b/postfix/makedefs index 15edabcd1..2ceff2e4c 100644 --- a/postfix/makedefs +++ b/postfix/makedefs @@ -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 diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 85a1359a4..a869f257d 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20130331" +#define MAIL_RELEASE_DATE "20130403" #define MAIL_VERSION_NUMBER "2.11" #ifdef SNAPSHOT diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h index 03dbf926b..7ccd29bfb 100644 --- a/postfix/src/smtp/smtp.h +++ b/postfix/src/smtp/smtp.h @@ -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 *); diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c index 21b5e4252..aba88009a 100644 --- a/postfix/src/smtp/smtp_connect.c +++ b/postfix/src/smtp/smtp_connect.c @@ -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 */ diff --git a/postfix/src/smtp/smtp_reuse.c b/postfix/src/smtp/smtp_reuse.c index 108b51ddc..fad09e1da 100644 --- a/postfix/src/smtp/smtp_reuse.c +++ b/postfix/src/smtp/smtp_reuse.c @@ -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); diff --git a/postfix/src/smtp/smtp_session.c b/postfix/src/smtp/smtp_session.c index 8390cccac..62937926a 100644 --- a/postfix/src/smtp/smtp_session.c +++ b/postfix/src/smtp/smtp_session.c @@ -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; /* @@ -30,13 +41,24 @@ /* 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 @@ -117,7 +143,6 @@ #include #include #include -#include /* Global library. */ @@ -134,15 +159,16 @@ /* 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; diff --git a/postfix/src/smtp/smtp_tls_sess.c b/postfix/src/smtp/smtp_tls_sess.c index f127a803a..74467da1d 100644 --- a/postfix/src/smtp/smtp_tls_sess.c +++ b/postfix/src/smtp/smtp_tls_sess.c @@ -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; @@ -21,8 +22,12 @@ /* 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 ((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) cbuf = vstring_alloc(10); -#define WHERE \ - vstring_str(vstring_sprintf(cbuf, "TLS policy table, %s \"%s\"", \ - site_class, site_name)) - + if ((lookup = maps_find(tls_policy, site_name, 0)) == 0) { + if (tls_policy->error) { + 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); 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. diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index b1bc22097..1f219e187 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -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); }