]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.4-20070221
authorWietse Venema <wietse@porcupine.org>
Wed, 21 Feb 2007 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:32:57 +0000 (06:32 +0000)
17 files changed:
postfix/HISTORY
postfix/html/postconf.5.html
postfix/html/smtp.8.html
postfix/html/smtpd.8.html
postfix/man/man5/postconf.5
postfix/man/man8/smtp.8
postfix/man/man8/smtpd.8
postfix/proto/postconf.proto
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/smtp/smtp.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtpd/smtpd.c
postfix/src/tls/tls.h
postfix/src/tls/tls_client.c
postfix/src/tls/tls_misc.c
postfix/src/tls/tls_server.c

index 0aa87e917284962a98eba84ee846cde004a92b67..cd536e6ab6fc106d552e7993c47d8ff3b8afe6f8 100644 (file)
@@ -13255,6 +13255,18 @@ Apologies for any names omitted.
        multi-valued macro. Files: util/sys_defs.h, util/events.c,
        master/multi_server.c, *qmgr/qmgr_transport.c.
 
+20070220
+
+       Work-around: 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.
+
 Wish list:
 
        Update message content length when adding/removing headers.
index b186681616224804eda934ea93033e72c2d92b2b..9933040cfe81502618b6cfd79a02e6cfde65d35b 100644 (file)
@@ -11772,7 +11772,7 @@ setting. </p>
 </DD>
 
 <DT><b><a name="tls_null_cipherlist">tls_null_cipherlist</a>
-(default: !aNULL:eNULL+kRSA)</b></DT><DD>
+(default: eNULL:!aNULL)</b></DT><DD>
 
 <p> The OpenSSL cipherlist for "NULL" grade ciphers that provide
 authentication without encryption. This defines the meaning of the "null"
index 418e3d1ef78aa349c765543fa763664b5b43c008..7d4246aabafc6a2e6f18bf099bbf73529dd5c679 100644 (file)
@@ -441,7 +441,7 @@ SMTP(8)                                                                SMTP(8)
               The OpenSSL cipherlist for "EXPORT" or higher grade
               ciphers.
 
-       <b><a href="postconf.5.html#tls_null_cipherlist">tls_null_cipherlist</a> (!aNULL:eNULL+kRSA)</b>
+       <b><a href="postconf.5.html#tls_null_cipherlist">tls_null_cipherlist</a> (eNULL:!aNULL)</b>
               The  OpenSSL  cipherlist  for  "NULL" grade ciphers
               that provide authentication without encryption.
 
index b4b0a63ed139651606adb5012b89fafb735f2824..38b474b630732d1e19036633e2e484d416369750 100644 (file)
@@ -471,7 +471,7 @@ SMTPD(8)                                                              SMTPD(8)
               The OpenSSL cipherlist for "EXPORT" or higher grade
               ciphers.
 
-       <b><a href="postconf.5.html#tls_null_cipherlist">tls_null_cipherlist</a> (!aNULL:eNULL+kRSA)</b>
+       <b><a href="postconf.5.html#tls_null_cipherlist">tls_null_cipherlist</a> (eNULL:!aNULL)</b>
               The OpenSSL cipherlist  for  "NULL"  grade  ciphers
               that provide authentication without encryption.
 
index 3522cd1ef4fad342d5ce5da80cbd92c3051e3545..f35515d7912d7761a3b53f8d75bbfcf1a32510dc 100644 (file)
@@ -7148,7 +7148,7 @@ certificates). You are strongly encouraged to not change this
 setting.
 .PP
 This feature is available in Postfix 2.3 and later.
-.SH tls_null_cipherlist (default: !aNULL:eNULL+kRSA)
+.SH tls_null_cipherlist (default: eNULL:!aNULL)
 The OpenSSL cipherlist for "NULL" grade ciphers that provide
 authentication without encryption. This defines the meaning of the "null"
 setting in smtpd_mandatory_tls_ciphers, smtp_tls_mandatory_ciphers and
index 9aebf56636e46b300c326d2c930b9a1431e690d9..198d25c7db72528c59d95cd6279852fb8bee454b 100644 (file)
@@ -362,7 +362,7 @@ The OpenSSL cipherlist for "MEDIUM" or higher grade ciphers.
 The OpenSSL cipherlist for "LOW" or higher grade ciphers.
 .IP "\fBtls_export_cipherlist (ALL:+RC4:@STRENGTH)\fR"
 The OpenSSL cipherlist for "EXPORT" or higher grade ciphers.
-.IP "\fBtls_null_cipherlist (!aNULL:eNULL+kRSA)\fR"
+.IP "\fBtls_null_cipherlist (eNULL:!aNULL)\fR"
 The OpenSSL cipherlist for "NULL" grade ciphers that provide
 authentication without encryption.
 .PP
index 2794c906bbbf1de0c0bed8ac9a0cb71acd5567b0..f76921ba0c56774e7b5e9368df8bc67e911b7180 100644 (file)
@@ -386,7 +386,7 @@ The OpenSSL cipherlist for "MEDIUM" or higher grade ciphers.
 The OpenSSL cipherlist for "LOW" or higher grade ciphers.
 .IP "\fBtls_export_cipherlist (ALL:+RC4:@STRENGTH)\fR"
 The OpenSSL cipherlist for "EXPORT" or higher grade ciphers.
-.IP "\fBtls_null_cipherlist (!aNULL:eNULL+kRSA)\fR"
+.IP "\fBtls_null_cipherlist (eNULL:!aNULL)\fR"
 The OpenSSL cipherlist for "NULL" grade ciphers that provide
 authentication without encryption.
 .SH "OBSOLETE STARTTLS CONTROLS"
index 33c9ac46f65d0022b52d5cde2ea8a9f3c76e88fd..844ecf173f89a9b8157e38fbb5f7dd440b51df88 100644 (file)
@@ -10399,7 +10399,7 @@ strongly encouraged to not change this setting. </p>
 
 <p> This feature is available in Postfix 2.3 and later. </p>
 
-%PARAM tls_null_cipherlist !aNULL:eNULL+kRSA
+%PARAM tls_null_cipherlist eNULL:!aNULL
 
 <p> The OpenSSL cipherlist for "NULL" grade ciphers that provide
 authentication without encryption. This defines the meaning of the "null"
index 75cd22c99ffc64ff8f2ea11e62f6fd8dbfd850af..ab4399f9803fe470bd54d226aeafbe7abe4efe4b 100644 (file)
@@ -2692,7 +2692,7 @@ extern char *var_tls_low_clist;
 extern char *var_tls_export_clist;
 
 #define VAR_TLS_NULL_CLIST     "tls_null_cipherlist"
-#define DEF_TLS_NULL_CLIST     "!aNULL:eNULL+kRSA"
+#define DEF_TLS_NULL_CLIST     "eNULL:!aNULL"
 extern char *var_tls_null_clist;
 
  /*
index 9d9e7676016688622089fe50d846cde87511fa5c..553171c02c7ccccbb2ce0120e689b7a7aedf1ecc 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      "20070218"
+#define MAIL_RELEASE_DATE      "20070221"
 #define MAIL_VERSION_NUMBER    "2.4"
 
 #ifdef SNAPSHOT
index bba3b3ba07c9674839bf4983ac0f84b5bdeb44ba..91ee7e81432c29b0cb9133cc033e95135d28d1f0 100644 (file)
 /*     The OpenSSL cipherlist for "LOW" or higher grade ciphers.
 /* .IP "\fBtls_export_cipherlist (ALL:+RC4:@STRENGTH)\fR"
 /*     The OpenSSL cipherlist for "EXPORT" or higher grade ciphers.
-/* .IP "\fBtls_null_cipherlist (!aNULL:eNULL+kRSA)\fR"
+/* .IP "\fBtls_null_cipherlist (eNULL:!aNULL)\fR"
 /*     The OpenSSL cipherlist for "NULL" grade ciphers that provide
 /*     authentication without encryption.
 /* .PP
index 1f4912bf0a6a55b1e4c2e0fb790aeb3100ca0125..ed8d3f75ede098f8a5651f3626d233dc38604623 100644 (file)
@@ -753,7 +753,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;
index cce5136cc2bb79f74a69fa118aa49b8c65b68e6b..f38a0fe3c00cb5f32aad7a35061c1d6d214bd1a3 100644 (file)
 /*     The OpenSSL cipherlist for "LOW" or higher grade ciphers.
 /* .IP "\fBtls_export_cipherlist (ALL:+RC4:@STRENGTH)\fR"
 /*     The OpenSSL cipherlist for "EXPORT" or higher grade ciphers.
-/* .IP "\fBtls_null_cipherlist (!aNULL:eNULL+kRSA)\fR"
+/* .IP "\fBtls_null_cipherlist (eNULL:!aNULL)\fR"
 /*     The OpenSSL cipherlist for "NULL" grade ciphers that provide
 /*     authentication without encryption.
 /* OBSOLETE STARTTLS CONTROLS
@@ -4311,6 +4311,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);
index 7865180e11d142c17607d7774f01cd426a19505f..b3ca996fe6cc8828ce143ff4a9fbaa48d6b9b726 100644 (file)
@@ -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
index 0680ee0cd14bdd5f034560456dbb03caa810f96c..0c77c8c4a672d5fae05e41c3e60ddf2713785a01 100644 (file)
@@ -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
index ee3fe919710e3713f9715f58e315c45174d17879..2d05b0ba82baeac04500eb585765f2c6b8681912 100644 (file)
 /*
 /*     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;
 /*
 /*     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
@@ -152,55 +161,144 @@ NAME_CODE tls_cipher_level_table[] = {
     0, TLS_CIPHER_NONE,
 };
 
-#if 0
+ /*
+  * Parsed OpenSSL version number.
+  */
+typedef struct {
+    int     major;
+    int     minor;
+    int     micro;
+    int     patch;
+    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   *algorithm;
-    char   *exclusion;
-} cipher_probe;
+    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,
+};
 
-static cipher_probe cipher_probe_list[] = {
+/* 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;
 
     /*
-     * Check for missing AES256, OpenSSL only checks for AES128, and then
-     * enables both, because they only have one "is AES" boolean flag in the
-     * cipher property mask. The implementation cannot distinguish between
-     * AES128 and AES256. When some O/S distributions play games with
-     * libcrypto and exclude just the AES256 ciphers, they break the OpenSSL
-     * cipherlist construction code, with clients and servers potentially
-     * negotiating unimplemented ciphers.
+     * 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.
      * 
-     * This problem is peculiar to AES, which is not a single cipher, but a
-     * family of related ciphers. The other OpenSSL symmetric ciphers are
-     * atomic, either implemented or not. We expect that future ciphers will
-     * either also be atomic, or will have one property bit per family member
-     * and will be filtered accurately by OpenSSL.
+     * The key size (typically 256) that OpenSSL fails check, and assumes is
+     * available when another key size (typically 128) is usable.
      * 
-     * If all else fails, this table can be expanded :-(
+     * 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".
      * 
-     * XXX: the probe for AES256 is enclosed in #ifdef. OpenSSL 0.9.6 and and
-     * earlier don't have AES 256, this requires 0.9.7 or later. We recommend
-     * against use of 0.9.6, it has open issues solved in 0.9.7l and 0.9.8d,
-     * but we are not yet prepared to drop support for 0.9.6.
+     * 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.
      */
-#ifdef SN_aes_256_cbc
-    SN_aes_256_cbc, SSL_TXT_AES "+HIGH",
-#endif
-    0, 0,
-};
+    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;
 
-#endif
+           /*
+            * 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);
+           }
 
- /*
-  * Parsed OpenSSL version number.
-  */
-typedef struct {
-    int     major;
-    int     minor;
-    int     micro;
-    int     patch;
-    int     status;
-} TLS_VINFO;
+           /*
+            * 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 */
 
@@ -208,11 +306,6 @@ const char *tls_cipher_list(int cipher_level,...)
 {
     const char *myname = "tls_cipher_list";
     static VSTRING *buf;
-#if 0
-    static ARGV *exclude_unavailable;
-    cipher_probe *probe;
-    int     i;
-#endif
     va_list ap;
     const char *exclude;
     char   *tok;
@@ -241,47 +334,34 @@ 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);
     }
 
-    if (VSTRING_LEN(buf) == 0)
-       msg_panic("%s: empty cipherlist", myname);
-
     /*
-     * Exclude ciphers that clueless distributions leave out of libcrypto.
+     * The base lists for each grade can't be empty.
      */
-#if 0
-    if (exclude_unavailable == 0) {
-       exclude_unavailable = argv_alloc(1);
-       for (probe = cipher_probe_list; probe->algorithm; ++probe)
-           if (!EVP_get_cipherbyname(probe->algorithm))
-               argv_add(exclude_unavailable, probe->exclusion, (char *) 0);
-    }
-    for (i = 0; i < exclude_unavailable->argc; ++i)
-       vstring_sprintf_append(buf, ":!%s", exclude_unavailable->argv[i]);
-#endif
+    if (VSTRING_LEN(buf) == 0)
+       msg_panic("%s: empty cipherlist", myname);
 
     va_start(ap, cipher_level);
     while ((exclude = va_arg(ap, char *)) != 0) {
        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);
index ed1ff72ba5558f5f455e07220a9a2fb0c3375142..481a67f98a44b79b097ba860d06b593380bbc666 100644 (file)
@@ -329,12 +329,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