The OCSP Stapling Callback function (s->ctx->tlsext_status_cb) is called 
during the parsing of the ClientHello message, before the server has 
decided which cipher to use.  However, since the choice of cipher can 
influence which server certificate is sent, this means that the wrong 
OCSP Response may be sent in cases where multiple server certificates 
are configured.

The attached patch against CVS HEAD makes the following changes:
   - Moves the s->ctx->tlsext_status_cb() call to just after the cipher 
has been chosen.  This involves splitting ssl_check_clienthello_tlsext() 
into two functions: "early" and "late".
   - Updates SSL_get_certificate() so that it returns the server 
certificate that actually gets sent.  (This is the function that Apache 
httpd's OCSP Stapling code calls in order to determine which OCSP 
Response to send).

I've tested this patch successfully with an installation of httpd 2.4.2 
that has both an RSA cert and an ECC cert configured.

If this patch is OK, I'd like to backport it to the OpenSSL 1.0.x branch 
as well.

-- 
Rob Stradling
Senior Research & Development Scientist
COMODO - Creating Trust Online

Index: ssl/s3_srvr.c
===================================================================
RCS file: /v/openssl/cvs/openssl/ssl/s3_srvr.c,v
retrieving revision 1.233
diff -u -r1.233 s3_srvr.c
--- ssl/s3_srvr.c       6 Jun 2012 12:52:19 -0000       1.233
+++ ssl/s3_srvr.c       19 Jun 2012 10:59:34 -0000
@@ -1424,6 +1424,16 @@
         * s->tmp.new_cipher    - the new cipher to use.
         */
 
+       /* Handles TLS extensions that we couldn't check earlier */
+       if (s->version >= SSL3_VERSION)
+               {
+               if (!ssl_check_clienthello_tlsext_late(s))
+                       {
+                       SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_PARSE_TLSEXT);
+                       goto err;
+                       }
+               }
+
        if (ret < 0) ret=1;
        if (0)
                {
Index: ssl/ssl_lib.c
===================================================================
RCS file: /v/openssl/cvs/openssl/ssl/ssl_lib.c,v
retrieving revision 1.234
diff -u -r1.234 ssl_lib.c
--- ssl/ssl_lib.c       18 Jun 2012 12:56:59 -0000      1.234
+++ ssl/ssl_lib.c       19 Jun 2012 10:59:34 -0000
@@ -2846,6 +2846,14 @@
 /* Fix this function so that it takes an optional type parameter */
 X509 *SSL_get_certificate(const SSL *s)
        {
+       if (s->server)
+               {
+               CERT_PKEY *certpkey;
+               certpkey = ssl_get_server_send_pkey((SSL *)s);
+               if (certpkey && certpkey->x509)
+                       return certpkey->x509;
+               }
+
        if (s->cert != NULL)
                return(s->cert->key->x509);
        else
Index: ssl/ssl_locl.h
===================================================================
RCS file: /v/openssl/cvs/openssl/ssl/ssl_locl.h,v
retrieving revision 1.141
diff -u -r1.141 ssl_locl.h
--- ssl/ssl_locl.h      18 Jun 2012 12:56:59 -0000      1.141
+++ ssl/ssl_locl.h      19 Jun 2012 10:59:34 -0000
@@ -1132,6 +1132,7 @@
 unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned 
char *limit); 
 unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned 
char *limit); 
 int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **data, unsigned char 
*d, int n);
+int ssl_check_clienthello_tlsext_late(SSL *s);
 int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **data, unsigned char 
*d, int n);
 int ssl_prepare_clienthello_tlsext(SSL *s);
 int ssl_prepare_serverhello_tlsext(SSL *s);
Index: ssl/t1_lib.c
===================================================================
RCS file: /v/openssl/cvs/openssl/ssl/t1_lib.c,v
retrieving revision 1.123
diff -u -r1.123 t1_lib.c
--- ssl/t1_lib.c        11 Jun 2012 09:23:55 -0000      1.123
+++ ssl/t1_lib.c        19 Jun 2012 10:59:34 -0000
@@ -123,7 +123,7 @@
 static int tls_decrypt_ticket(SSL *s, const unsigned char *tick, int ticklen,
                                const unsigned char *sess_id, int sesslen,
                                SSL_SESSION **psess);
-static int ssl_check_clienthello_tlsext(SSL *s);
+static int ssl_check_clienthello_tlsext_early(SSL *s);
 int ssl_check_serverhello_tlsext(SSL *s);
 #endif
 
@@ -1846,7 +1846,7 @@
                return 0;
                }
 
-       if (ssl_check_clienthello_tlsext(s) <= 0) 
+       if (ssl_check_clienthello_tlsext_early(s) <= 0) 
                {
                
SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_TLSEXT,SSL_R_CLIENTHELLO_TLSEXT);
                return 0;
@@ -2247,7 +2247,7 @@
        return 1;
        }
 
-static int ssl_check_clienthello_tlsext(SSL *s)
+static int ssl_check_clienthello_tlsext_early(SSL *s)
        {
        int ret=SSL_TLSEXT_ERR_NOACK;
        int al = SSL_AD_UNRECOGNIZED_NAME;
@@ -2266,42 +2266,11 @@
        else if (s->initial_ctx != NULL && 
s->initial_ctx->tlsext_servername_callback != 0)             
                ret = s->initial_ctx->tlsext_servername_callback(s, &al, 
s->initial_ctx->tlsext_servername_arg);
 
-       /* If status request then ask callback what to do.
-        * Note: this must be called after servername callbacks in case 
-        * the certificate has changed.
-        */
-       if ((s->tlsext_status_type != -1) && s->ctx && s->ctx->tlsext_status_cb)
-               {
-               int r;
-               r = s->ctx->tlsext_status_cb(s, s->ctx->tlsext_status_arg);
-               switch (r)
-                       {
-                       /* We don't want to send a status request response */
-                       case SSL_TLSEXT_ERR_NOACK:
-                               s->tlsext_status_expected = 0;
-                               break;
-                       /* status request response should be sent */
-                       case SSL_TLSEXT_ERR_OK:
-                               if (s->tlsext_ocsp_resp)
-                                       s->tlsext_status_expected = 1;
-                               else
-                                       s->tlsext_status_expected = 0;
-                               break;
-                       /* something bad happened */
-                       case SSL_TLSEXT_ERR_ALERT_FATAL:
-                               ret = SSL_TLSEXT_ERR_ALERT_FATAL;
-                               al = SSL_AD_INTERNAL_ERROR;
-                               goto err;
-                       }
-               }
-       else
-               s->tlsext_status_expected = 0;
-
 #ifdef TLSEXT_TYPE_opaque_prf_input
        {
                /* This sort of belongs into ssl_prepare_serverhello_tlsext(),
                 * but we might be sending an alert in response to the client 
hello,
-                * so this has to happen here in 
ssl_check_clienthello_tlsext(). */
+                * so this has to happen here in 
ssl_check_clienthello_tlsext_early(). */
 
                int r = 1;
        
@@ -2353,8 +2322,8 @@
                        }
        }
 
-#endif
  err:
+#endif
        switch (ret)
                {
                case SSL_TLSEXT_ERR_ALERT_FATAL:
@@ -2372,6 +2341,59 @@
                }
        }
 
+int ssl_check_clienthello_tlsext_late(SSL *s)
+       {
+       int ret=SSL_TLSEXT_ERR_OK;
+       int al;
+
+       /* If status request then ask callback what to do.
+        * Note: this must be called after servername callbacks in case 
+        * the certificate has changed, and must be called after the cipher
+        * has been chosen because this may influence which certificate is sent
+        */
+       if ((s->tlsext_status_type != -1) && s->ctx && s->ctx->tlsext_status_cb)
+               {
+               int r;
+               r = s->ctx->tlsext_status_cb(s, s->ctx->tlsext_status_arg);
+               switch (r)
+                       {
+                       /* We don't want to send a status request response */
+                       case SSL_TLSEXT_ERR_NOACK:
+                               s->tlsext_status_expected = 0;
+                               break;
+                       /* status request response should be sent */
+                       case SSL_TLSEXT_ERR_OK:
+                               if (s->tlsext_ocsp_resp)
+                                       s->tlsext_status_expected = 1;
+                               else
+                                       s->tlsext_status_expected = 0;
+                               break;
+                       /* something bad happened */
+                       case SSL_TLSEXT_ERR_ALERT_FATAL:
+                               ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+                               al = SSL_AD_INTERNAL_ERROR;
+                               goto err;
+                       }
+               }
+       else
+               s->tlsext_status_expected = 0;
+
+ err:
+       switch (ret)
+               {
+               case SSL_TLSEXT_ERR_ALERT_FATAL:
+                       ssl3_send_alert(s,SSL3_AL_FATAL,al); 
+                       return -1;
+
+               case SSL_TLSEXT_ERR_ALERT_WARNING:
+                       ssl3_send_alert(s,SSL3_AL_WARNING,al);
+                       return 1; 
+
+               default:
+                       return 1;
+               }
+       }
+
 int ssl_check_serverhello_tlsext(SSL *s)
        {
        int ret=SSL_TLSEXT_ERR_NOACK;

Reply via email to