Add support for distinct certificate chains per key type and per SSL
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.
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index b488cdb..d969379 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -3350,6 +3350,21 @@
 #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;
 		}
@@ -3642,6 +3657,20 @@
 			}
 		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);
 		}
diff --git a/ssl/ssl.h b/ssl/ssl.h
index b9325fe..3d027af 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -1615,6 +1615,9 @@
 #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) \
@@ -1656,6 +1659,24 @@
 #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);
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index 94b6dea..2f43d2e 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -317,11 +317,23 @@
 				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 @@
 			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 @@
 			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 @@
 	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 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 @@
 			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;
 		}
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 6660558..95b531e 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -472,6 +472,8 @@
 	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
@@ -824,6 +826,11 @@
 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);