Add certificate callback. If set this is called whenever a certificate
is required by client or server. An application can decide which
certificate chain to present based on arbitrary criteria: for example
supported signature algorithms. Add very simple example to s_server.
This fixes many of the problems and restrictions of the existing client
certificate callback: for example you can now clear existing certificates
and specify the whole chain.
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index c51f3d0..8d7bcfe 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -3180,6 +3180,13 @@
 
 	if (s->state ==	SSL3_ST_CW_CERT_A)
 		{
+		/* Let cert callback update client certificates if required */
+		if (s->cert->cert_cb
+			&& s->cert->cert_cb(s, s->cert->cert_cb_arg) <= 0)
+			{
+			ssl3_send_alert(s,SSL3_AL_FATAL,SSL_AD_INTERNAL_ERROR);
+			return 0;
+			}
 		if (ssl3_check_client_certificate(s))
 			s->state=SSL3_ST_CW_CERT_C;
 		else
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 6ae2c4d..879d074 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -1357,6 +1357,14 @@
 			SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_CIPHERS_PASSED);
 			goto f_err;
 			}
+		/* Let cert callback update server certificates if required */
+		if (s->cert->cert_cb
+			&& s->cert->cert_cb(s, s->cert->cert_cb_arg) <= 0)
+			{
+			al=SSL_AD_INTERNAL_ERROR;
+			SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CERT_CB_ERROR);
+			goto f_err;
+			}
 		ciphers=NULL;
 		c=ssl3_choose_cipher(s,s->session->ciphers,
 				     SSL_get_ciphers(s));
diff --git a/ssl/ssl.h b/ssl/ssl.h
index 3675d6e..afeb60d 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -1800,6 +1800,7 @@
 void	SSL_set_verify(SSL *s, int mode,
 		       int (*callback)(int ok,X509_STORE_CTX *ctx));
 void	SSL_set_verify_depth(SSL *s, int depth);
+void SSL_set_cert_cb(SSL *s, int (*cb)(SSL *ssl, void *arg), void *arg);
 #ifndef OPENSSL_NO_RSA
 int	SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa);
 #endif
@@ -1895,6 +1896,7 @@
 			int (*callback)(int, X509_STORE_CTX *));
 void SSL_CTX_set_verify_depth(SSL_CTX *ctx,int depth);
 void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, int (*cb)(X509_STORE_CTX *,void *), void *arg);
+void SSL_CTX_set_cert_cb(SSL_CTX *c, int (*cb)(SSL *ssl, void *arg), void *arg);
 #ifndef OPENSSL_NO_RSA
 int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa);
 #endif
@@ -2462,6 +2464,7 @@
 #define SSL_R_CA_DN_TOO_LONG				 132
 #define SSL_R_CCS_RECEIVED_EARLY			 133
 #define SSL_R_CERTIFICATE_VERIFY_FAILED			 134
+#define SSL_R_CERT_CB_ERROR				 377
 #define SSL_R_CERT_LENGTH_MISMATCH			 135
 #define SSL_R_CHALLENGE_IS_DIFFERENT			 136
 #define SSL_R_CIPHER_CODE_WRONG_LENGTH			 137
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index 89a5131..9aa7b04 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -379,6 +379,9 @@
 
 	ret->cert_flags = cert->cert_flags;
 
+	ret->cert_cb = cert->cert_cb;
+	ret->cert_cb_arg = cert->cert_cb_arg;
+
 	return(ret);
 	
 #if !defined(OPENSSL_NO_DH) || !defined(OPENSSL_NO_ECDH)
@@ -557,6 +560,12 @@
 	return 1;
 	}
 
+void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), void *arg)
+	{
+	c->cert_cb = cb;
+	c->cert_cb_arg = arg;
+	}
+
 SESS_CERT *ssl_sess_cert_new(void)
 	{
 	SESS_CERT *ret;
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 3b32f55..34695e8 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -356,6 +356,7 @@
 {ERR_REASON(SSL_R_CA_DN_TOO_LONG)        ,"ca dn too long"},
 {ERR_REASON(SSL_R_CCS_RECEIVED_EARLY)    ,"ccs received early"},
 {ERR_REASON(SSL_R_CERTIFICATE_VERIFY_FAILED),"certificate verify failed"},
+{ERR_REASON(SSL_R_CERT_CB_ERROR)         ,"cert cb error"},
 {ERR_REASON(SSL_R_CERT_LENGTH_MISMATCH)  ,"cert length mismatch"},
 {ERR_REASON(SSL_R_CHALLENGE_IS_DIFFERENT),"challenge is different"},
 {ERR_REASON(SSL_R_CIPHER_CODE_WRONG_LENGTH),"cipher code wrong length"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index b3836b7..18e80d4 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2048,6 +2048,16 @@
 	X509_VERIFY_PARAM_set_depth(ctx->param, depth);
 	}
 
+void SSL_CTX_set_cert_cb(SSL_CTX *c, int (*cb)(SSL *ssl, void *arg), void *arg)
+	{
+	ssl_cert_set_cert_cb(c->cert, cb, arg);
+	}
+
+void SSL_set_cert_cb(SSL *s, int (*cb)(SSL *ssl, void *arg), void *arg)
+	{
+	ssl_cert_set_cert_cb(s->cert, cb, arg);
+	}
+
 void ssl_set_cert_masks(CERT *c, const SSL_CIPHER *cipher)
 	{
 	CERT_PKEY *cpk;
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index c2547ad..17bbbf5 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -550,6 +550,16 @@
 	TLS_SIGALGS *shared_sigalgs;
 	size_t shared_sigalgslen;
 
+	/* Certificate setup callback: if set is called whenever a
+	 * certificate may be required (client or server). the callback
+	 * can then examine any appropriate parameters and setup any
+	 * certificates required. This allows advanced applications
+	 * to select certificates on the fly: for example based on
+	 * supported signature algorithms or curves.
+	 */
+	int (*cert_cb)(SSL *ssl, void *arg);
+	void *cert_cb_arg;
+
 	int references; /* >1 only if SSL_copy_session_id is used */
 	} CERT;
 
@@ -888,6 +898,7 @@
 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);
+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);
 int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l);
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 46b3a4c..6b0ddf2 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -3514,5 +3514,10 @@
 	tls1_check_chain(s, NULL, NULL, NULL, SSL_PKEY_DH_DSA);
 	tls1_check_chain(s, NULL, NULL, NULL, SSL_PKEY_ECC);
 	}
+/* User level utiity function to check a chain is suitable */
+int SSL_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain)
+	{
+	return tls1_check_chain(s, x, pk, chain, -1);
+	}
 
 #endif
diff --git a/ssl/tls1.h b/ssl/tls1.h
index e2acad4..4d087e0 100644
--- a/ssl/tls1.h
+++ b/ssl/tls1.h
@@ -318,6 +318,8 @@
 			int *psign, int *phash, int *psignandhash,
 			unsigned char *rsig, unsigned char *rhash);
 
+int SSL_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain);
+
 #define SSL_set_tlsext_host_name(s,name) \
 SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,(char *)name)