I've noticed that sending CA chains (of potentially untrusted
certificates), with a client certificate on a per-connection basis is
currently impossible, as per the BUGS section of the
SSL_CTX_set_client_cert_cb(3) page and bug #270 in the request tracker.
This doesn't seem to be a difficult change (but it does change the size
of the SSL structure), and I was wondering if something like the
attached patch could be included in future releases.
Note that this isn't currently tested, but if it looks OK I can check it
actually works properly (and write my code using it :) ).
Thanks
--
Andrew Oakley
diff -ur openssl-1.0.0-beta3/include/openssl/ssl.h
openssl-1.0.0-beta3-extra-certs/include/openssl/ssl.h
--- openssl-1.0.0-beta3/include/openssl/ssl.h 2009-07-15 12:32:57.000000000
+0100
+++ openssl-1.0.0-beta3-extra-certs/include/openssl/ssl.h 2009-09-03
16:21:37.000000000 +0100
@@ -1106,6 +1106,8 @@
/* for server side, keep the list of CA_dn we can use */
STACK_OF(X509_NAME) *client_CA;
+ STACK_OF(X509) *extra_certs;
+
int references;
unsigned long options; /* protocol behaviour */
unsigned long mode; /* API behaviour */
@@ -1426,6 +1428,9 @@
#define SSL_CTX_add_extra_chain_cert(ctx,x509) \
SSL_CTX_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509)
+#define SSL_add_extra_chain_cert(ctx,x509) \
+ SSL_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509)
+
#ifndef OPENSSL_NO_BIO
BIO_METHOD *BIO_f_ssl(void);
BIO *BIO_new_ssl(SSL_CTX *ctx,int client);
diff -ur openssl-1.0.0-beta3/ssl/d1_both.c
openssl-1.0.0-beta3-extra-certs/ssl/d1_both.c
--- openssl-1.0.0-beta3/ssl/d1_both.c 2009-07-15 12:32:57.000000000 +0100
+++ openssl-1.0.0-beta3-extra-certs/ssl/d1_both.c 2009-09-03
16:25:41.000000000 +0100
@@ -837,6 +837,7 @@
int i;
unsigned long l= 3 + DTLS1_HM_HEADER_LENGTH;
BUF_MEM *buf;
+ STACK_OF(X509) *extras;
/* TLSv1 sends a chain with nothing in it, instead of an alert */
buf=s->init_buf;
@@ -868,10 +869,14 @@
}
X509_STORE_CTX_cleanup(&xs_ctx);
}
- /* Thawte special :-) */
- for (i=0; i<sk_X509_num(s->ctx->extra_certs); i++)
+
+ if (s->extra_certs)
+ extras = s->extra_certs;
+ else
+ extras = s->ctx->extra_certs;
+ for (i=0; i<sk_X509_num(extras); i++)
{
- x=sk_X509_value(s->ctx->extra_certs,i);
+ x=sk_X509_value(extras,i);
if (!dtls1_add_cert_to_buf(buf, &l, x))
return 0;
}
diff -ur openssl-1.0.0-beta3/ssl/s3_both.c
openssl-1.0.0-beta3-extra-certs/ssl/s3_both.c
--- openssl-1.0.0-beta3/ssl/s3_both.c 2009-07-15 12:32:57.000000000 +0100
+++ openssl-1.0.0-beta3-extra-certs/ssl/s3_both.c 2009-09-03
16:00:50.000000000 +0100
@@ -288,8 +288,14 @@
unsigned long l=7;
BUF_MEM *buf;
int no_chain;
+ STACK_OF(X509) *extras;
- if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || s->ctx->extra_certs)
+ if (s->extra_certs)
+ extras = s->extra_certs;
+ else
+ extras = s->ctx->extra_certs;
+
+ if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || extras)
no_chain = 1;
else
no_chain = 0;
@@ -332,9 +338,9 @@
}
}
/* Thawte special :-) */
- for (i=0; i<sk_X509_num(s->ctx->extra_certs); i++)
+ for (i=0; i<sk_X509_num(extras); i++)
{
- x=sk_X509_value(s->ctx->extra_certs,i);
+ x=sk_X509_value(extras,i);
if (ssl3_add_cert_to_buf(buf, &l, x))
return(0);
}
diff -ur openssl-1.0.0-beta3/ssl/s3_lib.c
openssl-1.0.0-beta3-extra-certs/ssl/s3_lib.c
--- openssl-1.0.0-beta3/ssl/s3_lib.c 2009-05-28 19:10:47.000000000 +0100
+++ openssl-1.0.0-beta3-extra-certs/ssl/s3_lib.c 2009-09-03
16:16:29.000000000 +0100
@@ -2477,6 +2477,16 @@
break;
#endif /* !OPENSSL_NO_TLSEXT */
+
+ case SSL_CTRL_EXTRA_CHAIN_CERT:
+ if (s->extra_certs == NULL)
+ {
+ if ((s->extra_certs=sk_X509_new_null()) == NULL)
+ return(0);
+ }
+ sk_X509_push(s->extra_certs,(X509 *)parg);
+ break;
+
default:
break;
}
diff -ur openssl-1.0.0-beta3/ssl/ssl.h openssl-1.0.0-beta3-extra-certs/ssl/ssl.h
--- openssl-1.0.0-beta3/ssl/ssl.h 2009-07-15 12:32:57.000000000 +0100
+++ openssl-1.0.0-beta3-extra-certs/ssl/ssl.h 2009-09-03 16:21:37.000000000
+0100
@@ -1106,6 +1106,8 @@
/* for server side, keep the list of CA_dn we can use */
STACK_OF(X509_NAME) *client_CA;
+ STACK_OF(X509) *extra_certs;
+
int references;
unsigned long options; /* protocol behaviour */
unsigned long mode; /* API behaviour */
@@ -1426,6 +1428,9 @@
#define SSL_CTX_add_extra_chain_cert(ctx,x509) \
SSL_CTX_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509)
+#define SSL_add_extra_chain_cert(ctx,x509) \
+ SSL_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509)
+
#ifndef OPENSSL_NO_BIO
BIO_METHOD *BIO_f_ssl(void);
BIO *BIO_new_ssl(SSL_CTX *ctx,int client);
diff -ur openssl-1.0.0-beta3/ssl/ssl_lib.c
openssl-1.0.0-beta3-extra-certs/ssl/ssl_lib.c
--- openssl-1.0.0-beta3/ssl/ssl_lib.c 2009-06-30 12:57:24.000000000 +0100
+++ openssl-1.0.0-beta3-extra-certs/ssl/ssl_lib.c 2009-09-03
16:28:23.000000000 +0100
@@ -357,6 +357,9 @@
s->verify_result=X509_V_OK;
+ s->client_CA = NULL;
+ s->extra_certs = NULL;
+
s->method=ctx->method;
if (!s->method->ssl_new(s))
@@ -577,6 +580,8 @@
if (s->client_CA != NULL)
sk_X509_NAME_pop_free(s->client_CA,X509_NAME_free);
+ if (s->extra_certs != NULL)
+ sk_X509_pop_free(s->extra_certs,X509_free);
if (s->method != NULL) s->method->ssl_free(s);
@@ -2393,6 +2398,7 @@
{
STACK_OF(X509_NAME) *sk;
X509_NAME *xn;
+ X509 *x;
SSL *ret;
int i;
@@ -2510,6 +2516,20 @@
}
}
+ if (s->extra_certs != NULL)
+ {
+ if ((ret->extra_certs=sk_X509_dup(s->extra_certs)) == NULL)
goto err;
+ for (i=0; i<sk_X509_num(s->extra_certs); i++)
+ {
+ x=sk_X509_value(s->extra_certs,i);
+ if (sk_X509_set(s->extra_certs,i,X509_dup(x)) == NULL)
+ {
+ X509_free(x);
+ goto err;
+ }
+ }
+ }
+
if (0)
{
err: