Skip Menu | | Logout
Logged in as guest
RT for openssl.org
 
 
#1942: [PATCH] ssl3_output_cert_chain() selects wrong certificate as issuer.
X  Ticket metadata  
X  The Basics  
Id: 1942
Status: resolved
Left: 0 min
Priority: 0/0
Queue: OpenSSL-Bugs

X  Custom Fields  
Milestone:
  • (no value)
Subsystem:
  • (no value)
Severity:
  • (no value)
Broken in:
  • (no value)

X  People  
Owner: steve <steve@openssl.org>
Requestors: dwmw2@infradead.org
Cc:
AdminCc:

X  Dates  
Created: Sun May 31 22:08:10 2009
Starts: Not set
Started: Not set
Last Contact: Mon Jun 29 00:25:07 2009
Due: Not set
Closed: Thu Mar 25 00:19:16 2010
Updated: Thu Mar 25 00:19:17 2010 by steve

X  Links  
Depends on:
Depended on by:
Parents:
Children:
Refers to:
Referred to by:

X  More about David Woodhouse  
Comments about this user:
No comment entered about this user
This user's 10 highest priority tickets:
Groups this user belongs to:
  • Everyone
  • Unprivileged

X  History Display mode:[Brief headers] [Full headers]
#     Sun May 31 22:08:11 2009  dwmw2@infradead.org - Ticket created    
Subject: [PATCH] ssl3_output_cert_chain() selects wrong certificate as issuer.
Date: Sun, 31 May 2009 19:06:44 +0100
To: rt@openssl.org
From: David Woodhouse <dwmw2@infradead.org>
Download (untitled)
text/plain 3.2k
It's possible for multiple certificates to have the same subject name,
and if that happens then ssl3_output_cert_chain() may select the wrong
one because it just picks a certificate by name and doesn't actually
_check_ if it really is the right one.

There's a function which gets this right; X509_STORE_CTX_get1_issuer().
We should use it. dtls1_output_cert_chain() has the same problem.

We fix the check for self-signed certificates too, while we're at it.
Although that's much less likely to be a problem in practice.

Patch applies to 0.9.8 and HEAD.

Fix the
Index: ssl/d1_both.c
===================================================================
RCS file: /home/dwmw2/openssl-cvs/openssl/ssl/d1_both.c,v
retrieving revision 1.4.2.9
diff -u -p -r1.4.2.9 d1_both.c
--- ssl/d1_both.c 2 Apr 2009 22:32:15 -0000 1.4.2.9
+++ ssl/d1_both.c 31 May 2009 17:55:41 -0000
@@ -808,7 +808,6 @@ unsigned long dtls1_output_cert_chain(SS
unsigned long l= 3 + DTLS1_HM_HEADER_LENGTH;
BUF_MEM *buf;
X509_STORE_CTX xs_ctx;
- X509_OBJECT obj;

/* TLSv1 sends a chain with nothing in it, instead of an alert */
buf=s->init_buf;
@@ -827,6 +826,7 @@ unsigned long dtls1_output_cert_chain(SS

for (;;)
{
+ X509 *issuer;
n=i2d_X509(x,NULL);
if (!BUF_MEM_grow_clean(buf,(int)(n+l+3)))
{
@@ -837,16 +837,18 @@ unsigned long dtls1_output_cert_chain(SS
l2n3(n,p);
i2d_X509(x,&p);
l+=n+3;
- if (X509_NAME_cmp(X509_get_subject_name(x),
- X509_get_issuer_name(x)) == 0) break;

- i=X509_STORE_get_by_subject(&xs_ctx,X509_LU_X509,
- X509_get_issuer_name(x),&obj);
+ /* self-signed */
+ if (xs_ctx.check_issued(&xs_ctx, x, x))
+ break;
+
+ i=X509_STORE_CTX_get1_issuer(&issuer, &xs_ctx, x);
if (i <= 0) break;
- x=obj.data.x509;
+
/* Count is one too high since the X509_STORE_get uped the
* ref count */
X509_free(x);
+ x = issuer;
}

X509_STORE_CTX_cleanup(&xs_ctx);
Index: ssl/s3_both.c
===================================================================
RCS file: /home/dwmw2/openssl-cvs/openssl/ssl/s3_both.c,v
retrieving revision 1.43
diff -u -p -r1.43 s3_both.c
--- ssl/s3_both.c 26 Apr 2005 16:02:39 -0000 1.43
+++ ssl/s3_both.c 31 May 2009 17:51:31 -0000
@@ -271,8 +271,6 @@ unsigned long ssl3_output_cert_chain(SSL
unsigned long l=7;
BUF_MEM *buf;
X509_STORE_CTX xs_ctx;
- X509_OBJECT obj;
-
int no_chain;

if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || s->ctx->extra_certs)
@@ -297,6 +295,7 @@ unsigned long ssl3_output_cert_chain(SSL

for (;;)
{
+ X509 *issuer;
n=i2d_X509(x,NULL);
if (!BUF_MEM_grow_clean(buf,(int)(n+l+3)))
{
@@ -311,16 +310,17 @@ unsigned long ssl3_output_cert_chain(SSL
if (no_chain)
break;

- if (X509_NAME_cmp(X509_get_subject_name(x),
- X509_get_issuer_name(x)) == 0) break;
+ /* self-signed */
+ if (xs_ctx.check_issued(&xs_ctx, x, x))
+ break;

- i=X509_STORE_get_by_subject(&xs_ctx,X509_LU_X509,
- X509_get_issuer_name(x),&obj);
+ i=X509_STORE_CTX_get1_issuer(&issuer, &xs_ctx, x);
if (i <= 0) break;
- x=obj.data.x509;
+
/* Count is one too high since the X509_STORE_get uped the
* ref count */
X509_free(x);
+ x = issuer;
}
if (!no_chain)
X509_STORE_CTX_cleanup(&xs_ctx);

--
dwmw2
#     Tue Jun 02 13:40:36 2009  steve - Correspondence added    
Download (untitled)
text/plain 888b
> [dwmw2@infradead.org - Sun May 31 22:08:11 2009]:
>
> It's possible for multiple certificates to have the same subject name,
> and if that happens then ssl3_output_cert_chain() may select the wrong
> one because it just picks a certificate by name and doesn't actually
> _check_ if it really is the right one.
>
> There's a function which gets this right; X509_STORE_CTX_get1_issuer().
> We should use it. dtls1_output_cert_chain() has the same problem.
>
> We fix the check for self-signed certificates too, while we're at it.
> Although that's much less likely to be a problem in practice.
>
> Patch applies to 0.9.8 and HEAD.
>

I agree the existing logic is badly broken, it's one of those things
that has been almost untouched since SSLeay days.

If however we are going to revise this I'd say we should use
X509_verify_cert to build the chain instead of more ad-hoc stuff.
#     Tue Jun 02 13:40:36 2009  RT_System - Status changed from 'new' to 'open'    
#     Tue Jun 02 15:21:30 2009  dwmw2@infradead.org - Correspondence added    
Subject: Re: [openssl.org #1942] [PATCH] ssl3_output_cert_chain() selects wrong certificate as issuer.
Date: Tue, 02 Jun 2009 15:15:37 +0100
To: rt@openssl.org
From: David Woodhouse <dwmw2@infradead.org>
Download (untitled)
text/plain 5.9k
On Tue, 2009-06-02 at 13:40 +0200, Stephen Henson via RT wrote:
> If however we are going to revise this I'd say we should use
> X509_verify_cert to build the chain instead of more ad-hoc stuff.

This seems to work... only tested for SSLv3/TLSv1, not DTLS.

Index: ssl/d1_both.c
===================================================================
RCS file: /home/dwmw2/openssl-cvs/openssl/ssl/d1_both.c,v
retrieving revision 1.4.2.9
diff -u -p -r1.4.2.9 d1_both.c
--- ssl/d1_both.c 2 Apr 2009 22:32:15 -0000 1.4.2.9
+++ ssl/d1_both.c 2 Jun 2009 14:06:50 -0000
@@ -801,14 +801,30 @@ int dtls1_send_change_cipher_spec(SSL *s
return(dtls1_do_write(s,SSL3_RT_CHANGE_CIPHER_SPEC));
}

+static int dtls1_add_cert_to_buf(BUF_MEM *buf, unsigned long *l, X509 *x)
+ {
+ int n;
+ unsigned char *p;
+
+ n=i2d_X509(x,NULL);
+ if (!BUF_MEM_grow_clean(buf,(int)(n+(*l)+3)))
+ {
+ SSLerr(SSL_F_DTLS1_OUTPUT_CERT_CHAIN,ERR_R_BUF_LIB);
+ return(-1);
+ }
+ p=(unsigned char *)&(buf->data[*l]);
+ l2n3(n,p);
+ i2d_X509(x,&p);
+ *l+=n+3;
+
+ return(0);
+ }
unsigned long dtls1_output_cert_chain(SSL *s, X509 *x)
{
unsigned char *p;
- int n,i;
+ int i;
unsigned long l= 3 + DTLS1_HM_HEADER_LENGTH;
BUF_MEM *buf;
- X509_STORE_CTX xs_ctx;
- X509_OBJECT obj;

/* TLSv1 sends a chain with nothing in it, instead of an alert */
buf=s->init_buf;
@@ -819,54 +835,36 @@ unsigned long dtls1_output_cert_chain(SS
}
if (x != NULL)
{
- if(!X509_STORE_CTX_init(&xs_ctx,s->ctx->cert_store,NULL,NULL))
+ X509_STORE_CTX xs_ctx;
+
+ if (!X509_STORE_CTX_init(&xs_ctx,s->ctx->cert_store,NULL,NULL))
{
SSLerr(SSL_F_DTLS1_OUTPUT_CERT_CHAIN,ERR_R_X509_LIB);
return(0);
}

- for (;;)
+ xs_ctx.cert = x;
+ X509_verify_cert(&xs_ctx);
+ for (i=0; i < sk_X509_num(xs_ctx.chain); i++)
{
- n=i2d_X509(x,NULL);
- if (!BUF_MEM_grow_clean(buf,(int)(n+l+3)))
+ x = sk_X509_value(xs_ctx.chain, i);
+
+ if (dtls1_add_cert_to_buf(buf, &l, x))
{
- SSLerr(SSL_F_DTLS1_OUTPUT_CERT_CHAIN,ERR_R_BUF_LIB);
- return(0);
+ X509_STORE_CTX_cleanup(&xs_ctx);
+ return 0;
}
- p=(unsigned char *)&(buf->data[l]);
- l2n3(n,p);
- i2d_X509(x,&p);
- l+=n+3;
- if (X509_NAME_cmp(X509_get_subject_name(x),
- X509_get_issuer_name(x)) == 0) break;
-
- i=X509_STORE_get_by_subject(&xs_ctx,X509_LU_X509,
- X509_get_issuer_name(x),&obj);
- if (i <= 0) break;
- x=obj.data.x509;
- /* Count is one too high since the X509_STORE_get uped the
- * ref count */
- X509_free(x);
}
-
X509_STORE_CTX_cleanup(&xs_ctx);
}
-
/* Thawte special :-) */
if (s->ctx->extra_certs != NULL)
for (i=0; i<sk_X509_num(s->ctx->extra_certs); i++)
{
x=sk_X509_value(s->ctx->extra_certs,i);
- n=i2d_X509(x,NULL);
- if (!BUF_MEM_grow_clean(buf,(int)(n+l+3)))
- {
- SSLerr(SSL_F_DTLS1_OUTPUT_CERT_CHAIN,ERR_R_BUF_LIB);
+
+ if (dtls1_add_cert_to_buf(buf, &l, x))
return(0);
- }
- p=(unsigned char *)&(buf->data[l]);
- l2n3(n,p);
- i2d_X509(x,&p);
- l+=n+3;
}

l-= (3 + DTLS1_HM_HEADER_LENGTH);
Index: ssl/s3_both.c
===================================================================
RCS file: /home/dwmw2/openssl-cvs/openssl/ssl/s3_both.c,v
retrieving revision 1.43
diff -u -p -r1.43 s3_both.c
--- ssl/s3_both.c 26 Apr 2005 16:02:39 -0000 1.43
+++ ssl/s3_both.c 2 Jun 2009 14:07:13 -0000
@@ -264,15 +264,31 @@ int ssl3_send_change_cipher_spec(SSL *s,
return(ssl3_do_write(s,SSL3_RT_CHANGE_CIPHER_SPEC));
}

+static int ssl3_add_cert_to_buf(BUF_MEM *buf, unsigned long *l, X509 *x)
+ {
+ int n;
+ unsigned char *p;
+
+ n=i2d_X509(x,NULL);
+ if (!BUF_MEM_grow_clean(buf,(int)(n+(*l)+3)))
+ {
+ SSLerr(SSL_F_SSL3_OUTPUT_CERT_CHAIN,ERR_R_BUF_LIB);
+ return(-1);
+ }
+ p=(unsigned char *)&(buf->data[*l]);
+ l2n3(n,p);
+ i2d_X509(x,&p);
+ *l+=n+3;
+
+ return(0);
+ }
+
unsigned long ssl3_output_cert_chain(SSL *s, X509 *x)
{
unsigned char *p;
- int n,i;
+ int i;
unsigned long l=7;
BUF_MEM *buf;
- X509_STORE_CTX xs_ctx;
- X509_OBJECT obj;
-
int no_chain;

if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || s->ctx->extra_certs)
@@ -289,58 +305,45 @@ unsigned long ssl3_output_cert_chain(SSL
}
if (x != NULL)
{
- if(!no_chain && !X509_STORE_CTX_init(&xs_ctx,s->ctx->cert_store,NULL,NULL))
+ if (no_chain)
{
- SSLerr(SSL_F_SSL3_OUTPUT_CERT_CHAIN,ERR_R_X509_LIB);
- return(0);
+ if (ssl3_add_cert_to_buf(buf, &l, x))
+ return(0);
}
-
- for (;;)
+ else
{
- n=i2d_X509(x,NULL);
- if (!BUF_MEM_grow_clean(buf,(int)(n+l+3)))
+ X509_STORE_CTX xs_ctx;
+
+ if (!X509_STORE_CTX_init(&xs_ctx,s->ctx->cert_store,NULL,NULL))
{
- SSLerr(SSL_F_SSL3_OUTPUT_CERT_CHAIN,ERR_R_BUF_LIB);
+ SSLerr(SSL_F_SSL3_OUTPUT_CERT_CHAIN,ERR_R_X509_LIB);
return(0);
}
- p=(unsigned char *)&(buf->data[l]);
- l2n3(n,p);
- i2d_X509(x,&p);
- l+=n+3;
-
- if (no_chain)
- break;
-
- if (X509_NAME_cmp(X509_get_subject_name(x),
- X509_get_issuer_name(x)) == 0) break;
-
- i=X509_STORE_get_by_subject(&xs_ctx,X509_LU_X509,
- X509_get_issuer_name(x),&obj);
- if (i <= 0) break;
- x=obj.data.x509;
- /* Count is one too high since the X509_STORE_get uped the
- * ref count */
- X509_free(x);
- }
- if (!no_chain)
+
+ xs_ctx.cert = x;
+ i = X509_verify_cert(&xs_ctx);
+ printf("X509_verify_cert() returned %d\n", i);
+ for (i=0; i < sk_X509_num(xs_ctx.chain); i++)
+ {
+ x = sk_X509_value(xs_ctx.chain, i);
+
+ if (ssl3_add_cert_to_buf(buf, &l, x))
+ {
+ X509_STORE_CTX_cleanup(&xs_ctx);
+ return 0;
+ }
+ }
X509_STORE_CTX_cleanup(&xs_ctx);
+ }
}
-
/* Thawte special :-) */
if (s->ctx->extra_certs != NULL)
for (i=0; i<sk_X509_num(s->ctx->extra_certs); i++)
{
x=sk_X509_value(s->ctx->extra_certs,i);
- n=i2d_X509(x,NULL);
- if (!BUF_MEM_grow_clean(buf,(int)(n+l+3)))
- {
- SSLerr(SSL_F_SSL3_OUTPUT_CERT_CHAIN,ERR_R_BUF_LIB);
+
+ if (ssl3_add_cert_to_buf(buf, &l, x))
return(0);
- }
- p=(unsigned char *)&(buf->data[l]);
- l2n3(n,p);
- i2d_X509(x,&p);
- l+=n+3;
}

l-=7;

--
dwmw2
#     Sun Jun 28 16:12:44 2009  steve - Taken    
#     Mon Jun 29 00:25:06 2009  steve - Correspondence added    
Download (untitled)
text/plain 406b
> [dwmw2@infradead.org - Tue Jun 02 15:21:30 2009]:
>
> On Tue, 2009-06-02 at 13:40 +0200, Stephen Henson via RT wrote:
> > If however we are going to revise this I'd say we should use
> > X509_verify_cert to build the chain instead of more ad-hoc stuff.
>
> This seems to work... only tested for SSLv3/TLSv1, not DTLS.
>

I've committed a slightly modified version of this. Let me know of any
problems.
#     Thu Mar 25 00:19:16 2010  steve - Status changed from 'open' to 'resolved'    
»|« RT 3.4.5 Copyright 1996-2005 Best Practical Solutions, LLC.
Time to display: 0.65727