Add Next Protocol Negotiation.
[openssl.git] / ssl / t1_lib.c
index ef40e5e..39b5e72 100644 (file)
@@ -494,6 +494,18 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
                        i2d_X509_EXTENSIONS(s->tlsext_ocsp_exts, &ret);
                }
 
+#ifndef OPENSSL_NO_NPN
+       if (s->ctx->next_proto_select_cb && !s->s3->tmp.finish_md_len)
+               {
+               /* The client advertises an emtpy extension to indicate its
+                * support for Next Protocol Negotiation */
+               if (limit - ret - 4 < 0)
+                       return NULL;
+               s2n(TLSEXT_TYPE_next_proto_neg,ret);
+               s2n(0,ret);
+               }
+#endif
+
        if ((extdatalen = ret-p-2)== 0) 
                return p;
 
@@ -505,6 +517,9 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
        {
        int extdatalen=0;
        unsigned char *ret = p;
+#ifndef OPENSSL_NO_NPN
+       char next_proto_neg_seen;
+#endif
 
        /* don't add extensions for SSLv3, unless doing secure renegotiation */
        if (s->version == SSL3_VERSION && !s->s3->send_connection_binding)
@@ -618,6 +633,28 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
 
                }
 
+#ifndef OPENSSL_NO_NPN
+       next_proto_neg_seen = s->s3->next_proto_neg_seen;
+       s->s3->next_proto_neg_seen = 0;
+       if (next_proto_neg_seen && s->ctx->next_protos_advertised_cb)
+               {
+               const unsigned char *npa;
+               unsigned int npalen;
+               int r;
+
+               r = s->ctx->next_protos_advertised_cb(s, &npa, &npalen, s->ctx->next_protos_advertised_cb_arg);
+               if (r == SSL_TLSEXT_ERR_OK)
+                       {
+                       if ((long)(limit - ret - 4 - npalen) < 0) return NULL;
+                       s2n(TLSEXT_TYPE_next_proto_neg,ret);
+                       s2n(npalen,ret);
+                       memcpy(ret, npa, npalen);
+                       ret += npalen;
+                       s->s3->next_proto_neg_seen = 1;
+                       }
+               }
+#endif
+
        if ((extdatalen = ret-p-2)== 0) 
                return p;
 
@@ -958,6 +995,28 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
                                else
                                        s->tlsext_status_type = -1;
                        }
+#ifndef OPENSSL_NO_NPN
+               else if (type == TLSEXT_TYPE_next_proto_neg &&
+                         s->s3->tmp.finish_md_len == 0)
+                       {
+                       /* We shouldn't accept this extension on a
+                        * renegotiation.
+                        *
+                        * s->new_session will be set on renegotiation, but we
+                        * probably shouldn't rely that it couldn't be set on
+                        * the initial renegotation too in certain cases (when
+                        * there's some other reason to disallow resuming an
+                        * earlier session -- the current code won't be doing
+                        * anything like that, but this might change).
+
+                        * A valid sign that there's been a previous handshake
+                        * in this connection is if s->s3->tmp.finish_md_len >
+                        * 0.  (We are talking about a check that will happen
+                        * in the Hello protocol round, well before a new
+                        * Finished message could have been computed.) */
+                       s->s3->next_proto_neg_seen = 1;
+                       }
+#endif
 
                /* session ticket processed earlier */
                data+=size;
@@ -981,6 +1040,26 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
        return 1;
        }
 
+#ifndef OPENSSL_NO_NPN
+/* ssl_next_proto_validate validates a Next Protocol Negotiation block. No
+ * elements of zero length are allowed and the set of elements must exactly fill
+ * the length of the block. */
+static char ssl_next_proto_validate(unsigned char *d, unsigned len)
+       {
+       unsigned int off = 0;
+
+       while (off < len)
+               {
+               if (d[off] == 0)
+                       return 0;
+               off += d[off];
+               off++;
+               }
+
+       return off == len;
+       }
+#endif
+
 int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al)
        {
        unsigned short length;
@@ -1115,6 +1194,39 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
                        /* Set flag to expect CertificateStatus message */
                        s->tlsext_status_expected = 1;
                        }
+#ifndef OPENSSL_NO_NPN
+               else if (type == TLSEXT_TYPE_next_proto_neg)
+                       {
+                       unsigned char *selected;
+                       unsigned char selected_len;
+
+                       /* We must have requested it. */
+                       if ((s->ctx->next_proto_select_cb == NULL))
+                               {
+                               *al = TLS1_AD_UNSUPPORTED_EXTENSION;
+                               return 0;
+                               }
+                       /* The data must be valid */
+                       if (!ssl_next_proto_validate(data, size))
+                               {
+                               *al = TLS1_AD_DECODE_ERROR;
+                               return 0;
+                               }
+                       if (s->ctx->next_proto_select_cb(s, &selected, &selected_len, data, size, s->ctx->next_proto_select_cb_arg) != SSL_TLSEXT_ERR_OK)
+                               {
+                               *al = TLS1_AD_INTERNAL_ERROR;
+                               return 0;
+                               }
+                       s->next_proto_negotiated = OPENSSL_malloc(selected_len);
+                       if (!s->next_proto_negotiated)
+                               {
+                               *al = TLS1_AD_INTERNAL_ERROR;
+                               return 0;
+                               }
+                       memcpy(s->next_proto_negotiated, selected, selected_len);
+                       s->next_proto_negotiated_len = selected_len;
+                       }
+#endif
                else if (type == TLSEXT_TYPE_renegotiate)
                        {
                        if(!ssl_parse_serverhello_renegotiate_ext(s, data, size, al))