diff --git a/postfix/HISTORY b/postfix/HISTORY index 6168ce6ca..b223ac052 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -12928,16 +12928,36 @@ Apologies for any names omitted. Workaround: don't insert empty-line header/body separator into malformed MIME attachments, to avoid breaking digital - signatures. This change introduces ambiguity. Postfix still - treats the remainder of the attachment as body content; - header_checks rules will not detect forbidden MIME types - inside a message/rfc822 attachment. With the empty-line - header/body separator no longer inserted by Postfix, other - software may process the malformed attachment differently, - and thus may become exposed to forbidden MIME types. This - is back-ported from Postfix 2.4. File: global/mime_state.c. + signatures. This change introduces ambiguity. As before, + Postfix treats the remainder of the attachment as body + content, and header_checks rules will not detect forbidden + MIME types inside a malformed message/rfc822 attachment. + With the empty-line header/body separator no longer inserted + by Postfix, other software may process the malformed + attachment differently, and thus may now become exposed to + forbidden MIME types. This is back-ported from Postfix + 2.4. File: global/mime_state.c. 20070118 Bugfix: match lists didn't implement ![ipv6address]. Problem reported by Paulo Pacheco. File: util/match_list.c. + +20070224 + + Workaround: GNU POP3D creates a new mailbox and deletes the + old one. Postfix now backs off and retries delivery later, + instead of appending mail to a deleted file. File: + global/mbox_open.c. + +20070225 + + Workaround: Disable SSL/TLS ciphers when the underlying + symmetric algorithm is not available in the OpenSSL crypto + library at the required bit strength. Problem observed with + SunOS 5.10's bundled OpenSSL 0.9.7 and AES 256. Also possible + with OpenSSL 0.9.8 and CAMELLIA 256. Root cause fixed in + upcoming OpenSSL 0.9.7m, 0.9.8e and 0.9.9 releases. Victor + Duchovni, Morgan Stanley. Files: src/smtp/smtp_proto.c, + src/smtpd/smtpd.c, src/tls/tls.h, src/tls/tls_client.c, + src/tls/tls_misc.c and src/tls/tls_server.c. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 929e14e5b..de425fbcc 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -17,13 +17,15 @@ Incompatible changes with Postfix 2.3.7 Postfix no longer inserts an empty-line header/body separator into malformed MIME attachments, to avoid breaking digital signatures. -This change introduces ambiguity. Postfix still treats the remainder -of the attachment as body content; header_checks rules will therefore -not detect forbidden MIME types inside a message/rfc822 attachment. +This change introduces ambiguity. As before, Postfix treats the +remainder of the attachment as body content, and header_checks rules +will not detect forbidden MIME types inside a malformed message/rfc822 +attachment. With the empty-line header/body separator no longer inserted by Postfix, other software may process the malformed attachment -differently, and thus may become exposed to forbidden MIME types. +differently, and thus may now become exposed to forbidden MIME types +that they would not have been exposed to earlier. Incompatible changes with Postfix 2.3.6 --------------------------------------- diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 2f4251817..832b79af3 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,8 +20,8 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20070130" -#define MAIL_VERSION_NUMBER "2.3.7" +#define MAIL_RELEASE_DATE "20070225" +#define MAIL_VERSION_NUMBER "2.3.8-RC1" #ifdef SNAPSHOT # define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE diff --git a/postfix/src/global/mbox_open.c b/postfix/src/global/mbox_open.c index de3d1551e..97cacdae4 100644 --- a/postfix/src/global/mbox_open.c +++ b/postfix/src/global/mbox_open.c @@ -184,6 +184,26 @@ MBOX *mbox_open(const char *path, int flags, mode_t mode, struct stat * st, return (0); } } + + /* + * Sanity check: reportedly, GNU POP3D creates a new mailbox file and + * deletes the old one. This does not play well with software that opens + * the mailbox first and then locks it. + * + * To detect that GNU POP3D deletes the mailbox file we look at the target + * file hard-link count. Note that safe_open() guarantees a hard-link + * count of 1, so any change in this count is a sign of trouble. + */ + if (S_ISREG(st->st_mode) + && (fstat(vstream_fileno(fp), st) < 0 || st->st_nlink != 1)) { + vstring_sprintf(why->reason, "target file status changed unexpectedly"); + dsb_status(why, mbox_dsn(EAGAIN, def_dsn)); + msg_warn("%s: file status changed unexpectedly", path); + if (locked & MBOX_DOT_LOCK) + dot_unlockfile(path); + vstream_fclose(fp); + return (0); + } mp = (MBOX *) mymalloc(sizeof(*mp)); mp->path = mystrdup(path); mp->fp = fp; diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index 442e3d695..1bd4e55cd 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -725,7 +725,7 @@ static int smtp_start_tls(SMTP_STATE *state) vstring_sprintf_append(serverid, "&p=%s", tls_protocol_names(VAR_SMTP_TLS_MAND_PROTO, session->tls_protocols)); - if (session->tls_level >= TLS_LEV_ENCRYPT && session->tls_cipherlist) + if (session->tls_level >= TLS_LEV_ENCRYPT) vstring_sprintf_append(serverid, "&c=%s", session->tls_cipherlist); tls_props.ctx = smtp_tls_ctx; diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 16f71294b..20f462397 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -4293,6 +4293,8 @@ static void pre_jail_init(char *unused_name, char **unused_argv) enforce_tls ? var_smtpd_tls_mand_excl : TLS_END_EXCLUDE, TLS_END_EXCLUDE); + if (props.cipherlist == 0) + msg_panic("NULL export cipherlist"); } if (havecert || oknocert) smtpd_tls_ctx = tls_server_init(&props); diff --git a/postfix/src/tls/Makefile.in b/postfix/src/tls/Makefile.in index 8b8d254bf..26037fe72 100644 --- a/postfix/src/tls/Makefile.in +++ b/postfix/src/tls/Makefile.in @@ -151,6 +151,7 @@ tls_mgr.o: ../../include/vstream.h tls_mgr.o: ../../include/vstring.h tls_mgr.o: tls_mgr.c tls_mgr.o: tls_mgr.h +tls_misc.o: ../../include/argv.h tls_misc.o: ../../include/msg.h tls_misc.o: ../../include/mymalloc.h tls_misc.o: ../../include/name_code.h diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h index f13702327..78b40421a 100644 --- a/postfix/src/tls/tls.h +++ b/postfix/src/tls/tls.h @@ -130,6 +130,7 @@ extern NAME_CODE tls_cipher_level_table[]; #define TLS_END_EXCLUDE ((char *)0) extern const char *tls_cipher_list(int,...); +extern const char *tls_set_cipher_list(SSL_CTX *, const char *); /* * tls_client.c diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c index 0680ee0cd..0c77c8c4a 100644 --- a/postfix/src/tls/tls_client.c +++ b/postfix/src/tls/tls_client.c @@ -626,6 +626,15 @@ TLScontext_t *tls_client_start(const tls_client_start_props *props) if (props->log_level >= 1) msg_info("setting up TLS connection to %s", props->host); + /* + * Before we create an SSL, update the SSL_CTX cipherlist if necessary. + */ + if (tls_set_cipher_list(props->ctx, props->cipherlist) == 0) { + msg_warn("Invalid cipherlist \"%s\": aborting TLS session", + props->cipherlist); + return (0); + } + /* * Allocate a new TLScontext for the new connection and get an SSL * structure. Add the location of TLScontext to the SSL to later retrieve @@ -710,24 +719,13 @@ TLScontext_t *tls_client_start(const tls_client_start_props *props) } /* - * Per session cipher selection for sessions with mandatory encryption + * Try to load an existing session from the TLS session cache. * * By the time a TLS client is negotiating ciphers it has already offered to * re-use a session, it is too late to renege on the offer. So we must * not attempt to re-use sessions whose ciphers are too weak. We expect * the caller to salt the session lookup key with the cipher list, so * that sessions found in the cache are always acceptable. - */ - if (props->cipherlist != 0) - if (SSL_set_cipher_list(TLScontext->con, props->cipherlist) == 0) { - msg_warn("Could not set cipherlist: %s", props->cipherlist); - tls_print_errors(); - tls_free_context(TLScontext); - return (0); - } - - /* - * Try to load an existing session from the TLS session cache. * * XXX To avoid memory leaks we must always call SSL_SESSION_free() after * calling SSL_set_session(), regardless of whether or not the session diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c index f87389819..2d05b0ba8 100644 --- a/postfix/src/tls/tls_misc.c +++ b/postfix/src/tls/tls_misc.c @@ -18,6 +18,10 @@ /* /* long tls_bug_bits() /* +/* const char *tls_set_cipher_list(ssl_ctx, cipher_list) +/* SSL_CTX *ssl_ctx; +/* char *cipher_list; +/* /* const char *tls_cipher_list(cipher_level, ...) /* int cipher_level; /* @@ -53,6 +57,11 @@ /* for the run-time library. Some of the bug work-arounds are /* not appropriate for some library versions. /* +/* tls_set_cipher_list() updates the cipher list of the specified SSL +/* context. Returns the new cipherlist on success, otherwise logs a +/* suitable warning and returns 0. The storage for the return value +/* is overwritted with each call. +/* /* tls_cipher_list() generates a cipher list from the specified /* grade, minus any ciphers specified via a null-terminated /* list of string-valued exclusions. The result is overwritten @@ -104,6 +113,7 @@ #include #include #include +#include /* TLS library. */ @@ -162,6 +172,134 @@ typedef struct { int status; } TLS_VINFO; + /* + * OpenSSL adopted the cipher selection patch, so we don't expect any more + * broken ciphers other than AES and CAMELLIA. + */ +typedef struct { + char *ssl_name; + int alg_bits; + char *evp_name; +} cipher_probe_t; + +static cipher_probe_t cipher_probes[] = { + "AES", 256, "AES-256-CBC", + "CAMELLIA", 256, "CAMELLIA-256-CBC", + 0, 0, 0, +}; + +/* tls_exclude_missing - Append exclusions for missing ciphers */ + +static void tls_exclude_missing(SSL_CTX *ctx, VSTRING *buf) +{ + const char *myname = "tls_exclude_missing"; + static ARGV *exclude; /* Cached */ + SSL *s = 0; + + STACK_OF(SSL_CIPHER) * ciphers; + SSL_CIPHER *c; + cipher_probe_t *probe; + int alg_bits; + int num; + int i; + + /* + * Process a list of probes which specify: + * + * An SSL cipher-suite name for a family of ciphers that use the same + * symmetric algorithm at two or more key sizes, typically 128/256 bits. + * + * The key size (typically 256) that OpenSSL fails check, and assumes is + * available when another key size (typically 128) is usable. + * + * The OpenSSL name of the symmetric algorithm associated with the SSL + * cipher-suite. Typically, this is MUMBLE-256-CBC, where "MUMBLE" is the + * name of the SSL cipher-suite that use the MUMBLE symmetric algorithm. + * On systems that support the required encryption algorithm, the name is + * listed in the output of "openssl list-cipher-algorithms". + * + * When an encryption algorithm is not available at the given key size but + * the corresponding OpenSSL cipher-suite contains ciphers that have have + * this key size, the problem ciphers are explicitly disabled in Postfix. + * The list is cached in the static "exclude" array. + */ + if (exclude == 0) { + exclude = argv_alloc(1); + + /* + * Iterate over the probe list + */ + for (probe = cipher_probes; probe->ssl_name; ++probe) { + /* No exclusions if evp_name is a valid algorithm */ + if (EVP_get_cipherbyname(probe->evp_name)) + continue; + + /* + * Sadly there is no SSL_CTX_get_ciphers() interface, so we are + * forced to allocate and free an SSL object. Fatal error if we + * can't allocate the SSL object. + */ + ERR_clear_error(); + if (s == 0 && (s = SSL_new(ctx)) == 0) { + tls_print_errors(); + msg_fatal("%s: error allocating SSL object", myname); + } + + /* + * Cipher is not supported by libcrypto, nothing to do if also + * not supported by libssl. Flush the OpenSSL error stack. + * + * XXX: There may be additional places in pre-existing code where + * SSL errors are generated and ignored, that require a similar + * "flush". Better yet, is to always flush before calls that run + * tls_print_errors() on failure. + * + * Contrary to documentation, on SunOS 5.10 SSL_set_cipher_list() + * returns success with no ciphers selected, when this happens + * SSL_get_ciphers() produces a stack with 0 elements! + */ + if (SSL_set_cipher_list(s, probe->ssl_name) == 0 + || (ciphers = SSL_get_ciphers(s)) == 0 + || (num = sk_SSL_CIPHER_num(ciphers)) == 0) { + ERR_clear_error(); /* flush any generated errors */ + continue; + } + for (i = 0; i < num; ++i) { + c = sk_SSL_CIPHER_value(ciphers, i); + (void) SSL_CIPHER_get_bits(c, &alg_bits); + if (alg_bits == probe->alg_bits) + argv_add(exclude, SSL_CIPHER_get_name(c), ARGV_END); + } + } + if (s != 0) + SSL_free(s); + } + for (i = 0; i < exclude->argc; ++i) + vstring_sprintf_append(buf, ":!%s", exclude->argv[i]); +} + +/* tls_set_cipher_list - Set SSL_CTX cipher list */ + +const char *tls_set_cipher_list(SSL_CTX *ssl_ctx, const char *spec) +{ + static VSTRING *buf; + const char *ex_spec; + + if (buf == 0) + buf = vstring_alloc(10); + + vstring_strcpy(buf, spec); + tls_exclude_missing(ssl_ctx, buf); + ex_spec = vstring_str(buf); + + ERR_clear_error(); + if (SSL_CTX_set_cipher_list(ssl_ctx, ex_spec) != 0) + return (ex_spec); + + tls_print_errors(); + return (0); +} + /* tls_cipher_list - Cipherlist for given grade, less exclusions */ const char *tls_cipher_list(int cipher_level,...) @@ -196,9 +334,16 @@ const char *tls_cipher_list(int cipher_level,...) case TLS_CIPHER_NONE: return 0; default: + + /* + * The caller MUST provide a valid cipher grade + */ msg_panic("%s: invalid cipher grade: %d", myname, cipher_level); } + /* + * The base lists for each grade can't be empty. + */ if (VSTRING_LEN(buf) == 0) msg_panic("%s: empty cipherlist", myname); @@ -207,22 +352,16 @@ const char *tls_cipher_list(int cipher_level,...) if (*exclude == '\0') continue; save = cp = mystrdup(exclude); - while ((tok = mystrtok(&cp, "\t\n\r ,")) != 0) { + while ((tok = mystrtok(&cp, "\t\n\r ,:")) != 0) { /* - * Can't exclude ciphers that start with modifiers, or - * multi-element (":" separated) ciphers. + * Can't exclude ciphers that start with modifiers. */ if (strchr("!+-@", *tok)) { msg_warn("%s: can't exclude '!+-@' modifiers, '%s' ignored", myname, tok); continue; } - if (strchr(tok, ':')) { - msg_warn("%s: can't exclude compound ciphers, '%s' ignored", - myname, tok); - continue; - } vstring_sprintf_append(buf, ":!%s", tok); } myfree(save); diff --git a/postfix/src/tls/tls_server.c b/postfix/src/tls/tls_server.c index 3c6dac341..85b1a656d 100644 --- a/postfix/src/tls/tls_server.c +++ b/postfix/src/tls/tls_server.c @@ -328,12 +328,12 @@ SSL_CTX *tls_server_init(const tls_server_props *props) /* * Override the default cipher list with our own list. */ - if (*props->cipherlist != 0) - if (SSL_CTX_set_cipher_list(server_ctx, props->cipherlist) == 0) { - tls_print_errors(); - SSL_CTX_free(server_ctx); /* 200411 */ - return (0); - } + if (tls_set_cipher_list(server_ctx, props->cipherlist) == 0) { + SSL_CTX_free(server_ctx); + msg_warn("Invalid cipherlist \"%s\": disabling TLS support", + props->cipherlist); + return (0); /* Already logged */ + } /* * Load the CA public key certificates for both the server cert and for