Add support for distinct certificate chains per key type and per SSL
authorDr. Stephen Henson <steve@openssl.org>
Fri, 6 Apr 2012 17:22:48 +0000 (17:22 +0000)
committerDr. Stephen Henson <steve@openssl.org>
Fri, 6 Apr 2012 17:22:48 +0000 (17:22 +0000)
structure.

Before this the only way to add a custom chain was in the parent SSL_CTX
(which is shared by all key types and SSL structures) or rely on auto
chain building (which is performed on each handshake) from the trust store.
(backport from HEAD)

CHANGES
ssl/s3_lib.c
ssl/ssl.h
ssl/ssl_cert.c
ssl/ssl_locl.h

diff --git a/CHANGES b/CHANGES
index 6439302fa1bbe1512918f5d6c7cf551fba5e7b27..8b15b52c5a919d07b54a9868f4bfe80dd607a8aa 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,10 @@
 
  Changes between 1.0.1 and 1.0.2 [xx XXX xxxx]
 
+  *) Enhance SSL/TLS certificate chain handling to support different
+     chains for each certificate instead of one chain in the parent SSL_CTX.
+     [Steve Henson]
+
   *) Support for fixed DH ciphersuite client authentication: where both
      server and client use DH certificates with common parameters.
      [Steve Henson]
index d51a31fff8738073138b43cfdb6ea6fc200a0a1d..4478b42cc25eac53e4b9996b0eca86209fa1659f 100644 (file)
@@ -3350,6 +3350,21 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
 #endif
 
 #endif /* !OPENSSL_NO_TLSEXT */
+
+       case SSL_CTRL_CHAIN:
+               if (larg)
+                       return ssl_cert_set1_chain(s->cert,
+                                               (STACK_OF (X509) *)parg);
+               else
+                       return ssl_cert_set0_chain(s->cert,
+                                               (STACK_OF (X509) *)parg);
+
+       case SSL_CTRL_CHAIN_CERT:
+               if (larg)
+                       return ssl_cert_add1_chain_cert(s->cert, (X509 *)parg);
+               else
+                       return ssl_cert_add0_chain_cert(s->cert, (X509 *)parg);
+
        default:
                break;
                }
@@ -3637,6 +3652,20 @@ long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg)
                        }
                break;
 
+       case SSL_CTRL_CHAIN:
+               if (larg)
+                       return ssl_cert_set1_chain(ctx->cert,
+                                               (STACK_OF (X509) *)parg);
+               else
+                       return ssl_cert_set0_chain(ctx->cert,
+                                               (STACK_OF (X509) *)parg);
+
+       case SSL_CTRL_CHAIN_CERT:
+               if (larg)
+                       return ssl_cert_add1_chain_cert(ctx->cert, (X509 *)parg);
+               else
+                       return ssl_cert_add0_chain_cert(ctx->cert, (X509 *)parg);
+
        default:
                return(0);
                }
index ed016f4cf4b3da91461b08b477df48577b1470b8..2949f45b199b33d038aec773aec8fa7a4636c593 100644 (file)
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -1604,6 +1604,9 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 #define SSL_CTRL_GET_EXTRA_CHAIN_CERTS         82
 #define SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS       83
 
+#define SSL_CTRL_CHAIN                         88
+#define SSL_CTRL_CHAIN_CERT                    89
+
 #define DTLSv1_get_timeout(ssl, arg) \
        SSL_ctrl(ssl,DTLS_CTRL_GET_TIMEOUT,0, (void *)arg)
 #define DTLSv1_handle_timeout(ssl) \
@@ -1645,6 +1648,24 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 #define SSL_CTX_clear_extra_chain_certs(ctx) \
        SSL_CTX_ctrl(ctx,SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS,0,NULL)
 
+#define SSL_CTX_set0_chain(ctx,sk) \
+       SSL_CTX_ctrl(ctx,SSL_CTRL_CHAIN,0,(char *)sk)
+#define SSL_CTX_set1_chain(ctx,sk) \
+       SSL_CTX_ctrl(ctx,SSL_CTRL_CHAIN,1,(char *)sk)
+#define SSL_CTX_add0_chain_cert(ctx,x509) \
+       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_set0_chain(ctx,sk) \
+       SSL_ctrl(ctx,SSL_CTRL_CHAIN,0,(char *)sk)
+#define SSL_set1_chain(ctx,sk) \
+       SSL_ctrl(ctx,SSL_CTRL_CHAIN,1,(char *)sk)
+#define SSL_add0_chain_cert(ctx,x509) \
+       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)
+
 #ifndef OPENSSL_NO_BIO
 BIO_METHOD *BIO_f_ssl(void);
 BIO *BIO_new_ssl(SSL_CTX *ctx,int client);
index 571839c9b48c7b1607d3d276329618a86bb8c82b..c48aa20923b647cec39756b0e7a8c4143cbdc99f 100644 (file)
@@ -317,11 +317,23 @@ CERT *ssl_cert_dup(CERT *cert)
                                SSLerr(SSL_F_SSL_CERT_DUP, SSL_R_LIBRARY_BUG);
                                }
                        }
+
+               if (cpk->chain)
+                       {
+                       rpk->chain = sk_X509_dup(cpk->chain);
+                       if (!rpk->chain)
+                               {
+                               SSLerr(SSL_F_SSL_CERT_DUP, ERR_R_MALLOC_FAILURE);
+                               goto err;
+                               }
+                       for (i = 0; i < sk_X509_num(rpk->chain); i++)
+                               {
+                               X509 *x = sk_X509_value(rpk->chain, i);
+                               CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
+                               }
+                       }
                }
        
-       /* ret->extra_certs *should* exist, but currently the own certificate
-        * chain is held inside SSL_CTX */
-
        ret->references=1;
        /* Set digests to defaults. NB: we don't copy existing values as they
         * will be set during handshake.
@@ -353,6 +365,8 @@ err:
                        X509_free(rpk->x509);
                if (rpk->privatekey != NULL)
                        EVP_PKEY_free(rpk->privatekey);
+               if (rpk->chain)
+                       sk_X509_pop_free(rpk->chain, X509_free);
                }
 
 
@@ -397,6 +411,8 @@ void ssl_cert_free(CERT *c)
                        X509_free(cpk->x509);
                if (cpk->privatekey != NULL)
                        EVP_PKEY_free(cpk->privatekey);
+               if (cpk->chain)
+                       sk_X509_pop_free(cpk->chain, X509_free);
 #if 0
                if (c->pkeys[i].publickey != NULL)
                        EVP_PKEY_free(c->pkeys[i].publickey);
@@ -433,6 +449,59 @@ int ssl_cert_inst(CERT **o)
        return(1);
        }
 
+int ssl_cert_set0_chain(CERT *c, STACK_OF(X509) *chain)
+       {
+       CERT_PKEY *cpk = c->key;
+       if (!cpk)
+               return 0;
+       if (cpk->chain)
+               sk_X509_pop_free(cpk->chain, X509_free);
+       cpk->chain = chain;
+       return 1;
+       }
+
+int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) *chain)
+       {
+       STACK_OF(X509) *dchain;
+       X509 *x;
+       int i;
+       if (!chain)
+               return ssl_cert_set0_chain(c, NULL);
+       dchain = sk_X509_dup(chain);
+       if (!dchain)
+               return 0;
+       for (i = 0; i < sk_X509_num(dchain); i++)
+               {
+               x = sk_X509_value(dchain, i);
+               CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
+               }
+       if (!ssl_cert_set0_chain(c, dchain))
+               {
+               sk_X509_pop_free(dchain, X509_free);
+               return 0;
+               }
+       return 1;
+       }
+
+int ssl_cert_add0_chain_cert(CERT *c, X509 *x)
+       {
+       CERT_PKEY *cpk = c->key;
+       if (!cpk)
+               return 0;
+       if (!cpk->chain)
+               cpk->chain = sk_X509_new_null();
+       if (!cpk->chain || !sk_X509_push(cpk->chain, x))
+               return 0;
+       return 1;
+       }
+
+int ssl_cert_add1_chain_cert(CERT *c, X509 *x)
+       {
+       if (!ssl_cert_add0_chain_cert(c, x))
+               return 0;
+       CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
+       return 1;
+       }
 
 SESS_CERT *ssl_sess_cert_new(void)
        {
@@ -884,13 +953,22 @@ int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l)
        int i;
 
        X509 *x;
+       STACK_OF(X509) *extra_certs;
 
        if (cpk)
                x = cpk->x509;
        else
                x = NULL;
 
-       if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || s->ctx->extra_certs)
+       /* If we have a certificate specific chain use it, else use
+        * parent ctx.
+        */
+       if (cpk && cpk->chain)
+               extra_certs = cpk->chain;
+       else
+               extra_certs = s->ctx->extra_certs;
+
+       if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || extra_certs)
                no_chain = 1;
        else
                no_chain = 0;
@@ -933,10 +1011,9 @@ int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l)
                        X509_STORE_CTX_cleanup(&xs_ctx);
                        }
                }
-       /* Thawte special :-) */
-       for (i=0; i<sk_X509_num(s->ctx->extra_certs); i++)
+       for (i=0; i<sk_X509_num(extra_certs); i++)
                {
-               x=sk_X509_value(s->ctx->extra_certs,i);
+               x=sk_X509_value(extra_certs,i);
                if (!ssl_add_cert_to_buf(buf, l, x))
                        return 0;
                }
index 322aa450009eb85c46f70c5493211c691dbbaef0..f82724524b73f65704691d7eadb3f8cb7a1ec0c4 100644 (file)
@@ -474,6 +474,8 @@ typedef struct cert_pkey_st
        EVP_PKEY *privatekey;
        /* Digest to use when signing */
        const EVP_MD *digest;
+       /* Chain for this certificate */
+       STACK_OF(X509) *chain;
        } CERT_PKEY;
 
 typedef struct cert_st
@@ -826,6 +828,11 @@ void ssl_update_cache(SSL *s, int mode);
 int ssl_cipher_get_evp(const SSL_SESSION *s,const EVP_CIPHER **enc,
                       const EVP_MD **md,int *mac_pkey_type,int *mac_secret_size, SSL_COMP **comp);
 int ssl_get_handshake_digest(int i,long *mask,const EVP_MD **md);                         
+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_verify_cert_chain(SSL *s,STACK_OF(X509) *sk);
 int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l);
 int ssl_undefined_function(SSL *s);