Fix for PEM_X509_INFO_read_bio.
[openssl.git] / ssl / t1_lib.c
index 19769c5888a29cd99f3aadb2d8ef1b7c3939a532..ee376de545ce1a39ec3fdb936e6e668e09e48ba7 100644 (file)
@@ -1404,6 +1404,18 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
                }
 #endif
 
                }
 #endif
 
+       if (s->alpn_client_proto_list && !s->s3->tmp.finish_md_len)
+               {
+               if ((size_t)(limit - ret) < 6 + s->alpn_client_proto_list_len)
+                       return NULL;
+               s2n(TLSEXT_TYPE_application_layer_protocol_negotiation,ret);
+               s2n(2 + s->alpn_client_proto_list_len,ret);
+               s2n(s->alpn_client_proto_list_len,ret);
+               memcpy(ret, s->alpn_client_proto_list,
+                      s->alpn_client_proto_list_len);
+               ret += s->alpn_client_proto_list_len;
+               }
+
         if(SSL_get_srtp_profiles(s))
                 {
                 int el;
         if(SSL_get_srtp_profiles(s))
                 {
                 int el;
@@ -1455,10 +1467,19 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
                        unsigned short outlen = 0;
 
                        record = &s->ctx->custom_cli_ext_records[i];
                        unsigned short outlen = 0;
 
                        record = &s->ctx->custom_cli_ext_records[i];
-                       if (record->fn1 && !record->fn1(s, record->ext_type,
+                       /* NULL callback sends empty extension */ 
+                       /* -1 from callback omits extension */
+                       if (record->fn1)
+                               {
+                               int cb_retval = 0;
+                               cb_retval = record->fn1(s, record->ext_type,
                                                        &out, &outlen,
                                                        &out, &outlen,
-                                                       record->arg))
-                               return NULL;
+                                                       record->arg);
+                               if (cb_retval == 0)
+                                       return NULL; /* error */
+                               if (cb_retval == -1)
+                                       continue; /* skip this extension */
+                               }
                        if (limit < ret + 4 + outlen)
                                return NULL;
                        s2n(record->ext_type, ret);
                        if (limit < ret + 4 + outlen)
                                return NULL;
                        s2n(record->ext_type, ret);
@@ -1751,11 +1772,18 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
                                        {
                                        const unsigned char *out = NULL;
                                        unsigned short outlen = 0;
                                        {
                                        const unsigned char *out = NULL;
                                        unsigned short outlen = 0;
-                                       if (record->fn2
-                                           && !record->fn2(s, record->ext_type,
-                                                           &out, &outlen,
-                                                           record->arg))
-                                               return NULL;
+                                       int cb_retval = 0;
+
+                                       /* NULL callback or -1 omits extension */
+                                       if (!record->fn2)
+                                               break;
+                                       cb_retval = record->fn2(s, record->ext_type,
+                                                               &out, &outlen,
+                                                               record->arg);
+                                       if (cb_retval == 0)
+                                               return NULL; /* error */
+                                       if (cb_retval == -1)
+                                               break; /* skip this extension */
                                        if (limit < ret + 4 + outlen)
                                                return NULL;
                                        s2n(record->ext_type, ret);
                                        if (limit < ret + 4 + outlen)
                                                return NULL;
                                        s2n(record->ext_type, ret);
@@ -1768,6 +1796,21 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
                        }
                }
 
                        }
                }
 
+       if (s->s3->alpn_selected)
+               {
+               const unsigned char *selected = s->s3->alpn_selected;
+               unsigned len = s->s3->alpn_selected_len;
+
+               if ((long)(limit - ret - 4 - 2 - 1 - len) < 0)
+                       return NULL;
+               s2n(TLSEXT_TYPE_application_layer_protocol_negotiation,ret);
+               s2n(3 + len,ret);
+               s2n(1 + len,ret);
+               *ret++ = len;
+               memcpy(ret, selected, len);
+               ret += len;
+               }
+
        if ((extdatalen = ret-p-2)== 0) 
                return p;
 
        if ((extdatalen = ret-p-2)== 0) 
                return p;
 
@@ -1775,6 +1818,76 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
        return ret;
        }
 
        return ret;
        }
 
+/* tls1_alpn_handle_client_hello is called to process the ALPN extension in a
+ * ClientHello.
+ *   data: the contents of the extension, not including the type and length.
+ *   data_len: the number of bytes in |data|
+ *   al: a pointer to the alert value to send in the event of a non-zero
+ *       return.
+ *
+ *   returns: 0 on success. */
+static int tls1_alpn_handle_client_hello(SSL *s, const unsigned char *data,
+                                        unsigned data_len, int *al)
+       {
+       unsigned i;
+       unsigned proto_len;
+       const unsigned char *selected;
+       unsigned char selected_len;
+       int r;
+
+       if (s->ctx->alpn_select_cb == NULL)
+               return 0;
+
+       if (data_len < 2)
+               goto parse_error;
+
+       /* data should contain a uint16 length followed by a series of 8-bit,
+        * length-prefixed strings. */
+       i = ((unsigned) data[0]) << 8 |
+           ((unsigned) data[1]);
+       data_len -= 2;
+       data += 2;
+       if (data_len != i)
+               goto parse_error;
+
+       if (data_len < 2)
+               goto parse_error;
+
+       for (i = 0; i < data_len;)
+               {
+               proto_len = data[i];
+               i++;
+
+               if (proto_len == 0)
+                       goto parse_error;
+
+               if (i + proto_len < i || i + proto_len > data_len)
+                       goto parse_error;
+
+               i += proto_len;
+               }
+
+       r = s->ctx->alpn_select_cb(s, &selected, &selected_len, data, data_len,
+                                  s->ctx->alpn_select_cb_arg);
+       if (r == SSL_TLSEXT_ERR_OK) {
+               if (s->s3->alpn_selected)
+                       OPENSSL_free(s->s3->alpn_selected);
+               s->s3->alpn_selected = OPENSSL_malloc(selected_len);
+               if (!s->s3->alpn_selected)
+                       {
+                       *al = SSL_AD_INTERNAL_ERROR;
+                       return -1;
+                       }
+               memcpy(s->s3->alpn_selected, selected, selected_len);
+               s->s3->alpn_selected_len = selected_len;
+       }
+       return 0;
+
+parse_error:
+       *al = SSL_AD_DECODE_ERROR;
+       return -1;
+       }
+
 static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al) 
        {       
        unsigned short type;
 static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al) 
        {       
        unsigned short type;
@@ -1790,6 +1903,20 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
        s->s3->next_proto_neg_seen = 0;
 #endif
 
        s->s3->next_proto_neg_seen = 0;
 #endif
 
+       if (s->s3->alpn_selected)
+               {
+               OPENSSL_free(s->s3->alpn_selected);
+               s->s3->alpn_selected = NULL;
+               }
+
+       /* Clear observed custom extensions */
+       s->s3->tlsext_custom_types_count = 0;
+       if (s->s3->tlsext_custom_types != NULL)
+               {
+               OPENSSL_free(s->s3->tlsext_custom_types);
+               s->s3->tlsext_custom_types = NULL;
+               }               
+
 #ifndef OPENSSL_NO_HEARTBEATS
        s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED |
                               SSL_TLSEXT_HB_DONT_SEND_REQUESTS);
 #ifndef OPENSSL_NO_HEARTBEATS
        s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED |
                               SSL_TLSEXT_HB_DONT_SEND_REQUESTS);
@@ -2245,7 +2372,8 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
 #endif
 #ifndef OPENSSL_NO_NEXTPROTONEG
                else if (type == TLSEXT_TYPE_next_proto_neg &&
 #endif
 #ifndef OPENSSL_NO_NEXTPROTONEG
                else if (type == TLSEXT_TYPE_next_proto_neg &&
-                        s->s3->tmp.finish_md_len == 0)
+                        s->s3->tmp.finish_md_len == 0 &&
+                        s->s3->alpn_selected == NULL)
                        {
                        /* We shouldn't accept this extension on a
                         * renegotiation.
                        {
                        /* We shouldn't accept this extension on a
                         * renegotiation.
@@ -2266,6 +2394,16 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
                        }
 #endif
 
                        }
 #endif
 
+               else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation &&
+                        s->ctx->alpn_select_cb &&
+                        s->s3->tmp.finish_md_len == 0)
+                       {
+                       if (tls1_alpn_handle_client_hello(s, data, size, al) != 0)
+                               return 0;
+                       /* ALPN takes precedence over NPN. */
+                       s->s3->next_proto_neg_seen = 0;
+                       }
+
                /* session ticket processed earlier */
                else if (type == TLSEXT_TYPE_use_srtp)
                         {
                /* session ticket processed earlier */
                else if (type == TLSEXT_TYPE_use_srtp)
                         {
@@ -2348,19 +2486,19 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
                                record = &s->ctx->custom_srv_ext_records[i];
                                if (type == record->ext_type)
                                        {
                                record = &s->ctx->custom_srv_ext_records[i];
                                if (type == record->ext_type)
                                        {
-                                       /* Error on duplicate TLS Extensions */
                                        size_t j;
 
                                        size_t j;
 
+                                       /* Error on duplicate TLS Extensions */
                                        for (j = 0; j < s->s3->tlsext_custom_types_count; j++)
                                                {
                                        for (j = 0; j < s->s3->tlsext_custom_types_count; j++)
                                                {
-                                               if (s->s3->tlsext_custom_types[j] == type)
+                                               if (type == s->s3->tlsext_custom_types[j])
                                                        {
                                                        *al = TLS1_AD_DECODE_ERROR;
                                                        return 0;
                                                        }
                                                }
 
                                                        {
                                                        *al = TLS1_AD_DECODE_ERROR;
                                                        return 0;
                                                        }
                                                }
 
-                                       /* Callback */
+                                       /* NULL callback still notes the extension */ 
                                        if (record->fn1 && !record->fn1(s, type, data, size, al, record->arg))
                                                return 0;
                                                
                                        if (record->fn1 && !record->fn1(s, type, data, size, al, record->arg))
                                                return 0;
                                                
@@ -2368,7 +2506,7 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
                                        s->s3->tlsext_custom_types_count++;
                                        s->s3->tlsext_custom_types = OPENSSL_realloc(
                                                        s->s3->tlsext_custom_types,
                                        s->s3->tlsext_custom_types_count++;
                                        s->s3->tlsext_custom_types = OPENSSL_realloc(
                                                        s->s3->tlsext_custom_types,
-                                                       s->s3->tlsext_custom_types_count*2);
+                                                       s->s3->tlsext_custom_types_count * 2);
                                        if (s->s3->tlsext_custom_types == NULL)
                                                {
                                                s->s3->tlsext_custom_types = 0;
                                        if (s->s3->tlsext_custom_types == NULL)
                                                {
                                                s->s3->tlsext_custom_types = 0;
@@ -2376,7 +2514,7 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
                                                return 0;
                                                }
                                        s->s3->tlsext_custom_types[
                                                return 0;
                                                }
                                        s->s3->tlsext_custom_types[
-                                                       s->s3->tlsext_custom_types_count-1] = type;
+                                                       s->s3->tlsext_custom_types_count - 1] = type;
                                        }                                               
                                }
                        }
                                        }                                               
                                }
                        }
@@ -2455,6 +2593,12 @@ static int ssl_scan_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char
        s->s3->next_proto_neg_seen = 0;
 #endif
 
        s->s3->next_proto_neg_seen = 0;
 #endif
 
+       if (s->s3->alpn_selected)
+               {
+               OPENSSL_free(s->s3->alpn_selected);
+               s->s3->alpn_selected = NULL;
+               }
+
 #ifndef OPENSSL_NO_HEARTBEATS
        s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED |
                               SSL_TLSEXT_HB_DONT_SEND_REQUESTS);
 #ifndef OPENSSL_NO_HEARTBEATS
        s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED |
                               SSL_TLSEXT_HB_DONT_SEND_REQUESTS);
@@ -2617,6 +2761,52 @@ static int ssl_scan_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char
                        s->s3->next_proto_neg_seen = 1;
                        }
 #endif
                        s->s3->next_proto_neg_seen = 1;
                        }
 #endif
+
+               else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation)
+                       {
+                       unsigned len;
+
+                       /* We must have requested it. */
+                       if (s->alpn_client_proto_list == NULL)
+                               {
+                               *al = TLS1_AD_UNSUPPORTED_EXTENSION;
+                               return 0;
+                               }
+                       if (size < 4)
+                               {
+                               *al = TLS1_AD_DECODE_ERROR;
+                               return 0;
+                               }
+                       /* The extension data consists of:
+                        *   uint16 list_length
+                        *   uint8 proto_length;
+                        *   uint8 proto[proto_length]; */
+                       len = data[0];
+                       len <<= 8;
+                       len |= data[1];
+                       if (len != (unsigned) size - 2)
+                               {
+                               *al = TLS1_AD_DECODE_ERROR;
+                               return 0;
+                               }
+                       len = data[2];
+                       if (len != (unsigned) size - 3)
+                               {
+                               *al = TLS1_AD_DECODE_ERROR;
+                               return 0;
+                               }
+                       if (s->s3->alpn_selected)
+                               OPENSSL_free(s->s3->alpn_selected);
+                       s->s3->alpn_selected = OPENSSL_malloc(len);
+                       if (!s->s3->alpn_selected)
+                               {
+                               *al = TLS1_AD_INTERNAL_ERROR;
+                               return 0;
+                               }
+                       memcpy(s->s3->alpn_selected, data + 3, len);
+                       s->s3->alpn_selected_len = len;
+                       }
+
                else if (type == TLSEXT_TYPE_renegotiate)
                        {
                        if(!ssl_parse_serverhello_renegotiate_ext(s, data, size, al))
                else if (type == TLSEXT_TYPE_renegotiate)
                        {
                        if(!ssl_parse_serverhello_renegotiate_ext(s, data, size, al))