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/CHANGES b/CHANGES
index c690bb3..2fe4847 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,15 @@
 
  Changes between 1.0.1 and 1.1.0  [xx XXX xxxx]
 
+  *) 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 an existing
+     certificate and specify the whole chain.
+     [Steve Henson]
+
   *) Add new "valid_flags" field to CERT_PKEY structure which determines what
      the certificate can be used for (if anything). Set valid_flags field 
      in new tls1_check_chain function. Simplify ssl_set_cert_masks which used
diff --git a/apps/s_apps.h b/apps/s_apps.h
index 8c644ec..3491b1a 100644
--- a/apps/s_apps.h
+++ b/apps/s_apps.h
@@ -181,3 +181,11 @@
 
 int MS_CALLBACK generate_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len);
 int MS_CALLBACK verify_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned int cookie_len);
+
+typedef struct ssl_excert_st SSL_EXCERT;
+
+void ssl_ctx_set_excert(SSL_CTX *ctx, SSL_EXCERT *exc);
+void ssl_excert_free(SSL_EXCERT *exc);
+int args_excert(char ***pargs, int *pargc,
+			int *badarg, BIO *err, SSL_EXCERT **pexc);
+int load_excert(SSL_EXCERT **pexc, BIO *err);
diff --git a/apps/s_cb.c b/apps/s_cb.c
index 79e8ae5..34c0185 100644
--- a/apps/s_cb.c
+++ b/apps/s_cb.c
@@ -1037,3 +1037,246 @@
 
 	return 0;
 	}
+
+/* Example of extended certificate handling. Where the standard support
+ * of one certificate per algorithm is not sufficient an application
+ * can decide which certificate(s) to use at runtime based on whatever
+ * criteria it deems appropriate.
+ */
+
+/* Linked list of certificates, keys and chains */
+struct  ssl_excert_st
+	{
+	int certform;
+	const char *certfile;
+	int keyform;
+	const char *keyfile;
+	const char *chainfile;
+	X509 *cert;
+	EVP_PKEY *key;
+	STACK_OF(X509) *chain;
+	struct ssl_excert_st *next, *prev;
+	};
+
+/* Very basic selection callback: just use any certificate chain
+ * reported as valid. More sophisticated could prioritise according
+ * to local policy.
+ */
+static int set_cert_cb(SSL *ssl, void *arg)
+	{
+	SSL_EXCERT *exc = arg;
+	SSL_certs_clear(ssl);
+
+	if (!exc)
+		return 1;
+
+	/* Go to end of list and traverse backwards since we prepend
+	 * newer entries this retains the original order.
+	 */
+	while (exc->next)
+		exc = exc->next;
+	
+	while(exc)
+		{
+		if (SSL_check_chain(ssl, exc->cert, exc->key, exc->chain))
+			{
+			SSL_use_certificate(ssl, exc->cert);
+			SSL_use_PrivateKey(ssl, exc->key);
+			if (exc->chain)
+				SSL_set1_chain(ssl, exc->chain);
+			}
+		exc = exc->prev;
+		}
+	return 1;
+	}
+
+void ssl_ctx_set_excert(SSL_CTX *ctx, SSL_EXCERT *exc)
+	{
+	SSL_CTX_set_cert_cb(ctx, set_cert_cb, exc);
+	}
+
+static int ssl_excert_prepend(SSL_EXCERT **pexc)
+	{
+	SSL_EXCERT *exc;
+	exc = OPENSSL_malloc(sizeof(SSL_EXCERT));
+	if (!exc)
+		return 0;
+	exc->certfile = NULL;
+	exc->keyfile = NULL;
+	exc->chainfile = NULL;
+	exc->cert = NULL;
+	exc->key = NULL;
+	exc->chain = NULL;
+	exc->prev = NULL;
+
+	exc->next = *pexc;
+	*pexc = exc;
+			
+	if (exc->next)
+		{
+		exc->certform = exc->next->certform;
+		exc->keyform = exc->next->keyform;
+		exc->next->prev = exc;
+		}
+	else
+		{
+		exc->certform = FORMAT_PEM;
+		exc->keyform = FORMAT_PEM;
+		}
+	return 1;
+
+	}
+
+void ssl_excert_free(SSL_EXCERT *exc)
+	{
+	SSL_EXCERT *curr;
+	while (exc)
+		{
+		if (exc->cert)
+			X509_free(exc->cert);
+		if (exc->key)
+			EVP_PKEY_free(exc->key);
+		if (exc->chain)
+			sk_X509_pop_free(exc->chain, X509_free);
+		curr = exc;
+		exc = exc->next;
+		OPENSSL_free(curr);
+		}
+	}
+
+int load_excert(SSL_EXCERT **pexc, BIO *err)
+	{
+	SSL_EXCERT *exc = *pexc;
+	if (!exc)
+		return 1;
+	/* If nothing in list, free and set to NULL */
+	if (!exc->certfile && !exc->next)
+		{
+		ssl_excert_free(exc);
+		*pexc = NULL;
+		return 1;
+		}
+	for(; exc; exc=exc->next)
+		{
+		if (!exc->certfile)
+			{
+			BIO_printf(err, "Missing filename\n");
+			return 0;
+			}
+		exc->cert = load_cert(err, exc->certfile, exc->certform,
+					NULL, NULL, "Server Certificate");
+		if (!exc->cert)
+			return 0;
+		if (exc->keyfile)
+			exc->keyfile = exc->certfile;
+		exc->key = load_key(err, exc->certfile, exc->certform, 0,
+					NULL, NULL, "Server Certificate");
+		if (!exc->key)
+			return 0;
+		if (exc->chainfile)
+			{
+			exc->chain = load_certs(err,
+						exc->chainfile, FORMAT_PEM,
+						NULL, NULL,
+						"Server Chain");
+			if (!exc->chainfile)
+				return 0;
+			}
+		}
+	return 1;
+	}
+		
+
+int args_excert(char ***pargs, int *pargc,
+			int *badarg, BIO *err, SSL_EXCERT **pexc)
+	{
+	char *arg = **pargs, *argn = (*pargs)[1];
+	SSL_EXCERT *exc = *pexc;
+	if (!exc && !ssl_excert_prepend(&exc))
+		{
+		BIO_printf(err, "Error initialising xcert\n");
+		*badarg = 1;
+		goto err;
+		}
+	if (strcmp(arg, "-xcert") == 0)
+		{
+		if (!argn)
+			{
+			*badarg = 1;
+			return 1;
+			}
+		if (exc->certfile && !ssl_excert_prepend(&exc))
+			{
+			BIO_printf(err, "Error adding xcert\n");
+			*badarg = 1;
+			goto err;
+			}
+		exc->certfile = argn;
+		}
+	else if (strcmp(arg,"-xkey") == 0)
+		{
+		if (!argn)
+			{
+			*badarg = 1;
+			return 1;
+			}
+		if (exc->keyfile)
+			{
+			BIO_printf(err, "Key already specified\n");
+			*badarg = 1;
+			return 1;
+			}
+		exc->keyfile = argn;
+		}
+	else if (strcmp(arg,"-xchain") == 0)
+		{
+		if (!argn)
+			{
+			*badarg = 1;
+			return 1;
+			}
+		if (exc->chainfile)
+			{
+			BIO_printf(err, "Chain already specified\n");
+			*badarg = 1;
+			return 1;
+			}
+		exc->chainfile = argn;
+		}
+	else if (strcmp(arg,"-xcertform") == 0)
+		{
+		if (!argn)
+			{
+			*badarg = 1;
+			goto err;
+			}
+		exc->certform = str2fmt(argn);
+		}
+	else if (strcmp(arg,"-xkeyform") == 0)
+		{
+		if (!argn)
+			{
+			*badarg = 1;
+			goto err;
+			}
+		exc->keyform = str2fmt(argn);
+		}
+	else
+		return 0;
+
+	(*pargs) += 2;
+
+	if (pargc)
+		*pargc -= 2;
+
+	*pexc = exc;
+
+	return 1;
+
+	err:
+	ERR_print_errors(err);
+	ssl_excert_free(exc);
+	*pexc = NULL;
+	return 1;
+	}
+
diff --git a/apps/s_server.c b/apps/s_server.c
index f190d8e..649a8a9 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -991,6 +991,7 @@
 	char *srpuserseed = NULL;
 	char *srp_verifier_file = NULL;
 #endif
+	SSL_EXCERT *exc = NULL;
 	meth=SSLv23_server_method();
 
 	local_argc=argc;
@@ -1143,6 +1144,12 @@
 				goto bad;
 			continue;
 			}
+		else if (args_excert(&argv, &argc, &badarg, bio_err, &exc))
+			{
+			if (badarg)
+				goto bad;
+			continue;
+			}
 		else if (strcmp(*argv,"-verify_return_error") == 0)
 			verify_return_error = 1;
 		else if	(strcmp(*argv,"-serverpref") == 0)
@@ -1456,6 +1463,9 @@
 		s_key_file2 = s_cert_file2;
 #endif
 
+	if (!load_excert(&exc, bio_err))
+		goto end;
+
 	if (nocert == 0)
 		{
 		s_key = load_key(bio_err, s_key_file, s_key_format, 0, pass, e,
@@ -1618,6 +1628,7 @@
 	if (hack) SSL_CTX_set_options(ctx,SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG);
 	SSL_CTX_set_options(ctx,off);
 	if (cert_flags) SSL_CTX_set_cert_flags(ctx, cert_flags);
+	if (exc) ssl_ctx_set_excert(ctx, exc);
 	/* DTLS: partial reads end up discarding unread UDP bytes :-( 
 	 * Setting read ahead solves this problem.
 	 */
@@ -1692,6 +1703,7 @@
 		if (hack) SSL_CTX_set_options(ctx2,SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG);
 		SSL_CTX_set_options(ctx2,off);
 		if (cert_flags) SSL_CTX_set_cert_flags(ctx2, cert_flags);
+		if (exc) ssl_ctx_set_excert(ctx2, exc);
 		/* DTLS: partial reads end up discarding unread UDP bytes :-( 
 		 * Setting read ahead solves this problem.
 		 */
@@ -2035,6 +2047,7 @@
 	if (authz_in != NULL)
 		BIO_free(authz_in);
 #endif
+	ssl_excert_free(exc);
 	if (bio_s_out != NULL)
 		{
         BIO_free(bio_s_out);
diff --git a/demos/certs/apps/mkxcerts.sh b/demos/certs/apps/mkxcerts.sh
new file mode 100644
index 0000000..88fb1c5
--- /dev/null
+++ b/demos/certs/apps/mkxcerts.sh
@@ -0,0 +1,29 @@
+
+# Create certificates using various algorithms to test multi-certificate
+# functionality.
+
+OPENSSL=../../../apps/openssl
+CN="OpenSSL Test RSA SHA-1 cert" $OPENSSL req \
+	-config apps.cnf -extensions usr_cert -x509 -nodes \
+	-keyout tsha1.pem -out tsha1.pem -new -days 3650 -sha1
+CN="OpenSSL Test RSA SHA-256 cert" $OPENSSL req \
+	-config apps.cnf -extensions usr_cert -x509 -nodes \
+	-keyout tsha256.pem -out tsha256.pem -new -days 3650 -sha256
+CN="OpenSSL Test RSA SHA-512 cert" $OPENSSL req \
+	-config apps.cnf -extensions usr_cert -x509 -nodes \
+	-keyout tsha512.pem -out tsha512.pem -new -days 3650 -sha512
+
+# Create EC parameters 
+
+$OPENSSL ecparam -name P-256 -out ecp256.pem
+$OPENSSL ecparam -name P-384 -out ecp384.pem
+
+CN="OpenSSL Test P-256 SHA-256 cert" $OPENSSL req \
+	-config apps.cnf -extensions usr_cert -x509 -nodes \
+	-nodes -keyout tecp256.pem -out tecp256.pem -newkey ec:ecp256.pem \
+	-days 3650 -sha256
+
+CN="OpenSSL Test P-384 SHA-384 cert" $OPENSSL req \
+	-config apps.cnf -extensions usr_cert -x509 -nodes \
+	-nodes -keyout tecp384.pem -out tecp384.pem -newkey ec:ecp384.pem \
+	-days 3650 -sha384
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)