From: Wietse Venema Date: Tue, 2 Apr 2013 03:23:11 +0000 (-0400) Subject: postfix-2.11-20130401-nonprod X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6a446f616b4a22265b149dc59f18aeb54775e3db;p=thirdparty%2Fpostfix.git postfix-2.11-20130401-nonprod --- diff --git a/postfix/HISTORY b/postfix/HISTORY index ec8a2b775..37d4d8a39 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -18398,5 +18398,9 @@ Apologies for any names omitted. src/smtp/smtp_reuse.c, src/smtp/smtp_session.c, src/smtp/smtp_tls_sess.c. +20130401 + + Refactoring: allow smtp_session_alloc() to fail gracefully + and report an error. Cleanup: "zero time limit" corner case in read_wait() and write_wait() emulation. Files: util/poll_fd.c, util/iostuff.h. diff --git a/postfix/makedefs b/postfix/makedefs index 15edabcd1..746e7762e 100644 --- a/postfix/makedefs +++ b/postfix/makedefs @@ -623,7 +623,7 @@ CCARGS="$CCARGS -DSNAPSHOT" # Non-production: needs thorough testing, or major changes are still # needed before the code stabilizes. -#CCARGS="$CCARGS -DNONPROD" +CCARGS="$CCARGS -DNONPROD" sed 's/ / /g' < 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..79c89b8f0 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,15 @@ /* 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) { 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 +193,71 @@ 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 + /* + * XXX Not documented: calling smtp_session_alloc() with a null pointer + * DSN_BUF argument. + * + * XXX Not documented: calling smtp_tls_sess_alloc() with a null pointer + * host argument. + */ #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); +#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 + 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 + 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 +299,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 +465,14 @@ 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. We disable TLS policy lookups, and therefore + * smtp_session_alloc() will never fail?!? */ -#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..c715ce998 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,11 @@ /* 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. /* /* smtp_tls_sess_free() destroys an SMTP_TLS_SESS structure and its /* members. A null pointer is returned for convenience. @@ -126,8 +130,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 +159,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 +191,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 +231,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 +240,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 +269,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 +283,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 +304,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 +335,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 +404,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 +418,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 +450,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 +476,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.