On Mon, May 12, 2014 at 04:43:27PM +0100, SW wrote:

>    Certificate chain
>      0 s:/OU=Domain Control Validated/OU=PositiveSSL/CN=mail.domain.com
>        i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO 
> RSA Domain Validation Secure Server CA

Notice that the issuer of the leaf certificate is called:

    COMODO RSA Domain Validation Secure Server CA

>      1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO 
> ECC Domain Validation Secure Server CA
>        i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA

While the subject of the second certificate is called:

    CN=COMODO ECC Domain Validation Secure Server CA

So this chain does not have matching issuer-subject pairs.

> So the question is, how do I get this new ECDSA certificate to work in
> Postfix and why doesn't it like the chain file I have created? It looks like
> its using the RSA certificate in the chain for the ECDSA certificate which
> is confusing! In case anyone's wondering, Postfix does support running more
> than one certificate at once. See here:
> http://postfix.cs.utah.edu/TLS_README.html.

The issue appears to be an OpenSSL limitation that is fixed in the
master 1.1.0 development branch and in the upcoming 1.0.2 release
branch.  (In other words, broken in all currently released stable
branches).

The problem is that there is no separate per-algorithm chain.  Only
a single global chain.  Loading the chain for any given algorithm
clears the global chain and replaces it with the issuers from the
most recently loaded chain file (last one wins).

A work-around is to list all the relevant CAs in the chain files
for both algorithms.  The patches that resolve this for 1.0.2 are
attached for educational purposes only.  They are unlikely to apply
to 1.0.1 or earlier in isolation, and in any case would be entirely
untested with 1.0.1 as a base.

The first patch implements the relevant internal mechanisms, and
the second uses them in an updated implementation of the chain
file loading function.

So the Postfix documentation for multiple key types promises more
than OpenSSL currently delivers.  Unfortunately the limitation was
not apparent from earlier OpenSSL documentation.

-- 
        Viktor.
commit dc4bdf592f2865197312ebf7aee2c1d0a1e30b4f
Author: Rob Stradling <[email protected]>
Date:   Mon Nov 11 18:04:24 2013 +0100

    Additional "chain_cert" functions.
    
    PR#3169
    
    This patch, which currently applies successfully against master and
    1_0_2, adds the following functions:
    
    SSL_[CTX_]select_current_cert() - set the current certificate without
    disturbing the existing structure.
    
    SSL_[CTX_]get0_chain_certs() - get the current certificate's chain.
    
    SSL_[CTX_]clear_chain_certs() - clear the current certificate's chain.
    
    The patch also adds these functions to, and fixes some existing errors
    in, SSL_CTX_add1_chain_cert.pod.
    (cherry picked from commit 2f56c9c015dbca45379c9a725915b3b8e765a119)

diff --git a/doc/ssl/SSL_CTX_add1_chain_cert.pod 
b/doc/ssl/SSL_CTX_add1_chain_cert.pod
index ef26c9f..a979692 100644
--- a/doc/ssl/SSL_CTX_add1_chain_cert.pod
+++ b/doc/ssl/SSL_CTX_add1_chain_cert.pod
@@ -3,9 +3,11 @@
 =head1 NAME
 
 SSL_CTX_set0_chain, SSL_CTX_set1_chain, SSL_CTX_add0_chain_cert,
-SSL_CTX_add1_chain_cert, SSL_set0_chain, SSL_set1_chain,
-SSL_add0_chain_cert, SSL_add1_chain_cert, SSL_CTX_build_cert_chain,
-SSL_build_cert_chain - extra chain certificate processing
+SSL_CTX_add1_chain_cert, SSL_CTX_get0_chain_certs, SSL_CTX_clear_chain_certs,
+SSL_set0_chain, SSL_set1_chain, SSL_add0_chain_cert, SSL_add1_chain_cert,
+SSL_get0_chain_certs, SSL_clear_chain_certs, SSL_CTX_build_cert_chain,
+SSL_build_cert_chain, SSL_CTX_select_current_cert,
+SSL_select_current_cert - extra chain certificate processing
 
 =head1 SYNOPSIS
 
@@ -13,36 +15,58 @@ SSL_build_cert_chain - extra chain certificate processing
 
  int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *sk);
  int SSL_CTX_set1_chain(SSL_CTX *ctx, STACK_OF(X509) *sk);
- int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, STACK_OF(X509) *x509);
+ int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509);
  int SSL_CTX_add1_chain_cert(SSL_CTX *ctx, X509 *x509);
+ int SSL_CTX_get0_chain_certs(SSL_CTX *ctx, STACK_OF(X509) **sk);
+ int SSL_CTX_clear_chain_certs(SSL_CTX *ctx);
 
  int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *sk);
  int SSL_set1_chain(SSL *ssl, STACK_OF(X509) *sk);
- int SSL_add0_chain_cert(SSL *ssl, STACK_OF(X509) *x509);
+ int SSL_add0_chain_cert(SSL *ssl, X509 *x509);
  int SSL_add1_chain_cert(SSL *ssl, X509 *x509);
+ int SSL_get0_chain_certs(SSL *ssl, STACK_OF(X509) **sk);
+ int SSL_clear_chain_certs(SSL *ssl);
 
  int SSL_CTX_build_cert_chain(SSL_CTX *ctx, flags);
- int SSL_build_cert_chain(SSL_CTX *ctx, flags);
+ int SSL_build_cert_chain(SSL *ssl, flags);
+
+ int SSL_CTX_select_current_cert(SSL_CTX *ctx, X509 *x509);
+ int SSL_select_current_cert(SSL *ssl, X509 *x509);
 
 =head1 DESCRIPTION
 
 SSL_CTX_set0_chain() and SSL_CTX_set1_chain() set the certificate chain
-associated with the current certificate of B<ctx> to B<sk>. If B<sk> is set
-to B<NULL> any existing chain is cleared.
+associated with the current certificate of B<ctx> to B<sk>.
 
 SSL_CTX_add0_chain_cert() and SSL_CTX_add1_chain_cert() append the single
 certificate B<x509> to the chain associated with the current certificate of
 B<ctx>.
 
+SSL_CTX_get0_chain_certs() retrieves the chain associated with the current
+certificate of B<ctx>.
+
+SSL_CTX_clear_chain_certs() clears any existing chain associated with the
+current certificate of B<ctx>.  (This is implemented by calling
+SSL_CTX_set0_chain() with B<sk> set to B<NULL>).
+
 SSL_CTX_build_cert_chain() builds the certificate chain for B<ctx> using the
 chain store. Any existing chain certificates are used as untrusted CAs.
 If the function is successful the built chain will replace any existing chain.
 The B<flags> parameter can be set to B<SSL_BUILD_CHAIN_FLAG_NO_ROOT> to omit
 the root CA from the built chain.
 
-SSL_set0_chain(), SSL_set1_chain(), SSL_add0_chain_cert(), 
SSL_add0_chain_cert()
-and SSL_build_cert_chain() are similar except they apply to SSL structure
-B<ssl>.
+Each of these functions operates on the I<current> end entity
+(i.e. server or client) certificate. This is the last certificate loaded or
+selected on the corresponding B<ctx> structure.
+
+SSL_CTX_select_current_cert() selects B<x509> as the current end entity
+certificate, but only if B<x509> has already been loaded into B<ctx> using a
+function such as SSL_CTX_use_certificate().
+
+SSL_set0_chain(), SSL_set1_chain(), SSL_add0_chain_cert(),
+SSL_add1_chain_cert(), SSL_get0_chain_certs(), SSL_clear_chain_certs(),
+SSL_build_cert_chain() and SSL_select_current_cert() are similar except they
+apply to SSL structure B<ssl>.
 
 All these functions are implemented as macros. Those containing a B<1>
 increment the reference count of the supplied certificate or chain so it must
@@ -56,10 +80,6 @@ The chains associate with an SSL_CTX structure are copied to 
any SSL
 structures when SSL_new() is called. SSL structures will not be affected
 by any chains subsequently changed in the parent SSL_CTX.
 
-Each of these functions operates on the I<current> end entity
-(i.e. server or client) certificate. This is the last certificate set
-on the corresponding B<ctx> or B<ssl> structure.
-
 One chain can be set for each key type supported by a server. So, for example,
 an RSA and a DSA certificate can (and often will) have different chains.
 
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 92b87c3..e52d2db 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -3431,6 +3431,13 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
                else
                        return ssl_cert_add0_chain_cert(s->cert, (X509 *)parg);
 
+       case SSL_CTRL_GET_CHAIN_CERTS:
+               *(STACK_OF(X509) **)parg = s->cert->key->chain;
+               break;
+
+       case SSL_CTRL_SELECT_CURRENT_CERT:
+               return ssl_cert_select_current(s->cert, (X509 *)parg);
+
 #ifndef OPENSSL_NO_EC
        case SSL_CTRL_GET_CURVES:
                {
@@ -3929,6 +3936,13 @@ long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, 
void *parg)
                else
                        return ssl_cert_add0_chain_cert(ctx->cert, (X509 
*)parg);
 
+       case SSL_CTRL_GET_CHAIN_CERTS:
+               *(STACK_OF(X509) **)parg = ctx->cert->key->chain;
+               break;
+
+       case SSL_CTRL_SELECT_CURRENT_CERT:
+               return ssl_cert_select_current(ctx->cert, (X509 *)parg);
+
        default:
                return(0);
                }
diff --git a/ssl/ssl.h b/ssl/ssl.h
index 5e5a295..a33b0fb 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -1863,6 +1863,9 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 #define SSL_CTRL_SET_TLSA_RECORD               113
 #define SSL_CTRL_PULL_TLSA_RECORD              114
 
+#define SSL_CTRL_GET_CHAIN_CERTS               115
+#define SSL_CTRL_SELECT_CURRENT_CERT           116
+
 #define DTLSv1_get_timeout(ssl, arg) \
        SSL_ctrl(ssl,DTLS_CTRL_GET_TIMEOUT,0, (void *)arg)
 #define DTLSv1_handle_timeout(ssl) \
@@ -1912,8 +1915,14 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
        SSL_CTX_ctrl(ctx,SSL_CTRL_CHAIN_CERT,0,(char *)x509)
 #define SSL_CTX_add1_chain_cert(ctx,x509) \
        SSL_CTX_ctrl(ctx,SSL_CTRL_CHAIN_CERT,1,(char *)x509)
+#define SSL_CTX_get0_chain_certs(ctx,px509) \
+       SSL_CTX_ctrl(ctx,SSL_CTRL_GET_CHAIN_CERTS,0,px509)
+#define SSL_CTX_clear_chain_certs(ctx) \
+       SSL_CTX_set0_chain(ctx,NULL)
 #define SSL_CTX_build_cert_chain(ctx, flags) \
        SSL_CTX_ctrl(ctx,SSL_CTRL_BUILD_CERT_CHAIN, flags, NULL)
+#define SSL_CTX_select_current_cert(ctx,x509) \
+       SSL_CTX_ctrl(ctx,SSL_CTRL_SELECT_CURRENT_CERT,0,(char *)x509)
 
 #define SSL_CTX_set0_verify_cert_store(ctx,st) \
        SSL_CTX_ctrl(ctx,SSL_CTRL_SET_VERIFY_CERT_STORE,0,(char *)st)
@@ -1932,8 +1941,15 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
        SSL_ctrl(ctx,SSL_CTRL_CHAIN_CERT,0,(char *)x509)
 #define SSL_add1_chain_cert(ctx,x509) \
        SSL_ctrl(ctx,SSL_CTRL_CHAIN_CERT,1,(char *)x509)
+#define SSL_get0_chain_certs(ctx,px509) \
+       SSL_ctrl(ctx,SSL_CTRL_GET_CHAIN_CERTS,0,px509)
+#define SSL_clear_chain_certs(ctx) \
+       SSL_set0_chain(ctx,NULL)
 #define SSL_build_cert_chain(s, flags) \
        SSL_ctrl(s,SSL_CTRL_BUILD_CERT_CHAIN, flags, NULL)
+#define SSL_select_current_cert(ctx,x509) \
+       SSL_ctrl(ctx,SSL_CTRL_SELECT_CURRENT_CERT,0,(char *)x509)
+
 #define SSL_set0_verify_cert_store(s,st) \
        SSL_ctrl(s,SSL_CTRL_SET_VERIFY_CERT_STORE,0,(char *)st)
 #define SSL_set1_verify_cert_store(s,st) \
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index 1180f15..a4550ed 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -621,6 +621,20 @@ int ssl_cert_add1_chain_cert(CERT *c, X509 *x)
        return 1;
        }
 
+int ssl_cert_select_current(CERT *c, X509 *x)
+       {
+       int i;
+       for (i = 0; i < SSL_PKEY_NUM; i++)
+               {
+               if (c->pkeys[i].x509 == x)
+                       {
+                       c->key = &c->pkeys[i];
+                       return 1;
+                       }
+               }
+       return 0;
+       }
+
 void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), void *arg)
        {
        c->cert_cb = cb;
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 52e93e2..273f277 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1000,6 +1000,7 @@ int ssl_cert_set0_chain(CERT *c, STACK_OF(X509) *chain);
 int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) *chain);
 int ssl_cert_add0_chain_cert(CERT *c, X509 *x);
 int ssl_cert_add1_chain_cert(CERT *c, X509 *x);
+int ssl_cert_select_current(CERT *c, X509 *x);
 void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), void *arg);
 
 int ssl_verify_cert_chain(SSL *s,STACK_OF(X509) *sk);
commit b9fa413a08d436d6b522749b5e808fcd931fd943
Author: Dr. Stephen Henson <[email protected]>
Date:   Fri Jan 3 22:38:03 2014 +0000

    Use algorithm specific chains for certificates.
    
    Fix a limitation in SSL_CTX_use_certificate_chain_file(): use algorithm
    specific chains instead of the shared chain.
    
    Update docs.
    (cherry picked from commit a4339ea3ba045b7da038148f0d48ce25f2996971)
    
    Conflicts:
    
        CHANGES

diff --git a/CHANGES b/CHANGES
index a3059d1..76a477c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,10 @@
 
  Changes between 1.0.1e and 1.0.2 [xx XXX xxxx]
 
+  *) Use algorithm specific chains in SSL_CTX_use_certificate_chain_file():
+     this fixes a limiation in previous versions of OpenSSL.
+     [Steve Henson]
+
   *) TLS pad extension: draft-agl-tls-padding-02
 
      Workaround for the "TLS hang bug" (see FAQ and PR#2771): if the
diff --git a/doc/ssl/SSL_CTX_use_certificate.pod 
b/doc/ssl/SSL_CTX_use_certificate.pod
index 10be95f..80321b8 100644
--- a/doc/ssl/SSL_CTX_use_certificate.pod
+++ b/doc/ssl/SSL_CTX_use_certificate.pod
@@ -109,10 +109,9 @@ this B<ssl>, the last item added into B<ctx> will be 
checked.
 
 =head1 NOTES
   
-The internal certificate store of OpenSSL can hold two private key/certificate
-pairs at a time: one key/certificate of type RSA and one key/certificate
-of type DSA. The certificate used depends on the cipher select, see
-also L<SSL_CTX_set_cipher_list(3)|SSL_CTX_set_cipher_list(3)>.
+The internal certificate store of OpenSSL can hold several private
+key/certificate pairs at a time. The certificate used depends on the
+cipher selected, see also 
L<SSL_CTX_set_cipher_list(3)|SSL_CTX_set_cipher_list(3)>.
 
 When reading certificates and private keys from file, files of type
 SSL_FILETYPE_ASN1 (also known as B<DER>, binary encoding) can only contain
@@ -122,16 +121,13 @@ Files of type SSL_FILETYPE_PEM can contain more than one 
item.
 
 SSL_CTX_use_certificate_chain_file() adds the first certificate found
 in the file to the certificate store. The other certificates are added
-to the store of chain certificates using
-L<SSL_CTX_add_extra_chain_cert(3)|SSL_CTX_add_extra_chain_cert(3)>.
-There exists only one extra chain store, so that the same chain is appended
-to both types of certificates, RSA and DSA! If it is not intended to use
-both type of certificate at the same time, it is recommended to use the
-SSL_CTX_use_certificate_chain_file() instead of the
-SSL_CTX_use_certificate_file() function in order to allow the use of
-complete certificate chains even when no trusted CA storage is used or
-when the CA issuing the certificate shall not be added to the trusted
-CA storage.
+to the store of chain certificates using 
L<SSL_CTX_add1_chain_cert(3)|SSL_CTX_add1_chain_cert(3)>. Note: versions of 
OpenSSL before 1.0.2 only had a single
+certificate chain store for all certificate types, OpenSSL 1.0.2 and later
+have a separate chain store for each type. 
SSL_CTX_use_certificate_chain_file() 
+should be used instead of the SSL_CTX_use_certificate_file() function in order
+to allow the use of complete certificate chains even when no trusted CA
+storage is used or when the CA issuing the certificate shall not be added to
+the trusted CA storage.
 
 If additional certificates are needed to complete the chain during the
 TLS negotiation, CA certificates are additionally looked up in the
diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c
index 55dc1b3..73e9179 100644
--- a/ssl/ssl_rsa.c
+++ b/ssl/ssl_rsa.c
@@ -762,19 +762,15 @@ int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, 
const char *file)
                X509 *ca;
                int r;
                unsigned long err;
-               
-               if (ctx->extra_certs != NULL)
-                       {
-                       sk_X509_pop_free(ctx->extra_certs, X509_free);
-                       ctx->extra_certs = NULL;
-                       }
 
+               SSL_CTX_clear_chain_certs(ctx);
+               
                while ((ca = PEM_read_bio_X509(in, NULL,
                                        ctx->default_passwd_callback,
                                        ctx->default_passwd_callback_userdata))
                        != NULL)
                        {
-                       r = SSL_CTX_add_extra_chain_cert(ctx, ca);
+                       r = SSL_CTX_add0_chain_cert(ctx, ca);
                        if (!r) 
                                {
                                X509_free(ca);

Reply via email to